From 0d09e41a51aa0752b1ce525ce084f7cd210e461b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 17:06:20 +0100 Subject: hw: move headers to include/ Many of these should be cleaned up with proper qdev-/QOM-ification. Right now there are many catch-all headers in include/hw/ARCH depending on cpu.h, and this makes it necessary to compile these files per-target. However, fixing this does not belong in these patches. Signed-off-by: Paolo Bonzini --- include/block/scsi.h | 307 +++++ include/exec/memory-internal.h | 2 +- include/hw/acpi/acpi.h | 157 +++ include/hw/acpi/ich9.h | 52 + include/hw/arm.h | 70 ++ include/hw/arm/devices.h | 70 ++ include/hw/arm/exynos4210.h | 137 +++ include/hw/arm/imx.h | 34 + include/hw/arm/omap.h | 1015 +++++++++++++++ include/hw/arm/primecell.h | 12 + include/hw/arm/pxa.h | 191 +++ include/hw/arm/sharpsl.h | 17 + include/hw/arm/soc_dma.h | 116 ++ include/hw/audio/audio.h | 25 + include/hw/audio/pcspk.h | 45 + include/hw/block/block.h | 79 ++ include/hw/block/fdc.h | 24 + include/hw/block/flash.h | 64 + include/hw/boards.h | 53 + include/hw/bt.h | 2190 +++++++++++++++++++++++++++++++++ include/hw/char/escc.h | 13 + include/hw/char/serial.h | 101 ++ include/hw/cris/etraxfs.h | 51 + include/hw/cris/etraxfs_dma.h | 34 + include/hw/elf_ops.h | 309 +++++ include/hw/empty_slot.h | 7 + include/hw/hw.h | 76 ++ include/hw/i2c/i2c.h | 88 ++ include/hw/i2c/pm_smbus.h | 20 + include/hw/i2c/smbus.h | 83 ++ include/hw/i386/apic-msidef.h | 30 + include/hw/i386/apic.h | 32 + include/hw/i386/apic_internal.h | 149 +++ include/hw/i386/ich9.h | 219 ++++ include/hw/i386/ioapic.h | 27 + include/hw/i386/ioapic_internal.h | 102 ++ include/hw/i386/pc.h | 246 ++++ include/hw/i386/smbios.h | 162 +++ include/hw/ide.h | 32 + include/hw/input/adb.h | 87 ++ include/hw/input/hid.h | 83 ++ include/hw/input/ps2.h | 38 + include/hw/irq.h | 57 + include/hw/isa/apm.h | 25 + include/hw/isa/i8259_internal.h | 82 ++ include/hw/isa/isa.h | 104 ++ include/hw/isa/pc87312.h | 68 + include/hw/isa/vt82c686.h | 11 + include/hw/kvm/clock.h | 24 + include/hw/lm32/lm32_juart.h | 11 + include/hw/lm32/lm32_pic.h | 14 + include/hw/loader.h | 52 + include/hw/m68k/mcf.h | 30 + include/hw/mips/bios.h | 8 + include/hw/mips/cpudevs.h | 15 + include/hw/mips/mips.h | 29 + include/hw/misc/tmp105_regs.h | 50 + include/hw/nvram/eeprom93xx.h | 40 + include/hw/nvram/fw_cfg.h | 71 ++ include/hw/pci-host/apb.h | 10 + include/hw/pci-host/pam.h | 97 ++ include/hw/pci-host/ppce500.h | 9 + include/hw/pci-host/q35.h | 150 +++ include/hw/pci-host/spapr.h | 92 ++ include/hw/pci/msi.h | 50 + include/hw/pci/msix.h | 46 + include/hw/pci/pci.h | 725 +++++++++++ include/hw/pci/pci_bridge.h | 65 + include/hw/pci/pci_bus.h | 78 ++ include/hw/pci/pci_host.h | 61 + include/hw/pci/pci_ids.h | 154 +++ include/hw/pci/pci_regs.h | 717 +++++++++++ include/hw/pci/pcie.h | 143 +++ include/hw/pci/pcie_aer.h | 106 ++ include/hw/pci/pcie_host.h | 54 + include/hw/pci/pcie_port.h | 51 + include/hw/pci/pcie_regs.h | 156 +++ include/hw/pci/shpc.h | 48 + include/hw/pci/slotid_cap.h | 11 + include/hw/pcmcia.h | 56 + include/hw/ppc/mac_dbdma.h | 48 + include/hw/ppc/openpic.h | 18 + include/hw/ppc/ppc.h | 99 ++ include/hw/ppc/ppc4xx.h | 64 + include/hw/ppc/spapr.h | 358 ++++++ include/hw/ppc/spapr_vio.h | 136 ++ include/hw/ppc/xics.h | 41 + include/hw/ptimer.h | 39 + include/hw/qdev-addr.h | 10 + include/hw/qdev-core.h | 299 +++++ include/hw/qdev-dma.h | 10 + include/hw/qdev-properties.h | 182 +++ include/hw/qdev.h | 8 + include/hw/s390x/event-facility.h | 96 ++ include/hw/s390x/sclp.h | 118 ++ include/hw/scsi/esp.h | 132 ++ include/hw/scsi/scsi.h | 256 ++++ include/hw/sd.h | 80 ++ include/hw/sh4/sh.h | 57 + include/hw/sh4/sh_intc.h | 83 ++ include/hw/sparc/firmware_abi.h | 73 ++ include/hw/sparc/grlib.h | 126 ++ include/hw/sparc/sparc32_dma.h | 12 + include/hw/sparc/sun4m.h | 36 + include/hw/ssi.h | 93 ++ include/hw/stream.h | 31 + include/hw/sysbus.h | 85 ++ include/hw/timer/hpet.h | 74 ++ include/hw/timer/i8254.h | 68 + include/hw/timer/i8254_internal.h | 85 ++ include/hw/timer/m48t59.h | 34 + include/hw/timer/mc146818rtc.h | 11 + include/hw/timer/mc146818rtc_regs.h | 67 + include/hw/unicore32/puv3.h | 49 + include/hw/usb.h | 570 +++++++++ include/hw/virtio/dataplane/hostmem.h | 57 + include/hw/virtio/dataplane/vring.h | 62 + include/hw/virtio/vhost.h | 68 + include/hw/virtio/virtio-9p.h | 24 + include/hw/virtio/virtio-balloon.h | 72 ++ include/hw/virtio/virtio-blk.h | 152 +++ include/hw/virtio/virtio-bus.h | 94 ++ include/hw/virtio/virtio-net.h | 246 ++++ include/hw/virtio/virtio-rng.h | 47 + include/hw/virtio/virtio-scsi.h | 66 + include/hw/virtio/virtio-serial.h | 247 ++++ include/hw/virtio/virtio.h | 292 +++++ include/hw/xen/xen.h | 62 + include/hw/xen/xen_backend.h | 109 ++ include/hw/xen/xen_common.h | 160 +++ include/hw/xilinx.h | 91 ++ include/net/vhost_net.h | 23 + include/sysemu/watchdog.h | 43 + 133 files changed, 15681 insertions(+), 1 deletion(-) create mode 100644 include/block/scsi.h create mode 100644 include/hw/acpi/acpi.h create mode 100644 include/hw/acpi/ich9.h create mode 100644 include/hw/arm.h create mode 100644 include/hw/arm/devices.h create mode 100644 include/hw/arm/exynos4210.h create mode 100644 include/hw/arm/imx.h create mode 100644 include/hw/arm/omap.h create mode 100644 include/hw/arm/primecell.h create mode 100644 include/hw/arm/pxa.h create mode 100644 include/hw/arm/sharpsl.h create mode 100644 include/hw/arm/soc_dma.h create mode 100644 include/hw/audio/audio.h create mode 100644 include/hw/audio/pcspk.h create mode 100644 include/hw/block/block.h create mode 100644 include/hw/block/fdc.h create mode 100644 include/hw/block/flash.h create mode 100644 include/hw/boards.h create mode 100644 include/hw/bt.h create mode 100644 include/hw/char/escc.h create mode 100644 include/hw/char/serial.h create mode 100644 include/hw/cris/etraxfs.h create mode 100644 include/hw/cris/etraxfs_dma.h create mode 100644 include/hw/elf_ops.h create mode 100644 include/hw/empty_slot.h create mode 100644 include/hw/hw.h create mode 100644 include/hw/i2c/i2c.h create mode 100644 include/hw/i2c/pm_smbus.h create mode 100644 include/hw/i2c/smbus.h create mode 100644 include/hw/i386/apic-msidef.h create mode 100644 include/hw/i386/apic.h create mode 100644 include/hw/i386/apic_internal.h create mode 100644 include/hw/i386/ich9.h create mode 100644 include/hw/i386/ioapic.h create mode 100644 include/hw/i386/ioapic_internal.h create mode 100644 include/hw/i386/pc.h create mode 100644 include/hw/i386/smbios.h create mode 100644 include/hw/ide.h create mode 100644 include/hw/input/adb.h create mode 100644 include/hw/input/hid.h create mode 100644 include/hw/input/ps2.h create mode 100644 include/hw/irq.h create mode 100644 include/hw/isa/apm.h create mode 100644 include/hw/isa/i8259_internal.h create mode 100644 include/hw/isa/isa.h create mode 100644 include/hw/isa/pc87312.h create mode 100644 include/hw/isa/vt82c686.h create mode 100644 include/hw/kvm/clock.h create mode 100644 include/hw/lm32/lm32_juart.h create mode 100644 include/hw/lm32/lm32_pic.h create mode 100644 include/hw/loader.h create mode 100644 include/hw/m68k/mcf.h create mode 100644 include/hw/mips/bios.h create mode 100644 include/hw/mips/cpudevs.h create mode 100644 include/hw/mips/mips.h create mode 100644 include/hw/misc/tmp105_regs.h create mode 100644 include/hw/nvram/eeprom93xx.h create mode 100644 include/hw/nvram/fw_cfg.h create mode 100644 include/hw/pci-host/apb.h create mode 100644 include/hw/pci-host/pam.h create mode 100644 include/hw/pci-host/ppce500.h create mode 100644 include/hw/pci-host/q35.h create mode 100644 include/hw/pci-host/spapr.h create mode 100644 include/hw/pci/msi.h create mode 100644 include/hw/pci/msix.h create mode 100644 include/hw/pci/pci.h create mode 100644 include/hw/pci/pci_bridge.h create mode 100644 include/hw/pci/pci_bus.h create mode 100644 include/hw/pci/pci_host.h create mode 100644 include/hw/pci/pci_ids.h create mode 100644 include/hw/pci/pci_regs.h create mode 100644 include/hw/pci/pcie.h create mode 100644 include/hw/pci/pcie_aer.h create mode 100644 include/hw/pci/pcie_host.h create mode 100644 include/hw/pci/pcie_port.h create mode 100644 include/hw/pci/pcie_regs.h create mode 100644 include/hw/pci/shpc.h create mode 100644 include/hw/pci/slotid_cap.h create mode 100644 include/hw/pcmcia.h create mode 100644 include/hw/ppc/mac_dbdma.h create mode 100644 include/hw/ppc/openpic.h create mode 100644 include/hw/ppc/ppc.h create mode 100644 include/hw/ppc/ppc4xx.h create mode 100644 include/hw/ppc/spapr.h create mode 100644 include/hw/ppc/spapr_vio.h create mode 100644 include/hw/ppc/xics.h create mode 100644 include/hw/ptimer.h create mode 100644 include/hw/qdev-addr.h create mode 100644 include/hw/qdev-core.h create mode 100644 include/hw/qdev-dma.h create mode 100644 include/hw/qdev-properties.h create mode 100644 include/hw/qdev.h create mode 100644 include/hw/s390x/event-facility.h create mode 100644 include/hw/s390x/sclp.h create mode 100644 include/hw/scsi/esp.h create mode 100644 include/hw/scsi/scsi.h create mode 100644 include/hw/sd.h create mode 100644 include/hw/sh4/sh.h create mode 100644 include/hw/sh4/sh_intc.h create mode 100644 include/hw/sparc/firmware_abi.h create mode 100644 include/hw/sparc/grlib.h create mode 100644 include/hw/sparc/sparc32_dma.h create mode 100644 include/hw/sparc/sun4m.h create mode 100644 include/hw/ssi.h create mode 100644 include/hw/stream.h create mode 100644 include/hw/sysbus.h create mode 100644 include/hw/timer/hpet.h create mode 100644 include/hw/timer/i8254.h create mode 100644 include/hw/timer/i8254_internal.h create mode 100644 include/hw/timer/m48t59.h create mode 100644 include/hw/timer/mc146818rtc.h create mode 100644 include/hw/timer/mc146818rtc_regs.h create mode 100644 include/hw/unicore32/puv3.h create mode 100644 include/hw/usb.h create mode 100644 include/hw/virtio/dataplane/hostmem.h create mode 100644 include/hw/virtio/dataplane/vring.h create mode 100644 include/hw/virtio/vhost.h create mode 100644 include/hw/virtio/virtio-9p.h create mode 100644 include/hw/virtio/virtio-balloon.h create mode 100644 include/hw/virtio/virtio-blk.h create mode 100644 include/hw/virtio/virtio-bus.h create mode 100644 include/hw/virtio/virtio-net.h create mode 100644 include/hw/virtio/virtio-rng.h create mode 100644 include/hw/virtio/virtio-scsi.h create mode 100644 include/hw/virtio/virtio-serial.h create mode 100644 include/hw/virtio/virtio.h create mode 100644 include/hw/xen/xen.h create mode 100644 include/hw/xen/xen_backend.h create mode 100644 include/hw/xen/xen_common.h create mode 100644 include/hw/xilinx.h create mode 100644 include/net/vhost_net.h create mode 100644 include/sysemu/watchdog.h (limited to 'include') diff --git a/include/block/scsi.h b/include/block/scsi.h new file mode 100644 index 0000000000..9ab045b613 --- /dev/null +++ b/include/block/scsi.h @@ -0,0 +1,307 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ +#ifndef HW_SCSI_DEFS_H +#define HW_SCSI_DEFS_H 1 + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REWIND 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define INITIALIZE_ELEMENT_STATUS 0x07 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SET_CAPACITY 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define LOAD_UNLOAD 0x1b +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e +#define READ_CAPACITY_10 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define LOCATE_10 0x2b +#define POSITION_TO_ELEMENT 0x2b +#define WRITE_VERIFY_10 0x2e +#define VERIFY_10 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define INITIALIZE_ELEMENT_STATUS_WITH_RANGE 0x37 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG_10 0x3e +#define WRITE_LONG_10 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME_10 0x41 +#define UNMAP 0x42 +#define READ_TOC 0x43 +#define REPORT_DENSITY_SUPPORT 0x44 +#define GET_CONFIGURATION 0x46 +#define SANITIZE 0x48 +#define GET_EVENT_STATUS_NOTIFICATION 0x4a +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define READ_DISC_INFORMATION 0x51 +#define RESERVE_TRACK 0x53 +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define SEND_CUE_SHEET 0x5d +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define VARLENGTH_CDB 0x7f +#define WRITE_FILEMARKS_16 0x80 +#define READ_REVERSE_16 0x81 +#define ALLOW_OVERWRITE 0x82 +#define EXTENDED_COPY 0x83 +#define ATA_PASSTHROUGH_16 0x85 +#define ACCESS_CONTROL_IN 0x86 +#define ACCESS_CONTROL_OUT 0x87 +#define READ_16 0x88 +#define COMPARE_AND_WRITE 0x89 +#define WRITE_16 0x8a +#define WRITE_VERIFY_16 0x8e +#define VERIFY_16 0x8f +#define PRE_FETCH_16 0x90 +#define SPACE_16 0x91 +#define SYNCHRONIZE_CACHE_16 0x91 +#define LOCATE_16 0x92 +#define WRITE_SAME_16 0x93 +#define ERASE_16 0x93 +#define SERVICE_ACTION_IN_16 0x9e +#define WRITE_LONG_16 0x9f +#define REPORT_LUNS 0xa0 +#define ATA_PASSTHROUGH_12 0xa1 +#define MAINTENANCE_IN 0xa3 +#define MAINTENANCE_OUT 0xa4 +#define MOVE_MEDIUM 0xa5 +#define EXCHANGE_MEDIUM 0xa6 +#define SET_READ_AHEAD 0xa7 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define SERVICE_ACTION_IN_12 0xab +#define ERASE_12 0xac +#define READ_DVD_STRUCTURE 0xad +#define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define READ_DEFECT_DATA_12 0xb7 +#define SET_CD_SPEED 0xbb +#define MECHANISM_STATUS 0xbd +#define READ_CD 0xbe +#define SEND_DVD_STRUCTURE 0xbf + +/* + * SERVICE ACTION IN subcodes + */ +#define SAI_READ_CAPACITY_16 0x10 + +/* + * READ POSITION service action codes + */ +#define SHORT_FORM_BLOCK_ID 0x00 +#define SHORT_FORM_VENDOR_SPECIFIC 0x01 +#define LONG_FORM 0x06 +#define EXTENDED_FORM 0x08 + +/* + * SAM Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x02 +#define CONDITION_GOOD 0x04 +#define BUSY 0x08 +#define INTERMEDIATE_GOOD 0x10 +#define INTERMEDIATE_C_GOOD 0x14 +#define RESERVATION_CONFLICT 0x18 +#define COMMAND_TERMINATED 0x22 +#define TASK_SET_FULL 0x28 +#define ACA_ACTIVE 0x30 +#define TASK_ABORTED 0x40 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PRINTER 0x02 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */ +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */ +#define TYPE_OSD 0x11 /* Object-storage Device */ +#define TYPE_WLUN 0x1e /* Well known LUN */ +#define TYPE_NOT_PRESENT 0x1f +#define TYPE_INACTIVE 0x20 +#define TYPE_NO_LUN 0x7f + +/* Mode page codes for mode sense/set */ +#define MODE_PAGE_R_W_ERROR 0x01 +#define MODE_PAGE_HD_GEOMETRY 0x04 +#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05 +#define MODE_PAGE_CACHING 0x08 +#define MODE_PAGE_AUDIO_CTL 0x0e +#define MODE_PAGE_POWER 0x1a +#define MODE_PAGE_FAULT_FAIL 0x1c +#define MODE_PAGE_TO_PROTECT 0x1d +#define MODE_PAGE_CAPABILITIES 0x2a +#define MODE_PAGE_ALLS 0x3f +/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor + * of MODE_PAGE_SENSE_POWER */ +#define MODE_PAGE_CDROM 0x0d + +/* Event notification classes for GET EVENT STATUS NOTIFICATION */ +#define GESN_NO_EVENTS 0 +#define GESN_OPERATIONAL_CHANGE 1 +#define GESN_POWER_MANAGEMENT 2 +#define GESN_EXTERNAL_REQUEST 3 +#define GESN_MEDIA 4 +#define GESN_MULTIPLE_HOSTS 5 +#define GESN_DEVICE_BUSY 6 + +/* Event codes for MEDIA event status notification */ +#define MEC_NO_CHANGE 0 +#define MEC_EJECT_REQUESTED 1 +#define MEC_NEW_MEDIA 2 +#define MEC_MEDIA_REMOVAL 3 /* only for media changers */ +#define MEC_MEDIA_CHANGED 4 /* only for media changers */ +#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */ +#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */ + +#define MS_TRAY_OPEN 1 +#define MS_MEDIA_PRESENT 2 + +/* + * Based on values from but extending CD_MINS + * to the maximum common size allowed by the Orange's Book ATIP + * + * 90 and 99 min CDs are also available but using them as the + * upper limit reduces the effectiveness of the heuristic to + * detect DVDs burned to less than 25% of their maximum capacity + */ + +/* Some generally useful CD-ROM information */ +#define CD_MINS 80 /* max. minutes per CD */ +#define CD_SECS 60 /* seconds per minute */ +#define CD_FRAMES 75 /* frames per second */ +#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ +#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE) +#define CD_MAX_SECTORS (CD_MAX_BYTES / 512) + +/* + * The MMC values are not IDE specific and might need to be moved + * to a common header if they are also needed for the SCSI emulation + */ + +/* Profile list from MMC-6 revision 1 table 91 */ +#define MMC_PROFILE_NONE 0x0000 +#define MMC_PROFILE_CD_ROM 0x0008 +#define MMC_PROFILE_CD_R 0x0009 +#define MMC_PROFILE_CD_RW 0x000A +#define MMC_PROFILE_DVD_ROM 0x0010 +#define MMC_PROFILE_DVD_R_SR 0x0011 +#define MMC_PROFILE_DVD_RAM 0x0012 +#define MMC_PROFILE_DVD_RW_RO 0x0013 +#define MMC_PROFILE_DVD_RW_SR 0x0014 +#define MMC_PROFILE_DVD_R_DL_SR 0x0015 +#define MMC_PROFILE_DVD_R_DL_JR 0x0016 +#define MMC_PROFILE_DVD_RW_DL 0x0017 +#define MMC_PROFILE_DVD_DDR 0x0018 +#define MMC_PROFILE_DVD_PLUS_RW 0x001A +#define MMC_PROFILE_DVD_PLUS_R 0x001B +#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A +#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B +#define MMC_PROFILE_BD_ROM 0x0040 +#define MMC_PROFILE_BD_R_SRM 0x0041 +#define MMC_PROFILE_BD_R_RRM 0x0042 +#define MMC_PROFILE_BD_RE 0x0043 +#define MMC_PROFILE_HDDVD_ROM 0x0050 +#define MMC_PROFILE_HDDVD_R 0x0051 +#define MMC_PROFILE_HDDVD_RAM 0x0052 +#define MMC_PROFILE_HDDVD_RW 0x0053 +#define MMC_PROFILE_HDDVD_R_DL 0x0058 +#define MMC_PROFILE_HDDVD_RW_DL 0x005A +#define MMC_PROFILE_INVALID 0xFFFF + +#endif diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 1da240039d..977467bb57 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -20,7 +20,7 @@ #define MEMORY_INTERNAL_H #ifndef CONFIG_USER_ONLY -#include "hw/xen.h" +#include "hw/xen/xen.h" typedef struct PhysPageEntry PhysPageEntry; diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h new file mode 100644 index 0000000000..e18ef28fd3 --- /dev/null +++ b/include/hw/acpi/acpi.h @@ -0,0 +1,157 @@ +#ifndef QEMU_HW_ACPI_H +#define QEMU_HW_ACPI_H +/* + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ + +/* from linux include/acpi/actype.h */ +/* Default ACPI register widths */ + +#define ACPI_GPE_REGISTER_WIDTH 8 +#define ACPI_PM1_REGISTER_WIDTH 16 +#define ACPI_PM2_REGISTER_WIDTH 8 +#define ACPI_PM_TIMER_WIDTH 32 + +/* PM Timer ticks per second (HZ) */ +#define PM_TIMER_FREQUENCY 3579545 + + +/* ACPI fixed hardware registers */ + +/* from linux/drivers/acpi/acpica/aclocal.h */ +/* Masks used to access the bit_registers */ + +/* PM1x_STS */ +#define ACPI_BITMASK_TIMER_STATUS 0x0001 +#define ACPI_BITMASK_BUS_MASTER_STATUS 0x0010 +#define ACPI_BITMASK_GLOBAL_LOCK_STATUS 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200 +#define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ +#define ACPI_BITMASK_WAKE_STATUS 0x8000 + +#define ACPI_BITMASK_ALL_FIXED_STATUS (\ + ACPI_BITMASK_TIMER_STATUS | \ + ACPI_BITMASK_BUS_MASTER_STATUS | \ + ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ + ACPI_BITMASK_POWER_BUTTON_STATUS | \ + ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ + ACPI_BITMASK_RT_CLOCK_STATUS | \ + ACPI_BITMASK_WAKE_STATUS) + +/* PM1x_EN */ +#define ACPI_BITMASK_TIMER_ENABLE 0x0001 +#define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200 +#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */ + +/* PM1x_CNT */ +#define ACPI_BITMASK_SCI_ENABLE 0x0001 +#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002 +#define ACPI_BITMASK_GLOBAL_LOCK_RELEASE 0x0004 +#define ACPI_BITMASK_SLEEP_TYPE 0x1C00 +#define ACPI_BITMASK_SLEEP_ENABLE 0x2000 + +/* PM2_CNT */ +#define ACPI_BITMASK_ARB_DISABLE 0x0001 + +/* structs */ +typedef struct ACPIPMTimer ACPIPMTimer; +typedef struct ACPIPM1EVT ACPIPM1EVT; +typedef struct ACPIPM1CNT ACPIPM1CNT; +typedef struct ACPIGPE ACPIGPE; +typedef struct ACPIREGS ACPIREGS; + +typedef void (*acpi_update_sci_fn)(ACPIREGS *ar); + +struct ACPIPMTimer { + QEMUTimer *timer; + MemoryRegion io; + int64_t overflow_time; + + acpi_update_sci_fn update_sci; +}; + +struct ACPIPM1EVT { + MemoryRegion io; + uint16_t sts; + uint16_t en; + acpi_update_sci_fn update_sci; +}; + +struct ACPIPM1CNT { + MemoryRegion io; + uint16_t cnt; + uint8_t s4_val; +}; + +struct ACPIGPE { + uint8_t len; + + uint8_t *sts; + uint8_t *en; +}; + +struct ACPIREGS { + ACPIPMTimer tmr; + ACPIGPE gpe; + struct { + ACPIPM1EVT evt; + ACPIPM1CNT cnt; + } pm1; + Notifier wakeup; +}; + +/* PM_TMR */ +void acpi_pm_tmr_update(ACPIREGS *ar, bool enable); +void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar); +void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, + MemoryRegion *parent); +void acpi_pm_tmr_reset(ACPIREGS *ar); + +#include "qemu/timer.h" +static inline int64_t acpi_pm_tmr_get_clock(void) +{ + return muldiv64(qemu_get_clock_ns(vm_clock), PM_TIMER_FREQUENCY, + get_ticks_per_sec()); +} + +/* PM1a_EVT: piix and ich9 don't implement PM1b. */ +uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar); +void acpi_pm1_evt_power_down(ACPIREGS *ar); +void acpi_pm1_evt_reset(ACPIREGS *ar); +void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, + MemoryRegion *parent); + +/* PM1a_CNT: piix and ich9 don't implement PM1b CNT. */ +void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val); +void acpi_pm1_cnt_update(ACPIREGS *ar, + bool sci_enable, bool sci_disable); +void acpi_pm1_cnt_reset(ACPIREGS *ar); + +/* GPE0 */ +void acpi_gpe_init(ACPIREGS *ar, uint8_t len); +void acpi_gpe_reset(ACPIREGS *ar); + +void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val); +uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr); + +#endif /* !QEMU_HW_ACPI_H */ diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h new file mode 100644 index 0000000000..85b82ee50c --- /dev/null +++ b/include/hw/acpi/ich9.h @@ -0,0 +1,52 @@ +/* + * QEMU GMCH/ICH9 LPC PM Emulation + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef HW_ACPI_ICH9_H +#define HW_ACPI_ICH9_H + +#include "hw/acpi/acpi.h" + +typedef struct ICH9LPCPMRegs { + /* + * In ich9 spec says that pm1_cnt register is 32bit width and + * that the upper 16bits are reserved and unused. + * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t. + */ + ACPIREGS acpi_regs; + + MemoryRegion io; + MemoryRegion io_gpe; + MemoryRegion io_smi; + + uint32_t smi_en; + uint32_t smi_sts; + + qemu_irq irq; /* SCI */ + + uint32_t pm_io_base; + Notifier powerdown_notifier; +} ICH9LPCPMRegs; + +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, + qemu_irq sci_irq, qemu_irq cmos_s3_resume); +void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); +extern const VMStateDescription vmstate_ich9_pm; + +#endif /* HW_ACPI_ICH9_H */ diff --git a/include/hw/arm.h b/include/hw/arm.h new file mode 100644 index 0000000000..7b2b02daaf --- /dev/null +++ b/include/hw/arm.h @@ -0,0 +1,70 @@ +/* + * Misc ARM declarations + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + * + */ + +#ifndef ARM_MISC_H +#define ARM_MISC_H 1 + +#include "exec/memory.h" +#include "hw/irq.h" + +/* The CPU is also modelled as an interrupt controller. */ +#define ARM_PIC_CPU_IRQ 0 +#define ARM_PIC_CPU_FIQ 1 +qemu_irq *arm_pic_init_cpu(ARMCPU *cpu); + +/* armv7m.c */ +qemu_irq *armv7m_init(MemoryRegion *address_space_mem, + int flash_size, int sram_size, + const char *kernel_filename, const char *cpu_model); + +/* arm_boot.c */ +struct arm_boot_info { + uint64_t ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; + const char *dtb_filename; + hwaddr loader_start; + /* multicore boards that use the default secondary core boot functions + * need to put the address of the secondary boot code, the boot reg, + * and the GIC address in the next 3 values, respectively. boards that + * have their own boot functions can use these values as they want. + */ + hwaddr smp_loader_start; + hwaddr smp_bootreg_addr; + hwaddr gic_cpu_if_addr; + int nb_cpus; + int board_id; + int (*atag_board)(const struct arm_boot_info *info, void *p); + /* multicore boards that use the default secondary core boot functions + * can ignore these two function calls. If the default functions won't + * work, then write_secondary_boot() should write a suitable blob of + * code mimicking the secondary CPU startup process used by the board's + * boot loader/boot ROM code, and secondary_cpu_reset_hook() should + * perform any necessary CPU reset handling and set the PC for the + * secondary CPUs to point at this boot blob. + */ + void (*write_secondary_boot)(ARMCPU *cpu, + const struct arm_boot_info *info); + void (*secondary_cpu_reset_hook)(ARMCPU *cpu, + const struct arm_boot_info *info); + /* Used internally by arm_boot.c */ + int is_linux; + hwaddr initrd_start; + hwaddr initrd_size; + hwaddr entry; +}; +void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); + +/* Multiplication factor to convert from system clock ticks to qemu timer + ticks. */ +extern int system_clock_scale; + +#endif /* !ARM_MISC_H */ diff --git a/include/hw/arm/devices.h b/include/hw/arm/devices.h new file mode 100644 index 0000000000..c60bcabae3 --- /dev/null +++ b/include/hw/arm/devices.h @@ -0,0 +1,70 @@ +#ifndef QEMU_DEVICES_H +#define QEMU_DEVICES_H + +#include "hw/irq.h" + +/* ??? Not all users of this file can include cpu-common.h. */ +struct MemoryRegion; + +/* Devices that have nowhere better to go. */ + +/* smc91c111.c */ +void smc91c111_init(NICInfo *, uint32_t, qemu_irq); + +/* lan9118.c */ +void lan9118_init(NICInfo *, uint32_t, qemu_irq); + +/* tsc210x.c */ +uWireSlave *tsc2102_init(qemu_irq pint); +uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav); +I2SCodec *tsc210x_codec(uWireSlave *chip); +uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len); +void tsc210x_set_transform(uWireSlave *chip, + MouseTransformInfo *info); +void tsc210x_key_event(uWireSlave *chip, int key, int down); + +/* tsc2005.c */ +void *tsc2005_init(qemu_irq pintdav); +uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len); +void tsc2005_set_transform(void *opaque, MouseTransformInfo *info); + +/* stellaris_input.c */ +void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); + +/* blizzard.c */ +void *s1d13745_init(qemu_irq gpio_int); +void s1d13745_write(void *opaque, int dc, uint16_t value); +void s1d13745_write_block(void *opaque, int dc, + void *buf, size_t len, int pitch); +uint16_t s1d13745_read(void *opaque, int dc); + +/* cbus.c */ +typedef struct { + qemu_irq clk; + qemu_irq dat; + qemu_irq sel; +} CBus; +CBus *cbus_init(qemu_irq dat_out); +void cbus_attach(CBus *bus, void *slave_opaque); + +void *retu_init(qemu_irq irq, int vilma); +void *tahvo_init(qemu_irq irq, int betty); + +void retu_key_event(void *retu, int state); + +/* tc6393xb.c */ +typedef struct TC6393xbState TC6393xbState; +#define TC6393XB_RAM 0x110000 /* amount of ram for Video and USB */ +TC6393xbState *tc6393xb_init(struct MemoryRegion *sysmem, + uint32_t base, qemu_irq irq); +void tc6393xb_gpio_out_set(TC6393xbState *s, int line, + qemu_irq handler); +qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s); +qemu_irq tc6393xb_l3v_get(TC6393xbState *s); + +/* sm501.c */ +void sm501_init(struct MemoryRegion *address_space_mem, uint32_t base, + uint32_t local_mem_bytes, qemu_irq irq, + CharDriverState *chr); + +#endif diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h new file mode 100644 index 0000000000..bb9a1dddc8 --- /dev/null +++ b/include/hw/arm/exynos4210.h @@ -0,0 +1,137 @@ +/* + * Samsung exynos4210 SoC emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * Maksim Kozlov + * Evgeny Voevodin + * Igor Mitsyanko + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + */ + + +#ifndef EXYNOS4210_H_ +#define EXYNOS4210_H_ + +#include "qemu-common.h" +#include "exec/memory.h" + +#define EXYNOS4210_NCPUS 2 + +#define EXYNOS4210_DRAM0_BASE_ADDR 0x40000000 +#define EXYNOS4210_DRAM1_BASE_ADDR 0xa0000000 +#define EXYNOS4210_DRAM_MAX_SIZE 0x60000000 /* 1.5 GB */ + +#define EXYNOS4210_IROM_BASE_ADDR 0x00000000 +#define EXYNOS4210_IROM_SIZE 0x00010000 /* 64 KB */ +#define EXYNOS4210_IROM_MIRROR_BASE_ADDR 0x02000000 +#define EXYNOS4210_IROM_MIRROR_SIZE 0x00010000 /* 64 KB */ + +#define EXYNOS4210_IRAM_BASE_ADDR 0x02020000 +#define EXYNOS4210_IRAM_SIZE 0x00020000 /* 128 KB */ + +/* Secondary CPU startup code is in IROM memory */ +#define EXYNOS4210_SMP_BOOT_ADDR EXYNOS4210_IROM_BASE_ADDR +#define EXYNOS4210_SMP_BOOT_SIZE 0x1000 +#define EXYNOS4210_BASE_BOOT_ADDR EXYNOS4210_DRAM0_BASE_ADDR +/* Secondary CPU polling address to get loader start from */ +#define EXYNOS4210_SECOND_CPU_BOOTREG 0x10020814 + +#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR 0x10500000 +#define EXYNOS4210_L2X0_BASE_ADDR 0x10502000 + +/* + * exynos4210 IRQ subsystem stub definitions. + */ +#define EXYNOS4210_IRQ_GATE_NINPUTS 2 /* Internal and External GIC */ + +#define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64 +#define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16 +#define EXYNOS4210_MAX_INT_COMBINER_IN_IRQ \ + (EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ * 8) +#define EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ \ + (EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ * 8) + +#define EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit) ((grp)*8 + (bit)) +#define EXYNOS4210_COMBINER_GET_GRP_NUM(irq) ((irq) / 8) +#define EXYNOS4210_COMBINER_GET_BIT_NUM(irq) \ + ((irq) - 8 * EXYNOS4210_COMBINER_GET_GRP_NUM(irq)) + +/* IRQs number for external and internal GIC */ +#define EXYNOS4210_EXT_GIC_NIRQ (160-32) +#define EXYNOS4210_INT_GIC_NIRQ 64 + +#define EXYNOS4210_I2C_NUMBER 9 + +typedef struct Exynos4210Irq { + qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; + qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ]; + qemu_irq int_gic_irq[EXYNOS4210_INT_GIC_NIRQ]; + qemu_irq ext_gic_irq[EXYNOS4210_EXT_GIC_NIRQ]; + qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; +} Exynos4210Irq; + +typedef struct Exynos4210State { + ARMCPU *cpu[EXYNOS4210_NCPUS]; + Exynos4210Irq irqs; + qemu_irq *irq_table; + + MemoryRegion chipid_mem; + MemoryRegion iram_mem; + MemoryRegion irom_mem; + MemoryRegion irom_alias_mem; + MemoryRegion dram0_mem; + MemoryRegion dram1_mem; + MemoryRegion boot_secondary; + MemoryRegion bootreg_mem; + i2c_bus *i2c_if[EXYNOS4210_I2C_NUMBER]; +} Exynos4210State; + +void exynos4210_write_secondary(ARMCPU *cpu, + const struct arm_boot_info *info); + +Exynos4210State *exynos4210_init(MemoryRegion *system_mem, + unsigned long ram_size); + +/* Initialize exynos4210 IRQ subsystem stub */ +qemu_irq *exynos4210_init_irq(Exynos4210Irq *env); + +/* Initialize board IRQs. + * These IRQs contain splitted Int/External Combiner and External Gic IRQs */ +void exynos4210_init_board_irqs(Exynos4210Irq *s); + +/* Get IRQ number from exynos4210 IRQ subsystem stub. + * To identify IRQ source use internal combiner group and bit number + * grp - group number + * bit - bit number inside group */ +uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit); + +/* + * Get Combiner input GPIO into irqs structure + */ +void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, + int ext); + +/* + * exynos4210 UART + */ +DeviceState *exynos4210_uart_create(hwaddr addr, + int fifo_size, + int channel, + CharDriverState *chr, + qemu_irq irq); + +#endif /* EXYNOS4210_H_ */ diff --git a/include/hw/arm/imx.h b/include/hw/arm/imx.h new file mode 100644 index 0000000000..ea9e093277 --- /dev/null +++ b/include/hw/arm/imx.h @@ -0,0 +1,34 @@ +/* + * i.MX31 emulation + * + * Copyright (C) 2012 Peter Chubb + * NICTA + * + * This code is released under the GPL, version 2.0 or later + * See the file `../COPYING' for details. + */ + +#ifndef IMX_H +#define IMX_H + +void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq); + +typedef enum { + NOCLK, + MCU, + HSP, + IPG, + CLK_32k +} IMXClk; + +uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock); + +void imx_timerp_create(const hwaddr addr, + qemu_irq irq, + DeviceState *ccm); +void imx_timerg_create(const hwaddr addr, + qemu_irq irq, + DeviceState *ccm); + + +#endif /* IMX_H */ diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h new file mode 100644 index 0000000000..188cda8771 --- /dev/null +++ b/include/hw/arm/omap.h @@ -0,0 +1,1015 @@ +/* + * Texas Instruments OMAP processors. + * + * Copyright (C) 2006-2008 Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#ifndef hw_omap_h +#include "exec/memory.h" +# define hw_omap_h "omap.h" +#include "hw/irq.h" + +# define OMAP_EMIFS_BASE 0x00000000 +# define OMAP2_Q0_BASE 0x00000000 +# define OMAP_CS0_BASE 0x00000000 +# define OMAP_CS1_BASE 0x04000000 +# define OMAP_CS2_BASE 0x08000000 +# define OMAP_CS3_BASE 0x0c000000 +# define OMAP_EMIFF_BASE 0x10000000 +# define OMAP_IMIF_BASE 0x20000000 +# define OMAP_LOCALBUS_BASE 0x30000000 +# define OMAP2_Q1_BASE 0x40000000 +# define OMAP2_L4_BASE 0x48000000 +# define OMAP2_SRAM_BASE 0x40200000 +# define OMAP2_L3_BASE 0x68000000 +# define OMAP2_Q2_BASE 0x80000000 +# define OMAP2_Q3_BASE 0xc0000000 +# define OMAP_MPUI_BASE 0xe1000000 + +# define OMAP730_SRAM_SIZE 0x00032000 +# define OMAP15XX_SRAM_SIZE 0x00030000 +# define OMAP16XX_SRAM_SIZE 0x00004000 +# define OMAP1611_SRAM_SIZE 0x0003e800 +# define OMAP242X_SRAM_SIZE 0x000a0000 +# define OMAP243X_SRAM_SIZE 0x00010000 +# define OMAP_CS0_SIZE 0x04000000 +# define OMAP_CS1_SIZE 0x04000000 +# define OMAP_CS2_SIZE 0x04000000 +# define OMAP_CS3_SIZE 0x04000000 + +/* omap_clk.c */ +struct omap_mpu_state_s; +typedef struct clk *omap_clk; +omap_clk omap_findclk(struct omap_mpu_state_s *mpu, const char *name); +void omap_clk_init(struct omap_mpu_state_s *mpu); +void omap_clk_adduser(struct clk *clk, qemu_irq user); +void omap_clk_get(omap_clk clk); +void omap_clk_put(omap_clk clk); +void omap_clk_onoff(omap_clk clk, int on); +void omap_clk_canidle(omap_clk clk, int can); +void omap_clk_setrate(omap_clk clk, int divide, int multiply); +int64_t omap_clk_getrate(omap_clk clk); +void omap_clk_reparent(omap_clk clk, omap_clk parent); + +/* OMAP2 l4 Interconnect */ +struct omap_l4_s; +struct omap_l4_region_s { + hwaddr offset; + size_t size; + int access; +}; +struct omap_l4_agent_info_s { + int ta; + int region; + int regions; + int ta_region; +}; +struct omap_target_agent_s { + MemoryRegion iomem; + struct omap_l4_s *bus; + int regions; + const struct omap_l4_region_s *start; + hwaddr base; + uint32_t component; + uint32_t control; + uint32_t status; +}; +struct omap_l4_s *omap_l4_init(MemoryRegion *address_space, + hwaddr base, int ta_num); + +struct omap_target_agent_s; +struct omap_target_agent_s *omap_l4ta_get( + struct omap_l4_s *bus, + const struct omap_l4_region_s *regions, + const struct omap_l4_agent_info_s *agents, + int cs); +hwaddr omap_l4_attach(struct omap_target_agent_s *ta, + int region, MemoryRegion *mr); +hwaddr omap_l4_region_base(struct omap_target_agent_s *ta, + int region); +hwaddr omap_l4_region_size(struct omap_target_agent_s *ta, + int region); + +/* OMAP2 SDRAM controller */ +struct omap_sdrc_s; +struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem, + hwaddr base); +void omap_sdrc_reset(struct omap_sdrc_s *s); + +/* OMAP2 general purpose memory controller */ +struct omap_gpmc_s; +struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, + hwaddr base, + qemu_irq irq, qemu_irq drq); +void omap_gpmc_reset(struct omap_gpmc_s *s); +void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem); +void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand); + +/* + * Common IRQ numbers for level 1 interrupt handler + * See /usr/include/asm-arm/arch-omap/irqs.h in Linux. + */ +# define OMAP_INT_CAMERA 1 +# define OMAP_INT_FIQ 3 +# define OMAP_INT_RTDX 6 +# define OMAP_INT_DSP_MMU_ABORT 7 +# define OMAP_INT_HOST 8 +# define OMAP_INT_ABORT 9 +# define OMAP_INT_BRIDGE_PRIV 13 +# define OMAP_INT_GPIO_BANK1 14 +# define OMAP_INT_UART3 15 +# define OMAP_INT_TIMER3 16 +# define OMAP_INT_DMA_CH0_6 19 +# define OMAP_INT_DMA_CH1_7 20 +# define OMAP_INT_DMA_CH2_8 21 +# define OMAP_INT_DMA_CH3 22 +# define OMAP_INT_DMA_CH4 23 +# define OMAP_INT_DMA_CH5 24 +# define OMAP_INT_DMA_LCD 25 +# define OMAP_INT_TIMER1 26 +# define OMAP_INT_WD_TIMER 27 +# define OMAP_INT_BRIDGE_PUB 28 +# define OMAP_INT_TIMER2 30 +# define OMAP_INT_LCD_CTRL 31 + +/* + * Common OMAP-15xx IRQ numbers for level 1 interrupt handler + */ +# define OMAP_INT_15XX_IH2_IRQ 0 +# define OMAP_INT_15XX_LB_MMU 17 +# define OMAP_INT_15XX_LOCAL_BUS 29 + +/* + * OMAP-1510 specific IRQ numbers for level 1 interrupt handler + */ +# define OMAP_INT_1510_SPI_TX 4 +# define OMAP_INT_1510_SPI_RX 5 +# define OMAP_INT_1510_DSP_MAILBOX1 10 +# define OMAP_INT_1510_DSP_MAILBOX2 11 + +/* + * OMAP-310 specific IRQ numbers for level 1 interrupt handler + */ +# define OMAP_INT_310_McBSP2_TX 4 +# define OMAP_INT_310_McBSP2_RX 5 +# define OMAP_INT_310_HSB_MAILBOX1 12 +# define OMAP_INT_310_HSAB_MMU 18 + +/* + * OMAP-1610 specific IRQ numbers for level 1 interrupt handler + */ +# define OMAP_INT_1610_IH2_IRQ 0 +# define OMAP_INT_1610_IH2_FIQ 2 +# define OMAP_INT_1610_McBSP2_TX 4 +# define OMAP_INT_1610_McBSP2_RX 5 +# define OMAP_INT_1610_DSP_MAILBOX1 10 +# define OMAP_INT_1610_DSP_MAILBOX2 11 +# define OMAP_INT_1610_LCD_LINE 12 +# define OMAP_INT_1610_GPTIMER1 17 +# define OMAP_INT_1610_GPTIMER2 18 +# define OMAP_INT_1610_SSR_FIFO_0 29 + +/* + * OMAP-730 specific IRQ numbers for level 1 interrupt handler + */ +# define OMAP_INT_730_IH2_FIQ 0 +# define OMAP_INT_730_IH2_IRQ 1 +# define OMAP_INT_730_USB_NON_ISO 2 +# define OMAP_INT_730_USB_ISO 3 +# define OMAP_INT_730_ICR 4 +# define OMAP_INT_730_EAC 5 +# define OMAP_INT_730_GPIO_BANK1 6 +# define OMAP_INT_730_GPIO_BANK2 7 +# define OMAP_INT_730_GPIO_BANK3 8 +# define OMAP_INT_730_McBSP2TX 10 +# define OMAP_INT_730_McBSP2RX 11 +# define OMAP_INT_730_McBSP2RX_OVF 12 +# define OMAP_INT_730_LCD_LINE 14 +# define OMAP_INT_730_GSM_PROTECT 15 +# define OMAP_INT_730_TIMER3 16 +# define OMAP_INT_730_GPIO_BANK5 17 +# define OMAP_INT_730_GPIO_BANK6 18 +# define OMAP_INT_730_SPGIO_WR 29 + +/* + * Common IRQ numbers for level 2 interrupt handler + */ +# define OMAP_INT_KEYBOARD 1 +# define OMAP_INT_uWireTX 2 +# define OMAP_INT_uWireRX 3 +# define OMAP_INT_I2C 4 +# define OMAP_INT_MPUIO 5 +# define OMAP_INT_USB_HHC_1 6 +# define OMAP_INT_McBSP3TX 10 +# define OMAP_INT_McBSP3RX 11 +# define OMAP_INT_McBSP1TX 12 +# define OMAP_INT_McBSP1RX 13 +# define OMAP_INT_UART1 14 +# define OMAP_INT_UART2 15 +# define OMAP_INT_USB_W2FC 20 +# define OMAP_INT_1WIRE 21 +# define OMAP_INT_OS_TIMER 22 +# define OMAP_INT_OQN 23 +# define OMAP_INT_GAUGE_32K 24 +# define OMAP_INT_RTC_TIMER 25 +# define OMAP_INT_RTC_ALARM 26 +# define OMAP_INT_DSP_MMU 28 + +/* + * OMAP-1510 specific IRQ numbers for level 2 interrupt handler + */ +# define OMAP_INT_1510_BT_MCSI1TX 16 +# define OMAP_INT_1510_BT_MCSI1RX 17 +# define OMAP_INT_1510_SoSSI_MATCH 19 +# define OMAP_INT_1510_MEM_STICK 27 +# define OMAP_INT_1510_COM_SPI_RO 31 + +/* + * OMAP-310 specific IRQ numbers for level 2 interrupt handler + */ +# define OMAP_INT_310_FAC 0 +# define OMAP_INT_310_USB_HHC_2 7 +# define OMAP_INT_310_MCSI1_FE 16 +# define OMAP_INT_310_MCSI2_FE 17 +# define OMAP_INT_310_USB_W2FC_ISO 29 +# define OMAP_INT_310_USB_W2FC_NON_ISO 30 +# define OMAP_INT_310_McBSP2RX_OF 31 + +/* + * OMAP-1610 specific IRQ numbers for level 2 interrupt handler + */ +# define OMAP_INT_1610_FAC 0 +# define OMAP_INT_1610_USB_HHC_2 7 +# define OMAP_INT_1610_USB_OTG 8 +# define OMAP_INT_1610_SoSSI 9 +# define OMAP_INT_1610_BT_MCSI1TX 16 +# define OMAP_INT_1610_BT_MCSI1RX 17 +# define OMAP_INT_1610_SoSSI_MATCH 19 +# define OMAP_INT_1610_MEM_STICK 27 +# define OMAP_INT_1610_McBSP2RX_OF 31 +# define OMAP_INT_1610_STI 32 +# define OMAP_INT_1610_STI_WAKEUP 33 +# define OMAP_INT_1610_GPTIMER3 34 +# define OMAP_INT_1610_GPTIMER4 35 +# define OMAP_INT_1610_GPTIMER5 36 +# define OMAP_INT_1610_GPTIMER6 37 +# define OMAP_INT_1610_GPTIMER7 38 +# define OMAP_INT_1610_GPTIMER8 39 +# define OMAP_INT_1610_GPIO_BANK2 40 +# define OMAP_INT_1610_GPIO_BANK3 41 +# define OMAP_INT_1610_MMC2 42 +# define OMAP_INT_1610_CF 43 +# define OMAP_INT_1610_WAKE_UP_REQ 46 +# define OMAP_INT_1610_GPIO_BANK4 48 +# define OMAP_INT_1610_SPI 49 +# define OMAP_INT_1610_DMA_CH6 53 +# define OMAP_INT_1610_DMA_CH7 54 +# define OMAP_INT_1610_DMA_CH8 55 +# define OMAP_INT_1610_DMA_CH9 56 +# define OMAP_INT_1610_DMA_CH10 57 +# define OMAP_INT_1610_DMA_CH11 58 +# define OMAP_INT_1610_DMA_CH12 59 +# define OMAP_INT_1610_DMA_CH13 60 +# define OMAP_INT_1610_DMA_CH14 61 +# define OMAP_INT_1610_DMA_CH15 62 +# define OMAP_INT_1610_NAND 63 + +/* + * OMAP-730 specific IRQ numbers for level 2 interrupt handler + */ +# define OMAP_INT_730_HW_ERRORS 0 +# define OMAP_INT_730_NFIQ_PWR_FAIL 1 +# define OMAP_INT_730_CFCD 2 +# define OMAP_INT_730_CFIREQ 3 +# define OMAP_INT_730_I2C 4 +# define OMAP_INT_730_PCC 5 +# define OMAP_INT_730_MPU_EXT_NIRQ 6 +# define OMAP_INT_730_SPI_100K_1 7 +# define OMAP_INT_730_SYREN_SPI 8 +# define OMAP_INT_730_VLYNQ 9 +# define OMAP_INT_730_GPIO_BANK4 10 +# define OMAP_INT_730_McBSP1TX 11 +# define OMAP_INT_730_McBSP1RX 12 +# define OMAP_INT_730_McBSP1RX_OF 13 +# define OMAP_INT_730_UART_MODEM_IRDA_2 14 +# define OMAP_INT_730_UART_MODEM_1 15 +# define OMAP_INT_730_MCSI 16 +# define OMAP_INT_730_uWireTX 17 +# define OMAP_INT_730_uWireRX 18 +# define OMAP_INT_730_SMC_CD 19 +# define OMAP_INT_730_SMC_IREQ 20 +# define OMAP_INT_730_HDQ_1WIRE 21 +# define OMAP_INT_730_TIMER32K 22 +# define OMAP_INT_730_MMC_SDIO 23 +# define OMAP_INT_730_UPLD 24 +# define OMAP_INT_730_USB_HHC_1 27 +# define OMAP_INT_730_USB_HHC_2 28 +# define OMAP_INT_730_USB_GENI 29 +# define OMAP_INT_730_USB_OTG 30 +# define OMAP_INT_730_CAMERA_IF 31 +# define OMAP_INT_730_RNG 32 +# define OMAP_INT_730_DUAL_MODE_TIMER 33 +# define OMAP_INT_730_DBB_RF_EN 34 +# define OMAP_INT_730_MPUIO_KEYPAD 35 +# define OMAP_INT_730_SHA1_MD5 36 +# define OMAP_INT_730_SPI_100K_2 37 +# define OMAP_INT_730_RNG_IDLE 38 +# define OMAP_INT_730_MPUIO 39 +# define OMAP_INT_730_LLPC_LCD_CTRL_OFF 40 +# define OMAP_INT_730_LLPC_OE_FALLING 41 +# define OMAP_INT_730_LLPC_OE_RISING 42 +# define OMAP_INT_730_LLPC_VSYNC 43 +# define OMAP_INT_730_WAKE_UP_REQ 46 +# define OMAP_INT_730_DMA_CH6 53 +# define OMAP_INT_730_DMA_CH7 54 +# define OMAP_INT_730_DMA_CH8 55 +# define OMAP_INT_730_DMA_CH9 56 +# define OMAP_INT_730_DMA_CH10 57 +# define OMAP_INT_730_DMA_CH11 58 +# define OMAP_INT_730_DMA_CH12 59 +# define OMAP_INT_730_DMA_CH13 60 +# define OMAP_INT_730_DMA_CH14 61 +# define OMAP_INT_730_DMA_CH15 62 +# define OMAP_INT_730_NAND 63 + +/* + * OMAP-24xx common IRQ numbers + */ +# define OMAP_INT_24XX_STI 4 +# define OMAP_INT_24XX_SYS_NIRQ 7 +# define OMAP_INT_24XX_L3_IRQ 10 +# define OMAP_INT_24XX_PRCM_MPU_IRQ 11 +# define OMAP_INT_24XX_SDMA_IRQ0 12 +# define OMAP_INT_24XX_SDMA_IRQ1 13 +# define OMAP_INT_24XX_SDMA_IRQ2 14 +# define OMAP_INT_24XX_SDMA_IRQ3 15 +# define OMAP_INT_243X_MCBSP2_IRQ 16 +# define OMAP_INT_243X_MCBSP3_IRQ 17 +# define OMAP_INT_243X_MCBSP4_IRQ 18 +# define OMAP_INT_243X_MCBSP5_IRQ 19 +# define OMAP_INT_24XX_GPMC_IRQ 20 +# define OMAP_INT_24XX_GUFFAW_IRQ 21 +# define OMAP_INT_24XX_IVA_IRQ 22 +# define OMAP_INT_24XX_EAC_IRQ 23 +# define OMAP_INT_24XX_CAM_IRQ 24 +# define OMAP_INT_24XX_DSS_IRQ 25 +# define OMAP_INT_24XX_MAIL_U0_MPU 26 +# define OMAP_INT_24XX_DSP_UMA 27 +# define OMAP_INT_24XX_DSP_MMU 28 +# define OMAP_INT_24XX_GPIO_BANK1 29 +# define OMAP_INT_24XX_GPIO_BANK2 30 +# define OMAP_INT_24XX_GPIO_BANK3 31 +# define OMAP_INT_24XX_GPIO_BANK4 32 +# define OMAP_INT_243X_GPIO_BANK5 33 +# define OMAP_INT_24XX_MAIL_U3_MPU 34 +# define OMAP_INT_24XX_WDT3 35 +# define OMAP_INT_24XX_WDT4 36 +# define OMAP_INT_24XX_GPTIMER1 37 +# define OMAP_INT_24XX_GPTIMER2 38 +# define OMAP_INT_24XX_GPTIMER3 39 +# define OMAP_INT_24XX_GPTIMER4 40 +# define OMAP_INT_24XX_GPTIMER5 41 +# define OMAP_INT_24XX_GPTIMER6 42 +# define OMAP_INT_24XX_GPTIMER7 43 +# define OMAP_INT_24XX_GPTIMER8 44 +# define OMAP_INT_24XX_GPTIMER9 45 +# define OMAP_INT_24XX_GPTIMER10 46 +# define OMAP_INT_24XX_GPTIMER11 47 +# define OMAP_INT_24XX_GPTIMER12 48 +# define OMAP_INT_24XX_PKA_IRQ 50 +# define OMAP_INT_24XX_SHA1MD5_IRQ 51 +# define OMAP_INT_24XX_RNG_IRQ 52 +# define OMAP_INT_24XX_MG_IRQ 53 +# define OMAP_INT_24XX_I2C1_IRQ 56 +# define OMAP_INT_24XX_I2C2_IRQ 57 +# define OMAP_INT_24XX_MCBSP1_IRQ_TX 59 +# define OMAP_INT_24XX_MCBSP1_IRQ_RX 60 +# define OMAP_INT_24XX_MCBSP2_IRQ_TX 62 +# define OMAP_INT_24XX_MCBSP2_IRQ_RX 63 +# define OMAP_INT_243X_MCBSP1_IRQ 64 +# define OMAP_INT_24XX_MCSPI1_IRQ 65 +# define OMAP_INT_24XX_MCSPI2_IRQ 66 +# define OMAP_INT_24XX_SSI1_IRQ0 67 +# define OMAP_INT_24XX_SSI1_IRQ1 68 +# define OMAP_INT_24XX_SSI2_IRQ0 69 +# define OMAP_INT_24XX_SSI2_IRQ1 70 +# define OMAP_INT_24XX_SSI_GDD_IRQ 71 +# define OMAP_INT_24XX_UART1_IRQ 72 +# define OMAP_INT_24XX_UART2_IRQ 73 +# define OMAP_INT_24XX_UART3_IRQ 74 +# define OMAP_INT_24XX_USB_IRQ_GEN 75 +# define OMAP_INT_24XX_USB_IRQ_NISO 76 +# define OMAP_INT_24XX_USB_IRQ_ISO 77 +# define OMAP_INT_24XX_USB_IRQ_HGEN 78 +# define OMAP_INT_24XX_USB_IRQ_HSOF 79 +# define OMAP_INT_24XX_USB_IRQ_OTG 80 +# define OMAP_INT_24XX_VLYNQ_IRQ 81 +# define OMAP_INT_24XX_MMC_IRQ 83 +# define OMAP_INT_24XX_MS_IRQ 84 +# define OMAP_INT_24XX_FAC_IRQ 85 +# define OMAP_INT_24XX_MCSPI3_IRQ 91 +# define OMAP_INT_243X_HS_USB_MC 92 +# define OMAP_INT_243X_HS_USB_DMA 93 +# define OMAP_INT_243X_CARKIT 94 +# define OMAP_INT_34XX_GPTIMER12 95 + +/* omap_dma.c */ +enum omap_dma_model { + omap_dma_3_0, + omap_dma_3_1, + omap_dma_3_2, + omap_dma_4, +}; + +struct soc_dma_s; +struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, + MemoryRegion *sysmem, + qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, + enum omap_dma_model model); +struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, + MemoryRegion *sysmem, + struct omap_mpu_state_s *mpu, int fifo, + int chans, omap_clk iclk, omap_clk fclk); +void omap_dma_reset(struct soc_dma_s *s); + +struct dma_irq_map { + int ih; + int intr; +}; + +/* Only used in OMAP DMA 3.x gigacells */ +enum omap_dma_port { + emiff = 0, + emifs, + imif, /* omap16xx: ocp_t1 */ + tipb, + local, /* omap16xx: ocp_t2 */ + tipb_mpui, + __omap_dma_port_last, +}; + +typedef enum { + constant = 0, + post_incremented, + single_index, + double_index, +} omap_dma_addressing_t; + +/* Only used in OMAP DMA 3.x gigacells */ +struct omap_dma_lcd_channel_s { + enum omap_dma_port src; + hwaddr src_f1_top; + hwaddr src_f1_bottom; + hwaddr src_f2_top; + hwaddr src_f2_bottom; + + /* Used in OMAP DMA 3.2 gigacell */ + unsigned char brust_f1; + unsigned char pack_f1; + unsigned char data_type_f1; + unsigned char brust_f2; + unsigned char pack_f2; + unsigned char data_type_f2; + unsigned char end_prog; + unsigned char repeat; + unsigned char auto_init; + unsigned char priority; + unsigned char fs; + unsigned char running; + unsigned char bs; + unsigned char omap_3_1_compatible_disable; + unsigned char dst; + unsigned char lch_type; + int16_t element_index_f1; + int16_t element_index_f2; + int32_t frame_index_f1; + int32_t frame_index_f2; + uint16_t elements_f1; + uint16_t frames_f1; + uint16_t elements_f2; + uint16_t frames_f2; + omap_dma_addressing_t mode_f1; + omap_dma_addressing_t mode_f2; + + /* Destination port is fixed. */ + int interrupts; + int condition; + int dual; + + int current_frame; + hwaddr phys_framebuffer[2]; + qemu_irq irq; + struct omap_mpu_state_s *mpu; +} *omap_dma_get_lcdch(struct soc_dma_s *s); + +/* + * DMA request numbers for OMAP1 + * See /usr/include/asm-arm/arch-omap/dma.h in Linux. + */ +# define OMAP_DMA_NO_DEVICE 0 +# define OMAP_DMA_MCSI1_TX 1 +# define OMAP_DMA_MCSI1_RX 2 +# define OMAP_DMA_I2C_RX 3 +# define OMAP_DMA_I2C_TX 4 +# define OMAP_DMA_EXT_NDMA_REQ0 5 +# define OMAP_DMA_EXT_NDMA_REQ1 6 +# define OMAP_DMA_UWIRE_TX 7 +# define OMAP_DMA_MCBSP1_TX 8 +# define OMAP_DMA_MCBSP1_RX 9 +# define OMAP_DMA_MCBSP3_TX 10 +# define OMAP_DMA_MCBSP3_RX 11 +# define OMAP_DMA_UART1_TX 12 +# define OMAP_DMA_UART1_RX 13 +# define OMAP_DMA_UART2_TX 14 +# define OMAP_DMA_UART2_RX 15 +# define OMAP_DMA_MCBSP2_TX 16 +# define OMAP_DMA_MCBSP2_RX 17 +# define OMAP_DMA_UART3_TX 18 +# define OMAP_DMA_UART3_RX 19 +# define OMAP_DMA_CAMERA_IF_RX 20 +# define OMAP_DMA_MMC_TX 21 +# define OMAP_DMA_MMC_RX 22 +# define OMAP_DMA_NAND 23 /* Not in OMAP310 */ +# define OMAP_DMA_IRQ_LCD_LINE 24 /* Not in OMAP310 */ +# define OMAP_DMA_MEMORY_STICK 25 /* Not in OMAP310 */ +# define OMAP_DMA_USB_W2FC_RX0 26 +# define OMAP_DMA_USB_W2FC_RX1 27 +# define OMAP_DMA_USB_W2FC_RX2 28 +# define OMAP_DMA_USB_W2FC_TX0 29 +# define OMAP_DMA_USB_W2FC_TX1 30 +# define OMAP_DMA_USB_W2FC_TX2 31 + +/* These are only for 1610 */ +# define OMAP_DMA_CRYPTO_DES_IN 32 +# define OMAP_DMA_SPI_TX 33 +# define OMAP_DMA_SPI_RX 34 +# define OMAP_DMA_CRYPTO_HASH 35 +# define OMAP_DMA_CCP_ATTN 36 +# define OMAP_DMA_CCP_FIFO_NOT_EMPTY 37 +# define OMAP_DMA_CMT_APE_TX_CHAN_0 38 +# define OMAP_DMA_CMT_APE_RV_CHAN_0 39 +# define OMAP_DMA_CMT_APE_TX_CHAN_1 40 +# define OMAP_DMA_CMT_APE_RV_CHAN_1 41 +# define OMAP_DMA_CMT_APE_TX_CHAN_2 42 +# define OMAP_DMA_CMT_APE_RV_CHAN_2 43 +# define OMAP_DMA_CMT_APE_TX_CHAN_3 44 +# define OMAP_DMA_CMT_APE_RV_CHAN_3 45 +# define OMAP_DMA_CMT_APE_TX_CHAN_4 46 +# define OMAP_DMA_CMT_APE_RV_CHAN_4 47 +# define OMAP_DMA_CMT_APE_TX_CHAN_5 48 +# define OMAP_DMA_CMT_APE_RV_CHAN_5 49 +# define OMAP_DMA_CMT_APE_TX_CHAN_6 50 +# define OMAP_DMA_CMT_APE_RV_CHAN_6 51 +# define OMAP_DMA_CMT_APE_TX_CHAN_7 52 +# define OMAP_DMA_CMT_APE_RV_CHAN_7 53 +# define OMAP_DMA_MMC2_TX 54 +# define OMAP_DMA_MMC2_RX 55 +# define OMAP_DMA_CRYPTO_DES_OUT 56 + +/* + * DMA request numbers for the OMAP2 + */ +# define OMAP24XX_DMA_NO_DEVICE 0 +# define OMAP24XX_DMA_XTI_DMA 1 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_EXT_DMAREQ0 2 +# define OMAP24XX_DMA_EXT_DMAREQ1 3 +# define OMAP24XX_DMA_GPMC 4 +# define OMAP24XX_DMA_GFX 5 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_DSS 6 +# define OMAP24XX_DMA_VLYNQ_TX 7 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_CWT 8 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_AES_TX 9 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_AES_RX 10 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_DES_TX 11 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_DES_RX 12 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_SHA1MD5_RX 13 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_EXT_DMAREQ2 14 +# define OMAP24XX_DMA_EXT_DMAREQ3 15 +# define OMAP24XX_DMA_EXT_DMAREQ4 16 +# define OMAP24XX_DMA_EAC_AC_RD 17 +# define OMAP24XX_DMA_EAC_AC_WR 18 +# define OMAP24XX_DMA_EAC_MD_UL_RD 19 +# define OMAP24XX_DMA_EAC_MD_UL_WR 20 +# define OMAP24XX_DMA_EAC_MD_DL_RD 21 +# define OMAP24XX_DMA_EAC_MD_DL_WR 22 +# define OMAP24XX_DMA_EAC_BT_UL_RD 23 +# define OMAP24XX_DMA_EAC_BT_UL_WR 24 +# define OMAP24XX_DMA_EAC_BT_DL_RD 25 +# define OMAP24XX_DMA_EAC_BT_DL_WR 26 +# define OMAP24XX_DMA_I2C1_TX 27 +# define OMAP24XX_DMA_I2C1_RX 28 +# define OMAP24XX_DMA_I2C2_TX 29 +# define OMAP24XX_DMA_I2C2_RX 30 +# define OMAP24XX_DMA_MCBSP1_TX 31 +# define OMAP24XX_DMA_MCBSP1_RX 32 +# define OMAP24XX_DMA_MCBSP2_TX 33 +# define OMAP24XX_DMA_MCBSP2_RX 34 +# define OMAP24XX_DMA_SPI1_TX0 35 +# define OMAP24XX_DMA_SPI1_RX0 36 +# define OMAP24XX_DMA_SPI1_TX1 37 +# define OMAP24XX_DMA_SPI1_RX1 38 +# define OMAP24XX_DMA_SPI1_TX2 39 +# define OMAP24XX_DMA_SPI1_RX2 40 +# define OMAP24XX_DMA_SPI1_TX3 41 +# define OMAP24XX_DMA_SPI1_RX3 42 +# define OMAP24XX_DMA_SPI2_TX0 43 +# define OMAP24XX_DMA_SPI2_RX0 44 +# define OMAP24XX_DMA_SPI2_TX1 45 +# define OMAP24XX_DMA_SPI2_RX1 46 + +# define OMAP24XX_DMA_UART1_TX 49 +# define OMAP24XX_DMA_UART1_RX 50 +# define OMAP24XX_DMA_UART2_TX 51 +# define OMAP24XX_DMA_UART2_RX 52 +# define OMAP24XX_DMA_UART3_TX 53 +# define OMAP24XX_DMA_UART3_RX 54 +# define OMAP24XX_DMA_USB_W2FC_TX0 55 +# define OMAP24XX_DMA_USB_W2FC_RX0 56 +# define OMAP24XX_DMA_USB_W2FC_TX1 57 +# define OMAP24XX_DMA_USB_W2FC_RX1 58 +# define OMAP24XX_DMA_USB_W2FC_TX2 59 +# define OMAP24XX_DMA_USB_W2FC_RX2 60 +# define OMAP24XX_DMA_MMC1_TX 61 +# define OMAP24XX_DMA_MMC1_RX 62 +# define OMAP24XX_DMA_MS 63 /* Not in OMAP2420 */ +# define OMAP24XX_DMA_EXT_DMAREQ5 64 + +/* omap[123].c */ +/* OMAP2 gp timer */ +struct omap_gp_timer_s; +struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, + qemu_irq irq, omap_clk fclk, omap_clk iclk); +void omap_gp_timer_reset(struct omap_gp_timer_s *s); + +/* OMAP2 sysctimer */ +struct omap_synctimer_s; +struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, + struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk); +void omap_synctimer_reset(struct omap_synctimer_s *s); + +struct omap_uart_s; +struct omap_uart_s *omap_uart_init(hwaddr base, + qemu_irq irq, omap_clk fclk, omap_clk iclk, + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr); +struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, + struct omap_target_agent_s *ta, + qemu_irq irq, omap_clk fclk, omap_clk iclk, + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr); +void omap_uart_reset(struct omap_uart_s *s); +void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr); + +struct omap_mpuio_s; +qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); +void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler); +void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down); + +struct uWireSlave { + uint16_t (*receive)(void *opaque); + void (*send)(void *opaque, uint16_t data); + void *opaque; +}; +struct omap_uwire_s; +void omap_uwire_attach(struct omap_uwire_s *s, + uWireSlave *slave, int chipselect); + +/* OMAP2 spi */ +struct omap_mcspi_s; +struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, + qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk); +void omap_mcspi_attach(struct omap_mcspi_s *s, + uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, + int chipselect); +void omap_mcspi_reset(struct omap_mcspi_s *s); + +struct I2SCodec { + void *opaque; + + /* The CPU can call this if it is generating the clock signal on the + * i2s port. The CODEC can ignore it if it is set up as a clock + * master and generates its own clock. */ + void (*set_rate)(void *opaque, int in, int out); + + void (*tx_swallow)(void *opaque); + qemu_irq rx_swallow; + qemu_irq tx_start; + + int tx_rate; + int cts; + int rx_rate; + int rts; + + struct i2s_fifo_s { + uint8_t *fifo; + int len; + int start; + int size; + } in, out; +}; +struct omap_mcbsp_s; +void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave); + +void omap_tap_init(struct omap_target_agent_s *ta, + struct omap_mpu_state_s *mpu); + +/* omap_lcdc.c */ +struct omap_lcd_panel_s; +void omap_lcdc_reset(struct omap_lcd_panel_s *s); +struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq, + struct omap_dma_lcd_channel_s *dma, + omap_clk clk); + +/* omap_dss.c */ +struct rfbi_chip_s { + void *opaque; + void (*write)(void *opaque, int dc, uint16_t value); + void (*block)(void *opaque, int dc, void *buf, size_t len, int pitch); + uint16_t (*read)(void *opaque, int dc); +}; +struct omap_dss_s; +void omap_dss_reset(struct omap_dss_s *s); +struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, + MemoryRegion *sysmem, + hwaddr l3_base, + qemu_irq irq, qemu_irq drq, + omap_clk fck1, omap_clk fck2, omap_clk ck54m, + omap_clk ick1, omap_clk ick2); +void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip); + +/* omap_mmc.c */ +struct omap_mmc_s; +struct omap_mmc_s *omap_mmc_init(hwaddr base, + MemoryRegion *sysmem, + BlockDriverState *bd, + qemu_irq irq, qemu_irq dma[], omap_clk clk); +struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta, + BlockDriverState *bd, qemu_irq irq, qemu_irq dma[], + omap_clk fclk, omap_clk iclk); +void omap_mmc_reset(struct omap_mmc_s *s); +void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover); +void omap_mmc_enable(struct omap_mmc_s *s, int enable); + +/* omap_i2c.c */ +i2c_bus *omap_i2c_bus(DeviceState *omap_i2c); + +# define cpu_is_omap310(cpu) (cpu->mpu_model == omap310) +# define cpu_is_omap1510(cpu) (cpu->mpu_model == omap1510) +# define cpu_is_omap1610(cpu) (cpu->mpu_model == omap1610) +# define cpu_is_omap1710(cpu) (cpu->mpu_model == omap1710) +# define cpu_is_omap2410(cpu) (cpu->mpu_model == omap2410) +# define cpu_is_omap2420(cpu) (cpu->mpu_model == omap2420) +# define cpu_is_omap2430(cpu) (cpu->mpu_model == omap2430) +# define cpu_is_omap3430(cpu) (cpu->mpu_model == omap3430) +# define cpu_is_omap3630(cpu) (cpu->mpu_model == omap3630) + +# define cpu_is_omap15xx(cpu) \ + (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu)) +# define cpu_is_omap16xx(cpu) \ + (cpu_is_omap1610(cpu) || cpu_is_omap1710(cpu)) +# define cpu_is_omap24xx(cpu) \ + (cpu_is_omap2410(cpu) || cpu_is_omap2420(cpu) || cpu_is_omap2430(cpu)) + +# define cpu_class_omap1(cpu) \ + (cpu_is_omap15xx(cpu) || cpu_is_omap16xx(cpu)) +# define cpu_class_omap2(cpu) cpu_is_omap24xx(cpu) +# define cpu_class_omap3(cpu) \ + (cpu_is_omap3430(cpu) || cpu_is_omap3630(cpu)) + +struct omap_mpu_state_s { + enum omap_mpu_model { + omap310, + omap1510, + omap1610, + omap1710, + omap2410, + omap2420, + omap2422, + omap2423, + omap2430, + omap3430, + omap3630, + } mpu_model; + + ARMCPU *cpu; + + qemu_irq *drq; + + qemu_irq wakeup; + + MemoryRegion ulpd_pm_iomem; + MemoryRegion pin_cfg_iomem; + MemoryRegion id_iomem; + MemoryRegion id_iomem_e18; + MemoryRegion id_iomem_ed4; + MemoryRegion id_iomem_e20; + MemoryRegion mpui_iomem; + MemoryRegion tcmi_iomem; + MemoryRegion clkm_iomem; + MemoryRegion clkdsp_iomem; + MemoryRegion mpui_io_iomem; + MemoryRegion tap_iomem; + MemoryRegion imif_ram; + MemoryRegion emiff_ram; + MemoryRegion sdram; + MemoryRegion sram; + + struct omap_dma_port_if_s { + uint32_t (*read[3])(struct omap_mpu_state_s *s, + hwaddr offset); + void (*write[3])(struct omap_mpu_state_s *s, + hwaddr offset, uint32_t value); + int (*addr_valid)(struct omap_mpu_state_s *s, + hwaddr addr); + } port[__omap_dma_port_last]; + + unsigned long sdram_size; + unsigned long sram_size; + + /* MPUI-TIPB peripherals */ + struct omap_uart_s *uart[3]; + + DeviceState *gpio; + + struct omap_mcbsp_s *mcbsp1; + struct omap_mcbsp_s *mcbsp3; + + /* MPU public TIPB peripherals */ + struct omap_32khz_timer_s *os_timer; + + struct omap_mmc_s *mmc; + + struct omap_mpuio_s *mpuio; + + struct omap_uwire_s *microwire; + + struct omap_pwl_s *pwl; + struct omap_pwt_s *pwt; + DeviceState *i2c[2]; + + struct omap_rtc_s *rtc; + + struct omap_mcbsp_s *mcbsp2; + + struct omap_lpg_s *led[2]; + + /* MPU private TIPB peripherals */ + DeviceState *ih[2]; + + struct soc_dma_s *dma; + + struct omap_mpu_timer_s *timer[3]; + struct omap_watchdog_timer_s *wdt; + + struct omap_lcd_panel_s *lcd; + + uint32_t ulpd_pm_regs[21]; + int64_t ulpd_gauge_start; + + uint32_t func_mux_ctrl[14]; + uint32_t comp_mode_ctrl[1]; + uint32_t pull_dwn_ctrl[4]; + uint32_t gate_inh_ctrl[1]; + uint32_t voltage_ctrl[1]; + uint32_t test_dbg_ctrl[1]; + uint32_t mod_conf_ctrl[1]; + int compat1509; + + uint32_t mpui_ctrl; + + struct omap_tipb_bridge_s *private_tipb; + struct omap_tipb_bridge_s *public_tipb; + + uint32_t tcmi_regs[17]; + + struct dpll_ctl_s *dpll[3]; + + omap_clk clks; + struct { + int cold_start; + int clocking_scheme; + uint16_t arm_ckctl; + uint16_t arm_idlect1; + uint16_t arm_idlect2; + uint16_t arm_ewupct; + uint16_t arm_rstct1; + uint16_t arm_rstct2; + uint16_t arm_ckout1; + int dpll1_mode; + uint16_t dsp_idlect1; + uint16_t dsp_idlect2; + uint16_t dsp_rstct2; + } clkm; + + /* OMAP2-only peripherals */ + struct omap_l4_s *l4; + + struct omap_gp_timer_s *gptimer[12]; + struct omap_synctimer_s *synctimer; + + struct omap_prcm_s *prcm; + struct omap_sdrc_s *sdrc; + struct omap_gpmc_s *gpmc; + struct omap_sysctl_s *sysc; + + struct omap_mcspi_s *mcspi[2]; + + struct omap_dss_s *dss; + + struct omap_eac_s *eac; +}; + +/* omap1.c */ +struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, + unsigned long sdram_size, + const char *core); + +/* omap2.c */ +struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, + unsigned long sdram_size, + const char *core); + +#define OMAP_FMT_plx "%#08" HWADDR_PRIx + +uint32_t omap_badwidth_read8(void *opaque, hwaddr addr); +void omap_badwidth_write8(void *opaque, hwaddr addr, + uint32_t value); +uint32_t omap_badwidth_read16(void *opaque, hwaddr addr); +void omap_badwidth_write16(void *opaque, hwaddr addr, + uint32_t value); +uint32_t omap_badwidth_read32(void *opaque, hwaddr addr); +void omap_badwidth_write32(void *opaque, hwaddr addr, + uint32_t value); + +void omap_mpu_wakeup(void *opaque, int irq, int req); + +# define OMAP_BAD_REG(paddr) \ + fprintf(stderr, "%s: Bad register " OMAP_FMT_plx "\n", \ + __FUNCTION__, paddr) +# define OMAP_RO_REG(paddr) \ + fprintf(stderr, "%s: Read-only register " OMAP_FMT_plx "\n", \ + __FUNCTION__, paddr) + +/* OMAP-specific Linux bootloader tags for the ATAG_BOARD area + (Board-specifc tags are not here) */ +#define OMAP_TAG_CLOCK 0x4f01 +#define OMAP_TAG_MMC 0x4f02 +#define OMAP_TAG_SERIAL_CONSOLE 0x4f03 +#define OMAP_TAG_USB 0x4f04 +#define OMAP_TAG_LCD 0x4f05 +#define OMAP_TAG_GPIO_SWITCH 0x4f06 +#define OMAP_TAG_UART 0x4f07 +#define OMAP_TAG_FBMEM 0x4f08 +#define OMAP_TAG_STI_CONSOLE 0x4f09 +#define OMAP_TAG_CAMERA_SENSOR 0x4f0a +#define OMAP_TAG_PARTITION 0x4f0b +#define OMAP_TAG_TEA5761 0x4f10 +#define OMAP_TAG_TMP105 0x4f11 +#define OMAP_TAG_BOOT_REASON 0x4f80 +#define OMAP_TAG_FLASH_PART_STR 0x4f81 +#define OMAP_TAG_VERSION_STR 0x4f82 + +enum { + OMAP_GPIOSW_TYPE_COVER = 0 << 4, + OMAP_GPIOSW_TYPE_CONNECTION = 1 << 4, + OMAP_GPIOSW_TYPE_ACTIVITY = 2 << 4, +}; + +#define OMAP_GPIOSW_INVERTED 0x0001 +#define OMAP_GPIOSW_OUTPUT 0x0002 + +# define TCMI_VERBOSE 1 + +# ifdef TCMI_VERBOSE +# define OMAP_8B_REG(paddr) \ + fprintf(stderr, "%s: 8-bit register " OMAP_FMT_plx "\n", \ + __FUNCTION__, paddr) +# define OMAP_16B_REG(paddr) \ + fprintf(stderr, "%s: 16-bit register " OMAP_FMT_plx "\n", \ + __FUNCTION__, paddr) +# define OMAP_32B_REG(paddr) \ + fprintf(stderr, "%s: 32-bit register " OMAP_FMT_plx "\n", \ + __FUNCTION__, paddr) +# else +# define OMAP_8B_REG(paddr) +# define OMAP_16B_REG(paddr) +# define OMAP_32B_REG(paddr) +# endif + +# define OMAP_MPUI_REG_MASK 0x000007ff + +#endif /* hw_omap_h */ diff --git a/include/hw/arm/primecell.h b/include/hw/arm/primecell.h new file mode 100644 index 0000000000..7337c3b3ca --- /dev/null +++ b/include/hw/arm/primecell.h @@ -0,0 +1,12 @@ +#ifndef PRIMECELL_H +#define PRIMECELL_H + +/* Declarations for ARM PrimeCell based periperals. */ +/* Also includes some devices that are currently only used by the + ARM boards. */ + +/* arm_sysctl GPIO lines */ +#define ARM_SYSCTL_GPIO_MMC_WPROT 0 +#define ARM_SYSCTL_GPIO_MMC_CARDIN 1 + +#endif diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h new file mode 100644 index 0000000000..668232cead --- /dev/null +++ b/include/hw/arm/pxa.h @@ -0,0 +1,191 @@ +/* + * Intel XScale PXA255/270 processor support. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GNU GPL v2. + */ +#ifndef PXA_H +# define PXA_H "pxa.h" + +#include "exec/memory.h" + +/* Interrupt numbers */ +# define PXA2XX_PIC_SSP3 0 +# define PXA2XX_PIC_USBH2 2 +# define PXA2XX_PIC_USBH1 3 +# define PXA2XX_PIC_KEYPAD 4 +# define PXA2XX_PIC_PWRI2C 6 +# define PXA25X_PIC_HWUART 7 +# define PXA27X_PIC_OST_4_11 7 +# define PXA2XX_PIC_GPIO_0 8 +# define PXA2XX_PIC_GPIO_1 9 +# define PXA2XX_PIC_GPIO_X 10 +# define PXA2XX_PIC_I2S 13 +# define PXA26X_PIC_ASSP 15 +# define PXA25X_PIC_NSSP 16 +# define PXA27X_PIC_SSP2 16 +# define PXA2XX_PIC_LCD 17 +# define PXA2XX_PIC_I2C 18 +# define PXA2XX_PIC_ICP 19 +# define PXA2XX_PIC_STUART 20 +# define PXA2XX_PIC_BTUART 21 +# define PXA2XX_PIC_FFUART 22 +# define PXA2XX_PIC_MMC 23 +# define PXA2XX_PIC_SSP 24 +# define PXA2XX_PIC_DMA 25 +# define PXA2XX_PIC_OST_0 26 +# define PXA2XX_PIC_RTC1HZ 30 +# define PXA2XX_PIC_RTCALARM 31 + +/* DMA requests */ +# define PXA2XX_RX_RQ_I2S 2 +# define PXA2XX_TX_RQ_I2S 3 +# define PXA2XX_RX_RQ_BTUART 4 +# define PXA2XX_TX_RQ_BTUART 5 +# define PXA2XX_RX_RQ_FFUART 6 +# define PXA2XX_TX_RQ_FFUART 7 +# define PXA2XX_RX_RQ_SSP1 13 +# define PXA2XX_TX_RQ_SSP1 14 +# define PXA2XX_RX_RQ_SSP2 15 +# define PXA2XX_TX_RQ_SSP2 16 +# define PXA2XX_RX_RQ_ICP 17 +# define PXA2XX_TX_RQ_ICP 18 +# define PXA2XX_RX_RQ_STUART 19 +# define PXA2XX_TX_RQ_STUART 20 +# define PXA2XX_RX_RQ_MMCI 21 +# define PXA2XX_TX_RQ_MMCI 22 +# define PXA2XX_USB_RQ(x) ((x) + 24) +# define PXA2XX_RX_RQ_SSP3 66 +# define PXA2XX_TX_RQ_SSP3 67 + +# define PXA2XX_SDRAM_BASE 0xa0000000 +# define PXA2XX_INTERNAL_BASE 0x5c000000 +# define PXA2XX_INTERNAL_SIZE 0x40000 + +/* pxa2xx_pic.c */ +DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu); + +/* pxa2xx_gpio.c */ +DeviceState *pxa2xx_gpio_init(hwaddr base, + ARMCPU *cpu, DeviceState *pic, int lines); +void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); + +/* pxa2xx_dma.c */ +DeviceState *pxa255_dma_init(hwaddr base, qemu_irq irq); +DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq); + +/* pxa2xx_lcd.c */ +typedef struct PXA2xxLCDState PXA2xxLCDState; +PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, + hwaddr base, qemu_irq irq); +void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler); +void pxa2xx_lcdc_oritentation(void *opaque, int angle); + +/* pxa2xx_mmci.c */ +typedef struct PXA2xxMMCIState PXA2xxMMCIState; +PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, + hwaddr base, + BlockDriverState *bd, qemu_irq irq, + qemu_irq rx_dma, qemu_irq tx_dma); +void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, + qemu_irq coverswitch); + +/* pxa2xx_pcmcia.c */ +typedef struct PXA2xxPCMCIAState PXA2xxPCMCIAState; +PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, + hwaddr base); +int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card); +int pxa2xx_pcmcia_dettach(void *opaque); +void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq); + +/* pxa2xx_keypad.c */ +struct keymap { + int column; + int row; +}; +typedef struct PXA2xxKeyPadState PXA2xxKeyPadState; +PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq); +void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map, + int size); + +/* pxa2xx.c */ +typedef struct PXA2xxI2CState PXA2xxI2CState; +PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, + qemu_irq irq, uint32_t page_size); +i2c_bus *pxa2xx_i2c_bus(PXA2xxI2CState *s); + +typedef struct PXA2xxI2SState PXA2xxI2SState; +typedef struct PXA2xxFIrState PXA2xxFIrState; + +typedef struct { + ARMCPU *cpu; + DeviceState *pic; + qemu_irq reset; + MemoryRegion sdram; + MemoryRegion internal; + MemoryRegion cm_iomem; + MemoryRegion mm_iomem; + MemoryRegion pm_iomem; + DeviceState *dma; + DeviceState *gpio; + PXA2xxLCDState *lcd; + SSIBus **ssp; + PXA2xxI2CState *i2c[2]; + PXA2xxMMCIState *mmc; + PXA2xxPCMCIAState *pcmcia[2]; + PXA2xxI2SState *i2s; + PXA2xxFIrState *fir; + PXA2xxKeyPadState *kp; + + /* Power management */ + hwaddr pm_base; + uint32_t pm_regs[0x40]; + + /* Clock management */ + hwaddr cm_base; + uint32_t cm_regs[4]; + uint32_t clkcfg; + + /* Memory management */ + hwaddr mm_base; + uint32_t mm_regs[0x1a]; + + /* Performance monitoring */ + uint32_t pmnc; +} PXA2xxState; + +struct PXA2xxI2SState { + MemoryRegion iomem; + qemu_irq irq; + qemu_irq rx_dma; + qemu_irq tx_dma; + void (*data_req)(void *, int, int); + + uint32_t control[2]; + uint32_t status; + uint32_t mask; + uint32_t clk; + + int enable; + int rx_len; + int tx_len; + void (*codec_out)(void *, uint32_t); + uint32_t (*codec_in)(void *); + void *opaque; + + int fifo_len; + uint32_t fifo[16]; +}; + +# define PA_FMT "0x%08lx" +# define REG_FMT "0x" TARGET_FMT_plx + +PXA2xxState *pxa270_init(MemoryRegion *address_space, unsigned int sdram_size, + const char *revision); +PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size); + +#endif /* PXA_H */ diff --git a/include/hw/arm/sharpsl.h b/include/hw/arm/sharpsl.h new file mode 100644 index 0000000000..13981a6d03 --- /dev/null +++ b/include/hw/arm/sharpsl.h @@ -0,0 +1,17 @@ +/* + * Common declarations for the Zaurii. + * + * This file is licensed under the GNU GPL. + */ +#ifndef QEMU_SHARPSL_H +#define QEMU_SHARPSL_H + +#define zaurus_printf(format, ...) \ + fprintf(stderr, "%s: " format, __FUNCTION__, ##__VA_ARGS__) + +/* zaurus.c */ + +#define SL_PXA_PARAM_BASE 0xa0000a00 +void sl_bootparam_write(hwaddr ptr); + +#endif diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h new file mode 100644 index 0000000000..7379731afd --- /dev/null +++ b/include/hw/arm/soc_dma.h @@ -0,0 +1,116 @@ +/* + * On-chip DMA controller framework. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_SOC_DMA_H +#define HW_SOC_DMA_H 1 + + +#include "exec/memory.h" +#include "hw/irq.h" + +struct soc_dma_s; +struct soc_dma_ch_s; +typedef void (*soc_dma_io_t)(void *opaque, uint8_t *buf, int len); +typedef void (*soc_dma_transfer_t)(struct soc_dma_ch_s *ch); + +enum soc_dma_port_type { + soc_dma_port_mem, + soc_dma_port_fifo, + soc_dma_port_other, +}; + +enum soc_dma_access_type { + soc_dma_access_const, + soc_dma_access_linear, + soc_dma_access_other, +}; + +struct soc_dma_ch_s { + /* Private */ + struct soc_dma_s *dma; + int num; + QEMUTimer *timer; + + /* Set by soc_dma.c */ + int enable; + int update; + + /* This should be set by dma->setup_fn(). */ + int bytes; + /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ + enum soc_dma_access_type type[2]; + hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ + /* Private */ + void *paddr[2]; + soc_dma_io_t io_fn[2]; + void *io_opaque[2]; + + int running; + soc_dma_transfer_t transfer_fn; + + /* Set and used by the DMA module. */ + void *opaque; +}; + +struct soc_dma_s { + /* Following fields are set by the SoC DMA module and can be used + * by anybody. */ + uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */ + qemu_irq *drq; + void *opaque; + int64_t freq; + soc_dma_transfer_t transfer_fn; + soc_dma_transfer_t setup_fn; + /* Set by soc_dma_init() for use by the DMA module. */ + struct soc_dma_ch_s *ch; +}; + +/* Call to activate or stop a DMA channel. */ +void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); +/* Call after every write to one of the following fields and before + * calling soc_dma_set_request(ch, 1): + * ch->type[0...1], + * ch->vaddr[0...1], + * ch->paddr[0...1], + * or after a soc_dma_port_add_fifo() or soc_dma_port_add_mem(). */ +void soc_dma_ch_update(struct soc_dma_ch_s *ch); + +/* The SoC should call this when the DMA module is being reset. */ +void soc_dma_reset(struct soc_dma_s *s); +struct soc_dma_s *soc_dma_init(int n); + +void soc_dma_port_add_fifo(struct soc_dma_s *dma, hwaddr virt_base, + soc_dma_io_t fn, void *opaque, int out); +void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, + hwaddr virt_base, size_t size); + +static inline void soc_dma_port_add_fifo_in(struct soc_dma_s *dma, + hwaddr virt_base, soc_dma_io_t fn, void *opaque) +{ + return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 0); +} + +static inline void soc_dma_port_add_fifo_out(struct soc_dma_s *dma, + hwaddr virt_base, soc_dma_io_t fn, void *opaque) +{ + return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 1); +} + +#endif diff --git a/include/hw/audio/audio.h b/include/hw/audio/audio.h new file mode 100644 index 0000000000..428274f929 --- /dev/null +++ b/include/hw/audio/audio.h @@ -0,0 +1,25 @@ +#ifndef HW_AUDIODEV_H +#define HW_AUDIODEV_H 1 + +/* es1370.c */ +int es1370_init(PCIBus *bus); + +/* sb16.c */ +int SB16_init(ISABus *bus); + +/* adlib.c */ +int Adlib_init(ISABus *bus); + +/* gus.c */ +int GUS_init(ISABus *bus); + +/* ac97.c */ +int ac97_init(PCIBus *bus); + +/* cs4231a.c */ +int cs4231a_init(ISABus *bus); + +/* intel-hda.c + hda-audio.c */ +int intel_hda_and_codec_init(PCIBus *bus); + +#endif diff --git a/include/hw/audio/pcspk.h b/include/hw/audio/pcspk.h new file mode 100644 index 0000000000..ce8ef4f9ea --- /dev/null +++ b/include/hw/audio/pcspk.h @@ -0,0 +1,45 @@ +/* + * QEMU PC speaker emulation + * + * Copyright (c) 2006 Joachim Henke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_PCSPK_H +#define HW_PCSPK_H + +#include "hw/hw.h" +#include "hw/isa/isa.h" + +static inline ISADevice *pcspk_init(ISABus *bus, ISADevice *pit) +{ + ISADevice *dev; + + dev = isa_create(bus, "isa-pcspk"); + qdev_prop_set_uint32(&dev->qdev, "iobase", 0x61); + qdev_prop_set_ptr(&dev->qdev, "pit", pit); + qdev_init_nofail(&dev->qdev); + + return dev; +} + +int pcspk_audio_init(ISABus *bus); + +#endif /* !HW_PCSPK_H */ diff --git a/include/hw/block/block.h b/include/hw/block/block.h new file mode 100644 index 0000000000..dd115320c9 --- /dev/null +++ b/include/hw/block/block.h @@ -0,0 +1,79 @@ +/* + * Common code for block device models + * + * Copyright (C) 2012 Red Hat, Inc. + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef HW_BLOCK_COMMON_H +#define HW_BLOCK_COMMON_H + +#include "qemu-common.h" + +/* Configuration */ + +typedef struct BlockConf { + BlockDriverState *bs; + uint16_t physical_block_size; + uint16_t logical_block_size; + uint16_t min_io_size; + uint32_t opt_io_size; + int32_t bootindex; + uint32_t discard_granularity; + /* geometry, not all devices use this */ + uint32_t cyls, heads, secs; +} BlockConf; + +static inline unsigned int get_physical_block_exp(BlockConf *conf) +{ + unsigned int exp = 0, size; + + for (size = conf->physical_block_size; + size > conf->logical_block_size; + size >>= 1) { + exp++; + } + + return exp; +} + +#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ + DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \ + DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ + _conf.logical_block_size, 512), \ + DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ + _conf.physical_block_size, 512), \ + DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ + DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ + DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \ + DEFINE_PROP_UINT32("discard_granularity", _state, \ + _conf.discard_granularity, -1) + +#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \ + DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \ + DEFINE_PROP_UINT32("heads", _state, _conf.heads, 0), \ + DEFINE_PROP_UINT32("secs", _state, _conf.secs, 0) + +/* Configuration helpers */ + +void blkconf_serial(BlockConf *conf, char **serial); +int blkconf_geometry(BlockConf *conf, int *trans, + unsigned cyls_max, unsigned heads_max, unsigned secs_max); + +/* Hard disk geometry */ + +#define BIOS_ATA_TRANSLATION_AUTO 0 +#define BIOS_ATA_TRANSLATION_NONE 1 +#define BIOS_ATA_TRANSLATION_LBA 2 +#define BIOS_ATA_TRANSLATION_LARGE 3 +#define BIOS_ATA_TRANSLATION_RECHS 4 + +void hd_geometry_guess(BlockDriverState *bs, + uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, + int *ptrans); +int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs); + +#endif diff --git a/include/hw/block/fdc.h b/include/hw/block/fdc.h new file mode 100644 index 0000000000..a8f6f7c850 --- /dev/null +++ b/include/hw/block/fdc.h @@ -0,0 +1,24 @@ +#ifndef HW_FDC_H +#define HW_FDC_H + +#include "qemu-common.h" + +/* fdc.c */ +#define MAX_FD 2 + +typedef enum FDriveType { + FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ + FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ + FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ + FDRIVE_DRV_NONE = 0x03, /* No drive connected */ +} FDriveType; + +ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds); +void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, + hwaddr mmio_base, DriveInfo **fds); +void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, + DriveInfo **fds, qemu_irq *fdc_tc); + +FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i); + +#endif diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h new file mode 100644 index 0000000000..920d7596e3 --- /dev/null +++ b/include/hw/block/flash.h @@ -0,0 +1,64 @@ +#ifndef HW_FLASH_H +#define HW_FLASH_H 1 + +/* NOR flash devices */ + +#include "exec/memory.h" + +typedef struct pflash_t pflash_t; + +/* pflash_cfi01.c */ +pflash_t *pflash_cfi01_register(hwaddr base, + DeviceState *qdev, const char *name, + hwaddr size, + BlockDriverState *bs, + uint32_t sector_len, int nb_blocs, int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, int be); + +/* pflash_cfi02.c */ +pflash_t *pflash_cfi02_register(hwaddr base, + DeviceState *qdev, const char *name, + hwaddr size, + BlockDriverState *bs, uint32_t sector_len, + int nb_blocs, int nb_mappings, int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, + uint16_t unlock_addr0, uint16_t unlock_addr1, + int be); + +MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl); + +/* nand.c */ +DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id); +void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, + uint8_t ce, uint8_t wp, uint8_t gnd); +void nand_getpins(DeviceState *dev, int *rb); +void nand_setio(DeviceState *dev, uint32_t value); +uint32_t nand_getio(DeviceState *dev); +uint32_t nand_getbuswidth(DeviceState *dev); + +#define NAND_MFR_TOSHIBA 0x98 +#define NAND_MFR_SAMSUNG 0xec +#define NAND_MFR_FUJITSU 0x04 +#define NAND_MFR_NATIONAL 0x8f +#define NAND_MFR_RENESAS 0x07 +#define NAND_MFR_STMICRO 0x20 +#define NAND_MFR_HYNIX 0xad +#define NAND_MFR_MICRON 0x2c + +/* onenand.c */ +void *onenand_raw_otp(DeviceState *onenand_device); + +/* ecc.c */ +typedef struct { + uint8_t cp; /* Column parity */ + uint16_t lp[2]; /* Line parity */ + uint16_t count; +} ECCState; + +uint8_t ecc_digest(ECCState *s, uint8_t sample); +void ecc_reset(ECCState *s); +extern VMStateDescription vmstate_ecc_state; + +#endif diff --git a/include/hw/boards.h b/include/hw/boards.h new file mode 100644 index 0000000000..425bdc74a8 --- /dev/null +++ b/include/hw/boards.h @@ -0,0 +1,53 @@ +/* Declarations for use by board files for creating devices. */ + +#ifndef HW_BOARDS_H +#define HW_BOARDS_H + +#include "sysemu/blockdev.h" +#include "hw/qdev.h" + +#define DEFAULT_MACHINE_OPTIONS \ + .boot_order = "cad" + +typedef struct QEMUMachineInitArgs { + ram_addr_t ram_size; + const char *boot_device; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; + const char *cpu_model; +} QEMUMachineInitArgs; + +typedef void QEMUMachineInitFunc(QEMUMachineInitArgs *args); + +typedef void QEMUMachineResetFunc(void); + +typedef struct QEMUMachine { + const char *name; + const char *alias; + const char *desc; + QEMUMachineInitFunc *init; + QEMUMachineResetFunc *reset; + BlockInterfaceType block_default_type; + int max_cpus; + unsigned int no_serial:1, + no_parallel:1, + use_virtcon:1, + use_sclp:1, + no_floppy:1, + no_cdrom:1, + no_sdcard:1; + int is_default; + const char *default_machine_opts; + const char *boot_order; + GlobalProperty *compat_props; + struct QEMUMachine *next; + const char *hw_version; +} QEMUMachine; + +int qemu_register_machine(QEMUMachine *m); +QEMUMachine *find_default_machine(void); + +extern QEMUMachine *current_machine; + +#endif diff --git a/include/hw/bt.h b/include/hw/bt.h new file mode 100644 index 0000000000..830af94735 --- /dev/null +++ b/include/hw/bt.h @@ -0,0 +1,2190 @@ +/* + * QEMU Bluetooth HCI helpers. + * + * Copyright (C) 2007 OpenMoko, Inc. + * Written by Andrzej Zaborowski + * + * Useful definitions taken from BlueZ project's headers. + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2006 Marcel Holtmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef HW_BT_H +#define HW_BT_H 1 + +#include "hw/irq.h" + +/* BD Address */ +typedef struct { + uint8_t b[6]; +} QEMU_PACKED bdaddr_t; + +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) +#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) +#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) + +/* Copy, swap, convert BD Address */ +static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) +{ + return memcmp(ba1, ba2, sizeof(bdaddr_t)); +} +static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) +{ + memcpy(dst, src, sizeof(bdaddr_t)); +} + +#define BAINIT(orig) { .b = { \ + (orig)->b[0], (orig)->b[1], (orig)->b[2], \ + (orig)->b[3], (orig)->b[4], (orig)->b[5], \ +}, } + +/* The twisted structures of a bluetooth environment */ +struct bt_device_s; +struct bt_scatternet_s; +struct bt_piconet_s; +struct bt_link_s; + +struct bt_scatternet_s { + struct bt_device_s *slave; +}; + +struct bt_link_s { + struct bt_device_s *slave, *host; + uint16_t handle; /* Master (host) side handle */ + uint16_t acl_interval; + enum { + acl_active, + acl_hold, + acl_sniff, + acl_parked, + } acl_mode; +}; + +struct bt_device_s { + int lt_addr; + bdaddr_t bd_addr; + int mtu; + int setup; + struct bt_scatternet_s *net; + + uint8_t key[16]; + int key_present; + uint8_t class[3]; + + uint8_t reject_reason; + + uint64_t lmp_caps; + const char *lmp_name; + void (*lmp_connection_request)(struct bt_link_s *link); + void (*lmp_connection_complete)(struct bt_link_s *link); + void (*lmp_disconnect_master)(struct bt_link_s *link); + void (*lmp_disconnect_slave)(struct bt_link_s *link); + void (*lmp_acl_data)(struct bt_link_s *link, const uint8_t *data, + int start, int len); + void (*lmp_acl_resp)(struct bt_link_s *link, const uint8_t *data, + int start, int len); + void (*lmp_mode_change)(struct bt_link_s *link); + + void (*handle_destroy)(struct bt_device_s *device); + struct bt_device_s *next; /* Next in the piconet/scatternet */ + + int inquiry_scan; + int page_scan; + + uint16_t clkoff; /* Note: Always little-endian */ +}; + +/* bt.c */ +void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net); +void bt_device_done(struct bt_device_s *dev); + +/* bt-hci.c */ +struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net); + +/* bt-vhci.c */ +void bt_vhci_init(struct HCIInfo *info); + +/* bt-hci-csr.c */ +enum { + csrhci_pin_reset, + csrhci_pin_wakeup, + __csrhci_pins, +}; +qemu_irq *csrhci_pins_get(CharDriverState *chr); +CharDriverState *uart_hci_init(qemu_irq wakeup); + +/* bt-l2cap.c */ +struct bt_l2cap_device_s; +struct bt_l2cap_conn_params_s; +struct bt_l2cap_psm_s; +void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, + struct bt_scatternet_s *net); +void bt_l2cap_device_done(struct bt_l2cap_device_s *dev); +void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, + int min_mtu, int (*new_channel)(struct bt_l2cap_device_s *dev, + struct bt_l2cap_conn_params_s *params)); + +struct bt_l2cap_device_s { + struct bt_device_s device; + struct bt_l2cap_psm_s *first_psm; +}; + +struct bt_l2cap_conn_params_s { + /* Input */ + uint8_t *(*sdu_out)(struct bt_l2cap_conn_params_s *chan, int len); + void (*sdu_submit)(struct bt_l2cap_conn_params_s *chan); + int remote_mtu; + /* Output */ + void *opaque; + void (*sdu_in)(void *opaque, const uint8_t *data, int len); + void (*close)(void *opaque); +}; + +enum bt_l2cap_psm_predef { + BT_PSM_SDP = 0x0001, + BT_PSM_RFCOMM = 0x0003, + BT_PSM_TELEPHONY = 0x0005, + BT_PSM_TCS = 0x0007, + BT_PSM_BNEP = 0x000f, + BT_PSM_HID_CTRL = 0x0011, + BT_PSM_HID_INTR = 0x0013, + BT_PSM_UPNP = 0x0015, + BT_PSM_AVCTP = 0x0017, + BT_PSM_AVDTP = 0x0019, +}; + +/* bt-sdp.c */ +void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev); + +/* bt-hid.c */ +struct bt_device_s *bt_mouse_init(struct bt_scatternet_s *net); +struct bt_device_s *bt_tablet_init(struct bt_scatternet_s *net); +struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net); + +/* Link Management Protocol layer defines */ + +#define LLID_ACLU_CONT 0x1 +#define LLID_ACLU_START 0x2 +#define LLID_ACLC 0x3 + +enum lmp_pdu_type { + LMP_NAME_REQ = 0x0001, + LMP_NAME_RES = 0x0002, + LMP_ACCEPTED = 0x0003, + LMP_NOT_ACCEPTED = 0x0004, + LMP_CLKOFFSET_REQ = 0x0005, + LMP_CLKOFFSET_RES = 0x0006, + LMP_DETACH = 0x0007, + LMP_IN_RAND = 0x0008, + LMP_COMB_KEY = 0x0009, + LMP_UNIT_KEY = 0x000a, + LMP_AU_RAND = 0x000b, + LMP_SRES = 0x000c, + LMP_TEMP_RAND = 0x000d, + LMP_TEMP_KEY = 0x000e, + LMP_CRYPT_MODE_REQ = 0x000f, + LMP_CRYPT_KEY_SIZE_REQ = 0x0010, + LMP_START_ENCRYPT_REQ = 0x0011, + LMP_STOP_ENCRYPT_REQ = 0x0012, + LMP_SWITCH_REQ = 0x0013, + LMP_HOLD = 0x0014, + LMP_HOLD_REQ = 0x0015, + LMP_SNIFF_REQ = 0x0017, + LMP_UNSNIFF_REQ = 0x0018, + LMP_LMP_PARK_REQ = 0x0019, + LMP_SET_BCAST_SCAN_WND = 0x001b, + LMP_MODIFY_BEACON = 0x001c, + LMP_UNPARK_BD_ADDR_REQ = 0x001d, + LMP_UNPARK_PM_ADDR_REQ = 0x001e, + LMP_INCR_POWER_REQ = 0x001f, + LMP_DECR_POWER_REQ = 0x0020, + LMP_MAX_POWER = 0x0021, + LMP_MIN_POWER = 0x0022, + LMP_AUTO_RATE = 0x0023, + LMP_PREFERRED_RATE = 0x0024, + LMP_VERSION_REQ = 0x0025, + LMP_VERSION_RES = 0x0026, + LMP_FEATURES_REQ = 0x0027, + LMP_FEATURES_RES = 0x0028, + LMP_QUALITY_OF_SERVICE = 0x0029, + LMP_QOS_REQ = 0x002a, + LMP_RM_SCO_LINK_REQ = 0x002b, + LMP_SCO_LINK_REQ = 0x002c, + LMP_MAX_SLOT = 0x002d, + LMP_MAX_SLOT_REQ = 0x002e, + LMP_TIMING_ACCURACY_REQ = 0x002f, + LMP_TIMING_ACCURACY_RES = 0x0030, + LMP_SETUP_COMPLETE = 0x0031, + LMP_USE_SEMIPERM_KEY = 0x0032, + LMP_HOST_CONNECTION_REQ = 0x0033, + LMP_SLOT_OFFSET = 0x0034, + LMP_PAGE_MODE_REQ = 0x0035, + LMP_PAGE_SCAN_MODE_REQ = 0x0036, + LMP_SUPERVISION_TIMEOUT = 0x0037, + LMP_TEST_ACTIVATE = 0x0038, + LMP_TEST_CONTROL = 0x0039, + LMP_CRYPT_KEY_MASK_REQ = 0x003a, + LMP_CRYPT_KEY_MASK_RES = 0x003b, + LMP_SET_AFH = 0x003c, + LMP_ACCEPTED_EXT = 0x7f01, + LMP_NOT_ACCEPTED_EXT = 0x7f02, + LMP_FEATURES_REQ_EXT = 0x7f03, + LMP_FEATURES_RES_EXT = 0x7f04, + LMP_PACKET_TYPE_TBL_REQ = 0x7f0b, + LMP_ESCO_LINK_REQ = 0x7f0c, + LMP_RM_ESCO_LINK_REQ = 0x7f0d, + LMP_CHANNEL_CLASS_REQ = 0x7f10, + LMP_CHANNEL_CLASS = 0x7f11, +}; + +/* Host Controller Interface layer defines */ + +enum hci_packet_type { + HCI_COMMAND_PKT = 0x01, + HCI_ACLDATA_PKT = 0x02, + HCI_SCODATA_PKT = 0x03, + HCI_EVENT_PKT = 0x04, + HCI_VENDOR_PKT = 0xff, +}; + +enum bt_packet_type { + HCI_2DH1 = 1 << 1, + HCI_3DH1 = 1 << 2, + HCI_DM1 = 1 << 3, + HCI_DH1 = 1 << 4, + HCI_2DH3 = 1 << 8, + HCI_3DH3 = 1 << 9, + HCI_DM3 = 1 << 10, + HCI_DH3 = 1 << 11, + HCI_2DH5 = 1 << 12, + HCI_3DH5 = 1 << 13, + HCI_DM5 = 1 << 14, + HCI_DH5 = 1 << 15, +}; + +enum sco_packet_type { + HCI_HV1 = 1 << 5, + HCI_HV2 = 1 << 6, + HCI_HV3 = 1 << 7, +}; + +enum ev_packet_type { + HCI_EV3 = 1 << 3, + HCI_EV4 = 1 << 4, + HCI_EV5 = 1 << 5, + HCI_2EV3 = 1 << 6, + HCI_3EV3 = 1 << 7, + HCI_2EV5 = 1 << 8, + HCI_3EV5 = 1 << 9, +}; + +enum hci_error_code { + HCI_SUCCESS = 0x00, + HCI_UNKNOWN_COMMAND = 0x01, + HCI_NO_CONNECTION = 0x02, + HCI_HARDWARE_FAILURE = 0x03, + HCI_PAGE_TIMEOUT = 0x04, + HCI_AUTHENTICATION_FAILURE = 0x05, + HCI_PIN_OR_KEY_MISSING = 0x06, + HCI_MEMORY_FULL = 0x07, + HCI_CONNECTION_TIMEOUT = 0x08, + HCI_MAX_NUMBER_OF_CONNECTIONS = 0x09, + HCI_MAX_NUMBER_OF_SCO_CONNECTIONS = 0x0a, + HCI_ACL_CONNECTION_EXISTS = 0x0b, + HCI_COMMAND_DISALLOWED = 0x0c, + HCI_REJECTED_LIMITED_RESOURCES = 0x0d, + HCI_REJECTED_SECURITY = 0x0e, + HCI_REJECTED_PERSONAL = 0x0f, + HCI_HOST_TIMEOUT = 0x10, + HCI_UNSUPPORTED_FEATURE = 0x11, + HCI_INVALID_PARAMETERS = 0x12, + HCI_OE_USER_ENDED_CONNECTION = 0x13, + HCI_OE_LOW_RESOURCES = 0x14, + HCI_OE_POWER_OFF = 0x15, + HCI_CONNECTION_TERMINATED = 0x16, + HCI_REPEATED_ATTEMPTS = 0x17, + HCI_PAIRING_NOT_ALLOWED = 0x18, + HCI_UNKNOWN_LMP_PDU = 0x19, + HCI_UNSUPPORTED_REMOTE_FEATURE = 0x1a, + HCI_SCO_OFFSET_REJECTED = 0x1b, + HCI_SCO_INTERVAL_REJECTED = 0x1c, + HCI_AIR_MODE_REJECTED = 0x1d, + HCI_INVALID_LMP_PARAMETERS = 0x1e, + HCI_UNSPECIFIED_ERROR = 0x1f, + HCI_UNSUPPORTED_LMP_PARAMETER_VALUE = 0x20, + HCI_ROLE_CHANGE_NOT_ALLOWED = 0x21, + HCI_LMP_RESPONSE_TIMEOUT = 0x22, + HCI_LMP_ERROR_TRANSACTION_COLLISION = 0x23, + HCI_LMP_PDU_NOT_ALLOWED = 0x24, + HCI_ENCRYPTION_MODE_NOT_ACCEPTED = 0x25, + HCI_UNIT_LINK_KEY_USED = 0x26, + HCI_QOS_NOT_SUPPORTED = 0x27, + HCI_INSTANT_PASSED = 0x28, + HCI_PAIRING_NOT_SUPPORTED = 0x29, + HCI_TRANSACTION_COLLISION = 0x2a, + HCI_QOS_UNACCEPTABLE_PARAMETER = 0x2c, + HCI_QOS_REJECTED = 0x2d, + HCI_CLASSIFICATION_NOT_SUPPORTED = 0x2e, + HCI_INSUFFICIENT_SECURITY = 0x2f, + HCI_PARAMETER_OUT_OF_RANGE = 0x30, + HCI_ROLE_SWITCH_PENDING = 0x32, + HCI_SLOT_VIOLATION = 0x34, + HCI_ROLE_SWITCH_FAILED = 0x35, +}; + +enum acl_flag_bits { + ACL_CONT = 1 << 0, + ACL_START = 1 << 1, + ACL_ACTIVE_BCAST = 1 << 2, + ACL_PICO_BCAST = 1 << 3, +}; + +enum baseband_link_type { + SCO_LINK = 0x00, + ACL_LINK = 0x01, +}; + +enum lmp_feature_bits0 { + LMP_3SLOT = 1 << 0, + LMP_5SLOT = 1 << 1, + LMP_ENCRYPT = 1 << 2, + LMP_SOFFSET = 1 << 3, + LMP_TACCURACY = 1 << 4, + LMP_RSWITCH = 1 << 5, + LMP_HOLD_MODE = 1 << 6, + LMP_SNIFF_MODE = 1 << 7, +}; + +enum lmp_feature_bits1 { + LMP_PARK = 1 << 0, + LMP_RSSI = 1 << 1, + LMP_QUALITY = 1 << 2, + LMP_SCO = 1 << 3, + LMP_HV2 = 1 << 4, + LMP_HV3 = 1 << 5, + LMP_ULAW = 1 << 6, + LMP_ALAW = 1 << 7, +}; + +enum lmp_feature_bits2 { + LMP_CVSD = 1 << 0, + LMP_PSCHEME = 1 << 1, + LMP_PCONTROL = 1 << 2, + LMP_TRSP_SCO = 1 << 3, + LMP_BCAST_ENC = 1 << 7, +}; + +enum lmp_feature_bits3 { + LMP_EDR_ACL_2M = 1 << 1, + LMP_EDR_ACL_3M = 1 << 2, + LMP_ENH_ISCAN = 1 << 3, + LMP_ILACE_ISCAN = 1 << 4, + LMP_ILACE_PSCAN = 1 << 5, + LMP_RSSI_INQ = 1 << 6, + LMP_ESCO = 1 << 7, +}; + +enum lmp_feature_bits4 { + LMP_EV4 = 1 << 0, + LMP_EV5 = 1 << 1, + LMP_AFH_CAP_SLV = 1 << 3, + LMP_AFH_CLS_SLV = 1 << 4, + LMP_EDR_3SLOT = 1 << 7, +}; + +enum lmp_feature_bits5 { + LMP_EDR_5SLOT = 1 << 0, + LMP_SNIFF_SUBR = 1 << 1, + LMP_AFH_CAP_MST = 1 << 3, + LMP_AFH_CLS_MST = 1 << 4, + LMP_EDR_ESCO_2M = 1 << 5, + LMP_EDR_ESCO_3M = 1 << 6, + LMP_EDR_3S_ESCO = 1 << 7, +}; + +enum lmp_feature_bits6 { + LMP_EXT_INQ = 1 << 0, +}; + +enum lmp_feature_bits7 { + LMP_EXT_FEAT = 1 << 7, +}; + +enum hci_link_policy { + HCI_LP_RSWITCH = 1 << 0, + HCI_LP_HOLD = 1 << 1, + HCI_LP_SNIFF = 1 << 2, + HCI_LP_PARK = 1 << 3, +}; + +enum hci_link_mode { + HCI_LM_ACCEPT = 1 << 15, + HCI_LM_MASTER = 1 << 0, + HCI_LM_AUTH = 1 << 1, + HCI_LM_ENCRYPT = 1 << 2, + HCI_LM_TRUSTED = 1 << 3, + HCI_LM_RELIABLE = 1 << 4, + HCI_LM_SECURE = 1 << 5, +}; + +/* HCI Commands */ + +/* Link Control */ +#define OGF_LINK_CTL 0x01 + +#define OCF_INQUIRY 0x0001 +typedef struct { + uint8_t lap[3]; + uint8_t length; /* 1.28s units */ + uint8_t num_rsp; +} QEMU_PACKED inquiry_cp; +#define INQUIRY_CP_SIZE 5 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} QEMU_PACKED status_bdaddr_rp; +#define STATUS_BDADDR_RP_SIZE 7 + +#define OCF_INQUIRY_CANCEL 0x0002 + +#define OCF_PERIODIC_INQUIRY 0x0003 +typedef struct { + uint16_t max_period; /* 1.28s units */ + uint16_t min_period; /* 1.28s units */ + uint8_t lap[3]; + uint8_t length; /* 1.28s units */ + uint8_t num_rsp; +} QEMU_PACKED periodic_inquiry_cp; +#define PERIODIC_INQUIRY_CP_SIZE 9 + +#define OCF_EXIT_PERIODIC_INQUIRY 0x0004 + +#define OCF_CREATE_CONN 0x0005 +typedef struct { + bdaddr_t bdaddr; + uint16_t pkt_type; + uint8_t pscan_rep_mode; + uint8_t pscan_mode; + uint16_t clock_offset; + uint8_t role_switch; +} QEMU_PACKED create_conn_cp; +#define CREATE_CONN_CP_SIZE 13 + +#define OCF_DISCONNECT 0x0006 +typedef struct { + uint16_t handle; + uint8_t reason; +} QEMU_PACKED disconnect_cp; +#define DISCONNECT_CP_SIZE 3 + +#define OCF_ADD_SCO 0x0007 +typedef struct { + uint16_t handle; + uint16_t pkt_type; +} QEMU_PACKED add_sco_cp; +#define ADD_SCO_CP_SIZE 4 + +#define OCF_CREATE_CONN_CANCEL 0x0008 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} QEMU_PACKED create_conn_cancel_cp; +#define CREATE_CONN_CANCEL_CP_SIZE 6 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} QEMU_PACKED create_conn_cancel_rp; +#define CREATE_CONN_CANCEL_RP_SIZE 7 + +#define OCF_ACCEPT_CONN_REQ 0x0009 +typedef struct { + bdaddr_t bdaddr; + uint8_t role; +} QEMU_PACKED accept_conn_req_cp; +#define ACCEPT_CONN_REQ_CP_SIZE 7 + +#define OCF_REJECT_CONN_REQ 0x000A +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} QEMU_PACKED reject_conn_req_cp; +#define REJECT_CONN_REQ_CP_SIZE 7 + +#define OCF_LINK_KEY_REPLY 0x000B +typedef struct { + bdaddr_t bdaddr; + uint8_t link_key[16]; +} QEMU_PACKED link_key_reply_cp; +#define LINK_KEY_REPLY_CP_SIZE 22 + +#define OCF_LINK_KEY_NEG_REPLY 0x000C + +#define OCF_PIN_CODE_REPLY 0x000D +typedef struct { + bdaddr_t bdaddr; + uint8_t pin_len; + uint8_t pin_code[16]; +} QEMU_PACKED pin_code_reply_cp; +#define PIN_CODE_REPLY_CP_SIZE 23 + +#define OCF_PIN_CODE_NEG_REPLY 0x000E + +#define OCF_SET_CONN_PTYPE 0x000F +typedef struct { + uint16_t handle; + uint16_t pkt_type; +} QEMU_PACKED set_conn_ptype_cp; +#define SET_CONN_PTYPE_CP_SIZE 4 + +#define OCF_AUTH_REQUESTED 0x0011 +typedef struct { + uint16_t handle; +} QEMU_PACKED auth_requested_cp; +#define AUTH_REQUESTED_CP_SIZE 2 + +#define OCF_SET_CONN_ENCRYPT 0x0013 +typedef struct { + uint16_t handle; + uint8_t encrypt; +} QEMU_PACKED set_conn_encrypt_cp; +#define SET_CONN_ENCRYPT_CP_SIZE 3 + +#define OCF_CHANGE_CONN_LINK_KEY 0x0015 +typedef struct { + uint16_t handle; +} QEMU_PACKED change_conn_link_key_cp; +#define CHANGE_CONN_LINK_KEY_CP_SIZE 2 + +#define OCF_MASTER_LINK_KEY 0x0017 +typedef struct { + uint8_t key_flag; +} QEMU_PACKED master_link_key_cp; +#define MASTER_LINK_KEY_CP_SIZE 1 + +#define OCF_REMOTE_NAME_REQ 0x0019 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_mode; + uint16_t clock_offset; +} QEMU_PACKED remote_name_req_cp; +#define REMOTE_NAME_REQ_CP_SIZE 10 + +#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A +typedef struct { + bdaddr_t bdaddr; +} QEMU_PACKED remote_name_req_cancel_cp; +#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} QEMU_PACKED remote_name_req_cancel_rp; +#define REMOTE_NAME_REQ_CANCEL_RP_SIZE 7 + +#define OCF_READ_REMOTE_FEATURES 0x001B +typedef struct { + uint16_t handle; +} QEMU_PACKED read_remote_features_cp; +#define READ_REMOTE_FEATURES_CP_SIZE 2 + +#define OCF_READ_REMOTE_EXT_FEATURES 0x001C +typedef struct { + uint16_t handle; + uint8_t page_num; +} QEMU_PACKED read_remote_ext_features_cp; +#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3 + +#define OCF_READ_REMOTE_VERSION 0x001D +typedef struct { + uint16_t handle; +} QEMU_PACKED read_remote_version_cp; +#define READ_REMOTE_VERSION_CP_SIZE 2 + +#define OCF_READ_CLOCK_OFFSET 0x001F +typedef struct { + uint16_t handle; +} QEMU_PACKED read_clock_offset_cp; +#define READ_CLOCK_OFFSET_CP_SIZE 2 + +#define OCF_READ_LMP_HANDLE 0x0020 +typedef struct { + uint16_t handle; +} QEMU_PACKED read_lmp_handle_cp; +#define READ_LMP_HANDLE_CP_SIZE 2 + +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t lmp_handle; + uint32_t reserved; +} QEMU_PACKED read_lmp_handle_rp; +#define READ_LMP_HANDLE_RP_SIZE 8 + +#define OCF_SETUP_SYNC_CONN 0x0028 +typedef struct { + uint16_t handle; + uint32_t tx_bandwith; + uint32_t rx_bandwith; + uint16_t max_latency; + uint16_t voice_setting; + uint8_t retrans_effort; + uint16_t pkt_type; +} QEMU_PACKED setup_sync_conn_cp; +#define SETUP_SYNC_CONN_CP_SIZE 17 + +#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029 +typedef struct { + bdaddr_t bdaddr; + uint32_t tx_bandwith; + uint32_t rx_bandwith; + uint16_t max_latency; + uint16_t voice_setting; + uint8_t retrans_effort; + uint16_t pkt_type; +} QEMU_PACKED accept_sync_conn_req_cp; +#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21 + +#define OCF_REJECT_SYNC_CONN_REQ 0x002A +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} QEMU_PACKED reject_sync_conn_req_cp; +#define REJECT_SYNC_CONN_REQ_CP_SIZE 7 + +/* Link Policy */ +#define OGF_LINK_POLICY 0x02 + +#define OCF_HOLD_MODE 0x0001 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; +} QEMU_PACKED hold_mode_cp; +#define HOLD_MODE_CP_SIZE 6 + +#define OCF_SNIFF_MODE 0x0003 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; + uint16_t attempt; + uint16_t timeout; +} QEMU_PACKED sniff_mode_cp; +#define SNIFF_MODE_CP_SIZE 10 + +#define OCF_EXIT_SNIFF_MODE 0x0004 +typedef struct { + uint16_t handle; +} QEMU_PACKED exit_sniff_mode_cp; +#define EXIT_SNIFF_MODE_CP_SIZE 2 + +#define OCF_PARK_MODE 0x0005 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; +} QEMU_PACKED park_mode_cp; +#define PARK_MODE_CP_SIZE 6 + +#define OCF_EXIT_PARK_MODE 0x0006 +typedef struct { + uint16_t handle; +} QEMU_PACKED exit_park_mode_cp; +#define EXIT_PARK_MODE_CP_SIZE 2 + +#define OCF_QOS_SETUP 0x0007 +typedef struct { + uint8_t service_type; /* 1 = best effort */ + uint32_t token_rate; /* Byte per seconds */ + uint32_t peak_bandwidth; /* Byte per seconds */ + uint32_t latency; /* Microseconds */ + uint32_t delay_variation; /* Microseconds */ +} QEMU_PACKED hci_qos; +#define HCI_QOS_CP_SIZE 17 +typedef struct { + uint16_t handle; + uint8_t flags; /* Reserved */ + hci_qos qos; +} QEMU_PACKED qos_setup_cp; +#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE) + +#define OCF_ROLE_DISCOVERY 0x0009 +typedef struct { + uint16_t handle; +} QEMU_PACKED role_discovery_cp; +#define ROLE_DISCOVERY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t role; +} QEMU_PACKED role_discovery_rp; +#define ROLE_DISCOVERY_RP_SIZE 4 + +#define OCF_SWITCH_ROLE 0x000B +typedef struct { + bdaddr_t bdaddr; + uint8_t role; +} QEMU_PACKED switch_role_cp; +#define SWITCH_ROLE_CP_SIZE 7 + +#define OCF_READ_LINK_POLICY 0x000C +typedef struct { + uint16_t handle; +} QEMU_PACKED read_link_policy_cp; +#define READ_LINK_POLICY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t policy; +} QEMU_PACKED read_link_policy_rp; +#define READ_LINK_POLICY_RP_SIZE 5 + +#define OCF_WRITE_LINK_POLICY 0x000D +typedef struct { + uint16_t handle; + uint16_t policy; +} QEMU_PACKED write_link_policy_cp; +#define WRITE_LINK_POLICY_CP_SIZE 4 +typedef struct { + uint8_t status; + uint16_t handle; +} QEMU_PACKED write_link_policy_rp; +#define WRITE_LINK_POLICY_RP_SIZE 3 + +#define OCF_READ_DEFAULT_LINK_POLICY 0x000E + +#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F + +#define OCF_FLOW_SPECIFICATION 0x0010 + +#define OCF_SNIFF_SUBRATE 0x0011 +typedef struct { + uint16_t handle; + uint16_t max_remote_latency; + uint16_t max_local_latency; + uint16_t min_remote_timeout; + uint16_t min_local_timeout; +} QEMU_PACKED sniff_subrate_cp; +#define SNIFF_SUBRATE_CP_SIZE 10 + +/* Host Controller and Baseband */ +#define OGF_HOST_CTL 0x03 + +#define OCF_SET_EVENT_MASK 0x0001 +typedef struct { + uint8_t mask[8]; +} QEMU_PACKED set_event_mask_cp; +#define SET_EVENT_MASK_CP_SIZE 8 + +#define OCF_RESET 0x0003 + +#define OCF_SET_EVENT_FLT 0x0005 +typedef struct { + uint8_t flt_type; + uint8_t cond_type; + uint8_t condition[0]; +} QEMU_PACKED set_event_flt_cp; +#define SET_EVENT_FLT_CP_SIZE 2 + +enum bt_filter_type { + FLT_CLEAR_ALL = 0x00, + FLT_INQ_RESULT = 0x01, + FLT_CONN_SETUP = 0x02, +}; +enum inq_result_cond_type { + INQ_RESULT_RETURN_ALL = 0x00, + INQ_RESULT_RETURN_CLASS = 0x01, + INQ_RESULT_RETURN_BDADDR = 0x02, +}; +enum conn_setup_cond_type { + CONN_SETUP_ALLOW_ALL = 0x00, + CONN_SETUP_ALLOW_CLASS = 0x01, + CONN_SETUP_ALLOW_BDADDR = 0x02, +}; +enum conn_setup_cond { + CONN_SETUP_AUTO_OFF = 0x01, + CONN_SETUP_AUTO_ON = 0x02, +}; + +#define OCF_FLUSH 0x0008 +typedef struct { + uint16_t handle; +} QEMU_PACKED flush_cp; +#define FLUSH_CP_SIZE 2 + +typedef struct { + uint8_t status; + uint16_t handle; +} QEMU_PACKED flush_rp; +#define FLUSH_RP_SIZE 3 + +#define OCF_READ_PIN_TYPE 0x0009 +typedef struct { + uint8_t status; + uint8_t pin_type; +} QEMU_PACKED read_pin_type_rp; +#define READ_PIN_TYPE_RP_SIZE 2 + +#define OCF_WRITE_PIN_TYPE 0x000A +typedef struct { + uint8_t pin_type; +} QEMU_PACKED write_pin_type_cp; +#define WRITE_PIN_TYPE_CP_SIZE 1 + +#define OCF_CREATE_NEW_UNIT_KEY 0x000B + +#define OCF_READ_STORED_LINK_KEY 0x000D +typedef struct { + bdaddr_t bdaddr; + uint8_t read_all; +} QEMU_PACKED read_stored_link_key_cp; +#define READ_STORED_LINK_KEY_CP_SIZE 7 +typedef struct { + uint8_t status; + uint16_t max_keys; + uint16_t num_keys; +} QEMU_PACKED read_stored_link_key_rp; +#define READ_STORED_LINK_KEY_RP_SIZE 5 + +#define OCF_WRITE_STORED_LINK_KEY 0x0011 +typedef struct { + uint8_t num_keys; + /* variable length part */ +} QEMU_PACKED write_stored_link_key_cp; +#define WRITE_STORED_LINK_KEY_CP_SIZE 1 +typedef struct { + uint8_t status; + uint8_t num_keys; +} QEMU_PACKED write_stored_link_key_rp; +#define READ_WRITE_LINK_KEY_RP_SIZE 2 + +#define OCF_DELETE_STORED_LINK_KEY 0x0012 +typedef struct { + bdaddr_t bdaddr; + uint8_t delete_all; +} QEMU_PACKED delete_stored_link_key_cp; +#define DELETE_STORED_LINK_KEY_CP_SIZE 7 +typedef struct { + uint8_t status; + uint16_t num_keys; +} QEMU_PACKED delete_stored_link_key_rp; +#define DELETE_STORED_LINK_KEY_RP_SIZE 3 + +#define OCF_CHANGE_LOCAL_NAME 0x0013 +typedef struct { + char name[248]; +} QEMU_PACKED change_local_name_cp; +#define CHANGE_LOCAL_NAME_CP_SIZE 248 + +#define OCF_READ_LOCAL_NAME 0x0014 +typedef struct { + uint8_t status; + char name[248]; +} QEMU_PACKED read_local_name_rp; +#define READ_LOCAL_NAME_RP_SIZE 249 + +#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015 +typedef struct { + uint8_t status; + uint16_t timeout; +} QEMU_PACKED read_conn_accept_timeout_rp; +#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016 +typedef struct { + uint16_t timeout; +} QEMU_PACKED write_conn_accept_timeout_cp; +#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2 + +#define OCF_READ_PAGE_TIMEOUT 0x0017 +typedef struct { + uint8_t status; + uint16_t timeout; +} QEMU_PACKED read_page_timeout_rp; +#define READ_PAGE_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_PAGE_TIMEOUT 0x0018 +typedef struct { + uint16_t timeout; +} QEMU_PACKED write_page_timeout_cp; +#define WRITE_PAGE_TIMEOUT_CP_SIZE 2 + +#define OCF_READ_SCAN_ENABLE 0x0019 +typedef struct { + uint8_t status; + uint8_t enable; +} QEMU_PACKED read_scan_enable_rp; +#define READ_SCAN_ENABLE_RP_SIZE 2 + +#define OCF_WRITE_SCAN_ENABLE 0x001A +typedef struct { + uint8_t scan_enable; +} QEMU_PACKED write_scan_enable_cp; +#define WRITE_SCAN_ENABLE_CP_SIZE 1 + +enum scan_enable_bits { + SCAN_DISABLED = 0, + SCAN_INQUIRY = 1 << 0, + SCAN_PAGE = 1 << 1, +}; + +#define OCF_READ_PAGE_ACTIVITY 0x001B +typedef struct { + uint8_t status; + uint16_t interval; + uint16_t window; +} QEMU_PACKED read_page_activity_rp; +#define READ_PAGE_ACTIVITY_RP_SIZE 5 + +#define OCF_WRITE_PAGE_ACTIVITY 0x001C +typedef struct { + uint16_t interval; + uint16_t window; +} QEMU_PACKED write_page_activity_cp; +#define WRITE_PAGE_ACTIVITY_CP_SIZE 4 + +#define OCF_READ_INQ_ACTIVITY 0x001D +typedef struct { + uint8_t status; + uint16_t interval; + uint16_t window; +} QEMU_PACKED read_inq_activity_rp; +#define READ_INQ_ACTIVITY_RP_SIZE 5 + +#define OCF_WRITE_INQ_ACTIVITY 0x001E +typedef struct { + uint16_t interval; + uint16_t window; +} QEMU_PACKED write_inq_activity_cp; +#define WRITE_INQ_ACTIVITY_CP_SIZE 4 + +#define OCF_READ_AUTH_ENABLE 0x001F + +#define OCF_WRITE_AUTH_ENABLE 0x0020 + +#define AUTH_DISABLED 0x00 +#define AUTH_ENABLED 0x01 + +#define OCF_READ_ENCRYPT_MODE 0x0021 + +#define OCF_WRITE_ENCRYPT_MODE 0x0022 + +#define ENCRYPT_DISABLED 0x00 +#define ENCRYPT_P2P 0x01 +#define ENCRYPT_BOTH 0x02 + +#define OCF_READ_CLASS_OF_DEV 0x0023 +typedef struct { + uint8_t status; + uint8_t dev_class[3]; +} QEMU_PACKED read_class_of_dev_rp; +#define READ_CLASS_OF_DEV_RP_SIZE 4 + +#define OCF_WRITE_CLASS_OF_DEV 0x0024 +typedef struct { + uint8_t dev_class[3]; +} QEMU_PACKED write_class_of_dev_cp; +#define WRITE_CLASS_OF_DEV_CP_SIZE 3 + +#define OCF_READ_VOICE_SETTING 0x0025 +typedef struct { + uint8_t status; + uint16_t voice_setting; +} QEMU_PACKED read_voice_setting_rp; +#define READ_VOICE_SETTING_RP_SIZE 3 + +#define OCF_WRITE_VOICE_SETTING 0x0026 +typedef struct { + uint16_t voice_setting; +} QEMU_PACKED write_voice_setting_cp; +#define WRITE_VOICE_SETTING_CP_SIZE 2 + +#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027 + +#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028 + +#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029 + +#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A + +#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B + +#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C + +#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D +typedef struct { + uint16_t handle; + uint8_t type; +} QEMU_PACKED read_transmit_power_level_cp; +#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t level; +} QEMU_PACKED read_transmit_power_level_rp; +#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4 + +#define OCF_HOST_BUFFER_SIZE 0x0033 +typedef struct { + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_max_pkt; + uint16_t sco_max_pkt; +} QEMU_PACKED host_buffer_size_cp; +#define HOST_BUFFER_SIZE_CP_SIZE 7 + +#define OCF_HOST_NUMBER_OF_COMPLETED_PACKETS 0x0035 + +#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t link_sup_to; +} QEMU_PACKED read_link_supervision_timeout_rp; +#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5 + +#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037 +typedef struct { + uint16_t handle; + uint16_t link_sup_to; +} QEMU_PACKED write_link_supervision_timeout_cp; +#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4 +typedef struct { + uint8_t status; + uint16_t handle; +} QEMU_PACKED write_link_supervision_timeout_rp; +#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3 + +#define OCF_READ_NUM_SUPPORTED_IAC 0x0038 + +#define MAX_IAC_LAP 0x40 +#define OCF_READ_CURRENT_IAC_LAP 0x0039 +typedef struct { + uint8_t status; + uint8_t num_current_iac; + uint8_t lap[MAX_IAC_LAP][3]; +} QEMU_PACKED read_current_iac_lap_rp; +#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP + +#define OCF_WRITE_CURRENT_IAC_LAP 0x003A +typedef struct { + uint8_t num_current_iac; + uint8_t lap[MAX_IAC_LAP][3]; +} QEMU_PACKED write_current_iac_lap_cp; +#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP + +#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B + +#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C + +#define OCF_READ_PAGE_SCAN_MODE 0x003D + +#define OCF_WRITE_PAGE_SCAN_MODE 0x003E + +#define OCF_SET_AFH_CLASSIFICATION 0x003F +typedef struct { + uint8_t map[10]; +} QEMU_PACKED set_afh_classification_cp; +#define SET_AFH_CLASSIFICATION_CP_SIZE 10 +typedef struct { + uint8_t status; +} QEMU_PACKED set_afh_classification_rp; +#define SET_AFH_CLASSIFICATION_RP_SIZE 1 + +#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042 +typedef struct { + uint8_t status; + uint8_t type; +} QEMU_PACKED read_inquiry_scan_type_rp; +#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043 +typedef struct { + uint8_t type; +} QEMU_PACKED write_inquiry_scan_type_cp; +#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1 +typedef struct { + uint8_t status; +} QEMU_PACKED write_inquiry_scan_type_rp; +#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1 + +#define OCF_READ_INQUIRY_MODE 0x0044 +typedef struct { + uint8_t status; + uint8_t mode; +} QEMU_PACKED read_inquiry_mode_rp; +#define READ_INQUIRY_MODE_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_MODE 0x0045 +typedef struct { + uint8_t mode; +} QEMU_PACKED write_inquiry_mode_cp; +#define WRITE_INQUIRY_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} QEMU_PACKED write_inquiry_mode_rp; +#define WRITE_INQUIRY_MODE_RP_SIZE 1 + +#define OCF_READ_PAGE_SCAN_TYPE 0x0046 + +#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047 + +#define OCF_READ_AFH_MODE 0x0048 +typedef struct { + uint8_t status; + uint8_t mode; +} QEMU_PACKED read_afh_mode_rp; +#define READ_AFH_MODE_RP_SIZE 2 + +#define OCF_WRITE_AFH_MODE 0x0049 +typedef struct { + uint8_t mode; +} QEMU_PACKED write_afh_mode_cp; +#define WRITE_AFH_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} QEMU_PACKED write_afh_mode_rp; +#define WRITE_AFH_MODE_RP_SIZE 1 + +#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051 +typedef struct { + uint8_t status; + uint8_t fec; + uint8_t data[240]; +} QEMU_PACKED read_ext_inquiry_response_rp; +#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242 + +#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052 +typedef struct { + uint8_t fec; + uint8_t data[240]; +} QEMU_PACKED write_ext_inquiry_response_cp; +#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241 +typedef struct { + uint8_t status; +} QEMU_PACKED write_ext_inquiry_response_rp; +#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1 + +/* Informational Parameters */ +#define OGF_INFO_PARAM 0x04 + +#define OCF_READ_LOCAL_VERSION 0x0001 +typedef struct { + uint8_t status; + uint8_t hci_ver; + uint16_t hci_rev; + uint8_t lmp_ver; + uint16_t manufacturer; + uint16_t lmp_subver; +} QEMU_PACKED read_local_version_rp; +#define READ_LOCAL_VERSION_RP_SIZE 9 + +#define OCF_READ_LOCAL_COMMANDS 0x0002 +typedef struct { + uint8_t status; + uint8_t commands[64]; +} QEMU_PACKED read_local_commands_rp; +#define READ_LOCAL_COMMANDS_RP_SIZE 65 + +#define OCF_READ_LOCAL_FEATURES 0x0003 +typedef struct { + uint8_t status; + uint8_t features[8]; +} QEMU_PACKED read_local_features_rp; +#define READ_LOCAL_FEATURES_RP_SIZE 9 + +#define OCF_READ_LOCAL_EXT_FEATURES 0x0004 +typedef struct { + uint8_t page_num; +} QEMU_PACKED read_local_ext_features_cp; +#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1 +typedef struct { + uint8_t status; + uint8_t page_num; + uint8_t max_page_num; + uint8_t features[8]; +} QEMU_PACKED read_local_ext_features_rp; +#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11 + +#define OCF_READ_BUFFER_SIZE 0x0005 +typedef struct { + uint8_t status; + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_max_pkt; + uint16_t sco_max_pkt; +} QEMU_PACKED read_buffer_size_rp; +#define READ_BUFFER_SIZE_RP_SIZE 8 + +#define OCF_READ_COUNTRY_CODE 0x0007 +typedef struct { + uint8_t status; + uint8_t country_code; +} QEMU_PACKED read_country_code_rp; +#define READ_COUNTRY_CODE_RP_SIZE 2 + +#define OCF_READ_BD_ADDR 0x0009 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} QEMU_PACKED read_bd_addr_rp; +#define READ_BD_ADDR_RP_SIZE 7 + +/* Status params */ +#define OGF_STATUS_PARAM 0x05 + +#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t counter; +} QEMU_PACKED read_failed_contact_counter_rp; +#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4 + +#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002 +typedef struct { + uint8_t status; + uint16_t handle; +} QEMU_PACKED reset_failed_contact_counter_rp; +#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 4 + +#define OCF_READ_LINK_QUALITY 0x0003 +typedef struct { + uint16_t handle; +} QEMU_PACKED read_link_quality_cp; +#define READ_LINK_QUALITY_CP_SIZE 4 + +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t link_quality; +} QEMU_PACKED read_link_quality_rp; +#define READ_LINK_QUALITY_RP_SIZE 4 + +#define OCF_READ_RSSI 0x0005 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t rssi; +} QEMU_PACKED read_rssi_rp; +#define READ_RSSI_RP_SIZE 4 + +#define OCF_READ_AFH_MAP 0x0006 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t mode; + uint8_t map[10]; +} QEMU_PACKED read_afh_map_rp; +#define READ_AFH_MAP_RP_SIZE 14 + +#define OCF_READ_CLOCK 0x0007 +typedef struct { + uint16_t handle; + uint8_t which_clock; +} QEMU_PACKED read_clock_cp; +#define READ_CLOCK_CP_SIZE 3 +typedef struct { + uint8_t status; + uint16_t handle; + uint32_t clock; + uint16_t accuracy; +} QEMU_PACKED read_clock_rp; +#define READ_CLOCK_RP_SIZE 9 + +/* Testing commands */ +#define OGF_TESTING_CMD 0x3e + +/* Vendor specific commands */ +#define OGF_VENDOR_CMD 0x3f + +/* HCI Events */ + +#define EVT_INQUIRY_COMPLETE 0x01 + +#define EVT_INQUIRY_RESULT 0x02 +typedef struct { + uint8_t num_responses; + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t pscan_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; +} QEMU_PACKED inquiry_info; +#define INQUIRY_INFO_SIZE 14 + +#define EVT_CONN_COMPLETE 0x03 +typedef struct { + uint8_t status; + uint16_t handle; + bdaddr_t bdaddr; + uint8_t link_type; + uint8_t encr_mode; +} QEMU_PACKED evt_conn_complete; +#define EVT_CONN_COMPLETE_SIZE 11 + +#define EVT_CONN_REQUEST 0x04 +typedef struct { + bdaddr_t bdaddr; + uint8_t dev_class[3]; + uint8_t link_type; +} QEMU_PACKED evt_conn_request; +#define EVT_CONN_REQUEST_SIZE 10 + +#define EVT_DISCONN_COMPLETE 0x05 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t reason; +} QEMU_PACKED evt_disconn_complete; +#define EVT_DISCONN_COMPLETE_SIZE 4 + +#define EVT_AUTH_COMPLETE 0x06 +typedef struct { + uint8_t status; + uint16_t handle; +} QEMU_PACKED evt_auth_complete; +#define EVT_AUTH_COMPLETE_SIZE 3 + +#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; + char name[248]; +} QEMU_PACKED evt_remote_name_req_complete; +#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 + +#define EVT_ENCRYPT_CHANGE 0x08 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t encrypt; +} QEMU_PACKED evt_encrypt_change; +#define EVT_ENCRYPT_CHANGE_SIZE 5 + +#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 +typedef struct { + uint8_t status; + uint16_t handle; +} QEMU_PACKED evt_change_conn_link_key_complete; +#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3 + +#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t key_flag; +} QEMU_PACKED evt_master_link_key_complete; +#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4 + +#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t features[8]; +} QEMU_PACKED evt_read_remote_features_complete; +#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 + +#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t lmp_ver; + uint16_t manufacturer; + uint16_t lmp_subver; +} QEMU_PACKED evt_read_remote_version_complete; +#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 + +#define EVT_QOS_SETUP_COMPLETE 0x0D +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t flags; /* Reserved */ + hci_qos qos; +} QEMU_PACKED evt_qos_setup_complete; +#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE) + +#define EVT_CMD_COMPLETE 0x0E +typedef struct { + uint8_t ncmd; + uint16_t opcode; +} QEMU_PACKED evt_cmd_complete; +#define EVT_CMD_COMPLETE_SIZE 3 + +#define EVT_CMD_STATUS 0x0F +typedef struct { + uint8_t status; + uint8_t ncmd; + uint16_t opcode; +} QEMU_PACKED evt_cmd_status; +#define EVT_CMD_STATUS_SIZE 4 + +#define EVT_HARDWARE_ERROR 0x10 +typedef struct { + uint8_t code; +} QEMU_PACKED evt_hardware_error; +#define EVT_HARDWARE_ERROR_SIZE 1 + +#define EVT_FLUSH_OCCURRED 0x11 +typedef struct { + uint16_t handle; +} QEMU_PACKED evt_flush_occurred; +#define EVT_FLUSH_OCCURRED_SIZE 2 + +#define EVT_ROLE_CHANGE 0x12 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; + uint8_t role; +} QEMU_PACKED evt_role_change; +#define EVT_ROLE_CHANGE_SIZE 8 + +#define EVT_NUM_COMP_PKTS 0x13 +typedef struct { + uint8_t num_hndl; + struct { + uint16_t handle; + uint16_t num_packets; + } connection[0]; +} QEMU_PACKED evt_num_comp_pkts; +#define EVT_NUM_COMP_PKTS_SIZE(num_hndl) (1 + 4 * (num_hndl)) + +#define EVT_MODE_CHANGE 0x14 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t mode; + uint16_t interval; +} QEMU_PACKED evt_mode_change; +#define EVT_MODE_CHANGE_SIZE 6 + +#define EVT_RETURN_LINK_KEYS 0x15 +typedef struct { + uint8_t num_keys; + /* variable length part */ +} QEMU_PACKED evt_return_link_keys; +#define EVT_RETURN_LINK_KEYS_SIZE 1 + +#define EVT_PIN_CODE_REQ 0x16 +typedef struct { + bdaddr_t bdaddr; +} QEMU_PACKED evt_pin_code_req; +#define EVT_PIN_CODE_REQ_SIZE 6 + +#define EVT_LINK_KEY_REQ 0x17 +typedef struct { + bdaddr_t bdaddr; +} QEMU_PACKED evt_link_key_req; +#define EVT_LINK_KEY_REQ_SIZE 6 + +#define EVT_LINK_KEY_NOTIFY 0x18 +typedef struct { + bdaddr_t bdaddr; + uint8_t link_key[16]; + uint8_t key_type; +} QEMU_PACKED evt_link_key_notify; +#define EVT_LINK_KEY_NOTIFY_SIZE 23 + +#define EVT_LOOPBACK_COMMAND 0x19 + +#define EVT_DATA_BUFFER_OVERFLOW 0x1A +typedef struct { + uint8_t link_type; +} QEMU_PACKED evt_data_buffer_overflow; +#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1 + +#define EVT_MAX_SLOTS_CHANGE 0x1B +typedef struct { + uint16_t handle; + uint8_t max_slots; +} QEMU_PACKED evt_max_slots_change; +#define EVT_MAX_SLOTS_CHANGE_SIZE 3 + +#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t clock_offset; +} QEMU_PACKED evt_read_clock_offset_complete; +#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5 + +#define EVT_CONN_PTYPE_CHANGED 0x1D +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t ptype; +} QEMU_PACKED evt_conn_ptype_changed; +#define EVT_CONN_PTYPE_CHANGED_SIZE 5 + +#define EVT_QOS_VIOLATION 0x1E +typedef struct { + uint16_t handle; +} QEMU_PACKED evt_qos_violation; +#define EVT_QOS_VIOLATION_SIZE 2 + +#define EVT_PSCAN_REP_MODE_CHANGE 0x20 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; +} QEMU_PACKED evt_pscan_rep_mode_change; +#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7 + +#define EVT_FLOW_SPEC_COMPLETE 0x21 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t flags; + uint8_t direction; + hci_qos qos; +} QEMU_PACKED evt_flow_spec_complete; +#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE) + +#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 +typedef struct { + uint8_t num_responses; + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; +} QEMU_PACKED inquiry_info_with_rssi; +#define INQUIRY_INFO_WITH_RSSI_SIZE 15 +typedef struct { + uint8_t num_responses; + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t pscan_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; +} QEMU_PACKED inquiry_info_with_rssi_and_pscan_mode; +#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 16 + +#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t page_num; + uint8_t max_page_num; + uint8_t features[8]; +} QEMU_PACKED evt_read_remote_ext_features_complete; +#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13 + +#define EVT_SYNC_CONN_COMPLETE 0x2C +typedef struct { + uint8_t status; + uint16_t handle; + bdaddr_t bdaddr; + uint8_t link_type; + uint8_t trans_interval; + uint8_t retrans_window; + uint16_t rx_pkt_len; + uint16_t tx_pkt_len; + uint8_t air_mode; +} QEMU_PACKED evt_sync_conn_complete; +#define EVT_SYNC_CONN_COMPLETE_SIZE 17 + +#define EVT_SYNC_CONN_CHANGED 0x2D +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t trans_interval; + uint8_t retrans_window; + uint16_t rx_pkt_len; + uint16_t tx_pkt_len; +} QEMU_PACKED evt_sync_conn_changed; +#define EVT_SYNC_CONN_CHANGED_SIZE 9 + +#define EVT_SNIFF_SUBRATE 0x2E +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t max_remote_latency; + uint16_t max_local_latency; + uint16_t min_remote_timeout; + uint16_t min_local_timeout; +} QEMU_PACKED evt_sniff_subrate; +#define EVT_SNIFF_SUBRATE_SIZE 11 + +#define EVT_EXTENDED_INQUIRY_RESULT 0x2F +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; + uint8_t data[240]; +} QEMU_PACKED extended_inquiry_info; +#define EXTENDED_INQUIRY_INFO_SIZE 254 + +#define EVT_TESTING 0xFE + +#define EVT_VENDOR 0xFF + +/* Command opcode pack/unpack */ +#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10)) +#define cmd_opcode_ogf(op) (op >> 10) +#define cmd_opcode_ocf(op) (op & 0x03ff) + +/* ACL handle and flags pack/unpack */ +#define acl_handle_pack(h, f) (uint16_t)(((h) & 0x0fff)|((f) << 12)) +#define acl_handle(h) ((h) & 0x0fff) +#define acl_flags(h) ((h) >> 12) + +/* HCI Packet structures */ +#define HCI_COMMAND_HDR_SIZE 3 +#define HCI_EVENT_HDR_SIZE 2 +#define HCI_ACL_HDR_SIZE 4 +#define HCI_SCO_HDR_SIZE 3 + +struct hci_command_hdr { + uint16_t opcode; /* OCF & OGF */ + uint8_t plen; +} QEMU_PACKED; + +struct hci_event_hdr { + uint8_t evt; + uint8_t plen; +} QEMU_PACKED; + +struct hci_acl_hdr { + uint16_t handle; /* Handle & Flags(PB, BC) */ + uint16_t dlen; +} QEMU_PACKED; + +struct hci_sco_hdr { + uint16_t handle; + uint8_t dlen; +} QEMU_PACKED; + +/* L2CAP layer defines */ + +enum bt_l2cap_lm_bits { + L2CAP_LM_MASTER = 1 << 0, + L2CAP_LM_AUTH = 1 << 1, + L2CAP_LM_ENCRYPT = 1 << 2, + L2CAP_LM_TRUSTED = 1 << 3, + L2CAP_LM_RELIABLE = 1 << 4, + L2CAP_LM_SECURE = 1 << 5, +}; + +enum bt_l2cap_cid_predef { + L2CAP_CID_INVALID = 0x0000, + L2CAP_CID_SIGNALLING= 0x0001, + L2CAP_CID_GROUP = 0x0002, + L2CAP_CID_ALLOC = 0x0040, +}; + +/* L2CAP command codes */ +enum bt_l2cap_cmd { + L2CAP_COMMAND_REJ = 1, + L2CAP_CONN_REQ, + L2CAP_CONN_RSP, + L2CAP_CONF_REQ, + L2CAP_CONF_RSP, + L2CAP_DISCONN_REQ, + L2CAP_DISCONN_RSP, + L2CAP_ECHO_REQ, + L2CAP_ECHO_RSP, + L2CAP_INFO_REQ, + L2CAP_INFO_RSP, +}; + +enum bt_l2cap_sar_bits { + L2CAP_SAR_NO_SEG = 0, + L2CAP_SAR_START, + L2CAP_SAR_END, + L2CAP_SAR_CONT, +}; + +/* L2CAP structures */ +typedef struct { + uint16_t len; + uint16_t cid; + uint8_t data[0]; +} QEMU_PACKED l2cap_hdr; +#define L2CAP_HDR_SIZE 4 + +typedef struct { + uint8_t code; + uint8_t ident; + uint16_t len; +} QEMU_PACKED l2cap_cmd_hdr; +#define L2CAP_CMD_HDR_SIZE 4 + +typedef struct { + uint16_t reason; +} QEMU_PACKED l2cap_cmd_rej; +#define L2CAP_CMD_REJ_SIZE 2 + +typedef struct { + uint16_t dcid; + uint16_t scid; +} QEMU_PACKED l2cap_cmd_rej_cid; +#define L2CAP_CMD_REJ_CID_SIZE 4 + +/* reject reason */ +enum bt_l2cap_rej_reason { + L2CAP_REJ_CMD_NOT_UNDERSTOOD = 0, + L2CAP_REJ_SIG_TOOBIG, + L2CAP_REJ_CID_INVAL, +}; + +typedef struct { + uint16_t psm; + uint16_t scid; +} QEMU_PACKED l2cap_conn_req; +#define L2CAP_CONN_REQ_SIZE 4 + +typedef struct { + uint16_t dcid; + uint16_t scid; + uint16_t result; + uint16_t status; +} QEMU_PACKED l2cap_conn_rsp; +#define L2CAP_CONN_RSP_SIZE 8 + +/* connect result */ +enum bt_l2cap_conn_res { + L2CAP_CR_SUCCESS = 0, + L2CAP_CR_PEND, + L2CAP_CR_BAD_PSM, + L2CAP_CR_SEC_BLOCK, + L2CAP_CR_NO_MEM, +}; + +/* connect status */ +enum bt_l2cap_conn_stat { + L2CAP_CS_NO_INFO = 0, + L2CAP_CS_AUTHEN_PEND, + L2CAP_CS_AUTHOR_PEND, +}; + +typedef struct { + uint16_t dcid; + uint16_t flags; + uint8_t data[0]; +} QEMU_PACKED l2cap_conf_req; +#define L2CAP_CONF_REQ_SIZE(datalen) (4 + (datalen)) + +typedef struct { + uint16_t scid; + uint16_t flags; + uint16_t result; + uint8_t data[0]; +} QEMU_PACKED l2cap_conf_rsp; +#define L2CAP_CONF_RSP_SIZE(datalen) (6 + datalen) + +enum bt_l2cap_conf_res { + L2CAP_CONF_SUCCESS = 0, + L2CAP_CONF_UNACCEPT, + L2CAP_CONF_REJECT, + L2CAP_CONF_UNKNOWN, +}; + +typedef struct { + uint8_t type; + uint8_t len; + uint8_t val[0]; +} QEMU_PACKED l2cap_conf_opt; +#define L2CAP_CONF_OPT_SIZE 2 + +enum bt_l2cap_conf_val { + L2CAP_CONF_MTU = 1, + L2CAP_CONF_FLUSH_TO, + L2CAP_CONF_QOS, + L2CAP_CONF_RFC, + L2CAP_CONF_RFC_MODE = L2CAP_CONF_RFC, +}; + +typedef struct { + uint8_t flags; + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t latency; + uint32_t delay_variation; +} QEMU_PACKED l2cap_conf_opt_qos; +#define L2CAP_CONF_OPT_QOS_SIZE 22 + +enum bt_l2cap_conf_opt_qos_st { + L2CAP_CONF_QOS_NO_TRAFFIC = 0x00, + L2CAP_CONF_QOS_BEST_EFFORT, + L2CAP_CONF_QOS_GUARANTEED, +}; + +#define L2CAP_CONF_QOS_WILDCARD 0xffffffff + +enum bt_l2cap_mode { + L2CAP_MODE_BASIC = 0, + L2CAP_MODE_RETRANS = 1, + L2CAP_MODE_FLOWCTL = 2, +}; + +typedef struct { + uint16_t dcid; + uint16_t scid; +} QEMU_PACKED l2cap_disconn_req; +#define L2CAP_DISCONN_REQ_SIZE 4 + +typedef struct { + uint16_t dcid; + uint16_t scid; +} QEMU_PACKED l2cap_disconn_rsp; +#define L2CAP_DISCONN_RSP_SIZE 4 + +typedef struct { + uint16_t type; +} QEMU_PACKED l2cap_info_req; +#define L2CAP_INFO_REQ_SIZE 2 + +typedef struct { + uint16_t type; + uint16_t result; + uint8_t data[0]; +} QEMU_PACKED l2cap_info_rsp; +#define L2CAP_INFO_RSP_SIZE 4 + +/* info type */ +enum bt_l2cap_info_type { + L2CAP_IT_CL_MTU = 1, + L2CAP_IT_FEAT_MASK, +}; + +/* info result */ +enum bt_l2cap_info_result { + L2CAP_IR_SUCCESS = 0, + L2CAP_IR_NOTSUPP, +}; + +/* Service Discovery Protocol defines */ +/* Note that all multibyte values in lower layer protocols (above in this file) + * are little-endian while SDP is big-endian. */ + +/* Protocol UUIDs */ +enum sdp_proto_uuid { + SDP_UUID = 0x0001, + UDP_UUID = 0x0002, + RFCOMM_UUID = 0x0003, + TCP_UUID = 0x0004, + TCS_BIN_UUID = 0x0005, + TCS_AT_UUID = 0x0006, + OBEX_UUID = 0x0008, + IP_UUID = 0x0009, + FTP_UUID = 0x000a, + HTTP_UUID = 0x000c, + WSP_UUID = 0x000e, + BNEP_UUID = 0x000f, + UPNP_UUID = 0x0010, + HIDP_UUID = 0x0011, + HCRP_CTRL_UUID = 0x0012, + HCRP_DATA_UUID = 0x0014, + HCRP_NOTE_UUID = 0x0016, + AVCTP_UUID = 0x0017, + AVDTP_UUID = 0x0019, + CMTP_UUID = 0x001b, + UDI_UUID = 0x001d, + MCAP_CTRL_UUID = 0x001e, + MCAP_DATA_UUID = 0x001f, + L2CAP_UUID = 0x0100, +}; + +/* + * Service class identifiers of standard services and service groups + */ +enum service_class_id { + SDP_SERVER_SVCLASS_ID = 0x1000, + BROWSE_GRP_DESC_SVCLASS_ID = 0x1001, + PUBLIC_BROWSE_GROUP = 0x1002, + SERIAL_PORT_SVCLASS_ID = 0x1101, + LAN_ACCESS_SVCLASS_ID = 0x1102, + DIALUP_NET_SVCLASS_ID = 0x1103, + IRMC_SYNC_SVCLASS_ID = 0x1104, + OBEX_OBJPUSH_SVCLASS_ID = 0x1105, + OBEX_FILETRANS_SVCLASS_ID = 0x1106, + IRMC_SYNC_CMD_SVCLASS_ID = 0x1107, + HEADSET_SVCLASS_ID = 0x1108, + CORDLESS_TELEPHONY_SVCLASS_ID = 0x1109, + AUDIO_SOURCE_SVCLASS_ID = 0x110a, + AUDIO_SINK_SVCLASS_ID = 0x110b, + AV_REMOTE_TARGET_SVCLASS_ID = 0x110c, + ADVANCED_AUDIO_SVCLASS_ID = 0x110d, + AV_REMOTE_SVCLASS_ID = 0x110e, + VIDEO_CONF_SVCLASS_ID = 0x110f, + INTERCOM_SVCLASS_ID = 0x1110, + FAX_SVCLASS_ID = 0x1111, + HEADSET_AGW_SVCLASS_ID = 0x1112, + WAP_SVCLASS_ID = 0x1113, + WAP_CLIENT_SVCLASS_ID = 0x1114, + PANU_SVCLASS_ID = 0x1115, + NAP_SVCLASS_ID = 0x1116, + GN_SVCLASS_ID = 0x1117, + DIRECT_PRINTING_SVCLASS_ID = 0x1118, + REFERENCE_PRINTING_SVCLASS_ID = 0x1119, + IMAGING_SVCLASS_ID = 0x111a, + IMAGING_RESPONDER_SVCLASS_ID = 0x111b, + IMAGING_ARCHIVE_SVCLASS_ID = 0x111c, + IMAGING_REFOBJS_SVCLASS_ID = 0x111d, + HANDSFREE_SVCLASS_ID = 0x111e, + HANDSFREE_AGW_SVCLASS_ID = 0x111f, + DIRECT_PRT_REFOBJS_SVCLASS_ID = 0x1120, + REFLECTED_UI_SVCLASS_ID = 0x1121, + BASIC_PRINTING_SVCLASS_ID = 0x1122, + PRINTING_STATUS_SVCLASS_ID = 0x1123, + HID_SVCLASS_ID = 0x1124, + HCR_SVCLASS_ID = 0x1125, + HCR_PRINT_SVCLASS_ID = 0x1126, + HCR_SCAN_SVCLASS_ID = 0x1127, + CIP_SVCLASS_ID = 0x1128, + VIDEO_CONF_GW_SVCLASS_ID = 0x1129, + UDI_MT_SVCLASS_ID = 0x112a, + UDI_TA_SVCLASS_ID = 0x112b, + AV_SVCLASS_ID = 0x112c, + SAP_SVCLASS_ID = 0x112d, + PBAP_PCE_SVCLASS_ID = 0x112e, + PBAP_PSE_SVCLASS_ID = 0x112f, + PBAP_SVCLASS_ID = 0x1130, + PNP_INFO_SVCLASS_ID = 0x1200, + GENERIC_NETWORKING_SVCLASS_ID = 0x1201, + GENERIC_FILETRANS_SVCLASS_ID = 0x1202, + GENERIC_AUDIO_SVCLASS_ID = 0x1203, + GENERIC_TELEPHONY_SVCLASS_ID = 0x1204, + UPNP_SVCLASS_ID = 0x1205, + UPNP_IP_SVCLASS_ID = 0x1206, + UPNP_PAN_SVCLASS_ID = 0x1300, + UPNP_LAP_SVCLASS_ID = 0x1301, + UPNP_L2CAP_SVCLASS_ID = 0x1302, + VIDEO_SOURCE_SVCLASS_ID = 0x1303, + VIDEO_SINK_SVCLASS_ID = 0x1304, + VIDEO_DISTRIBUTION_SVCLASS_ID = 0x1305, + MDP_SVCLASS_ID = 0x1400, + MDP_SOURCE_SVCLASS_ID = 0x1401, + MDP_SINK_SVCLASS_ID = 0x1402, + APPLE_AGENT_SVCLASS_ID = 0x2112, +}; + +/* + * Standard profile descriptor identifiers; note these + * may be identical to some of the service classes defined above + */ +#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID +#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID +#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID +#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID +#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID +#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID +#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID +#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID +#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID +#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID +#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID +#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID +#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID +#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID +#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID +#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID +#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID +#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID +#define FAX_PROFILE_ID FAX_SVCLASS_ID +#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID +#define WAP_PROFILE_ID WAP_SVCLASS_ID +#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID +#define PANU_PROFILE_ID PANU_SVCLASS_ID +#define NAP_PROFILE_ID NAP_SVCLASS_ID +#define GN_PROFILE_ID GN_SVCLASS_ID +#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID +#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID +#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID +#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID +#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID +#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID +#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID +#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID +#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID +#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID +#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID +#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID +#define HID_PROFILE_ID HID_SVCLASS_ID +#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID +#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID +#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID +#define CIP_PROFILE_ID CIP_SVCLASS_ID +#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID +#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID +#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID +#define AV_PROFILE_ID AV_SVCLASS_ID +#define SAP_PROFILE_ID SAP_SVCLASS_ID +#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID +#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID +#define PBAP_PROFILE_ID PBAP_SVCLASS_ID +#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID +#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID +#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID +#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID +#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID +#define UPNP_PROFILE_ID UPNP_SVCLASS_ID +#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID +#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID +#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID +#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID +#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID +#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID +#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID +#define MDP_PROFILE_ID MDP_SVCLASS_ID +#define MDP_SOURCE_PROFILE_ID MDP_SROUCE_SVCLASS_ID +#define MDP_SINK_PROFILE_ID MDP_SINK_SVCLASS_ID +#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID + +/* Data Representation */ +enum bt_sdp_data_type { + SDP_DTYPE_NIL = 0 << 3, + SDP_DTYPE_UINT = 1 << 3, + SDP_DTYPE_SINT = 2 << 3, + SDP_DTYPE_UUID = 3 << 3, + SDP_DTYPE_STRING = 4 << 3, + SDP_DTYPE_BOOL = 5 << 3, + SDP_DTYPE_SEQ = 6 << 3, + SDP_DTYPE_ALT = 7 << 3, + SDP_DTYPE_URL = 8 << 3, +}; + +enum bt_sdp_data_size { + SDP_DSIZE_1 = 0, + SDP_DSIZE_2, + SDP_DSIZE_4, + SDP_DSIZE_8, + SDP_DSIZE_16, + SDP_DSIZE_NEXT1, + SDP_DSIZE_NEXT2, + SDP_DSIZE_NEXT4, + SDP_DSIZE_MASK = SDP_DSIZE_NEXT4, +}; + +enum bt_sdp_cmd { + SDP_ERROR_RSP = 0x01, + SDP_SVC_SEARCH_REQ = 0x02, + SDP_SVC_SEARCH_RSP = 0x03, + SDP_SVC_ATTR_REQ = 0x04, + SDP_SVC_ATTR_RSP = 0x05, + SDP_SVC_SEARCH_ATTR_REQ = 0x06, + SDP_SVC_SEARCH_ATTR_RSP = 0x07, +}; + +enum bt_sdp_errorcode { + SDP_INVALID_VERSION = 0x0001, + SDP_INVALID_RECORD_HANDLE = 0x0002, + SDP_INVALID_SYNTAX = 0x0003, + SDP_INVALID_PDU_SIZE = 0x0004, + SDP_INVALID_CSTATE = 0x0005, +}; + +/* + * String identifiers are based on the SDP spec stating that + * "base attribute id of the primary (universal) language must be 0x0100" + * + * Other languages should have their own offset; e.g.: + * #define XXXLangBase yyyy + * #define AttrServiceName_XXX 0x0000+XXXLangBase + */ +#define SDP_PRIMARY_LANG_BASE 0x0100 + +enum bt_sdp_attribute_id { + SDP_ATTR_RECORD_HANDLE = 0x0000, + SDP_ATTR_SVCLASS_ID_LIST = 0x0001, + SDP_ATTR_RECORD_STATE = 0x0002, + SDP_ATTR_SERVICE_ID = 0x0003, + SDP_ATTR_PROTO_DESC_LIST = 0x0004, + SDP_ATTR_BROWSE_GRP_LIST = 0x0005, + SDP_ATTR_LANG_BASE_ATTR_ID_LIST = 0x0006, + SDP_ATTR_SVCINFO_TTL = 0x0007, + SDP_ATTR_SERVICE_AVAILABILITY = 0x0008, + SDP_ATTR_PFILE_DESC_LIST = 0x0009, + SDP_ATTR_DOC_URL = 0x000a, + SDP_ATTR_CLNT_EXEC_URL = 0x000b, + SDP_ATTR_ICON_URL = 0x000c, + SDP_ATTR_ADD_PROTO_DESC_LIST = 0x000d, + + SDP_ATTR_SVCNAME_PRIMARY = SDP_PRIMARY_LANG_BASE + 0, + SDP_ATTR_SVCDESC_PRIMARY = SDP_PRIMARY_LANG_BASE + 1, + SDP_ATTR_SVCPROV_PRIMARY = SDP_PRIMARY_LANG_BASE + 2, + + SDP_ATTR_GROUP_ID = 0x0200, + SDP_ATTR_IP_SUBNET = 0x0200, + + /* SDP */ + SDP_ATTR_VERSION_NUM_LIST = 0x0200, + SDP_ATTR_SVCDB_STATE = 0x0201, + + SDP_ATTR_SERVICE_VERSION = 0x0300, + SDP_ATTR_EXTERNAL_NETWORK = 0x0301, + SDP_ATTR_SUPPORTED_DATA_STORES_LIST = 0x0301, + SDP_ATTR_FAX_CLASS1_SUPPORT = 0x0302, + SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL = 0x0302, + SDP_ATTR_FAX_CLASS20_SUPPORT = 0x0303, + SDP_ATTR_SUPPORTED_FORMATS_LIST = 0x0303, + SDP_ATTR_FAX_CLASS2_SUPPORT = 0x0304, + SDP_ATTR_AUDIO_FEEDBACK_SUPPORT = 0x0305, + SDP_ATTR_NETWORK_ADDRESS = 0x0306, + SDP_ATTR_WAP_GATEWAY = 0x0307, + SDP_ATTR_HOMEPAGE_URL = 0x0308, + SDP_ATTR_WAP_STACK_TYPE = 0x0309, + SDP_ATTR_SECURITY_DESC = 0x030a, + SDP_ATTR_NET_ACCESS_TYPE = 0x030b, + SDP_ATTR_MAX_NET_ACCESSRATE = 0x030c, + SDP_ATTR_IP4_SUBNET = 0x030d, + SDP_ATTR_IP6_SUBNET = 0x030e, + SDP_ATTR_SUPPORTED_CAPABILITIES = 0x0310, + SDP_ATTR_SUPPORTED_FEATURES = 0x0311, + SDP_ATTR_SUPPORTED_FUNCTIONS = 0x0312, + SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY = 0x0313, + SDP_ATTR_SUPPORTED_REPOSITORIES = 0x0314, + + /* PnP Information */ + SDP_ATTR_SPECIFICATION_ID = 0x0200, + SDP_ATTR_VENDOR_ID = 0x0201, + SDP_ATTR_PRODUCT_ID = 0x0202, + SDP_ATTR_VERSION = 0x0203, + SDP_ATTR_PRIMARY_RECORD = 0x0204, + SDP_ATTR_VENDOR_ID_SOURCE = 0x0205, + + /* BT HID */ + SDP_ATTR_DEVICE_RELEASE_NUMBER = 0x0200, + SDP_ATTR_PARSER_VERSION = 0x0201, + SDP_ATTR_DEVICE_SUBCLASS = 0x0202, + SDP_ATTR_COUNTRY_CODE = 0x0203, + SDP_ATTR_VIRTUAL_CABLE = 0x0204, + SDP_ATTR_RECONNECT_INITIATE = 0x0205, + SDP_ATTR_DESCRIPTOR_LIST = 0x0206, + SDP_ATTR_LANG_ID_BASE_LIST = 0x0207, + SDP_ATTR_SDP_DISABLE = 0x0208, + SDP_ATTR_BATTERY_POWER = 0x0209, + SDP_ATTR_REMOTE_WAKEUP = 0x020a, + SDP_ATTR_PROFILE_VERSION = 0x020b, + SDP_ATTR_SUPERVISION_TIMEOUT = 0x020c, + SDP_ATTR_NORMALLY_CONNECTABLE = 0x020d, + SDP_ATTR_BOOT_DEVICE = 0x020e, +}; + +#endif diff --git a/include/hw/char/escc.h b/include/hw/char/escc.h new file mode 100644 index 0000000000..bda3213317 --- /dev/null +++ b/include/hw/char/escc.h @@ -0,0 +1,13 @@ +#ifndef HW_ESCC_H +#define HW_ESCC_H 1 + +/* escc.c */ +#define ESCC_SIZE 4 +MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, + CharDriverState *chrA, CharDriverState *chrB, + int clock, int it_shift); + +void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq, + int disabled, int clock, int it_shift); + +#endif diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h new file mode 100644 index 0000000000..e884499607 --- /dev/null +++ b/include/hw/char/serial.h @@ -0,0 +1,101 @@ +/* + * QEMU 16550A UART emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef HW_SERIAL_H +#define HW_SERIAL_H 1 + +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "exec/memory.h" + +#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ + +typedef struct SerialFIFO { + uint8_t data[UART_FIFO_LENGTH]; + uint8_t count; + uint8_t itl; /* Interrupt Trigger Level */ + uint8_t tail; + uint8_t head; +} SerialFIFO; + +struct SerialState { + uint16_t divider; + uint8_t rbr; /* receive register */ + uint8_t thr; /* transmit holding register */ + uint8_t tsr; /* transmit shift register */ + uint8_t ier; + uint8_t iir; /* read only */ + uint8_t lcr; + uint8_t mcr; + uint8_t lsr; /* read only */ + uint8_t msr; /* read only */ + uint8_t scr; + uint8_t fcr; + uint8_t fcr_vmstate; /* we can't write directly this value + it has side effects */ + /* NOTE: this hidden state is necessary for tx irq generation as + it can be reset while reading iir */ + int thr_ipending; + qemu_irq irq; + CharDriverState *chr; + int last_break_enable; + int it_shift; + int baudbase; + int tsr_retry; + uint32_t wakeup; + + /* Time when the last byte was successfully sent out of the tsr */ + uint64_t last_xmit_ts; + SerialFIFO recv_fifo; + SerialFIFO xmit_fifo; + + struct QEMUTimer *fifo_timeout_timer; + int timeout_ipending; /* timeout interrupt pending state */ + + uint64_t char_transmit_time; /* time to transmit a char in ticks */ + int poll_msl; + + struct QEMUTimer *modem_status_poll; + MemoryRegion io; +}; + +extern const VMStateDescription vmstate_serial; +extern const MemoryRegionOps serial_io_ops; + +void serial_init_core(SerialState *s); +void serial_exit_core(SerialState *s); +void serial_set_frequency(SerialState *s, uint32_t frequency); + +/* legacy pre qom */ +SerialState *serial_init(int base, qemu_irq irq, int baudbase, + CharDriverState *chr, MemoryRegion *system_io); +SerialState *serial_mm_init(MemoryRegion *address_space, + hwaddr base, int it_shift, + qemu_irq irq, int baudbase, + CharDriverState *chr, enum device_endian end); + +/* serial-isa.c */ +bool serial_isa_init(ISABus *bus, int index, CharDriverState *chr); + +#endif diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h new file mode 100644 index 0000000000..ab30559c79 --- /dev/null +++ b/include/hw/cris/etraxfs.h @@ -0,0 +1,51 @@ +/* + * QEMU ETRAX System Emulator + * + * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_EXTRAXFS_H +#define HW_EXTRAXFS_H 1 + +#include "net/net.h" +#include "hw/cris/etraxfs_dma.h" + +qemu_irq *cris_pic_init_cpu(CPUCRISState *env); + +/* Instantiate an ETRAXFS Ethernet MAC. */ +static inline DeviceState * +etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, + void *dma_out, void *dma_in) +{ + DeviceState *dev; + qemu_check_nic_model(nd, "fseth"); + + dev = qdev_create(NULL, "etraxfs-eth"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint32(dev, "phyaddr", phyaddr); + qdev_prop_set_ptr(dev, "dma_out", dma_out); + qdev_prop_set_ptr(dev, "dma_in", dma_in); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + return dev; +} + +#endif diff --git a/include/hw/cris/etraxfs_dma.h b/include/hw/cris/etraxfs_dma.h new file mode 100644 index 0000000000..38104a67b5 --- /dev/null +++ b/include/hw/cris/etraxfs_dma.h @@ -0,0 +1,34 @@ +#ifndef HW_ETRAXFS_DMA_H +#define HW_ETRAXFS_DMA_H 1 + +struct dma_context_metadata { + /* data descriptor md */ + uint16_t metadata; +}; + +struct etraxfs_dma_client +{ + /* DMA controller. */ + int channel; + void *ctrl; + + /* client. */ + struct { + int (*push)(void *opaque, unsigned char *buf, + int len, bool eop); + void (*pull)(void *opaque); + void (*metadata_push)(void *opaque, + const struct dma_context_metadata *md); + void *opaque; + } client; +}; + +void *etraxfs_dmac_init(hwaddr base, int nr_channels); +void etraxfs_dmac_connect(void *opaque, int channel, qemu_irq *line, + int input); +void etraxfs_dmac_connect_client(void *opaque, int c, + struct etraxfs_dma_client *cl); +int etraxfs_dmac_input(struct etraxfs_dma_client *client, + void *buf, int len, int eop); + +#endif diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h new file mode 100644 index 0000000000..acc701e3a4 --- /dev/null +++ b/include/hw/elf_ops.h @@ -0,0 +1,309 @@ +static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ + bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ + bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswapSZs(&phdr->p_offset); /* Segment file offset */ + bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ + bswapSZs(&phdr->p_paddr); /* Segment physical address */ + bswapSZs(&phdr->p_filesz); /* Segment size in file */ + bswapSZs(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswapSZs(&phdr->p_align); /* Segment alignment */ +} + +static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswapSZs(&shdr->sh_flags); + bswapSZs(&shdr->sh_addr); + bswapSZs(&shdr->sh_offset); + bswapSZs(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswapSZs(&shdr->sh_addralign); + bswapSZs(&shdr->sh_entsize); +} + +static void glue(bswap_sym, SZ)(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswapSZs(&sym->st_value); + bswapSZs(&sym->st_size); + bswap16s(&sym->st_shndx); +} + +static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, + int n, int type) +{ + int i; + for(i=0;ist_value) { + result = -1; + } else if (addr >= sym->st_value + sym->st_size) { + result = 1; + } + return result; +} + +static const char *glue(lookup_symbol, SZ)(struct syminfo *s, + hwaddr orig_addr) +{ + struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); + struct elf_sym *sym; + + sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), + glue(symfind, SZ)); + if (sym != NULL) { + return s->disas_strtab + sym->st_name; + } + + return ""; +} + +static int glue(symcmp, SZ)(const void *s0, const void *s1) +{ + struct elf_sym *sym0 = (struct elf_sym *)s0; + struct elf_sym *sym1 = (struct elf_sym *)s1; + return (sym0->st_value < sym1->st_value) + ? -1 + : ((sym0->st_value > sym1->st_value) ? 1 : 0); +} + +static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + int clear_lsb) +{ + struct elf_shdr *symtab, *strtab, *shdr_table = NULL; + struct elf_sym *syms = NULL; + struct syminfo *s; + int nsyms, i; + char *str = NULL; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) + return -1; + + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(shdr_table + i); + } + } + + symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); + if (!symtab) + goto fail; + syms = load_at(fd, symtab->sh_offset, symtab->sh_size); + if (!syms) + goto fail; + + nsyms = symtab->sh_size / sizeof(struct elf_sym); + + i = 0; + while (i < nsyms) { + if (must_swab) + glue(bswap_sym, SZ)(&syms[i]); + /* We are only interested in function symbols. + Throw everything else away. */ + if (syms[i].st_shndx == SHN_UNDEF || + syms[i].st_shndx >= SHN_LORESERVE || + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { + nsyms--; + if (i < nsyms) { + syms[i] = syms[nsyms]; + } + continue; + } + if (clear_lsb) { + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ + syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; + } + i++; + } + if (nsyms) { + syms = g_realloc(syms, nsyms * sizeof(*syms)); + + qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); + for (i = 0; i < nsyms - 1; i++) { + if (syms[i].st_size == 0) { + syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; + } + } + } else { + g_free(syms); + syms = NULL; + } + + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) + goto fail; + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) + goto fail; + + /* Commit */ + s = g_malloc0(sizeof(*s)); + s->lookup_symbol = glue(lookup_symbol, SZ); + glue(s->disas_symtab.elf, SZ) = syms; + s->disas_num_syms = nsyms; + s->disas_strtab = str; + s->next = syminfos; + syminfos = s; + g_free(shdr_table); + return 0; + fail: + g_free(syms); + g_free(str); + g_free(shdr_table); + return -1; +} + +static int glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + int elf_machine, int clear_lsb) +{ + struct elfhdr ehdr; + struct elf_phdr *phdr = NULL, *ph; + int size, i, total_size; + elf_word mem_size, file_size; + uint64_t addr, low = (uint64_t)-1, high = 0; + uint8_t *data = NULL; + char label[128]; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail; + if (must_swab) { + glue(bswap_ehdr, SZ)(&ehdr); + } + + switch (elf_machine) { + case EM_PPC64: + if (EM_PPC64 != ehdr.e_machine) + if (EM_PPC != ehdr.e_machine) + goto fail; + break; + case EM_X86_64: + if (EM_X86_64 != ehdr.e_machine) + if (EM_386 != ehdr.e_machine) + goto fail; + break; + case EM_MICROBLAZE: + if (EM_MICROBLAZE != ehdr.e_machine) + if (EM_MICROBLAZE_OLD != ehdr.e_machine) + goto fail; + break; + default: + if (elf_machine != ehdr.e_machine) + goto fail; + } + + if (pentry) + *pentry = (uint64_t)(elf_sword)ehdr.e_entry; + + glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb); + + size = ehdr.e_phnum * sizeof(phdr[0]); + lseek(fd, ehdr.e_phoff, SEEK_SET); + phdr = g_malloc0(size); + if (!phdr) + goto fail; + if (read(fd, phdr, size) != size) + goto fail; + if (must_swab) { + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + glue(bswap_phdr, SZ)(ph); + } + } + + total_size = 0; + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + if (ph->p_type == PT_LOAD) { + mem_size = ph->p_memsz; /* Size of the ROM */ + file_size = ph->p_filesz; /* Size of the allocated data */ + data = g_malloc0(file_size); + if (ph->p_filesz > 0) { + if (lseek(fd, ph->p_offset, SEEK_SET) < 0) { + goto fail; + } + if (read(fd, data, file_size) != file_size) { + goto fail; + } + } + /* address_offset is hack for kernel images that are + linked at the wrong physical address. */ + if (translate_fn) { + addr = translate_fn(translate_opaque, ph->p_paddr); + } else { + addr = ph->p_paddr; + } + + /* the entry pointer in the ELF header is a virtual + * address, if the text segments paddr and vaddr differ + * we need to adjust the entry */ + if (pentry && !translate_fn && + ph->p_vaddr != ph->p_paddr && + ehdr.e_entry >= ph->p_vaddr && + ehdr.e_entry < ph->p_vaddr + ph->p_filesz && + ph->p_flags & PF_X) { + *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; + } + + snprintf(label, sizeof(label), "phdr #%d: %s", i, name); + + /* rom_add_elf_program() seize the ownership of 'data' */ + rom_add_elf_program(label, data, file_size, mem_size, addr); + + total_size += mem_size; + if (addr < low) + low = addr; + if ((addr + mem_size) > high) + high = addr + mem_size; + + data = NULL; + } + } + g_free(phdr); + if (lowaddr) + *lowaddr = (uint64_t)(elf_sword)low; + if (highaddr) + *highaddr = (uint64_t)(elf_sword)high; + return total_size; + fail: + g_free(data); + g_free(phdr); + return -1; +} diff --git a/include/hw/empty_slot.h b/include/hw/empty_slot.h new file mode 100644 index 0000000000..6079602cdf --- /dev/null +++ b/include/hw/empty_slot.h @@ -0,0 +1,7 @@ +#ifndef HW_EMPTY_SLOT_H +#define HW_EMPTY_SLOT_H 1 + +/* empty_slot.c */ +void empty_slot_init(hwaddr addr, uint64_t slot_size); + +#endif diff --git a/include/hw/hw.h b/include/hw/hw.h new file mode 100644 index 0000000000..1fb9afa322 --- /dev/null +++ b/include/hw/hw.h @@ -0,0 +1,76 @@ +/* Declarations for use by hardware emulation. */ +#ifndef QEMU_HW_H +#define QEMU_HW_H + +#include "qemu-common.h" + +#if !defined(CONFIG_USER_ONLY) && !defined(NEED_CPU_H) +#include "exec/cpu-common.h" +#endif + +#include "exec/ioport.h" +#include "hw/irq.h" +#include "block/aio.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" +#include "qemu/log.h" + +#ifdef NEED_CPU_H +#if TARGET_LONG_BITS == 64 +#define qemu_put_betl qemu_put_be64 +#define qemu_get_betl qemu_get_be64 +#define qemu_put_betls qemu_put_be64s +#define qemu_get_betls qemu_get_be64s +#define qemu_put_sbetl qemu_put_sbe64 +#define qemu_get_sbetl qemu_get_sbe64 +#define qemu_put_sbetls qemu_put_sbe64s +#define qemu_get_sbetls qemu_get_sbe64s +#else +#define qemu_put_betl qemu_put_be32 +#define qemu_get_betl qemu_get_be32 +#define qemu_put_betls qemu_put_be32s +#define qemu_get_betls qemu_get_be32s +#define qemu_put_sbetl qemu_put_sbe32 +#define qemu_get_sbetl qemu_get_sbe32 +#define qemu_put_sbetls qemu_put_sbe32s +#define qemu_get_sbetls qemu_get_sbe32s +#endif +#endif + +typedef void QEMUResetHandler(void *opaque); + +void qemu_register_reset(QEMUResetHandler *func, void *opaque); +void qemu_unregister_reset(QEMUResetHandler *func, void *opaque); + +/* handler to set the boot_device order for a specific type of QEMUMachine */ +/* return 0 if success */ +typedef int QEMUBootSetHandler(void *opaque, const char *boot_devices); +void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque); +int qemu_boot_set(const char *boot_devices); + +#ifdef NEED_CPU_H +#if TARGET_LONG_BITS == 64 +#define VMSTATE_UINTTL_V(_f, _s, _v) \ + VMSTATE_UINT64_V(_f, _s, _v) +#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \ + VMSTATE_UINT64_EQUAL_V(_f, _s, _v) +#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) +#else +#define VMSTATE_UINTTL_V(_f, _s, _v) \ + VMSTATE_UINT32_V(_f, _s, _v) +#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \ + VMSTATE_UINT32_EQUAL_V(_f, _s, _v) +#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) +#endif +#define VMSTATE_UINTTL(_f, _s) \ + VMSTATE_UINTTL_V(_f, _s, 0) +#define VMSTATE_UINTTL_EQUAL(_f, _s) \ + VMSTATE_UINTTL_EQUAL_V(_f, _s, 0) +#define VMSTATE_UINTTL_ARRAY(_f, _s, _n) \ + VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, 0) + +#endif + +#endif diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h new file mode 100644 index 0000000000..461392f374 --- /dev/null +++ b/include/hw/i2c/i2c.h @@ -0,0 +1,88 @@ +#ifndef QEMU_I2C_H +#define QEMU_I2C_H + +#include "hw/qdev.h" + +/* The QEMU I2C implementation only supports simple transfers that complete + immediately. It does not support slave devices that need to be able to + defer their response (eg. CPU slave interfaces where the data is supplied + by the device driver in response to an interrupt). */ + +enum i2c_event { + I2C_START_RECV, + I2C_START_SEND, + I2C_FINISH, + I2C_NACK /* Masker NACKed a receive byte. */ +}; + +typedef struct I2CSlave I2CSlave; + +#define TYPE_I2C_SLAVE "i2c-slave" +#define I2C_SLAVE(obj) \ + OBJECT_CHECK(I2CSlave, (obj), TYPE_I2C_SLAVE) +#define I2C_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(I2CSlaveClass, (klass), TYPE_I2C_SLAVE) +#define I2C_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(I2CSlaveClass, (obj), TYPE_I2C_SLAVE) + +typedef struct I2CSlaveClass +{ + DeviceClass parent_class; + + /* Callbacks provided by the device. */ + int (*init)(I2CSlave *dev); + + /* Master to slave. */ + int (*send)(I2CSlave *s, uint8_t data); + + /* Slave to master. */ + int (*recv)(I2CSlave *s); + + /* Notify the slave of a bus state change. */ + void (*event)(I2CSlave *s, enum i2c_event event); +} I2CSlaveClass; + +struct I2CSlave +{ + DeviceState qdev; + + /* Remaining fields for internal use by the I2C code. */ + uint8_t address; +}; + +i2c_bus *i2c_init_bus(DeviceState *parent, const char *name); +void i2c_set_slave_address(I2CSlave *dev, uint8_t address); +int i2c_bus_busy(i2c_bus *bus); +int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv); +void i2c_end_transfer(i2c_bus *bus); +void i2c_nack(i2c_bus *bus); +int i2c_send(i2c_bus *bus, uint8_t data); +int i2c_recv(i2c_bus *bus); + +#define FROM_I2C_SLAVE(type, dev) DO_UPCAST(type, i2c, dev) + +DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr); + +/* wm8750.c */ +void wm8750_data_req_set(DeviceState *dev, + void (*data_req)(void *, int, int), void *opaque); +void wm8750_dac_dat(void *opaque, uint32_t sample); +uint32_t wm8750_adc_dat(void *opaque); +void *wm8750_dac_buffer(void *opaque, int samples); +void wm8750_dac_commit(void *opaque); +void wm8750_set_bclk_in(void *opaque, int new_hz); + +/* lm832x.c */ +void lm832x_key_event(DeviceState *dev, int key, int state); + +extern const VMStateDescription vmstate_i2c_slave; + +#define VMSTATE_I2C_SLAVE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(I2CSlave), \ + .vmsd = &vmstate_i2c_slave, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, I2CSlave), \ +} + +#endif diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h new file mode 100644 index 0000000000..e3069bf7d4 --- /dev/null +++ b/include/hw/i2c/pm_smbus.h @@ -0,0 +1,20 @@ +#ifndef PM_SMBUS_H +#define PM_SMBUS_H + +typedef struct PMSMBus { + i2c_bus *smbus; + MemoryRegion io; + + uint8_t smb_stat; + uint8_t smb_ctl; + uint8_t smb_cmd; + uint8_t smb_addr; + uint8_t smb_data0; + uint8_t smb_data1; + uint8_t smb_data[32]; + uint8_t smb_index; +} PMSMBus; + +void pm_smbus_init(DeviceState *parent, PMSMBus *smb); + +#endif /* !PM_SMBUS_H */ diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h new file mode 100644 index 0000000000..d764d759c4 --- /dev/null +++ b/include/hw/i2c/smbus.h @@ -0,0 +1,83 @@ +#ifndef QEMU_SMBUS_H +#define QEMU_SMBUS_H + +/* + * QEMU SMBus API + * + * Copyright (c) 2007 Arastra, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/i2c/i2c.h" + +#define TYPE_SMBUS_DEVICE "smbus-device" +#define SMBUS_DEVICE(obj) \ + OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE) +#define SMBUS_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE) +#define SMBUS_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE) + +typedef struct SMBusDeviceClass +{ + I2CSlaveClass parent_class; + int (*init)(SMBusDevice *dev); + void (*quick_cmd)(SMBusDevice *dev, uint8_t read); + void (*send_byte)(SMBusDevice *dev, uint8_t val); + uint8_t (*receive_byte)(SMBusDevice *dev); + /* We can't distinguish between a word write and a block write with + length 1, so pass the whole data block including the length byte + (if present). The device is responsible figuring out what type of + command this is. */ + void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len); + /* Likewise we can't distinguish between different reads, or even know + the length of the read until the read is complete, so read data a + byte at a time. The device is responsible for adding the length + byte on block reads. */ + uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n); +} SMBusDeviceClass; + +struct SMBusDevice { + /* The SMBus protocol is implemented on top of I2C. */ + I2CSlave i2c; + + /* Remaining fields for internal use only. */ + int mode; + int data_len; + uint8_t data_buf[34]; /* command + len + 32 bytes of data. */ + uint8_t command; +}; + +/* Master device commands. */ +void smbus_quick_command(i2c_bus *bus, uint8_t addr, int read); +uint8_t smbus_receive_byte(i2c_bus *bus, uint8_t addr); +void smbus_send_byte(i2c_bus *bus, uint8_t addr, uint8_t data); +uint8_t smbus_read_byte(i2c_bus *bus, uint8_t addr, uint8_t command); +void smbus_write_byte(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t data); +uint16_t smbus_read_word(i2c_bus *bus, uint8_t addr, uint8_t command); +void smbus_write_word(i2c_bus *bus, uint8_t addr, uint8_t command, uint16_t data); +int smbus_read_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data); +void smbus_write_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len); + +void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom, + const uint8_t *eeprom_spd, int size); + +#endif diff --git a/include/hw/i386/apic-msidef.h b/include/hw/i386/apic-msidef.h new file mode 100644 index 0000000000..6e2eb71f2f --- /dev/null +++ b/include/hw/i386/apic-msidef.h @@ -0,0 +1,30 @@ +#ifndef HW_APIC_MSIDEF_H +#define HW_APIC_MSIDEF_H + +/* + * Intel APIC constants: from include/asm/msidef.h + */ + +/* + * Shifts for MSI data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff + +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_TRIGGER_SHIFT 15 + +/* + * Shift/mask fields for msi address + */ + +#define MSI_ADDR_DEST_MODE_SHIFT 2 + +#define MSI_ADDR_REDIRECTION_SHIFT 3 + +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 + +#endif /* HW_APIC_MSIDEF_H */ diff --git a/include/hw/i386/apic.h b/include/hw/i386/apic.h new file mode 100644 index 0000000000..1d48e027c3 --- /dev/null +++ b/include/hw/i386/apic.h @@ -0,0 +1,32 @@ +#ifndef APIC_H +#define APIC_H + +#include "qemu-common.h" + +/* apic.c */ +void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, + uint8_t vector_num, uint8_t trigger_mode); +int apic_accept_pic_intr(DeviceState *s); +void apic_deliver_pic_intr(DeviceState *s, int level); +void apic_deliver_nmi(DeviceState *d); +int apic_get_interrupt(DeviceState *s); +void apic_reset_irq_delivered(void); +int apic_get_irq_delivered(void); +void cpu_set_apic_base(DeviceState *s, uint64_t val); +uint64_t cpu_get_apic_base(DeviceState *s); +void cpu_set_apic_tpr(DeviceState *s, uint8_t val); +uint8_t cpu_get_apic_tpr(DeviceState *s); +void apic_init_reset(DeviceState *s); +void apic_sipi(DeviceState *s); +void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, + TPRAccess access); +void apic_poll_irq(DeviceState *d); +void apic_designate_bsp(DeviceState *d); + +/* pc.c */ +DeviceState *cpu_get_current_apic(void); + +/* cpu.c */ +bool cpu_is_bsp(X86CPU *cpu); + +#endif diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h new file mode 100644 index 0000000000..578241f861 --- /dev/null +++ b/include/hw/i386/apic_internal.h @@ -0,0 +1,149 @@ +/* + * APIC support - internal interfaces + * + * Copyright (c) 2004-2005 Fabrice Bellard + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ +#ifndef QEMU_APIC_INTERNAL_H +#define QEMU_APIC_INTERNAL_H + +#include "exec/memory.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" + +/* APIC Local Vector Table */ +#define APIC_LVT_TIMER 0 +#define APIC_LVT_THERMAL 1 +#define APIC_LVT_PERFORM 2 +#define APIC_LVT_LINT0 3 +#define APIC_LVT_LINT1 4 +#define APIC_LVT_ERROR 5 +#define APIC_LVT_NB 6 + +/* APIC delivery modes */ +#define APIC_DM_FIXED 0 +#define APIC_DM_LOWPRI 1 +#define APIC_DM_SMI 2 +#define APIC_DM_NMI 4 +#define APIC_DM_INIT 5 +#define APIC_DM_SIPI 6 +#define APIC_DM_EXTINT 7 + +/* APIC destination mode */ +#define APIC_DESTMODE_FLAT 0xf +#define APIC_DESTMODE_CLUSTER 1 + +#define APIC_TRIGGER_EDGE 0 +#define APIC_TRIGGER_LEVEL 1 + +#define APIC_LVT_TIMER_PERIODIC (1<<17) +#define APIC_LVT_MASKED (1<<16) +#define APIC_LVT_LEVEL_TRIGGER (1<<15) +#define APIC_LVT_REMOTE_IRR (1<<14) +#define APIC_INPUT_POLARITY (1<<13) +#define APIC_SEND_PENDING (1<<12) + +#define ESR_ILLEGAL_ADDRESS (1 << 7) + +#define APIC_SV_DIRECTED_IO (1<<12) +#define APIC_SV_ENABLE (1<<8) + +#define VAPIC_ENABLE_BIT 0 +#define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT) + +#define MAX_APICS 255 + +#define MSI_SPACE_SIZE 0x100000 + +typedef struct APICCommonState APICCommonState; + +#define TYPE_APIC_COMMON "apic-common" +#define APIC_COMMON(obj) \ + OBJECT_CHECK(APICCommonState, (obj), TYPE_APIC_COMMON) +#define APIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(APICCommonClass, (klass), TYPE_APIC_COMMON) +#define APIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(APICCommonClass, (obj), TYPE_APIC_COMMON) + +typedef struct APICCommonClass +{ + SysBusDeviceClass parent_class; + + void (*init)(APICCommonState *s); + void (*set_base)(APICCommonState *s, uint64_t val); + void (*set_tpr)(APICCommonState *s, uint8_t val); + uint8_t (*get_tpr)(APICCommonState *s); + void (*enable_tpr_reporting)(APICCommonState *s, bool enable); + void (*vapic_base_update)(APICCommonState *s); + void (*external_nmi)(APICCommonState *s); + void (*pre_save)(APICCommonState *s); + void (*post_load)(APICCommonState *s); +} APICCommonClass; + +struct APICCommonState { + SysBusDevice busdev; + + MemoryRegion io_memory; + X86CPU *cpu; + uint32_t apicbase; + uint8_t id; + uint8_t arb_id; + uint8_t tpr; + uint32_t spurious_vec; + uint8_t log_dest; + uint8_t dest_mode; + uint32_t isr[8]; /* in service register */ + uint32_t tmr[8]; /* trigger mode register */ + uint32_t irr[8]; /* interrupt request register */ + uint32_t lvt[APIC_LVT_NB]; + uint32_t esr; /* error register */ + uint32_t icr[2]; + + uint32_t divide_conf; + int count_shift; + uint32_t initial_count; + int64_t initial_count_load_time; + int64_t next_time; + int idx; + QEMUTimer *timer; + int64_t timer_expiry; + int sipi_vector; + int wait_for_sipi; + + uint32_t vapic_control; + DeviceState *vapic; + hwaddr vapic_paddr; /* note: persistence via kvmvapic */ +}; + +typedef struct VAPICState { + uint8_t tpr; + uint8_t isr; + uint8_t zero; + uint8_t irr; + uint8_t enabled; +} QEMU_PACKED VAPICState; + +extern bool apic_report_tpr_access; + +void apic_report_irq_delivered(int delivered); +bool apic_next_timer(APICCommonState *s, int64_t current_time); +void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); +void apic_enable_vapic(DeviceState *d, hwaddr paddr); + +void vapic_report_tpr_access(DeviceState *dev, CPUState *cpu, target_ulong ip, + TPRAccess access); + +#endif /* !QEMU_APIC_INTERNAL_H */ diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h new file mode 100644 index 0000000000..51d59819ab --- /dev/null +++ b/include/hw/i386/ich9.h @@ -0,0 +1,219 @@ +#ifndef HW_ICH9_H +#define HW_ICH9_H + +#include "hw/hw.h" +#include "qemu/range.h" +#include "hw/isa/isa.h" +#include "hw/sysbus.h" +#include "hw/i386/pc.h" +#include "hw/isa/apm.h" +#include "hw/i386/ioapic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/ich9.h" +#include "hw/pci/pci_bus.h" + +void ich9_lpc_set_irq(void *opaque, int irq_num, int level); +int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); +PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin); +void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3); +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus); +i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); + +#define ICH9_CC_SIZE (16 * 1024) /* 16KB */ + +#define TYPE_ICH9_LPC_DEVICE "ICH9 LPC" +#define ICH9_LPC_DEVICE(obj) \ + OBJECT_CHECK(ICH9LPCState, (obj), TYPE_ICH9_LPC_DEVICE) + +typedef struct ICH9LPCState { + /* ICH9 LPC PCI to ISA bridge */ + PCIDevice d; + + /* (pci device, intx) -> pirq + * In real chipset case, the unused slots are never used + * as ICH9 supports only D25-D32 irq routing. + * On the other hand in qemu case, any slot/function can be populated + * via command line option. + * So fallback interrupt routing for any devices in any slots is necessary. + */ + uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS]; + + APMState apm; + ICH9LPCPMRegs pm; + uint32_t sci_level; /* track sci level */ + + /* 10.1 Chipset Configuration registers(Memory Space) + which is pointed by RCBA */ + uint8_t chip_config[ICH9_CC_SIZE]; + + /* + * 13.7.5 RST_CNT---Reset Control Register (LPC I/F---D31:F0) + * + * register contents and IO memory region + */ + uint8_t rst_cnt; + MemoryRegion rst_cnt_mem; + + /* isa bus */ + ISABus *isa_bus; + MemoryRegion rbca_mem; + Notifier machine_ready; + + qemu_irq *pic; + qemu_irq *ioapic; +} ICH9LPCState; + +#define Q35_MASK(bit, ms_bit, ls_bit) \ +((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) + +/* ICH9: Chipset Configuration Registers */ +#define ICH9_CC_ADDR_MASK (ICH9_CC_SIZE - 1) + +#define ICH9_CC +#define ICH9_CC_D28IP 0x310C +#define ICH9_CC_D28IP_SHIFT 4 +#define ICH9_CC_D28IP_MASK 0xf +#define ICH9_CC_D28IP_DEFAULT 0x00214321 +#define ICH9_CC_D31IR 0x3140 +#define ICH9_CC_D30IR 0x3142 +#define ICH9_CC_D29IR 0x3144 +#define ICH9_CC_D28IR 0x3146 +#define ICH9_CC_D27IR 0x3148 +#define ICH9_CC_D26IR 0x314C +#define ICH9_CC_D25IR 0x3150 +#define ICH9_CC_DIR_DEFAULT 0x3210 +#define ICH9_CC_D30IR_DEFAULT 0x0 +#define ICH9_CC_DIR_SHIFT 4 +#define ICH9_CC_DIR_MASK 0x7 +#define ICH9_CC_OIC 0x31FF +#define ICH9_CC_OIC_AEN 0x1 + +/* D28:F[0-5] */ +#define ICH9_PCIE_DEV 28 +#define ICH9_PCIE_FUNC_MAX 6 + + +/* D29:F0 USB UHCI Controller #1 */ +#define ICH9_USB_UHCI1_DEV 29 +#define ICH9_USB_UHCI1_FUNC 0 + +/* D30:F0 DMI-to-PCI brdige */ +#define ICH9_D2P_BRIDGE "ICH9 D2P BRIDGE" +#define ICH9_D2P_BRIDGE_SAVEVM_VERSION 0 + +#define ICH9_D2P_BRIDGE_DEV 30 +#define ICH9_D2P_BRIDGE_FUNC 0 + +#define ICH9_D2P_SECONDARY_DEFAULT (256 - 8) + +#define ICH9_D2P_A2_REVISION 0x92 + +/* D31:F0 LPC Processor Interface */ +#define ICH9_RST_CNT_IOPORT 0xCF9 + +/* D31:F1 LPC controller */ +#define ICH9_A2_LPC "ICH9 A2 LPC" +#define ICH9_A2_LPC_SAVEVM_VERSION 0 + +#define ICH9_LPC_DEV 31 +#define ICH9_LPC_FUNC 0 + +#define ICH9_A2_LPC_REVISION 0x2 +#define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */ + +#define ICH9_LPC_PMBASE 0x40 +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) +#define ICH9_LPC_PMBASE_RTE 0x1 +#define ICH9_LPC_PMBASE_DEFAULT 0x1 +#define ICH9_LPC_ACPI_CTRL 0x44 +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) +#define ICH9_LPC_ACPI_CTRL_9 0x0 +#define ICH9_LPC_ACPI_CTRL_10 0x1 +#define ICH9_LPC_ACPI_CTRL_11 0x2 +#define ICH9_LPC_ACPI_CTRL_20 0x4 +#define ICH9_LPC_ACPI_CTRL_21 0x5 +#define ICH9_LPC_ACPI_CTRL_DEFAULT 0x0 + +#define ICH9_LPC_PIRQA_ROUT 0x60 +#define ICH9_LPC_PIRQB_ROUT 0x61 +#define ICH9_LPC_PIRQC_ROUT 0x62 +#define ICH9_LPC_PIRQD_ROUT 0x63 + +#define ICH9_LPC_PIRQE_ROUT 0x68 +#define ICH9_LPC_PIRQF_ROUT 0x69 +#define ICH9_LPC_PIRQG_ROUT 0x6a +#define ICH9_LPC_PIRQH_ROUT 0x6b + +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0) +#define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80 + +#define ICH9_LPC_RCBA 0xf0 +#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14) +#define ICH9_LPC_RCBA_EN 0x1 +#define ICH9_LPC_RCBA_DEFAULT 0x0 + +#define ICH9_LPC_PIC_NUM_PINS 16 +#define ICH9_LPC_IOAPIC_NUM_PINS 24 + +/* D31:F2 SATA Controller #1 */ +#define ICH9_SATA1_DEV 31 +#define ICH9_SATA1_FUNC 2 + +/* D30:F1 power management I/O registers + offset from the address ICH9_LPC_PMBASE */ + +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ +#define ICH9_PMIO_SIZE 128 +#define ICH9_PMIO_MASK (ICH9_PMIO_SIZE - 1) + +#define ICH9_PMIO_PM1_STS 0x00 +#define ICH9_PMIO_PM1_EN 0x02 +#define ICH9_PMIO_PM1_CNT 0x04 +#define ICH9_PMIO_PM1_TMR 0x08 +#define ICH9_PMIO_GPE0_STS 0x20 +#define ICH9_PMIO_GPE0_EN 0x28 +#define ICH9_PMIO_GPE0_LEN 16 +#define ICH9_PMIO_SMI_EN 0x30 +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_STS 0x34 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define ICH9_APM_ACPI_ENABLE 0x2 +#define ICH9_APM_ACPI_DISABLE 0x3 + + +/* D31:F3 SMBus controller */ +#define ICH9_A2_SMB_REVISION 0x02 +#define ICH9_SMB_PI 0x00 + +#define ICH9_SMB_SMBMBAR0 0x10 +#define ICH9_SMB_SMBMBAR1 0x14 +#define ICH9_SMB_SMBM_BAR 0 +#define ICH9_SMB_SMBM_SIZE (1 << 8) +#define ICH9_SMB_SMB_BASE 0x20 +#define ICH9_SMB_SMB_BASE_BAR 4 +#define ICH9_SMB_SMB_BASE_SIZE (1 << 5) +#define ICH9_SMB_HOSTC 0x40 +#define ICH9_SMB_HOSTC_SSRESET ((uint8_t)(1 << 3)) +#define ICH9_SMB_HOSTC_I2C_EN ((uint8_t)(1 << 2)) +#define ICH9_SMB_HOSTC_SMB_SMI_EN ((uint8_t)(1 << 1)) +#define ICH9_SMB_HOSTC_HST_EN ((uint8_t)(1 << 0)) + +/* D31:F3 SMBus I/O and memory mapped I/O registers */ +#define ICH9_SMB_DEV 31 +#define ICH9_SMB_FUNC 3 + +#define ICH9_SMB_HST_STS 0x00 +#define ICH9_SMB_HST_CNT 0x02 +#define ICH9_SMB_HST_CMD 0x03 +#define ICH9_SMB_XMIT_SLVA 0x04 +#define ICH9_SMB_HST_D0 0x05 +#define ICH9_SMB_HST_D1 0x06 +#define ICH9_SMB_HOST_BLOCK_DB 0x07 + +#endif /* HW_ICH9_H */ diff --git a/include/hw/i386/ioapic.h b/include/hw/i386/ioapic.h new file mode 100644 index 0000000000..86e63dac74 --- /dev/null +++ b/include/hw/i386/ioapic.h @@ -0,0 +1,27 @@ +/* + * ioapic.c IOAPIC emulation logic + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef HW_IOAPIC_H +#define HW_IOAPIC_H + +#define IOAPIC_NUM_PINS 24 + +void ioapic_eoi_broadcast(int vector); + +#endif /* !HW_IOAPIC_H */ diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h new file mode 100644 index 0000000000..25576c819e --- /dev/null +++ b/include/hw/i386/ioapic_internal.h @@ -0,0 +1,102 @@ +/* + * IOAPIC emulation logic - internal interfaces + * + * Copyright (c) 2004-2005 Fabrice Bellard + * Copyright (c) 2009 Xiantao Zhang, Intel + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef QEMU_IOAPIC_INTERNAL_H +#define QEMU_IOAPIC_INTERNAL_H + +#include "hw/hw.h" +#include "exec/memory.h" +#include "hw/sysbus.h" + +#define MAX_IOAPICS 1 + +#define IOAPIC_VERSION 0x11 + +#define IOAPIC_LVT_DEST_SHIFT 56 +#define IOAPIC_LVT_MASKED_SHIFT 16 +#define IOAPIC_LVT_TRIGGER_MODE_SHIFT 15 +#define IOAPIC_LVT_REMOTE_IRR_SHIFT 14 +#define IOAPIC_LVT_POLARITY_SHIFT 13 +#define IOAPIC_LVT_DELIV_STATUS_SHIFT 12 +#define IOAPIC_LVT_DEST_MODE_SHIFT 11 +#define IOAPIC_LVT_DELIV_MODE_SHIFT 8 + +#define IOAPIC_LVT_MASKED (1 << IOAPIC_LVT_MASKED_SHIFT) +#define IOAPIC_LVT_REMOTE_IRR (1 << IOAPIC_LVT_REMOTE_IRR_SHIFT) + +#define IOAPIC_TRIGGER_EDGE 0 +#define IOAPIC_TRIGGER_LEVEL 1 + +/*io{apic,sapic} delivery mode*/ +#define IOAPIC_DM_FIXED 0x0 +#define IOAPIC_DM_LOWEST_PRIORITY 0x1 +#define IOAPIC_DM_PMI 0x2 +#define IOAPIC_DM_NMI 0x4 +#define IOAPIC_DM_INIT 0x5 +#define IOAPIC_DM_SIPI 0x6 +#define IOAPIC_DM_EXTINT 0x7 +#define IOAPIC_DM_MASK 0x7 + +#define IOAPIC_VECTOR_MASK 0xff + +#define IOAPIC_IOREGSEL 0x00 +#define IOAPIC_IOWIN 0x10 + +#define IOAPIC_REG_ID 0x00 +#define IOAPIC_REG_VER 0x01 +#define IOAPIC_REG_ARB 0x02 +#define IOAPIC_REG_REDTBL_BASE 0x10 +#define IOAPIC_ID 0x00 + +#define IOAPIC_ID_SHIFT 24 +#define IOAPIC_ID_MASK 0xf + +#define IOAPIC_VER_ENTRIES_SHIFT 16 + +typedef struct IOAPICCommonState IOAPICCommonState; + +#define TYPE_IOAPIC_COMMON "ioapic-common" +#define IOAPIC_COMMON(obj) \ + OBJECT_CHECK(IOAPICCommonState, (obj), TYPE_IOAPIC_COMMON) +#define IOAPIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(IOAPICCommonClass, (klass), TYPE_IOAPIC_COMMON) +#define IOAPIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IOAPICCommonClass, (obj), TYPE_IOAPIC_COMMON) + +typedef struct IOAPICCommonClass { + SysBusDeviceClass parent_class; + void (*init)(IOAPICCommonState *s, int instance_no); + void (*pre_save)(IOAPICCommonState *s); + void (*post_load)(IOAPICCommonState *s); +} IOAPICCommonClass; + +struct IOAPICCommonState { + SysBusDevice busdev; + MemoryRegion io_memory; + uint8_t id; + uint8_t ioregsel; + uint32_t irr; + uint64_t ioredtbl[IOAPIC_NUM_PINS]; +}; + +void ioapic_reset_common(DeviceState *dev); + +#endif /* !QEMU_IOAPIC_INTERNAL_H */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h new file mode 100644 index 0000000000..5d40914f49 --- /dev/null +++ b/include/hw/i386/pc.h @@ -0,0 +1,246 @@ +#ifndef HW_PC_H +#define HW_PC_H + +#include "qemu-common.h" +#include "exec/memory.h" +#include "exec/ioport.h" +#include "hw/isa/isa.h" +#include "hw/block/fdc.h" +#include "net/net.h" +#include "exec/memory.h" +#include "hw/i386/ioapic.h" + +/* PC-style peripherals (also used by other machines). */ + +/* parallel.c */ +static inline bool parallel_init(ISABus *bus, int index, CharDriverState *chr) +{ + ISADevice *dev; + + dev = isa_try_create(bus, "isa-parallel"); + if (!dev) { + return false; + } + qdev_prop_set_uint32(&dev->qdev, "index", index); + qdev_prop_set_chr(&dev->qdev, "chardev", chr); + if (qdev_init(&dev->qdev) < 0) { + return false; + } + return true; +} + +bool parallel_mm_init(MemoryRegion *address_space, + hwaddr base, int it_shift, qemu_irq irq, + CharDriverState *chr); + +/* i8259.c */ + +extern DeviceState *isa_pic; +qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); +qemu_irq *kvm_i8259_init(ISABus *bus); +int pic_read_irq(DeviceState *d); +int pic_get_output(DeviceState *d); +void pic_info(Monitor *mon, const QDict *qdict); +void irq_info(Monitor *mon, const QDict *qdict); + +/* Global System Interrupts */ + +#define GSI_NUM_PINS IOAPIC_NUM_PINS + +typedef struct GSIState { + qemu_irq i8259_irq[ISA_NUM_IRQS]; + qemu_irq ioapic_irq[IOAPIC_NUM_PINS]; +} GSIState; + +void gsi_handler(void *opaque, int n, int level); + +/* vmport.c */ +static inline void vmport_init(ISABus *bus) +{ + isa_create_simple(bus, "vmport"); +} +void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque); +void vmmouse_get_data(uint32_t *data); +void vmmouse_set_data(const uint32_t *data); + +/* pckbd.c */ + +void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base); +void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, + MemoryRegion *region, ram_addr_t size, + hwaddr mask); +void i8042_isa_mouse_fake_event(void *opaque); +void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out); + +/* pc.c */ +extern int fd_bootchk; + +void pc_register_ferr_irq(qemu_irq irq); +void pc_acpi_smi_interrupt(void *opaque, int irq, int level); + +void pc_cpus_init(const char *cpu_model); +void pc_acpi_init(const char *default_dsdt); +void *pc_memory_init(MemoryRegion *system_memory, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size, + MemoryRegion *rom_memory, + MemoryRegion **ram_memory); +qemu_irq *pc_allocate_cpu_irq(void); +DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus); +void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, + ISADevice **rtc_state, + ISADevice **floppy, + bool no_vmport); +void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd); +void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, + const char *boot_device, + ISADevice *floppy, BusState *ide0, BusState *ide1, + ISADevice *s); +void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus); +void pc_pci_device_init(PCIBus *pci_bus); + +typedef void (*cpu_set_smm_t)(int smm, void *arg); +void cpu_smm_register(cpu_set_smm_t callback, void *arg); + +void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); + +/* acpi.c */ +extern int acpi_enabled; +extern char unsigned *acpi_tables; +extern size_t acpi_tables_len; + +void acpi_bios_init(void); +void acpi_table_add(const QemuOpts *opts, Error **errp); + +/* acpi_piix.c */ + +i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, + qemu_irq sci_irq, qemu_irq smi_irq, + int kvm_enabled, void *fw_cfg); +void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr); + +/* hpet.c */ +extern int no_hpet; + +/* piix_pci.c */ +struct PCII440FXState; +typedef struct PCII440FXState PCII440FXState; + +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, + ISABus **isa_bus, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + ram_addr_t ram_size, + hwaddr pci_hole_start, + hwaddr pci_hole_size, + hwaddr pci_hole64_start, + hwaddr pci_hole64_size, + MemoryRegion *pci_memory, + MemoryRegion *ram_memory); + +/* piix4.c */ +extern PCIDevice *piix4_dev; +int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn); + +/* vga.c */ +enum vga_retrace_method { + VGA_RETRACE_DUMB, + VGA_RETRACE_PRECISE +}; + +extern enum vga_retrace_method vga_retrace_method; + +int isa_vga_mm_init(hwaddr vram_base, + hwaddr ctrl_base, int it_shift, + MemoryRegion *address_space); + +/* ne2000.c */ +static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd) +{ + ISADevice *dev; + + qemu_check_nic_model(nd, "ne2k_isa"); + + dev = isa_try_create(bus, "ne2k_isa"); + if (!dev) { + return false; + } + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_prop_set_uint32(&dev->qdev, "irq", irq); + qdev_set_nic_properties(&dev->qdev, nd); + qdev_init_nofail(&dev->qdev); + return true; +} + +/* pc_sysfw.c */ +void pc_system_firmware_init(MemoryRegion *rom_memory); + +/* e820 types */ +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 + +int e820_add_entry(uint64_t, uint64_t, uint32_t); + +#define PC_COMPAT_1_4 \ + {\ + .driver = "scsi-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-disk",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-drive",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-blk-pci",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-serial-pci",\ + .property = "vectors",\ + /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ + .value = stringify(0xFFFFFFFF),\ + },{\ + .driver = "e1000",\ + .property = "romfile",\ + .value = "pxe-e1000.rom",\ + },{\ + .driver = "ne2k_pci",\ + .property = "romfile",\ + .value = "pxe-ne2k_pci.rom",\ + },{\ + .driver = "pcnet",\ + .property = "romfile",\ + .value = "pxe-pcnet.rom",\ + },{\ + .driver = "rtl8139",\ + .property = "romfile",\ + .value = "pxe-rtl8139.rom",\ + },{\ + .driver = "virtio-net-pci",\ + .property = "romfile",\ + .value = "pxe-virtio.rom",\ + } + +#endif diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h new file mode 100644 index 0000000000..94e3641f9a --- /dev/null +++ b/include/hw/i386/smbios.h @@ -0,0 +1,162 @@ +#ifndef QEMU_SMBIOS_H +#define QEMU_SMBIOS_H +/* + * SMBIOS Support + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +int smbios_entry_add(const char *t); +void smbios_add_field(int type, int offset, int len, void *data); +uint8_t *smbios_get_table(size_t *length); + +/* + * SMBIOS spec defined tables + */ + +/* This goes at the beginning of every SMBIOS structure. */ +struct smbios_structure_header { + uint8_t type; + uint8_t length; + uint16_t handle; +} QEMU_PACKED; + +/* SMBIOS type 0 - BIOS Information */ +struct smbios_type_0 { + struct smbios_structure_header header; + uint8_t vendor_str; + uint8_t bios_version_str; + uint16_t bios_starting_address_segment; + uint8_t bios_release_date_str; + uint8_t bios_rom_size; + uint8_t bios_characteristics[8]; + uint8_t bios_characteristics_extension_bytes[2]; + uint8_t system_bios_major_release; + uint8_t system_bios_minor_release; + uint8_t embedded_controller_major_release; + uint8_t embedded_controller_minor_release; +} QEMU_PACKED; + +/* SMBIOS type 1 - System Information */ +struct smbios_type_1 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t product_name_str; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t uuid[16]; + uint8_t wake_up_type; + uint8_t sku_number_str; + uint8_t family_str; +} QEMU_PACKED; + +/* SMBIOS type 3 - System Enclosure (v2.3) */ +struct smbios_type_3 { + struct smbios_structure_header header; + uint8_t manufacturer_str; + uint8_t type; + uint8_t version_str; + uint8_t serial_number_str; + uint8_t asset_tag_number_str; + uint8_t boot_up_state; + uint8_t power_supply_state; + uint8_t thermal_state; + uint8_t security_status; + uint32_t oem_defined; + uint8_t height; + uint8_t number_of_power_cords; + uint8_t contained_element_count; + // contained elements follow +} QEMU_PACKED; + +/* SMBIOS type 4 - Processor Information (v2.0) */ +struct smbios_type_4 { + struct smbios_structure_header header; + uint8_t socket_designation_str; + uint8_t processor_type; + uint8_t processor_family; + uint8_t processor_manufacturer_str; + uint32_t processor_id[2]; + uint8_t processor_version_str; + uint8_t voltage; + uint16_t external_clock; + uint16_t max_speed; + uint16_t current_speed; + uint8_t status; + uint8_t processor_upgrade; + uint16_t l1_cache_handle; + uint16_t l2_cache_handle; + uint16_t l3_cache_handle; +} QEMU_PACKED; + +/* SMBIOS type 16 - Physical Memory Array + * Associated with one type 17 (Memory Device). + */ +struct smbios_type_16 { + struct smbios_structure_header header; + uint8_t location; + uint8_t use; + uint8_t error_correction; + uint32_t maximum_capacity; + uint16_t memory_error_information_handle; + uint16_t number_of_memory_devices; +} QEMU_PACKED; +/* SMBIOS type 17 - Memory Device + * Associated with one type 19 + */ +struct smbios_type_17 { + struct smbios_structure_header header; + uint16_t physical_memory_array_handle; + uint16_t memory_error_information_handle; + uint16_t total_width; + uint16_t data_width; + uint16_t size; + uint8_t form_factor; + uint8_t device_set; + uint8_t device_locator_str; + uint8_t bank_locator_str; + uint8_t memory_type; + uint16_t type_detail; +} QEMU_PACKED; + +/* SMBIOS type 19 - Memory Array Mapped Address */ +struct smbios_type_19 { + struct smbios_structure_header header; + uint32_t starting_address; + uint32_t ending_address; + uint16_t memory_array_handle; + uint8_t partition_width; +} QEMU_PACKED; + +/* SMBIOS type 20 - Memory Device Mapped Address */ +struct smbios_type_20 { + struct smbios_structure_header header; + uint32_t starting_address; + uint32_t ending_address; + uint16_t memory_device_handle; + uint16_t memory_array_mapped_address_handle; + uint8_t partition_row_position; + uint8_t interleave_position; + uint8_t interleaved_data_depth; +} QEMU_PACKED; + +/* SMBIOS type 32 - System Boot Information */ +struct smbios_type_32 { + struct smbios_structure_header header; + uint8_t reserved[6]; + uint8_t boot_status; +} QEMU_PACKED; + +/* SMBIOS type 127 -- End-of-table */ +struct smbios_type_127 { + struct smbios_structure_header header; +} QEMU_PACKED; + +#endif /*QEMU_SMBIOS_H */ diff --git a/include/hw/ide.h b/include/hw/ide.h new file mode 100644 index 0000000000..507e6d33cb --- /dev/null +++ b/include/hw/ide.h @@ -0,0 +1,32 @@ +#ifndef HW_IDE_H +#define HW_IDE_H + +#include "hw/isa/isa.h" +#include "hw/pci/pci.h" +#include "exec/memory.h" + +#define MAX_IDE_DEVS 2 + +/* ide-isa.c */ +ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, + DriveInfo *hd0, DriveInfo *hd1); + +/* ide-pci.c */ +void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, + int secondary_ide_enabled); +PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); + +/* ide-mmio.c */ +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); + +int ide_get_geometry(BusState *bus, int unit, + int16_t *cyls, int8_t *heads, int8_t *secs); +int ide_get_bios_chs_trans(BusState *bus, int unit); + +/* ide/core.c */ +void ide_drive_get(DriveInfo **hd, int max_bus); + +#endif /* HW_IDE_H */ diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h new file mode 100644 index 0000000000..bdfccd4041 --- /dev/null +++ b/include/hw/input/adb.h @@ -0,0 +1,87 @@ +/* + * QEMU ADB emulation shared definitions and prototypes + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if !defined(__ADB_H__) +#define __ADB_H__ + +#include "hw/qdev.h" + +#define MAX_ADB_DEVICES 16 + +#define ADB_MAX_OUT_LEN 16 + +typedef struct ADBBusState ADBBusState; +typedef struct ADBDevice ADBDevice; + +/* buf = NULL means polling */ +typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, + const uint8_t *buf, int len); + +#define TYPE_ADB_DEVICE "adb-device" +#define ADB_DEVICE(obj) OBJECT_CHECK(ADBDevice, (obj), TYPE_ADB_DEVICE) + +struct ADBDevice { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + int devaddr; + int handler; +}; + +#define ADB_DEVICE_CLASS(cls) \ + OBJECT_CLASS_CHECK(ADBDeviceClass, (cls), TYPE_ADB_DEVICE) +#define ADB_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBDeviceClass, (obj), TYPE_ADB_DEVICE) + +typedef struct ADBDeviceClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + + ADBDeviceRequest *devreq; +} ADBDeviceClass; + +#define TYPE_ADB_BUS "apple-desktop-bus" +#define ADB_BUS(obj) OBJECT_CHECK(ADBBusState, (obj), TYPE_ADB_BUS) + +struct ADBBusState { + /*< private >*/ + BusState parent_obj; + /*< public >*/ + + ADBDevice *devices[MAX_ADB_DEVICES]; + int nb_devices; + int poll_index; +}; + +int adb_request(ADBBusState *s, uint8_t *buf_out, + const uint8_t *buf, int len); +int adb_poll(ADBBusState *s, uint8_t *buf_out); + +#define TYPE_ADB_KEYBOARD "adb-keyboard" +#define TYPE_ADB_MOUSE "adb-mouse" + +#endif /* !defined(__ADB_H__) */ diff --git a/include/hw/input/hid.h b/include/hw/input/hid.h new file mode 100644 index 0000000000..56c71ed5ae --- /dev/null +++ b/include/hw/input/hid.h @@ -0,0 +1,83 @@ +#ifndef QEMU_HID_H +#define QEMU_HID_H + +#include "migration/vmstate.h" + +#define HID_MOUSE 1 +#define HID_TABLET 2 +#define HID_KEYBOARD 3 + +typedef struct HIDPointerEvent { + int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ + int32_t dz, buttons_state; +} HIDPointerEvent; + +#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ +#define QUEUE_MASK (QUEUE_LENGTH-1u) +#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) + +typedef struct HIDState HIDState; +typedef void (*HIDEventFunc)(HIDState *s); + +typedef struct HIDMouseState { + HIDPointerEvent queue[QUEUE_LENGTH]; + int mouse_grabbed; + QEMUPutMouseEntry *eh_entry; +} HIDMouseState; + +typedef struct HIDKeyboardState { + uint32_t keycodes[QUEUE_LENGTH]; + uint16_t modifiers; + uint8_t leds; + uint8_t key[16]; + int32_t keys; +} HIDKeyboardState; + +struct HIDState { + union { + HIDMouseState ptr; + HIDKeyboardState kbd; + }; + uint32_t head; /* index into circular queue */ + uint32_t n; + int kind; + int32_t protocol; + uint8_t idle; + bool idle_pending; + QEMUTimer *idle_timer; + HIDEventFunc event; +}; + +void hid_init(HIDState *hs, int kind, HIDEventFunc event); +void hid_reset(HIDState *hs); +void hid_free(HIDState *hs); + +bool hid_has_events(HIDState *hs); +void hid_set_next_idle(HIDState *hs); +void hid_pointer_activate(HIDState *hs); +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len); + +extern const VMStateDescription vmstate_hid_keyboard_device; + +#define VMSTATE_HID_KEYBOARD_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(HIDState), \ + .vmsd = &vmstate_hid_keyboard_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, HIDState), \ +} + +extern const VMStateDescription vmstate_hid_ptr_device; + +#define VMSTATE_HID_POINTER_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(HIDState), \ + .vmsd = &vmstate_hid_ptr_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, HIDState), \ +} + + +#endif /* QEMU_HID_H */ diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h new file mode 100644 index 0000000000..7c45ce7ced --- /dev/null +++ b/include/hw/input/ps2.h @@ -0,0 +1,38 @@ +/* + * QEMU PS/2 keyboard/mouse emulation + * + * Copyright (C) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_PS2_H +#define HW_PS2_H + +/* ps2.c */ +void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); +void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); +void ps2_write_mouse(void *, int val); +void ps2_write_keyboard(void *, int val); +uint32_t ps2_read_data(void *); +void ps2_queue(void *, int b); +void ps2_keyboard_set_translation(void *opaque, int mode); +void ps2_mouse_fake_event(void *opaque); + +#endif /* !HW_PS2_H */ diff --git a/include/hw/irq.h b/include/hw/irq.h new file mode 100644 index 0000000000..610e6b7623 --- /dev/null +++ b/include/hw/irq.h @@ -0,0 +1,57 @@ +#ifndef QEMU_IRQ_H +#define QEMU_IRQ_H + +/* Generic IRQ/GPIO pin infrastructure. */ + +typedef struct IRQState *qemu_irq; + +typedef void (*qemu_irq_handler)(void *opaque, int n, int level); + +void qemu_set_irq(qemu_irq irq, int level); + +static inline void qemu_irq_raise(qemu_irq irq) +{ + qemu_set_irq(irq, 1); +} + +static inline void qemu_irq_lower(qemu_irq irq) +{ + qemu_set_irq(irq, 0); +} + +static inline void qemu_irq_pulse(qemu_irq irq) +{ + qemu_set_irq(irq, 1); + qemu_set_irq(irq, 0); +} + +/* Returns an array of N IRQs. Each IRQ is assigned the argument handler and + * opaque data. + */ +qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n); + +/* Extends an Array of IRQs. Old IRQs have their handlers and opaque data + * preserved. New IRQs are assigned the argument handler and opaque data. + */ +qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, + void *opaque, int n); + +void qemu_free_irqs(qemu_irq *s); + +/* Returns a new IRQ with opposite polarity. */ +qemu_irq qemu_irq_invert(qemu_irq irq); + +/* Returns a new IRQ which feeds into both the passed IRQs */ +qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2); + +/* Returns a new IRQ set which connects 1:1 to another IRQ set, which + * may be set later. + */ +qemu_irq *qemu_irq_proxy(qemu_irq **target, int n); + +/* For internal use in qtest. Similar to qemu_irq_split, but operating + on an existing vector of qemu_irq. */ +void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n); +void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n); + +#endif diff --git a/include/hw/isa/apm.h b/include/hw/isa/apm.h new file mode 100644 index 0000000000..3edea5f623 --- /dev/null +++ b/include/hw/isa/apm.h @@ -0,0 +1,25 @@ +#ifndef APM_H +#define APM_H + +#include +#include "qemu-common.h" +#include "hw/hw.h" +#include "exec/memory.h" + +typedef void (*apm_ctrl_changed_t)(uint32_t val, void *arg); + +typedef struct APMState { + uint8_t apmc; + uint8_t apms; + + apm_ctrl_changed_t callback; + void *arg; + MemoryRegion io; +} APMState; + +void apm_init(PCIDevice *dev, APMState *s, apm_ctrl_changed_t callback, + void *arg); + +extern const VMStateDescription vmstate_apm; + +#endif /* APM_H */ diff --git a/include/hw/isa/i8259_internal.h b/include/hw/isa/i8259_internal.h new file mode 100644 index 0000000000..d3ddb27f4f --- /dev/null +++ b/include/hw/isa/i8259_internal.h @@ -0,0 +1,82 @@ +/* + * QEMU 8259 - internal interfaces + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_I8259_INTERNAL_H +#define QEMU_I8259_INTERNAL_H + +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/isa/isa.h" + +typedef struct PICCommonState PICCommonState; + +#define TYPE_PIC_COMMON "pic-common" +#define PIC_COMMON(obj) \ + OBJECT_CHECK(PICCommonState, (obj), TYPE_PIC_COMMON) +#define PIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(PICCommonClass, (klass), TYPE_PIC_COMMON) +#define PIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PICCommonClass, (obj), TYPE_PIC_COMMON) + +typedef struct PICCommonClass +{ + ISADeviceClass parent_class; + void (*init)(PICCommonState *s); + void (*pre_save)(PICCommonState *s); + void (*post_load)(PICCommonState *s); +} PICCommonClass; + +struct PICCommonState { + ISADevice dev; + uint8_t last_irr; /* edge detection */ + uint8_t irr; /* interrupt request register */ + uint8_t imr; /* interrupt mask register */ + uint8_t isr; /* interrupt service register */ + uint8_t priority_add; /* highest irq priority */ + uint8_t irq_base; + uint8_t read_reg_select; + uint8_t poll; + uint8_t special_mask; + uint8_t init_state; + uint8_t auto_eoi; + uint8_t rotate_on_auto_eoi; + uint8_t special_fully_nested_mode; + uint8_t init4; /* true if 4 byte init */ + uint8_t single_mode; /* true if slave pic is not initialized */ + uint8_t elcr; /* PIIX edge/trigger selection*/ + uint8_t elcr_mask; + qemu_irq int_out[1]; + uint32_t master; /* reflects /SP input pin */ + uint32_t iobase; + uint32_t elcr_addr; + MemoryRegion base_io; + MemoryRegion elcr_io; +}; + +void pic_reset_common(PICCommonState *s); + +ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master); + + +#endif /* !QEMU_I8259_INTERNAL_H */ diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h new file mode 100644 index 0000000000..82da37c11d --- /dev/null +++ b/include/hw/isa/isa.h @@ -0,0 +1,104 @@ +#ifndef HW_ISA_H +#define HW_ISA_H + +/* ISA bus */ + +#include "exec/ioport.h" +#include "exec/memory.h" +#include "hw/qdev.h" + +#define ISA_NUM_IRQS 16 + +#define TYPE_ISA_DEVICE "isa-device" +#define ISA_DEVICE(obj) \ + OBJECT_CHECK(ISADevice, (obj), TYPE_ISA_DEVICE) +#define ISA_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(ISADeviceClass, (klass), TYPE_ISA_DEVICE) +#define ISA_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ISADeviceClass, (obj), TYPE_ISA_DEVICE) + +#define TYPE_ISA_BUS "ISA" +#define ISA_BUS(obj) OBJECT_CHECK(ISABus, (obj), TYPE_ISA_BUS) + +typedef struct ISADeviceClass { + DeviceClass parent_class; + int (*init)(ISADevice *dev); +} ISADeviceClass; + +struct ISABus { + BusState qbus; + MemoryRegion *address_space_io; + qemu_irq *irqs; +}; + +struct ISADevice { + DeviceState qdev; + uint32_t isairq[2]; + int nirqs; + int ioport_id; +}; + +ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io); +void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); +qemu_irq isa_get_irq(ISADevice *dev, int isairq); +void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq); +MemoryRegion *isa_address_space(ISADevice *dev); +MemoryRegion *isa_address_space_io(ISADevice *dev); +ISADevice *isa_create(ISABus *bus, const char *name); +ISADevice *isa_try_create(ISABus *bus, const char *name); +ISADevice *isa_create_simple(ISABus *bus, const char *name); + +ISADevice *isa_vga_init(ISABus *bus); + +/** + * isa_register_ioport: Install an I/O port region on the ISA bus. + * + * Register an I/O port region via memory_region_add_subregion + * inside the ISA I/O address space. + * + * @dev: the ISADevice against which these are registered; may be NULL. + * @io: the #MemoryRegion being registered. + * @start: the base I/O port. + */ +void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start); + +/** + * isa_register_portio_list: Initialize a set of ISA io ports + * + * Several ISA devices have many dis-joint I/O ports. Worse, these I/O + * ports can be interleaved with I/O ports from other devices. This + * function makes it easy to create multiple MemoryRegions for a single + * device and use the legacy portio routines. + * + * @dev: the ISADevice against which these are registered; may be NULL. + * @start: the base I/O port against which the portio->offset is applied. + * @portio: the ports, sorted by offset. + * @opaque: passed into the old_portio callbacks. + * @name: passed into memory_region_init_io. + */ +void isa_register_portio_list(ISADevice *dev, uint16_t start, + const MemoryRegionPortio *portio, + void *opaque, const char *name); + +static inline ISABus *isa_bus_from_device(ISADevice *d) +{ + return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); +} + +extern hwaddr isa_mem_base; + +void isa_mmio_setup(MemoryRegion *mr, hwaddr size); +void isa_mmio_init(hwaddr base, hwaddr size); + +/* dma.c */ +int DMA_get_channel_mode (int nchan); +int DMA_read_memory (int nchan, void *buf, int pos, int size); +int DMA_write_memory (int nchan, void *buf, int pos, int size); +void DMA_hold_DREQ (int nchan); +void DMA_release_DREQ (int nchan); +void DMA_schedule(int nchan); +void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit); +void DMA_register_channel (int nchan, + DMA_transfer_handler transfer_handler, + void *opaque); +#endif diff --git a/include/hw/isa/pc87312.h b/include/hw/isa/pc87312.h new file mode 100644 index 0000000000..befc8bdc62 --- /dev/null +++ b/include/hw/isa/pc87312.h @@ -0,0 +1,68 @@ +/* + * QEMU National Semiconductor PC87312 (Super I/O) + * + * Copyright (c) 2010-2012 Herve Poussineau + * Copyright (c) 2011-2012 Andreas Färber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_PC87312_H +#define QEMU_PC87312_H + +#include "hw/isa/isa.h" + + +#define TYPE_PC87312 "pc87312" +#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312) + +typedef struct PC87312State { + ISADevice dev; + + uint32_t iobase; + uint8_t config; /* initial configuration */ + + struct { + ISADevice *dev; + } parallel; + + struct { + ISADevice *dev; + } uart[2]; + + struct { + ISADevice *dev; + BlockDriverState *drive[2]; + uint32_t base; + } fdc; + + struct { + ISADevice *dev; + uint32_t base; + } ide; + + MemoryRegion io; + + uint8_t read_id_step; + uint8_t selected_index; + + uint8_t regs[3]; +} PC87312State; + + +#endif diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h new file mode 100644 index 0000000000..6ef876d260 --- /dev/null +++ b/include/hw/isa/vt82c686.h @@ -0,0 +1,11 @@ +#ifndef HW_VT82C686_H +#define HW_VT82C686_H + +/* vt82c686.c */ +ISABus *vt82c686b_init(PCIBus * bus, int devfn); +void vt82c686b_ac97_init(PCIBus *bus, int devfn); +void vt82c686b_mc97_init(PCIBus *bus, int devfn); +i2c_bus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, + qemu_irq sci_irq); + +#endif diff --git a/include/hw/kvm/clock.h b/include/hw/kvm/clock.h new file mode 100644 index 0000000000..252ea13461 --- /dev/null +++ b/include/hw/kvm/clock.h @@ -0,0 +1,24 @@ +/* + * QEMU KVM support, paravirtual clock device + * + * Copyright (C) 2011 Siemens AG + * + * Authors: + * Jan Kiszka + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#ifdef CONFIG_KVM + +void kvmclock_create(void); + +#else /* CONFIG_KVM */ + +static inline void kvmclock_create(void) +{ +} + +#endif /* !CONFIG_KVM */ diff --git a/include/hw/lm32/lm32_juart.h b/include/hw/lm32/lm32_juart.h new file mode 100644 index 0000000000..67fc5866ea --- /dev/null +++ b/include/hw/lm32/lm32_juart.h @@ -0,0 +1,11 @@ +#ifndef QEMU_HW_LM32_JUART_H +#define QEMU_HW_LM32_JUART_H + +#include "qemu-common.h" + +uint32_t lm32_juart_get_jtx(DeviceState *d); +uint32_t lm32_juart_get_jrx(DeviceState *d); +void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx); +void lm32_juart_set_jrx(DeviceState *d, uint32_t jrx); + +#endif /* QEMU_HW_LM32_JUART_H */ diff --git a/include/hw/lm32/lm32_pic.h b/include/hw/lm32/lm32_pic.h new file mode 100644 index 0000000000..555680304e --- /dev/null +++ b/include/hw/lm32/lm32_pic.h @@ -0,0 +1,14 @@ +#ifndef QEMU_HW_LM32_PIC_H +#define QEMU_HW_LM32_PIC_H + +#include "qemu-common.h" + +uint32_t lm32_pic_get_ip(DeviceState *d); +uint32_t lm32_pic_get_im(DeviceState *d); +void lm32_pic_set_ip(DeviceState *d, uint32_t ip); +void lm32_pic_set_im(DeviceState *d, uint32_t im); + +void lm32_do_pic_info(Monitor *mon, const QDict *qdict); +void lm32_irq_info(Monitor *mon, const QDict *qdict); + +#endif /* QEMU_HW_LM32_PIC_H */ diff --git a/include/hw/loader.h b/include/hw/loader.h new file mode 100644 index 0000000000..0958f06934 --- /dev/null +++ b/include/hw/loader.h @@ -0,0 +1,52 @@ +#ifndef LOADER_H +#define LOADER_H +#include "qapi/qmp/qdict.h" + +/* loader.c */ +int get_image_size(const char *filename); +int load_image(const char *filename, uint8_t *addr); /* deprecated */ +int load_image_targphys(const char *filename, hwaddr, + uint64_t max_sz); +int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, int big_endian, int elf_machine, + int clear_lsb); +int load_aout(const char *filename, hwaddr addr, int max_sz, + int bswap_needed, hwaddr target_page_size); +int load_uimage(const char *filename, hwaddr *ep, + hwaddr *loadaddr, int *is_linux); + +ssize_t read_targphys(const char *name, + int fd, hwaddr dst_addr, size_t nbytes); +void pstrcpy_targphys(const char *name, + hwaddr dest, int buf_size, + const char *source); + + +int rom_add_file(const char *file, const char *fw_dir, + hwaddr addr, int32_t bootindex); +int rom_add_blob(const char *name, const void *blob, size_t len, + hwaddr addr); +int rom_add_elf_program(const char *name, void *data, size_t datasize, + size_t romsize, hwaddr addr); +int rom_load_all(void); +void rom_set_fw(void *f); +int rom_copy(uint8_t *dest, hwaddr addr, size_t size); +void *rom_ptr(hwaddr addr); +void do_info_roms(Monitor *mon, const QDict *qdict); + +#define rom_add_file_fixed(_f, _a, _i) \ + rom_add_file(_f, NULL, _a, _i) +#define rom_add_blob_fixed(_f, _b, _l, _a) \ + rom_add_blob(_f, _b, _l, _a) + +#define PC_ROM_MIN_VGA 0xc0000 +#define PC_ROM_MIN_OPTION 0xc8000 +#define PC_ROM_MAX 0xe0000 +#define PC_ROM_ALIGN 0x800 +#define PC_ROM_SIZE (PC_ROM_MAX - PC_ROM_MIN_VGA) + +int rom_add_vga(const char *file); +int rom_add_option(const char *file, int32_t bootindex); + +#endif diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h new file mode 100644 index 0000000000..fbc8dc26df --- /dev/null +++ b/include/hw/m68k/mcf.h @@ -0,0 +1,30 @@ +#ifndef HW_MCF_H +#define HW_MCF_H +/* Motorola ColdFire device prototypes. */ + +struct MemoryRegion; + +/* mcf_uart.c */ +uint64_t mcf_uart_read(void *opaque, hwaddr addr, + unsigned size); +void mcf_uart_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size); +void *mcf_uart_init(qemu_irq irq, CharDriverState *chr); +void mcf_uart_mm_init(struct MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq, CharDriverState *chr); + +/* mcf_intc.c */ +qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, + hwaddr base, + M68kCPU *cpu); + +/* mcf_fec.c */ +void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd, + hwaddr base, qemu_irq *irq); + +/* mcf5206.c */ +qemu_irq *mcf5206_init(struct MemoryRegion *sysmem, + uint32_t base, M68kCPU *cpu); + +#endif diff --git a/include/hw/mips/bios.h b/include/hw/mips/bios.h new file mode 100644 index 0000000000..b4b88ac43d --- /dev/null +++ b/include/hw/mips/bios.h @@ -0,0 +1,8 @@ +#include "cpu.h" + +#define BIOS_SIZE (4 * 1024 * 1024) +#ifdef TARGET_WORDS_BIGENDIAN +#define BIOS_FILENAME "mips_bios.bin" +#else +#define BIOS_FILENAME "mipsel_bios.bin" +#endif diff --git a/include/hw/mips/cpudevs.h b/include/hw/mips/cpudevs.h new file mode 100644 index 0000000000..6bea24bf10 --- /dev/null +++ b/include/hw/mips/cpudevs.h @@ -0,0 +1,15 @@ +#ifndef HW_MIPS_CPUDEVS_H +#define HW_MIPS_CPUDEVS_H +/* Definitions for MIPS CPU internal devices. */ + +/* mips_addr.c */ +uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr); +uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr); + +/* mips_int.c */ +void cpu_mips_irq_init_cpu(CPUMIPSState *env); + +/* mips_timer.c */ +void cpu_mips_clock_init(CPUMIPSState *); + +#endif diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h new file mode 100644 index 0000000000..291e85f6b9 --- /dev/null +++ b/include/hw/mips/mips.h @@ -0,0 +1,29 @@ +#ifndef HW_MIPS_H +#define HW_MIPS_H +/* Definitions for mips board emulation. */ + +#include "exec/memory.h" + +/* gt64xxx.c */ +PCIBus *gt64120_register(qemu_irq *pic); + +/* bonito.c */ +PCIBus *bonito_init(qemu_irq *pic); + +/* rc4030.c */ +typedef struct rc4030DMAState *rc4030_dma; +void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write); +void rc4030_dma_read(void *dma, uint8_t *buf, int len); +void rc4030_dma_write(void *dma, uint8_t *buf, int len); + +void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus, + qemu_irq **irqs, rc4030_dma **dmas, + MemoryRegion *sysmem); + +/* dp8393x.c */ +void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, + MemoryRegion *address_space, + qemu_irq irq, void* mem_opaque, + void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)); + +#endif diff --git a/include/hw/misc/tmp105_regs.h b/include/hw/misc/tmp105_regs.h new file mode 100644 index 0000000000..9b55abaf90 --- /dev/null +++ b/include/hw/misc/tmp105_regs.h @@ -0,0 +1,50 @@ +/* + * Texas Instruments TMP105 Temperature Sensor I2C messages + * + * Browse the data sheet: + * + * http://www.ti.com/lit/gpn/tmp105 + * + * Copyright (C) 2012 Alex Horn + * Copyright (C) 2008-2012 Andrzej Zaborowski + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#ifndef QEMU_TMP105_MSGS_H +#define QEMU_TMP105_MSGS_H + +/** + * TMP105Reg: + * @TMP105_REG_TEMPERATURE: Temperature register + * @TMP105_REG_CONFIG: Configuration register + * @TMP105_REG_T_LOW: Low temperature register (also known as T_hyst) + * @TMP105_REG_T_HIGH: High temperature register (also known as T_OS) + * + * The following temperature sensors are + * compatible with the TMP105 registers: + * - adt75 + * - ds1775 + * - ds75 + * - lm75 + * - lm75a + * - max6625 + * - max6626 + * - mcp980x + * - stds75 + * - tcn75 + * - tmp100 + * - tmp101 + * - tmp105 + * - tmp175 + * - tmp275 + * - tmp75 + **/ +typedef enum TMP105Reg { + TMP105_REG_TEMPERATURE = 0, + TMP105_REG_CONFIG, + TMP105_REG_T_LOW, + TMP105_REG_T_HIGH, +} TMP105Reg; + +#endif diff --git a/include/hw/nvram/eeprom93xx.h b/include/hw/nvram/eeprom93xx.h new file mode 100644 index 0000000000..8ba0e287f2 --- /dev/null +++ b/include/hw/nvram/eeprom93xx.h @@ -0,0 +1,40 @@ +/* + * QEMU EEPROM 93xx emulation + * + * Copyright (c) 2006-2007 Stefan Weil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef EEPROM93XX_H +#define EEPROM93XX_H + +typedef struct _eeprom_t eeprom_t; + +/* Create a new EEPROM with (nwords * 2) bytes. */ +eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords); + +/* Destroy an existing EEPROM. */ +void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom); + +/* Read from the EEPROM. */ +uint16_t eeprom93xx_read(eeprom_t *eeprom); + +/* Write to the EEPROM. */ +void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi); + +/* Get EEPROM data array. */ +uint16_t *eeprom93xx_data(eeprom_t *eeprom); + +#endif /* EEPROM93XX_H */ diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h new file mode 100644 index 0000000000..05c8df186f --- /dev/null +++ b/include/hw/nvram/fw_cfg.h @@ -0,0 +1,71 @@ +#ifndef FW_CFG_H +#define FW_CFG_H + +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 + +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_FILE_SLOTS 0x10 +#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS) + +#define FW_CFG_WRITE_CHANNEL 0x4000 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) + +#define FW_CFG_INVALID 0xffff + +#ifndef NO_QEMU_PROTOS +typedef struct FWCfgFile { + uint32_t size; /* file size */ + uint16_t select; /* write this to 0x510 to read it */ + uint16_t reserved; + char name[56]; +} FWCfgFile; + +typedef struct FWCfgFiles { + uint32_t count; + FWCfgFile f[]; +} FWCfgFiles; + +typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); + +typedef struct FWCfgState FWCfgState; +void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len); +void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value); +void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); +void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); +void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); +void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, + void *callback_opaque, void *data, size_t len); +void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, + size_t len); +FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, + hwaddr crl_addr, hwaddr data_addr); + +#endif /* NO_QEMU_PROTOS */ + +#endif diff --git a/include/hw/pci-host/apb.h b/include/hw/pci-host/apb.h new file mode 100644 index 0000000000..736db6118e --- /dev/null +++ b/include/hw/pci-host/apb.h @@ -0,0 +1,10 @@ +#ifndef APB_PCI_H +#define APB_PCI_H + +#include "qemu-common.h" + +PCIBus *pci_apb_init(hwaddr special_base, + hwaddr mem_base, + qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3, + qemu_irq **pbm_irqs); +#endif diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h new file mode 100644 index 0000000000..8e9e349b1d --- /dev/null +++ b/include/hw/pci-host/pam.h @@ -0,0 +1,97 @@ +#ifndef QEMU_PAM_H +#define QEMU_PAM_H + +/* + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2011 Isaku Yamahata + * VA Linux Systems Japan K.K. + * Copyright (c) 2012 Jason Baron + * + * Split out from piix_pci.c + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * SMRAM memory area and PAM memory area in Legacy address range for PC. + * PAM: Programmable Attribute Map registers + * + * 0xa0000 - 0xbffff compatible SMRAM + * + * 0xc0000 - 0xc3fff Expansion area memory segments + * 0xc4000 - 0xc7fff + * 0xc8000 - 0xcbfff + * 0xcc000 - 0xcffff + * 0xd0000 - 0xd3fff + * 0xd4000 - 0xd7fff + * 0xd8000 - 0xdbfff + * 0xdc000 - 0xdffff + * 0xe0000 - 0xe3fff Extended System BIOS Area Memory Segments + * 0xe4000 - 0xe7fff + * 0xe8000 - 0xebfff + * 0xec000 - 0xeffff + * + * 0xf0000 - 0xfffff System BIOS Area Memory Segments + */ + +#include "qemu-common.h" +#include "exec/memory.h" + +#define SMRAM_C_BASE 0xa0000 +#define SMRAM_C_END 0xc0000 +#define SMRAM_C_SIZE 0x20000 + +#define PAM_EXPAN_BASE 0xc0000 +#define PAM_EXPAN_SIZE 0x04000 + +#define PAM_EXBIOS_BASE 0xe0000 +#define PAM_EXBIOS_SIZE 0x04000 + +#define PAM_BIOS_BASE 0xf0000 +#define PAM_BIOS_END 0xfffff +/* 64KB: Intel 3 series express chipset family p. 58*/ +#define PAM_BIOS_SIZE 0x10000 + +/* PAM registers: log nibble and high nibble*/ +#define PAM_ATTR_WE ((uint8_t)2) +#define PAM_ATTR_RE ((uint8_t)1) +#define PAM_ATTR_MASK ((uint8_t)3) + +/* SMRAM register */ +#define SMRAM_D_OPEN ((uint8_t)(1 << 6)) +#define SMRAM_D_CLS ((uint8_t)(1 << 5)) +#define SMRAM_D_LCK ((uint8_t)(1 << 4)) +#define SMRAM_G_SMRAME ((uint8_t)(1 << 3)) +#define SMRAM_C_BASE_SEG_MASK ((uint8_t)0x7) +#define SMRAM_C_BASE_SEG ((uint8_t)0x2) /* hardwired to b010 */ + +typedef struct PAMMemoryRegion { + MemoryRegion alias[4]; /* index = PAM value */ + unsigned current; +} PAMMemoryRegion; + +void smram_update(MemoryRegion *smram_region, uint8_t smram, + uint8_t smm_enabled); +void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram, + MemoryRegion *smram_region); +void init_pam(MemoryRegion *ram, MemoryRegion *system, MemoryRegion *pci, + PAMMemoryRegion *mem, uint32_t start, uint32_t size); +void pam_update(PAMMemoryRegion *mem, int idx, uint8_t val); + +#endif /* QEMU_PAM_H */ diff --git a/include/hw/pci-host/ppce500.h b/include/hw/pci-host/ppce500.h new file mode 100644 index 0000000000..61f773ef30 --- /dev/null +++ b/include/hw/pci-host/ppce500.h @@ -0,0 +1,9 @@ +#ifndef PPCE500_PCI_H +#define PPCE500_PCI_H + +static inline int ppce500_pci_map_irq_slot(int devno, int irq_num) +{ + return (devno + irq_num) % 4; +} + +#endif diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h new file mode 100644 index 0000000000..6b50b5f34a --- /dev/null +++ b/include/hw/pci-host/q35.h @@ -0,0 +1,150 @@ +/* + * q35.h + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * Copyright (C) 2012 Jason Baron + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef HW_Q35_H +#define HW_Q35_H + +#include "hw/hw.h" +#include "qemu/range.h" +#include "hw/isa/isa.h" +#include "hw/sysbus.h" +#include "hw/i386/pc.h" +#include "hw/isa/apm.h" +#include "hw/i386/apic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie_host.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/ich9.h" +#include "hw/pci-host/pam.h" + +#define TYPE_Q35_HOST_DEVICE "q35-pcihost" +#define Q35_HOST_DEVICE(obj) \ + OBJECT_CHECK(Q35PCIHost, (obj), TYPE_Q35_HOST_DEVICE) + +#define TYPE_MCH_PCI_DEVICE "mch" +#define MCH_PCI_DEVICE(obj) \ + OBJECT_CHECK(MCHPCIState, (obj), TYPE_MCH_PCI_DEVICE) + +typedef struct MCHPCIState { + PCIDevice d; + MemoryRegion *ram_memory; + MemoryRegion *pci_address_space; + MemoryRegion *system_memory; + MemoryRegion *address_space_io; + PAMMemoryRegion pam_regions[13]; + MemoryRegion smram_region; + MemoryRegion pci_hole; + MemoryRegion pci_hole_64bit; + uint8_t smm_enabled; + ram_addr_t below_4g_mem_size; + ram_addr_t above_4g_mem_size; +} MCHPCIState; + +typedef struct Q35PCIHost { + PCIExpressHost host; + MCHPCIState mch; +} Q35PCIHost; + +#define Q35_MASK(bit, ms_bit, ls_bit) \ +((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) + +/* + * gmch part + */ + +/* PCI configuration */ +#define MCH_HOST_BRIDGE "MCH" + +#define MCH_HOST_BRIDGE_CONFIG_ADDR 0xcf8 +#define MCH_HOST_BRIDGE_CONFIG_DATA 0xcfc + +/* D0:F0 configuration space */ +#define MCH_HOST_BRIDGE_REVISION_DEFUALT 0x0 + +#define MCH_HOST_BRIDGE_PCIEXBAR 0x60 /* 64bit register */ +#define MCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */ +#define MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT 0xb0000000 +#define MCH_HOST_BRIDGE_PCIEXBAR_ADMSK Q35_MASK(64, 35, 28) +#define MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK ((uint64_t)(1 << 26)) +#define MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK ((uint64_t)(1 << 25)) +#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK ((uint64_t)(0x3 << 1)) +#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M ((uint64_t)(0x0 << 1)) +#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M ((uint64_t)(0x1 << 1)) +#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M ((uint64_t)(0x2 << 1)) +#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD ((uint64_t)(0x3 << 1)) +#define MCH_HOST_BRIDGE_PCIEXBAREN ((uint64_t)1) + +#define MCH_HOST_BRIDGE_PAM_NB 7 +#define MCH_HOST_BRIDGE_PAM_SIZE 7 +#define MCH_HOST_BRIDGE_PAM0 0x90 +#define MCH_HOST_BRIDGE_PAM_BIOS_AREA 0xf0000 +#define MCH_HOST_BRIDGE_PAM_AREA_SIZE 0x10000 /* 16KB */ +#define MCH_HOST_BRIDGE_PAM1 0x91 +#define MCH_HOST_BRIDGE_PAM_EXPAN_AREA 0xc0000 +#define MCH_HOST_BRIDGE_PAM_EXPAN_SIZE 0x04000 +#define MCH_HOST_BRIDGE_PAM2 0x92 +#define MCH_HOST_BRIDGE_PAM3 0x93 +#define MCH_HOST_BRIDGE_PAM4 0x94 +#define MCH_HOST_BRIDGE_PAM_EXBIOS_AREA 0xe0000 +#define MCH_HOST_BRIDGE_PAM_EXBIOS_SIZE 0x04000 +#define MCH_HOST_BRIDGE_PAM5 0x95 +#define MCH_HOST_BRIDGE_PAM6 0x96 +#define MCH_HOST_BRIDGE_PAM_WE_HI ((uint8_t)(0x2 << 4)) +#define MCH_HOST_BRIDGE_PAM_RE_HI ((uint8_t)(0x1 << 4)) +#define MCH_HOST_BRIDGE_PAM_HI_MASK ((uint8_t)(0x3 << 4)) +#define MCH_HOST_BRIDGE_PAM_WE_LO ((uint8_t)0x2) +#define MCH_HOST_BRIDGE_PAM_RE_LO ((uint8_t)0x1) +#define MCH_HOST_BRIDGE_PAM_LO_MASK ((uint8_t)0x3) +#define MCH_HOST_BRIDGE_PAM_WE ((uint8_t)0x2) +#define MCH_HOST_BRIDGE_PAM_RE ((uint8_t)0x1) +#define MCH_HOST_BRIDGE_PAM_MASK ((uint8_t)0x3) + +#define MCH_HOST_BRDIGE_SMRAM 0x9d +#define MCH_HOST_BRDIGE_SMRAM_SIZE 1 +#define MCH_HOST_BRIDGE_SMRAM_DEFAULT ((uint8_t)0x2) +#define MCH_HOST_BRIDGE_SMRAM_D_OPEN ((uint8_t)(1 << 6)) +#define MCH_HOST_BRIDGE_SMRAM_D_CLS ((uint8_t)(1 << 5)) +#define MCH_HOST_BRIDGE_SMRAM_D_LCK ((uint8_t)(1 << 4)) +#define MCH_HOST_BRIDGE_SMRAM_G_SMRAME ((uint8_t)(1 << 3)) +#define MCH_HOST_BRIDGE_SMRAM_C_BASE_SEG_MASK ((uint8_t)0x7) +#define MCH_HOST_BRIDGE_SMRAM_C_BASE_SEG ((uint8_t)0x2) /* hardwired to b010 */ +#define MCH_HOST_BRIDGE_SMRAM_C_BASE 0xa0000 +#define MCH_HOST_BRIDGE_SMRAM_C_END 0xc0000 +#define MCH_HOST_BRIDGE_SMRAM_C_SIZE 0x20000 +#define MCH_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END 0x100000 + +#define MCH_HOST_BRIDGE_ESMRAMC 0x9e +#define MCH_HOST_BRDIGE_ESMRAMC_H_SMRAME ((uint8_t)(1 << 6)) +#define MCH_HOST_BRDIGE_ESMRAMC_E_SMERR ((uint8_t)(1 << 5)) +#define MCH_HOST_BRDIGE_ESMRAMC_SM_CACHE ((uint8_t)(1 << 4)) +#define MCH_HOST_BRDIGE_ESMRAMC_SM_L1 ((uint8_t)(1 << 3)) +#define MCH_HOST_BRDIGE_ESMRAMC_SM_L2 ((uint8_t)(1 << 2)) +#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_MASK ((uint8_t)(0x3 << 1)) +#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_1MB ((uint8_t)(0x0 << 1)) +#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_2MB ((uint8_t)(0x1 << 1)) +#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_8MB ((uint8_t)(0x2 << 1)) +#define MCH_HOST_BRDIGE_ESMRAMC_T_EN ((uint8_t)1) + +/* D1:F0 PCIE* port*/ +#define MCH_PCIE_DEV 1 +#define MCH_PCIE_FUNC 0 + +#endif /* HW_Q35_H */ diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h new file mode 100644 index 0000000000..b21080cd58 --- /dev/null +++ b/include/hw/pci-host/spapr.h @@ -0,0 +1,92 @@ +/* + * QEMU SPAPR PCI BUS definitions + * + * Copyright (c) 2011 Alexey Kardashevskiy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#if !defined(__HW_SPAPR_H__) +#error Please include spapr.h before this file! +#endif + +#if !defined(__HW_SPAPR_PCI_H__) +#define __HW_SPAPR_PCI_H__ + +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ppc/xics.h" + +#define SPAPR_MSIX_MAX_DEVS 32 + +#define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge" + +#define SPAPR_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(sPAPRPHBState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) + +typedef struct sPAPRPHBState { + PCIHostState parent_obj; + + int32_t index; + uint64_t buid; + char *dtbusname; + + MemoryRegion memspace, iospace; + hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size; + hwaddr msi_win_addr; + MemoryRegion memwindow, iowindow, msiwindow; + + uint32_t dma_liobn; + uint64_t dma_window_start; + uint64_t dma_window_size; + DMAContext *dma; + + struct { + uint32_t irq; + } lsi_table[PCI_NUM_PINS]; + + struct { + uint32_t config_addr; + uint32_t irq; + int nvec; + } msi_table[SPAPR_MSIX_MAX_DEVS]; + + QLIST_ENTRY(sPAPRPHBState) list; +} sPAPRPHBState; + +#define SPAPR_PCI_BASE_BUID 0x800000020000000ULL + +#define SPAPR_PCI_WINDOW_BASE 0x10000000000ULL +#define SPAPR_PCI_WINDOW_SPACING 0x1000000000ULL +#define SPAPR_PCI_MMIO_WIN_OFF 0xA0000000 +#define SPAPR_PCI_MMIO_WIN_SIZE 0x20000000 +#define SPAPR_PCI_IO_WIN_OFF 0x80000000 +#define SPAPR_PCI_IO_WIN_SIZE 0x10000 +#define SPAPR_PCI_MSI_WIN_OFF 0x90000000 + +#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL + +static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) +{ + return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); +} + +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index); + +int spapr_populate_pci_dt(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt); + +void spapr_pci_rtas_init(void); + +#endif /* __HW_SPAPR_PCI_H__ */ diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h new file mode 100644 index 0000000000..81a3848a31 --- /dev/null +++ b/include/hw/pci/msi.h @@ -0,0 +1,50 @@ +/* + * msi.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * 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. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_MSI_H +#define QEMU_MSI_H + +#include "qemu-common.h" +#include "hw/pci/pci.h" + +struct MSIMessage { + uint64_t address; + uint32_t data; +}; + +extern bool msi_supported; + +void msi_set_message(PCIDevice *dev, MSIMessage msg); +MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); +bool msi_enabled(const PCIDevice *dev); +int msi_init(struct PCIDevice *dev, uint8_t offset, + unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); +void msi_uninit(struct PCIDevice *dev); +void msi_reset(PCIDevice *dev); +void msi_notify(PCIDevice *dev, unsigned int vector); +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); +unsigned int msi_nr_vectors_allocated(const PCIDevice *dev); + +static inline bool msi_present(const PCIDevice *dev) +{ + return dev->cap_present & QEMU_PCI_CAP_MSI; +} + +#endif /* QEMU_MSI_H */ diff --git a/include/hw/pci/msix.h b/include/hw/pci/msix.h new file mode 100644 index 0000000000..e648410535 --- /dev/null +++ b/include/hw/pci/msix.h @@ -0,0 +1,46 @@ +#ifndef QEMU_MSIX_H +#define QEMU_MSIX_H + +#include "qemu-common.h" +#include "hw/pci/pci.h" + +void msix_set_message(PCIDevice *dev, int vector, MSIMessage msg); +MSIMessage msix_get_message(PCIDevice *dev, unsigned int vector); +int msix_init(PCIDevice *dev, unsigned short nentries, + MemoryRegion *table_bar, uint8_t table_bar_nr, + unsigned table_offset, MemoryRegion *pba_bar, + uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos); +int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, + uint8_t bar_nr); + +void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); + +void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, + MemoryRegion *pba_bar); +void msix_uninit_exclusive_bar(PCIDevice *dev); + +unsigned int msix_nr_vectors_allocated(const PCIDevice *dev); + +void msix_save(PCIDevice *dev, QEMUFile *f); +void msix_load(PCIDevice *dev, QEMUFile *f); + +int msix_enabled(PCIDevice *dev); +int msix_present(PCIDevice *dev); + +bool msix_is_masked(PCIDevice *dev, unsigned vector); +void msix_set_pending(PCIDevice *dev, unsigned vector); + +int msix_vector_use(PCIDevice *dev, unsigned vector); +void msix_vector_unuse(PCIDevice *dev, unsigned vector); +void msix_unuse_all_vectors(PCIDevice *dev); + +void msix_notify(PCIDevice *dev, unsigned vector); + +void msix_reset(PCIDevice *dev); + +int msix_set_vector_notifiers(PCIDevice *dev, + MSIVectorUseNotifier use_notifier, + MSIVectorReleaseNotifier release_notifier, + MSIVectorPollNotifier poll_notifier); +void msix_unset_vector_notifiers(PCIDevice *dev); +#endif diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h new file mode 100644 index 0000000000..05315c0475 --- /dev/null +++ b/include/hw/pci/pci.h @@ -0,0 +1,725 @@ +#ifndef QEMU_PCI_H +#define QEMU_PCI_H + +#include "qemu-common.h" + +#include "hw/qdev.h" +#include "exec/memory.h" +#include "sysemu/dma.h" + +/* PCI includes legacy ISA access. */ +#include "hw/isa/isa.h" + +#include "hw/pci/pcie.h" + +/* PCI bus */ + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) +#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_FUNC(devfn) ((devfn) & 0x07) +#define PCI_SLOT_MAX 32 +#define PCI_FUNC_MAX 8 + +/* Class, Vendor and Device IDs from Linux's pci_ids.h */ +#include "hw/pci/pci_ids.h" + +/* QEMU-specific Vendor and Device ID definitions */ + +/* IBM (0x1014) */ +#define PCI_DEVICE_ID_IBM_440GX 0x027f +#define PCI_DEVICE_ID_IBM_OPENPIC2 0xffff + +/* Hitachi (0x1054) */ +#define PCI_VENDOR_ID_HITACHI 0x1054 +#define PCI_DEVICE_ID_HITACHI_SH7751R 0x350e + +/* Apple (0x106b) */ +#define PCI_DEVICE_ID_APPLE_343S1201 0x0010 +#define PCI_DEVICE_ID_APPLE_UNI_N_I_PCI 0x001e +#define PCI_DEVICE_ID_APPLE_UNI_N_PCI 0x001f +#define PCI_DEVICE_ID_APPLE_UNI_N_KEYL 0x0022 +#define PCI_DEVICE_ID_APPLE_IPID_USB 0x003f + +/* Realtek (0x10ec) */ +#define PCI_DEVICE_ID_REALTEK_8029 0x8029 + +/* Xilinx (0x10ee) */ +#define PCI_DEVICE_ID_XILINX_XC2VP30 0x0300 + +/* Marvell (0x11ab) */ +#define PCI_DEVICE_ID_MARVELL_GT6412X 0x4620 + +/* QEMU/Bochs VGA (0x1234) */ +#define PCI_VENDOR_ID_QEMU 0x1234 +#define PCI_DEVICE_ID_QEMU_VGA 0x1111 + +/* VMWare (0x15ad) */ +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405 +#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710 +#define PCI_DEVICE_ID_VMWARE_NET 0x0720 +#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730 +#define PCI_DEVICE_ID_VMWARE_IDE 0x1729 +#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0 + +/* Intel (0x8086) */ +#define PCI_DEVICE_ID_INTEL_82551IT 0x1209 +#define PCI_DEVICE_ID_INTEL_82557 0x1229 +#define PCI_DEVICE_ID_INTEL_82801IR 0x2922 + +/* Red Hat / Qumranet (for QEMU) -- see pci-ids.txt */ +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_SUBDEVICE_ID_QEMU 0x1100 + +#define PCI_DEVICE_ID_VIRTIO_NET 0x1000 +#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 +#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 +#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 +#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 +#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 +#define PCI_DEVICE_ID_VIRTIO_9P 0x1009 + +#define PCI_VENDOR_ID_REDHAT 0x1b36 +#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 +#define PCI_DEVICE_ID_REDHAT_SERIAL 0x0002 +#define PCI_DEVICE_ID_REDHAT_SERIAL2 0x0003 +#define PCI_DEVICE_ID_REDHAT_SERIAL4 0x0004 +#define PCI_DEVICE_ID_REDHAT_QXL 0x0100 + +#define FMT_PCIBUS PRIx64 + +typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, + uint32_t address, uint32_t data, int len); +typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, + uint32_t address, int len); +typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type); +typedef void PCIUnregisterFunc(PCIDevice *pci_dev); + +typedef struct PCIIORegion { + pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ +#define PCI_BAR_UNMAPPED (~(pcibus_t)0) + pcibus_t size; + uint8_t type; + MemoryRegion *memory; + MemoryRegion *address_space; +} PCIIORegion; + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 + +enum { + QEMU_PCI_VGA_MEM, + QEMU_PCI_VGA_IO_LO, + QEMU_PCI_VGA_IO_HI, + QEMU_PCI_VGA_NUM_REGIONS, +}; + +#define QEMU_PCI_VGA_MEM_BASE 0xa0000 +#define QEMU_PCI_VGA_MEM_SIZE 0x20000 +#define QEMU_PCI_VGA_IO_LO_BASE 0x3b0 +#define QEMU_PCI_VGA_IO_LO_SIZE 0xc +#define QEMU_PCI_VGA_IO_HI_BASE 0x3c0 +#define QEMU_PCI_VGA_IO_HI_SIZE 0x20 + +#include "hw/pci/pci_regs.h" + +/* PCI HEADER_TYPE */ +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 + +/* Size of the standard PCI config header */ +#define PCI_CONFIG_HEADER_SIZE 0x40 +/* Size of the standard PCI config space */ +#define PCI_CONFIG_SPACE_SIZE 0x100 +/* Size of the standart PCIe config space: 4KB */ +#define PCIE_CONFIG_SPACE_SIZE 0x1000 + +#define PCI_NUM_PINS 4 /* A-D */ + +/* Bits in cap_present field. */ +enum { + QEMU_PCI_CAP_MSI = 0x1, + QEMU_PCI_CAP_MSIX = 0x2, + QEMU_PCI_CAP_EXPRESS = 0x4, + + /* multifunction capable device */ +#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3 + QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR), + + /* command register SERR bit enabled */ +#define QEMU_PCI_CAP_SERR_BITNR 4 + QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR), + /* Standard hot plug controller. */ +#define QEMU_PCI_SHPC_BITNR 5 + QEMU_PCI_CAP_SHPC = (1 << QEMU_PCI_SHPC_BITNR), +#define QEMU_PCI_SLOTID_BITNR 6 + QEMU_PCI_CAP_SLOTID = (1 << QEMU_PCI_SLOTID_BITNR), +}; + +#define TYPE_PCI_DEVICE "pci-device" +#define PCI_DEVICE(obj) \ + OBJECT_CHECK(PCIDevice, (obj), TYPE_PCI_DEVICE) +#define PCI_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PCIDeviceClass, (klass), TYPE_PCI_DEVICE) +#define PCI_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PCIDeviceClass, (obj), TYPE_PCI_DEVICE) + +typedef struct PCIINTxRoute { + enum { + PCI_INTX_ENABLED, + PCI_INTX_INVERTED, + PCI_INTX_DISABLED, + } mode; + int irq; +} PCIINTxRoute; + +typedef struct PCIDeviceClass { + DeviceClass parent_class; + + int (*init)(PCIDevice *dev); + PCIUnregisterFunc *exit; + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint16_t class_id; + uint16_t subsystem_vendor_id; /* only for header type = 0 */ + uint16_t subsystem_id; /* only for header type = 0 */ + + /* + * pci-to-pci bridge or normal device. + * This doesn't mean pci host switch. + * When card bus bridge is supported, this would be enhanced. + */ + int is_bridge; + + /* pcie stuff */ + int is_express; /* is this device pci express? */ + + /* device isn't hot-pluggable */ + int no_hotplug; + + /* rom bar */ + const char *romfile; +} PCIDeviceClass; + +typedef void (*PCIINTxRoutingNotifier)(PCIDevice *dev); +typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, + MSIMessage msg); +typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector); +typedef void (*MSIVectorPollNotifier)(PCIDevice *dev, + unsigned int vector_start, + unsigned int vector_end); + +struct PCIDevice { + DeviceState qdev; + + /* PCI config space */ + uint8_t *config; + + /* Used to enable config checks on load. Note that writable bits are + * never checked even if set in cmask. */ + uint8_t *cmask; + + /* Used to implement R/W bytes */ + uint8_t *wmask; + + /* Used to implement RW1C(Write 1 to Clear) bytes */ + uint8_t *w1cmask; + + /* Used to allocate config space for capabilities. */ + uint8_t *used; + + /* the following fields are read only */ + PCIBus *bus; + int32_t devfn; + char name[64]; + PCIIORegion io_regions[PCI_NUM_REGIONS]; + AddressSpace bus_master_as; + MemoryRegion bus_master_enable_region; + DMAContext *dma; + + /* do not access the following fields */ + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + + /* IRQ objects for the INTA-INTD pins. */ + qemu_irq *irq; + + /* Legacy PCI VGA regions */ + MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS]; + bool has_vga; + + /* Current IRQ levels. Used internally by the generic PCI code. */ + uint8_t irq_state; + + /* Capability bits */ + uint32_t cap_present; + + /* Offset of MSI-X capability in config space */ + uint8_t msix_cap; + + /* MSI-X entries */ + int msix_entries_nr; + + /* Space to store MSIX table & pending bit array */ + uint8_t *msix_table; + uint8_t *msix_pba; + /* MemoryRegion container for msix exclusive BAR setup */ + MemoryRegion msix_exclusive_bar; + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + /* Reference-count for entries actually in use by driver. */ + unsigned *msix_entry_used; + /* MSIX function mask set or MSIX disabled */ + bool msix_function_masked; + /* Version id needed for VMState */ + int32_t version_id; + + /* Offset of MSI capability in config space */ + uint8_t msi_cap; + + /* PCI Express */ + PCIExpressDevice exp; + + /* SHPC */ + SHPCDevice *shpc; + + /* Location of option rom */ + char *romfile; + bool has_rom; + MemoryRegion rom; + uint32_t rom_bar; + + /* INTx routing notifier */ + PCIINTxRoutingNotifier intx_routing_notifier; + + /* MSI-X notifiers */ + MSIVectorUseNotifier msix_vector_use_notifier; + MSIVectorReleaseNotifier msix_vector_release_notifier; + MSIVectorPollNotifier msix_vector_poll_notifier; +}; + +void pci_register_bar(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory); +void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, + MemoryRegion *io_lo, MemoryRegion *io_hi); +void pci_unregister_vga(PCIDevice *pci_dev); +pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num); + +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, + uint8_t offset, uint8_t size); + +void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); + +uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id); + + +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len); +void pci_default_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void pci_device_save(PCIDevice *s, QEMUFile *f); +int pci_device_load(PCIDevice *s, QEMUFile *f); +MemoryRegion *pci_address_space(PCIDevice *dev); +MemoryRegion *pci_address_space_io(PCIDevice *dev); + +typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); +typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); +typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); + +typedef enum { + PCI_HOTPLUG_DISABLED, + PCI_HOTPLUG_ENABLED, + PCI_COLDPLUG_ENABLED, +} PCIHotplugState; + +typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, + PCIHotplugState state); + +#define TYPE_PCI_BUS "PCI" +#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) +#define TYPE_PCIE_BUS "PCIE" + +bool pci_bus_is_express(PCIBus *bus); +bool pci_bus_is_root(PCIBus *bus); +void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, const char *typename); +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, const char *typename); +void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, + void *irq_opaque, int nirq); +int pci_bus_get_irq_level(PCIBus *bus, int irq_num); +void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); +/* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ +int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin); +PCIBus *pci_register_bus(DeviceState *parent, const char *name, + pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, + void *irq_opaque, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, int nirq, const char *typename); +void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); +PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); +bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new); +void pci_bus_fire_intx_routing_notifier(PCIBus *bus); +void pci_device_set_intx_routing_notifier(PCIDevice *dev, + PCIINTxRoutingNotifier notifier); +void pci_device_reset(PCIDevice *dev); +void pci_bus_reset(PCIBus *bus); + +PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, + const char *default_devaddr); +PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, + const char *default_devaddr); + +PCIDevice *pci_vga_init(PCIBus *bus); + +int pci_bus_num(PCIBus *s); +void pci_for_each_device(PCIBus *bus, int bus_num, + void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), + void *opaque); +PCIBus *pci_find_root_bus(int domain); +int pci_find_domain(const PCIBus *bus); +PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); +int pci_qdev_find_device(const char *id, PCIDevice **pdev); +PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); + +int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, + unsigned *slotp); + +void pci_device_deassert_intx(PCIDevice *dev); + +typedef DMAContext *(*PCIDMAContextFunc)(PCIBus *, void *, int); + +void pci_setup_iommu(PCIBus *bus, PCIDMAContextFunc fn, void *opaque); + +static inline void +pci_set_byte(uint8_t *config, uint8_t val) +{ + *config = val; +} + +static inline uint8_t +pci_get_byte(const uint8_t *config) +{ + return *config; +} + +static inline void +pci_set_word(uint8_t *config, uint16_t val) +{ + cpu_to_le16wu((uint16_t *)config, val); +} + +static inline uint16_t +pci_get_word(const uint8_t *config) +{ + return le16_to_cpupu((const uint16_t *)config); +} + +static inline void +pci_set_long(uint8_t *config, uint32_t val) +{ + cpu_to_le32wu((uint32_t *)config, val); +} + +static inline uint32_t +pci_get_long(const uint8_t *config) +{ + return le32_to_cpupu((const uint32_t *)config); +} + +static inline void +pci_set_quad(uint8_t *config, uint64_t val) +{ + cpu_to_le64w((uint64_t *)config, val); +} + +static inline uint64_t +pci_get_quad(const uint8_t *config) +{ + return le64_to_cpup((const uint64_t *)config); +} + +static inline void +pci_config_set_vendor_id(uint8_t *pci_config, uint16_t val) +{ + pci_set_word(&pci_config[PCI_VENDOR_ID], val); +} + +static inline void +pci_config_set_device_id(uint8_t *pci_config, uint16_t val) +{ + pci_set_word(&pci_config[PCI_DEVICE_ID], val); +} + +static inline void +pci_config_set_revision(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_REVISION_ID], val); +} + +static inline void +pci_config_set_class(uint8_t *pci_config, uint16_t val) +{ + pci_set_word(&pci_config[PCI_CLASS_DEVICE], val); +} + +static inline void +pci_config_set_prog_interface(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_CLASS_PROG], val); +} + +static inline void +pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val); +} + +/* + * helper functions to do bit mask operation on configuration space. + * Just to set bit, use test-and-set and discard returned value. + * Just to clear bit, use test-and-clear and discard returned value. + * NOTE: They aren't atomic. + */ +static inline uint8_t +pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + pci_set_byte(config, val & ~mask); + return val & mask; +} + +static inline uint8_t +pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + pci_set_byte(config, val | mask); + return val & mask; +} + +static inline uint16_t +pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + pci_set_word(config, val & ~mask); + return val & mask; +} + +static inline uint16_t +pci_word_test_and_set_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + pci_set_word(config, val | mask); + return val & mask; +} + +static inline uint32_t +pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + pci_set_long(config, val & ~mask); + return val & mask; +} + +static inline uint32_t +pci_long_test_and_set_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + pci_set_long(config, val | mask); + return val & mask; +} + +static inline uint64_t +pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + pci_set_quad(config, val & ~mask); + return val & mask; +} + +static inline uint64_t +pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + pci_set_quad(config, val | mask); + return val & mask; +} + +/* Access a register specified by a mask */ +static inline void +pci_set_byte_by_mask(uint8_t *config, uint8_t mask, uint8_t reg) +{ + uint8_t val = pci_get_byte(config); + uint8_t rval = reg << (ffs(mask) - 1); + pci_set_byte(config, (~mask & val) | (mask & rval)); +} + +static inline uint8_t +pci_get_byte_by_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_word_by_mask(uint8_t *config, uint16_t mask, uint16_t reg) +{ + uint16_t val = pci_get_word(config); + uint16_t rval = reg << (ffs(mask) - 1); + pci_set_word(config, (~mask & val) | (mask & rval)); +} + +static inline uint16_t +pci_get_word_by_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_long_by_mask(uint8_t *config, uint32_t mask, uint32_t reg) +{ + uint32_t val = pci_get_long(config); + uint32_t rval = reg << (ffs(mask) - 1); + pci_set_long(config, (~mask & val) | (mask & rval)); +} + +static inline uint32_t +pci_get_long_by_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg) +{ + uint64_t val = pci_get_quad(config); + uint64_t rval = reg << (ffs(mask) - 1); + pci_set_quad(config, (~mask & val) | (mask & rval)); +} + +static inline uint64_t +pci_get_quad_by_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + return (val & mask) >> (ffs(mask) - 1); +} + +PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, + const char *name); +PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, + bool multifunction, + const char *name); +PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); +PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); + +static inline int pci_is_express(const PCIDevice *d) +{ + return d->cap_present & QEMU_PCI_CAP_EXPRESS; +} + +static inline uint32_t pci_config_size(const PCIDevice *d) +{ + return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; +} + +/* DMA access functions */ +static inline DMAContext *pci_dma_context(PCIDevice *dev) +{ + return dev->dma; +} + +static inline int pci_dma_rw(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len, DMADirection dir) +{ + dma_memory_rw(pci_dma_context(dev), addr, buf, len, dir); + return 0; +} + +static inline int pci_dma_read(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, buf, len, DMA_DIRECTION_TO_DEVICE); +} + +static inline int pci_dma_write(PCIDevice *dev, dma_addr_t addr, + const void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, (void *) buf, len, DMA_DIRECTION_FROM_DEVICE); +} + +#define PCI_DMA_DEFINE_LDST(_l, _s, _bits) \ + static inline uint##_bits##_t ld##_l##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr) \ + { \ + return ld##_l##_dma(pci_dma_context(dev), addr); \ + } \ + static inline void st##_s##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr, uint##_bits##_t val) \ + { \ + st##_s##_dma(pci_dma_context(dev), addr, val); \ + } + +PCI_DMA_DEFINE_LDST(ub, b, 8); +PCI_DMA_DEFINE_LDST(uw_le, w_le, 16) +PCI_DMA_DEFINE_LDST(l_le, l_le, 32); +PCI_DMA_DEFINE_LDST(q_le, q_le, 64); +PCI_DMA_DEFINE_LDST(uw_be, w_be, 16) +PCI_DMA_DEFINE_LDST(l_be, l_be, 32); +PCI_DMA_DEFINE_LDST(q_be, q_be, 64); + +#undef PCI_DMA_DEFINE_LDST + +static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr, + dma_addr_t *plen, DMADirection dir) +{ + void *buf; + + buf = dma_memory_map(pci_dma_context(dev), addr, plen, dir); + return buf; +} + +static inline void pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len, + DMADirection dir, dma_addr_t access_len) +{ + dma_memory_unmap(pci_dma_context(dev), buffer, len, dir, access_len); +} + +static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, + int alloc_hint) +{ + qemu_sglist_init(qsg, alloc_hint, pci_dma_context(dev)); +} + +extern const VMStateDescription vmstate_pci_device; + +#define VMSTATE_PCI_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PCIDevice), \ +} + +#define VMSTATE_PCI_DEVICE_POINTER(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, PCIDevice), \ +} + +#endif diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h new file mode 100644 index 0000000000..1868f7aea8 --- /dev/null +++ b/include/hw/pci/pci_bridge.h @@ -0,0 +1,65 @@ +/* + * QEMU PCI bridge + * + * Copyright (c) 2004 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc] + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef QEMU_PCI_BRIDGE_H +#define QEMU_PCI_BRIDGE_H + +#include "hw/pci/pci.h" + +int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, + uint16_t svid, uint16_t ssid); + +PCIDevice *pci_bridge_get_device(PCIBus *bus); +PCIBus *pci_bridge_get_sec_bus(PCIBridge *br); + +pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type); +pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type); + +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void pci_bridge_disable_base_limit(PCIDevice *dev); +void pci_bridge_reset_reg(PCIDevice *dev); +void pci_bridge_reset(DeviceState *qdev); + +int pci_bridge_initfn(PCIDevice *pci_dev, const char *typename); +void pci_bridge_exitfn(PCIDevice *pci_dev); + + +/* + * before qdev initialization(qdev_init()), this function sets bus_name and + * map_irq callback which are necessry for pci_bridge_initfn() to + * initialize bus. + */ +void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, + pci_map_irq_fn map_irq); + +/* TODO: add this define to pci_regs.h in linux and then in qemu. */ +#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ +#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ +#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ +#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ +#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ + +#endif /* QEMU_PCI_BRIDGE_H */ diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h new file mode 100644 index 0000000000..6ee443cf88 --- /dev/null +++ b/include/hw/pci/pci_bus.h @@ -0,0 +1,78 @@ +#ifndef QEMU_PCI_BUS_H +#define QEMU_PCI_BUS_H + +/* + * PCI Bus and Bridge datastructures. + * + * Do not access the following members directly; + * use accessor functions in pci.h, pci_bridge.h + */ + +struct PCIBus { + BusState qbus; + PCIDMAContextFunc dma_context_fn; + void *dma_context_opaque; + uint8_t devfn_min; + pci_set_irq_fn set_irq; + pci_map_irq_fn map_irq; + pci_route_irq_fn route_intx_to_irq; + pci_hotplug_fn hotplug; + DeviceState *hotplug_qdev; + void *irq_opaque; + PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; + PCIDevice *parent_dev; + MemoryRegion *address_space_mem; + MemoryRegion *address_space_io; + + QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ + QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ + + /* The bus IRQ state is the logical OR of the connected devices. + Keep a count of the number of devices with raised IRQs. */ + int nirq; + int *irq_count; +}; + +typedef struct PCIBridgeWindows PCIBridgeWindows; + +/* + * Aliases for each of the address space windows that the bridge + * can forward. Mapped into the bridge's parent's address space, + * as subregions. + */ +struct PCIBridgeWindows { + MemoryRegion alias_pref_mem; + MemoryRegion alias_mem; + MemoryRegion alias_io; + /* + * When bridge control VGA forwarding is enabled, bridges will + * provide positive decode on the PCI VGA defined I/O port and + * MMIO ranges. When enabled forwarding is only qualified on the + * I/O and memory enable bits in the bridge command register. + */ + MemoryRegion alias_vga[QEMU_PCI_VGA_NUM_REGIONS]; +}; + +struct PCIBridge { + PCIDevice dev; + + /* private member */ + PCIBus sec_bus; + /* + * Memory regions for the bridge's address spaces. These regions are not + * directly added to system_memory/system_io or its descendants. + * Bridge's secondary bus points to these, so that devices + * under the bridge see these regions as its address spaces. + * The regions are as large as the entire address space - + * they don't take into account any windows. + */ + MemoryRegion address_space_mem; + MemoryRegion address_space_io; + + PCIBridgeWindows *windows; + + pci_map_irq_fn map_irq; + const char *bus_name; +}; + +#endif /* QEMU_PCI_BUS_H */ diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h new file mode 100644 index 0000000000..236cd0f75c --- /dev/null +++ b/include/hw/pci/pci_host.h @@ -0,0 +1,61 @@ +/* + * QEMU Common PCI Host bridge configuration data space access routines. + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Worker routines for a PCI host controller that uses an {address,data} + register pair to access PCI configuration space. */ + +#ifndef PCI_HOST_H +#define PCI_HOST_H + +#include "hw/sysbus.h" + +#define TYPE_PCI_HOST_BRIDGE "pci-host-bridge" +#define PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PCIHostState, (obj), TYPE_PCI_HOST_BRIDGE) + +struct PCIHostState { + SysBusDevice busdev; + + MemoryRegion conf_mem; + MemoryRegion data_mem; + MemoryRegion mmcfg; + uint32_t config_reg; + PCIBus *bus; +}; + +/* common internal helpers for PCI/PCIe hosts, cut off overflows */ +void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t val, uint32_t len); +uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t len); + +void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len); +uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len); + +extern const MemoryRegionOps pci_host_conf_le_ops; +extern const MemoryRegionOps pci_host_conf_be_ops; +extern const MemoryRegionOps pci_host_data_le_ops; +extern const MemoryRegionOps pci_host_data_be_ops; + +#endif /* PCI_HOST_H */ diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h new file mode 100644 index 0000000000..d8dc2f1bf7 --- /dev/null +++ b/include/hw/pci/pci_ids.h @@ -0,0 +1,154 @@ +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + * + * Abbreviated version of linux/pci_ids.h + * + * QEMU-specific definitions belong in pci.h + */ +#ifndef HW_PCI_IDS_H +#define HW_PCI_IDS_H 1 + +/* Device classes and subclasses */ + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_BASE_CLASS_NETWORK 0x02 + +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_SATA 0x0106 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 + +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 + +#define PCI_CLASS_MEMORY_RAM 0x0500 + +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_CLASS_PROCESSOR_CO 0x0b40 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors and devices. Sort key: vendor first, device next. */ + +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_21154 0x0026 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 + +#define PCI_VENDOR_ID_IBM 0x1014 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 + +#define PCI_VENDOR_ID_TI 0x104c + +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b + +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_DEVICE_ID_SUN_EBUS 0x1000 +#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 +#define PCI_DEVICE_ID_SUN_SABRE 0xa000 + +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_DEVICE_ID_CMD_646 0x0646 + +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 + +#define PCI_VENDOR_ID_XILINX 0x10ee + +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_ISA_BRIDGE 0x0686 +#define PCI_DEVICE_ID_VIA_IDE 0x0571 +#define PCI_DEVICE_ID_VIA_UHCI 0x3038 +#define PCI_DEVICE_ID_VIA_ACPI 0x3057 +#define PCI_DEVICE_ID_VIA_AC97 0x3058 +#define PCI_DEVICE_ID_VIA_MC97 0x3068 + +#define PCI_VENDOR_ID_MARVELL 0x11ab + +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 + +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_DEVICE_ID_MPC8533E 0x0030 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e +#define PCI_DEVICE_ID_INTEL_82801D 0x24CD +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 + +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 + +#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 +#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a +#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c +#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed + +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 + +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_UPD720200 0x0194 + +#define PCI_VENDOR_ID_TEWS 0x1498 +#define PCI_DEVICE_ID_TEWS_TPCI200 0x30C8 + +#endif diff --git a/include/hw/pci/pci_regs.h b/include/hw/pci/pci_regs.h new file mode 100644 index 0000000000..56a404be6e --- /dev/null +++ b/include/hw/pci/pci_regs.h @@ -0,0 +1,717 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_ID_SATA 0x12 /* Serial ATA */ +#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ + +/* MSI-X registers */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_TABLE 4 +#define PCI_MSIX_PBA 8 +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) + +/* MSI-X entry's format */ +#define PCI_MSIX_ENTRY_SIZE 16 +#define PCI_MSIX_ENTRY_LOWER_ADDR 0 +#define PCI_MSIX_ENTRY_UPPER_ADDR 4 +#define PCI_MSIX_ENTRY_DATA 8 +#define PCI_MSIX_ENTRY_VECTOR_CTRL 12 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1 + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI Advanced Feature registers */ + +#define PCI_AF_LENGTH 2 +#define PCI_AF_CAP 3 +#define PCI_AF_CAP_TP 0x01 +#define PCI_AF_CAP_FLR 0x02 +#define PCI_AF_CTRL 4 +#define PCI_AF_CTRL_FLR 0x01 +#define PCI_AF_STATUS 5 +#define PCI_AF_STATUS_TP 0x01 + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Bridge Subsystem ID registers */ + +#define PCI_SSVID_VENDOR_ID 4 /* PCI-Bridge subsystem vendor id register */ +#define PCI_SSVID_DEVICE_ID 6 /* PCI-Bridge subsystem device id register */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ +#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ +#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ +#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */ +#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ +#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ +#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ +#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ +#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ +#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ +#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ +#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ +#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#define PCI_EXP_LNKSTA_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ +#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ +#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ +#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ +#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ +#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ +#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */ +#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */ +#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */ +#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */ +#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */ +#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */ +#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */ +#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */ +#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */ +#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */ +#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */ +#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */ +#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */ +#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ +#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ +#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ +#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ +#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ +#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ +#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ +#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */ +#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */ +#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */ +#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */ +#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ +#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ +#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ +#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ +#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ +#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ +#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ +#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ +#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ +#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ +#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ +#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 +#define PCI_EXT_CAP_ID_VNDR 11 +#define PCI_EXT_CAP_ID_ACS 13 +#define PCI_EXT_CAP_ID_ARI 14 +#define PCI_EXT_CAP_ID_ATS 15 +#define PCI_EXT_CAP_ID_SRIOV 16 +#define PCI_EXT_CAP_ID_LTR 24 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + +/* Alternative Routing-ID Interpretation */ +#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ +#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ + +/* Address Translation Service */ +#define PCI_ATS_CAP 0x04 /* ATS Capability Register */ +#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */ +#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */ +#define PCI_ATS_CTRL 0x06 /* ATS Control Register */ +#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ +#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ +#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ + +/* Single Root I/O Virtualization */ +#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ +#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ +#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */ +#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */ +#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */ +#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */ +#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */ +#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */ +#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */ +#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */ +#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */ +#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */ +#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */ +#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */ +#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */ +#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */ +#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */ +#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */ +#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */ +#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */ +#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */ +#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */ +#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/ +#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */ +#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */ +#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */ +#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ +#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ +#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ + +#define PCI_LTR_MAX_SNOOP_LAT 0x4 +#define PCI_LTR_MAX_NOSNOOP_LAT 0x6 +#define PCI_LTR_VALUE_MASK 0x000003ff +#define PCI_LTR_SCALE_MASK 0x00001c00 +#define PCI_LTR_SCALE_SHIFT 10 + +/* Access Control Service */ +#define PCI_ACS_CAP 0x04 /* ACS Capability Register */ +#define PCI_ACS_SV 0x01 /* Source Validation */ +#define PCI_ACS_TB 0x02 /* Translation Blocking */ +#define PCI_ACS_RR 0x04 /* P2P Request Redirect */ +#define PCI_ACS_CR 0x08 /* P2P Completion Redirect */ +#define PCI_ACS_UF 0x10 /* Upstream Forwarding */ +#define PCI_ACS_EC 0x20 /* P2P Egress Control */ +#define PCI_ACS_DT 0x40 /* Direct Translated P2P */ +#define PCI_ACS_CTRL 0x06 /* ACS Control Register */ +#define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ + +#endif /* LINUX_PCI_REGS_H */ diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h new file mode 100644 index 0000000000..c010007c5e --- /dev/null +++ b/include/hw/pci/pcie.h @@ -0,0 +1,143 @@ +/* + * pcie.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_PCIE_H +#define QEMU_PCIE_H + +#include "hw/hw.h" +#include "hw/pci/pci_regs.h" +#include "hw/pci/pcie_regs.h" +#include "hw/pci/pcie_aer.h" + +typedef enum { + /* for attention and power indicator */ + PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED, + PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON, + PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK, + PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF, +} PCIExpressIndicator; + +typedef enum { + /* these bits must match the bits in Slot Control/Status registers. + * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx + * + * Not all the bits of slot control register match with the ones of + * slot status. Not some bits of slot status register is used to + * show status, not to report event occurrence. + * So such bits must be masked out when checking the software + * notification condition. + */ + PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE, + /* attention button pressed */ + PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE, + /* presence detect changed */ + PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE, + /* command completed */ + + PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP | + PCI_EXP_HP_EV_PDC | + PCI_EXP_HP_EV_CCI, + /* supported event mask */ + + /* events not listed aren't supported */ +} PCIExpressHotPlugEvent; + +struct PCIExpressDevice { + /* Offset of express capability in config space */ + uint8_t exp_cap; + + /* SLOT */ + unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#) + * default is 0 = INTA# + * If the chip wants to use other interrupt + * line, initialize this member with the + * desired number. + * If the chip dynamically changes this member, + * also initialize it when loaded as + * appropreately. + */ + bool hpev_notified; /* Logical AND of conditions for hot plug event. + Following 6.7.3.4: + Software Notification of Hot-Plug Events, an interrupt + is sent whenever the logical and of these conditions + transitions from false to true. */ + + /* AER */ + uint16_t aer_cap; + PCIEAERLog aer_log; + unsigned int aer_intx; /* INTx for error reporting + * default is 0 = INTA# + * If the chip wants to use other interrupt + * line, initialize this member with the + * desired number. + * If the chip dynamically changes this member, + * also initialize it when loaded as + * appropreately. + */ +}; + +/* PCI express capability helper functions */ +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port); +int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset); +void pcie_cap_exit(PCIDevice *dev); +uint8_t pcie_cap_get_type(const PCIDevice *dev); +void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector); +uint8_t pcie_cap_flags_get_vector(PCIDevice *dev); + +void pcie_cap_deverr_init(PCIDevice *dev); +void pcie_cap_deverr_reset(PCIDevice *dev); + +void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot); +void pcie_cap_slot_reset(PCIDevice *dev); +void pcie_cap_slot_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); +int pcie_cap_slot_post_load(void *opaque, int version_id); +void pcie_cap_slot_push_attention_button(PCIDevice *dev); + +void pcie_cap_root_init(PCIDevice *dev); +void pcie_cap_root_reset(PCIDevice *dev); + +void pcie_cap_flr_init(PCIDevice *dev); +void pcie_cap_flr_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); + +void pcie_cap_ari_init(PCIDevice *dev); +void pcie_cap_ari_reset(PCIDevice *dev); +bool pcie_cap_is_ari_enabled(const PCIDevice *dev); + +/* PCI express extended capability helper functions */ +uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id); +void pcie_add_capability(PCIDevice *dev, + uint16_t cap_id, uint8_t cap_ver, + uint16_t offset, uint16_t size); + +void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); + +extern const VMStateDescription vmstate_pcie_device; + +#define VMSTATE_PCIE_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pcie_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PCIDevice), \ +} + +#endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h new file mode 100644 index 0000000000..bcac80a7b0 --- /dev/null +++ b/include/hw/pci/pcie_aer.h @@ -0,0 +1,106 @@ +/* + * pcie_aer.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_PCIE_AER_H +#define QEMU_PCIE_AER_H + +#include "hw/hw.h" + +/* definitions which PCIExpressDevice uses */ + +/* AER log */ +struct PCIEAERLog { + /* This structure is saved/loaded. + So explicitly size them instead of unsigned int */ + + /* the number of currently recorded log in log member */ + uint16_t log_num; + + /* + * The maximum number of the log. Errors can be logged up to this. + * + * This is configurable property. + * The specified value will be clipped down to PCIE_AER_LOG_MAX_LIMIT + * to avoid unreasonable memory usage. + * I bet that 128 log size would be big enough, otherwise too many errors + * for system to function normaly. But could consecutive errors occur? + */ +#define PCIE_AER_LOG_MAX_DEFAULT 8 +#define PCIE_AER_LOG_MAX_LIMIT 128 +#define PCIE_AER_LOG_MAX_UNSET 0xffff + uint16_t log_max; + + /* Error log. log_max-sized array */ + PCIEAERErr *log; +}; + +/* aer error message: error signaling message has only error sevirity and + source id. See 2.2.8.3 error signaling messages */ +struct PCIEAERMsg { + /* + * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN + * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE} + */ + uint32_t severity; + + uint16_t source_id; /* bdf */ +}; + +static inline bool +pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) +{ + return msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN || + msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN; +} + +/* error */ +struct PCIEAERErr { + uint32_t status; /* error status bits */ + uint16_t source_id; /* bdf */ + +#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ +#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ +#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ +#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ + uint16_t flags; + + uint32_t header[4]; /* TLP header */ + uint32_t prefix[4]; /* TLP header prefix */ +}; + +extern const VMStateDescription vmstate_pcie_aer_log; + +int pcie_aer_init(PCIDevice *dev, uint16_t offset); +void pcie_aer_exit(PCIDevice *dev); +void pcie_aer_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); + +/* aer root port */ +void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector); +void pcie_aer_root_init(PCIDevice *dev); +void pcie_aer_root_reset(PCIDevice *dev); +void pcie_aer_root_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len, + uint32_t root_cmd_prev); + +/* error injection */ +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err); + +#endif /* QEMU_PCIE_AER_H */ diff --git a/include/hw/pci/pcie_host.h b/include/hw/pci/pcie_host.h new file mode 100644 index 0000000000..1228e36cb2 --- /dev/null +++ b/include/hw/pci/pcie_host.h @@ -0,0 +1,54 @@ +/* + * pcie_host.h + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * 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. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef PCIE_HOST_H +#define PCIE_HOST_H + +#include "hw/pci/pci_host.h" +#include "exec/memory.h" + +#define TYPE_PCIE_HOST_BRIDGE "pcie-host-bridge" +#define PCIE_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PCIExpressHost, (obj), TYPE_PCIE_HOST_BRIDGE) + +struct PCIExpressHost { + PCIHostState pci; + + /* express part */ + + /* base address where MMCONFIG area is mapped. */ + hwaddr base_addr; + + /* the size of MMCONFIG area. It's host bridge dependent */ + hwaddr size; + + /* MMCONFIG mmio area */ + MemoryRegion mmio; +}; + +int pcie_host_init(PCIExpressHost *e); +void pcie_host_mmcfg_unmap(PCIExpressHost *e); +void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, uint32_t size); +void pcie_host_mmcfg_update(PCIExpressHost *e, + int enable, + hwaddr addr, + uint32_t size); + +#endif /* PCIE_HOST_H */ diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h new file mode 100644 index 0000000000..d89aa615c5 --- /dev/null +++ b/include/hw/pci/pcie_port.h @@ -0,0 +1,51 @@ +/* + * pcie_port.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_PCIE_PORT_H +#define QEMU_PCIE_PORT_H + +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" + +struct PCIEPort { + PCIBridge br; + + /* pci express switch port */ + uint8_t port; +}; + +void pcie_port_init_reg(PCIDevice *d); + +struct PCIESlot { + PCIEPort port; + + /* pci express switch port with slot */ + uint8_t chassis; + uint16_t slot; + QLIST_ENTRY(PCIESlot) next; +}; + +void pcie_chassis_create(uint8_t chassis_number); +void pcie_main_chassis_create(void); +PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot); +int pcie_chassis_add_slot(struct PCIESlot *slot); +void pcie_chassis_del_slot(PCIESlot *s); + +#endif /* QEMU_PCIE_PORT_H */ diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h new file mode 100644 index 0000000000..4d123d9fcc --- /dev/null +++ b/include/hw/pci/pcie_regs.h @@ -0,0 +1,156 @@ +/* + * constants for pcie configurations space from pci express spec. + * + * TODO: + * Those constants and macros should go to Linux pci_regs.h + * Once they're merged, they will go away. + */ +#ifndef QEMU_PCIE_REGS_H +#define QEMU_PCIE_REGS_H + + +/* express capability */ + +#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */ +#define PCI_EXT_CAP_VER_SHIFT 16 +#define PCI_EXT_CAP_NEXT_SHIFT 20 +#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT) + +#define PCI_EXT_CAP(id, ver, next) \ + ((id) | \ + ((ver) << PCI_EXT_CAP_VER_SHIFT) | \ + ((next) << PCI_EXT_CAP_NEXT_SHIFT)) + +#define PCI_EXT_CAP_ALIGN 4 +#define PCI_EXT_CAP_ALIGNUP(x) \ + (((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1)) + +/* PCI_EXP_FLAGS */ +#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */ +#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1) +#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1) + + +/* PCI_EXP_LINK{CAP, STA} */ +/* link speed */ +#define PCI_EXP_LNK_LS_25 1 + +#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1) +#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT) + +/* PCI_EXP_LINKCAP */ +#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1) +#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT) + +#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1) + +#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1) + +#define PCI_EXP_SLTCTL_IND_RESERVED 0x0 +#define PCI_EXP_SLTCTL_IND_ON 0x1 +#define PCI_EXP_SLTCTL_IND_BLINK 0x2 +#define PCI_EXP_SLTCTL_IND_OFF 0x3 +#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1) +#define PCI_EXP_SLTCTL_AIC_OFF \ + (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT) + +#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1) +#define PCI_EXP_SLTCTL_PIC_OFF \ + (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT) + +#define PCI_EXP_SLTCTL_SUPPORTED \ + (PCI_EXP_SLTCTL_ABPE | \ + PCI_EXP_SLTCTL_PDCE | \ + PCI_EXP_SLTCTL_CCIE | \ + PCI_EXP_SLTCTL_HPIE | \ + PCI_EXP_SLTCTL_AIC | \ + PCI_EXP_SLTCTL_PCC | \ + PCI_EXP_SLTCTL_EIC) + +#define PCI_EXP_DEVCAP2_EFF 0x100000 +#define PCI_EXP_DEVCAP2_EETLPP 0x200000 + +#define PCI_EXP_DEVCTL2_EETLPPB 0x80 + +/* ARI */ +#define PCI_ARI_VER 1 +#define PCI_ARI_SIZEOF 8 + +/* AER */ +#define PCI_ERR_VER 2 +#define PCI_ERR_SIZEOF 0x48 + +#define PCI_ERR_UNC_SDN 0x00000020 /* surprise down */ +#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ +#define PCI_ERR_UNC_INTN 0x00400000 /* Internal Error */ +#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC Blcoked TLP */ +#define PCI_ERR_UNC_ATOP_EBLOCKED 0x01000000 /* atomic op egress blocked */ +#define PCI_ERR_UNC_TLP_PRF_BLOCKED 0x02000000 /* TLP Prefix Blocked */ +#define PCI_ERR_COR_ADV_NONFATAL 0x00002000 /* Advisory Non-Fatal */ +#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ +#define PCI_ERR_COR_HL_OVERFLOW 0x00008000 /* Header Long Overflow */ +#define PCI_ERR_CAP_FEP_MASK 0x0000001f +#define PCI_ERR_CAP_MHRC 0x00000200 +#define PCI_ERR_CAP_MHRE 0x00000400 +#define PCI_ERR_CAP_TLP 0x00000800 + +#define PCI_ERR_HEADER_LOG_SIZE 16 +#define PCI_ERR_TLP_PREFIX_LOG 0x38 +#define PCI_ERR_TLP_PREFIX_LOG_SIZE 16 + +#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000 + +/* aer root error command/status */ +#define PCI_ERR_ROOT_CMD_EN_MASK (PCI_ERR_ROOT_CMD_COR_EN | \ + PCI_ERR_ROOT_CMD_NONFATAL_EN | \ + PCI_ERR_ROOT_CMD_FATAL_EN) + +#define PCI_ERR_ROOT_IRQ_MAX 32 +#define PCI_ERR_ROOT_IRQ 0xf8000000 +#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1) +#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV | \ + PCI_ERR_ROOT_MULTI_COR_RCV | \ + PCI_ERR_ROOT_UNCOR_RCV | \ + PCI_ERR_ROOT_MULTI_UNCOR_RCV | \ + PCI_ERR_ROOT_FIRST_FATAL | \ + PCI_ERR_ROOT_NONFATAL_RCV | \ + PCI_ERR_ROOT_FATAL_RCV) + +#define PCI_ERR_UNC_SUPPORTED (PCI_ERR_UNC_DLP | \ + PCI_ERR_UNC_SDN | \ + PCI_ERR_UNC_POISON_TLP | \ + PCI_ERR_UNC_FCP | \ + PCI_ERR_UNC_COMP_TIME | \ + PCI_ERR_UNC_COMP_ABORT | \ + PCI_ERR_UNC_UNX_COMP | \ + PCI_ERR_UNC_RX_OVER | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_ECRC | \ + PCI_ERR_UNC_UNSUP | \ + PCI_ERR_UNC_ACSV | \ + PCI_ERR_UNC_INTN | \ + PCI_ERR_UNC_MCBTLP | \ + PCI_ERR_UNC_ATOP_EBLOCKED | \ + PCI_ERR_UNC_TLP_PRF_BLOCKED) + +#define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \ + PCI_ERR_UNC_SDN | \ + PCI_ERR_UNC_FCP | \ + PCI_ERR_UNC_RX_OVER | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_INTN) + +#define PCI_ERR_COR_SUPPORTED (PCI_ERR_COR_RCVR | \ + PCI_ERR_COR_BAD_TLP | \ + PCI_ERR_COR_BAD_DLLP | \ + PCI_ERR_COR_REP_ROLL | \ + PCI_ERR_COR_REP_TIMER | \ + PCI_ERR_COR_ADV_NONFATAL | \ + PCI_ERR_COR_INTERNAL | \ + PCI_ERR_COR_HL_OVERFLOW) + +#define PCI_ERR_COR_MASK_DEFAULT (PCI_ERR_COR_ADV_NONFATAL | \ + PCI_ERR_COR_INTERNAL | \ + PCI_ERR_COR_HL_OVERFLOW) + +#endif /* QEMU_PCIE_REGS_H */ diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h new file mode 100644 index 0000000000..467911a558 --- /dev/null +++ b/include/hw/pci/shpc.h @@ -0,0 +1,48 @@ +#ifndef SHPC_H +#define SHPC_H + +#include "qemu-common.h" +#include "exec/memory.h" +#include "migration/vmstate.h" + +struct SHPCDevice { + /* Capability offset in device's config space */ + int cap; + + /* # of hot-pluggable slots */ + int nslots; + + /* SHPC WRS: working register set */ + uint8_t *config; + + /* Used to enable checks on load. Note that writable bits are + * never checked even if set in cmask. */ + uint8_t *cmask; + + /* Used to implement R/W bytes */ + uint8_t *wmask; + + /* Used to implement RW1C(Write 1 to Clear) bytes */ + uint8_t *w1cmask; + + /* MMIO for the SHPC BAR */ + MemoryRegion mmio; + + /* Bus controlled by this SHPC */ + PCIBus *sec_bus; + + /* MSI already requested for this event */ + int msi_requested; +}; + +void shpc_reset(PCIDevice *d); +int shpc_bar_size(PCIDevice *dev); +int shpc_init(PCIDevice *dev, PCIBus *sec_bus, MemoryRegion *bar, unsigned off); +void shpc_cleanup(PCIDevice *dev, MemoryRegion *bar); +void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); + +extern VMStateInfo shpc_vmstate_info; +#define SHPC_VMSTATE(_field, _type) \ + VMSTATE_BUFFER_UNSAFE_INFO(_field, _type, 0, shpc_vmstate_info, 0) + +#endif diff --git a/include/hw/pci/slotid_cap.h b/include/hw/pci/slotid_cap.h new file mode 100644 index 0000000000..70db0470b0 --- /dev/null +++ b/include/hw/pci/slotid_cap.h @@ -0,0 +1,11 @@ +#ifndef PCI_SLOTID_CAP_H +#define PCI_SLOTID_CAP_H + +#include "qemu-common.h" + +int slotid_cap_init(PCIDevice *dev, int nslots, + uint8_t chassis, + unsigned offset); +void slotid_cap_cleanup(PCIDevice *dev); + +#endif diff --git a/include/hw/pcmcia.h b/include/hw/pcmcia.h new file mode 100644 index 0000000000..f91669305e --- /dev/null +++ b/include/hw/pcmcia.h @@ -0,0 +1,56 @@ +#ifndef HW_PCMCIA_H +#define HW_PCMCIA_H 1 + +/* PCMCIA/Cardbus */ + +#include "qemu-common.h" + +typedef struct { + qemu_irq irq; + int attached; + const char *slot_string; + const char *card_string; +} PCMCIASocket; + +void pcmcia_socket_register(PCMCIASocket *socket); +void pcmcia_socket_unregister(PCMCIASocket *socket); +void pcmcia_info(Monitor *mon, const QDict *qdict); + +struct PCMCIACardState { + void *state; + PCMCIASocket *slot; + int (*attach)(void *state); + int (*detach)(void *state); + const uint8_t *cis; + int cis_len; + + /* Only valid if attached */ + uint8_t (*attr_read)(void *state, uint32_t address); + void (*attr_write)(void *state, uint32_t address, uint8_t value); + uint16_t (*common_read)(void *state, uint32_t address); + void (*common_write)(void *state, uint32_t address, uint16_t value); + uint16_t (*io_read)(void *state, uint32_t address); + void (*io_write)(void *state, uint32_t address, uint16_t value); +}; + +#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */ +#define CISTPL_NO_LINK 0x14 /* No Link Tuple */ +#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */ +#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */ +#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */ +#define CISTPL_CONFIG 0x1a /* Configuration Tuple */ +#define CISTPL_CFTABLE_ENTRY 0x1b /* 16-bit PCCard Configuration */ +#define CISTPL_DEVICE_OC 0x1c /* Additional Device Information */ +#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */ +#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */ +#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */ +#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */ +#define CISTPL_FUNCID 0x21 /* Function ID Tuple */ +#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */ +#define CISTPL_END 0xff /* Tuple End */ +#define CISTPL_ENDMARK 0xff + +/* dscm1xxxx.c */ +PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv); + +#endif diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h new file mode 100644 index 0000000000..691263eede --- /dev/null +++ b/include/hw/ppc/mac_dbdma.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009 Laurent Vivier + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef HW_MAC_DBDMA_H +#define HW_MAC_DBDMA_H 1 + +#include "exec/memory.h" + +typedef struct DBDMA_io DBDMA_io; + +typedef void (*DBDMA_flush)(DBDMA_io *io); +typedef void (*DBDMA_rw)(DBDMA_io *io); +typedef void (*DBDMA_end)(DBDMA_io *io); +struct DBDMA_io { + void *opaque; + void *channel; + hwaddr addr; + int len; + int is_last; + int is_dma_out; + DBDMA_end dma_end; +}; + + +void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, + DBDMA_rw rw, DBDMA_flush flush, + void *opaque); +void* DBDMA_init (MemoryRegion **dbdma_mem); + +#endif diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h new file mode 100644 index 0000000000..9dcaf0e7cd --- /dev/null +++ b/include/hw/ppc/openpic.h @@ -0,0 +1,18 @@ +#if !defined(__OPENPIC_H__) +#define __OPENPIC_H__ + +/* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */ +enum { + OPENPIC_OUTPUT_INT = 0, /* IRQ */ + OPENPIC_OUTPUT_CINT, /* critical IRQ */ + OPENPIC_OUTPUT_MCK, /* Machine check event */ + OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */ + OPENPIC_OUTPUT_RESET, /* Core reset event */ + OPENPIC_OUTPUT_NB, +}; + +#define OPENPIC_MODEL_RAVEN 0 +#define OPENPIC_MODEL_FSL_MPIC_20 1 +#define OPENPIC_MODEL_FSL_MPIC_42 2 + +#endif /* __OPENPIC_H__ */ diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h new file mode 100644 index 0000000000..acaf0d6580 --- /dev/null +++ b/include/hw/ppc/ppc.h @@ -0,0 +1,99 @@ +#ifndef HW_PPC_H +#define HW_PPC_H 1 + +void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); + +/* PowerPC hardware exceptions management helpers */ +typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); +typedef struct clk_setup_t clk_setup_t; +struct clk_setup_t { + clk_setup_cb cb; + void *opaque; +}; +static inline void clk_setup (clk_setup_t *clk, uint32_t freq) +{ + if (clk->cb != NULL) + (*clk->cb)(clk->opaque, freq); +} + +struct ppc_tb_t { + /* Time base management */ + int64_t tb_offset; /* Compensation */ + int64_t atb_offset; /* Compensation */ + uint32_t tb_freq; /* TB frequency */ + /* Decrementer management */ + uint64_t decr_next; /* Tick for next decr interrupt */ + uint32_t decr_freq; /* decrementer frequency */ + struct QEMUTimer *decr_timer; + /* Hypervisor decrementer management */ + uint64_t hdecr_next; /* Tick for next hdecr interrupt */ + struct QEMUTimer *hdecr_timer; + uint64_t purr_load; + uint64_t purr_start; + void *opaque; + uint32_t flags; +}; + +/* PPC Timers flags */ +#define PPC_TIMER_BOOKE (1 << 0) /* Enable Booke support */ +#define PPC_TIMER_E500 (1 << 1) /* Enable e500 support */ +#define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when + * the most significant bit + * changes from 0 to 1. + */ +#define PPC_DECR_ZERO_TRIGGERED (1 << 3) /* Decr interrupt triggered when + * the decrementer reaches zero. + */ + +uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); +clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq); +/* Embedded PowerPC DCR management */ +typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn); +typedef void (*dcr_write_cb)(void *opaque, int dcrn, uint32_t val); +int ppc_dcr_init (CPUPPCState *env, int (*dcr_read_error)(int dcrn), + int (*dcr_write_error)(int dcrn)); +int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque, + dcr_read_cb drc_read, dcr_write_cb dcr_write); +clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, + unsigned int decr_excp); + +/* Embedded PowerPC reset */ +void ppc40x_core_reset(PowerPCCPU *cpu); +void ppc40x_chip_reset(PowerPCCPU *cpu); +void ppc40x_system_reset(PowerPCCPU *cpu); +void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val); + +extern CPUWriteMemoryFunc * const PPC_io_write[]; +extern CPUReadMemoryFunc * const PPC_io_read[]; +void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); + +void ppc40x_irq_init (CPUPPCState *env); +void ppce500_irq_init (CPUPPCState *env); +void ppc6xx_irq_init (CPUPPCState *env); +void ppc970_irq_init (CPUPPCState *env); +void ppcPOWER7_irq_init (CPUPPCState *env); + +void ppce500_set_mpic_proxy(bool enabled); + +/* PPC machines for OpenBIOS */ +enum { + ARCH_PREP = 0, + ARCH_MAC99, + ARCH_HEATHROW, + ARCH_MAC99_U3, +}; + +#define FW_CFG_PPC_WIDTH (FW_CFG_ARCH_LOCAL + 0x00) +#define FW_CFG_PPC_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) +#define FW_CFG_PPC_DEPTH (FW_CFG_ARCH_LOCAL + 0x02) +#define FW_CFG_PPC_TBFREQ (FW_CFG_ARCH_LOCAL + 0x03) +#define FW_CFG_PPC_IS_KVM (FW_CFG_ARCH_LOCAL + 0x05) +#define FW_CFG_PPC_KVM_HC (FW_CFG_ARCH_LOCAL + 0x06) +#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) + +#define PPC_SERIAL_MM_BAUDBASE 399193 + +/* ppc_booke.c */ +void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags); + +#endif diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h new file mode 100644 index 0000000000..91d84bad63 --- /dev/null +++ b/include/hw/ppc/ppc4xx.h @@ -0,0 +1,64 @@ +/* + * QEMU PowerPC 4xx emulation shared definitions + * + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if !defined(PPC_4XX_H) +#define PPC_4XX_H + +#include "hw/pci/pci.h" + +/* PowerPC 4xx core initialization */ +PowerPCCPU *ppc4xx_init(const char *cpu_model, + clk_setup_t *cpu_clk, clk_setup_t *tb_clk, + uint32_t sysclk); + +/* PowerPC 4xx universal interrupt controller */ +enum { + PPCUIC_OUTPUT_INT = 0, + PPCUIC_OUTPUT_CINT = 1, + PPCUIC_OUTPUT_NB, +}; +qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, + uint32_t dcr_base, int has_ssr, int has_vr); + +ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, + MemoryRegion ram_memories[], + hwaddr ram_bases[], + hwaddr ram_sizes[], + const unsigned int sdram_bank_sizes[]); + +void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, + MemoryRegion ram_memories[], + hwaddr *ram_bases, + hwaddr *ram_sizes, + int do_init); + +#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost" + +PCIBus *ppc4xx_pci_init(CPUPPCState *env, qemu_irq pci_irqs[4], + hwaddr config_space, + hwaddr int_ack, + hwaddr special_cycle, + hwaddr registers); + +#endif /* !defined(PPC_4XX_H) */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h new file mode 100644 index 0000000000..864bee9d00 --- /dev/null +++ b/include/hw/ppc/spapr.h @@ -0,0 +1,358 @@ +#if !defined(__HW_SPAPR_H__) +#define __HW_SPAPR_H__ + +#include "sysemu/dma.h" +#include "hw/ppc/xics.h" + +struct VIOsPAPRBus; +struct sPAPRPHBState; +struct sPAPRNVRAM; +struct icp_state; + +typedef struct sPAPREnvironment { + struct VIOsPAPRBus *vio_bus; + QLIST_HEAD(, sPAPRPHBState) phbs; + struct sPAPRNVRAM *nvram; + struct icp_state *icp; + + hwaddr ram_limit; + void *htab; + long htab_shift; + hwaddr rma_size; + int vrma_adjust; + hwaddr fdt_addr, rtas_addr; + long rtas_size; + void *fdt_skel; + target_ulong entry_point; + int next_irq; + int rtc_offset; + char *cpu_model; + bool has_graphics; + + uint32_t epow_irq; + Notifier epow_notifier; +} sPAPREnvironment; + +#define H_SUCCESS 0 +#define H_BUSY 1 /* Hardware busy -- retry later */ +#define H_CLOSED 2 /* Resource closed */ +#define H_NOT_AVAILABLE 3 +#define H_CONSTRAINED 4 /* Resource request constrained to max allowed */ +#define H_PARTIAL 5 +#define H_IN_PROGRESS 14 /* Kind of like busy */ +#define H_PAGE_REGISTERED 15 +#define H_PARTIAL_STORE 16 +#define H_PENDING 17 /* returned from H_POLL_PENDING */ +#define H_CONTINUE 18 /* Returned from H_Join on success */ +#define H_LONG_BUSY_START_RANGE 9900 /* Start of long busy range */ +#define H_LONG_BUSY_ORDER_1_MSEC 9900 /* Long busy, hint that 1msec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_10_MSEC 9901 /* Long busy, hint that 10msec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_100_MSEC 9902 /* Long busy, hint that 100msec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_1_SEC 9903 /* Long busy, hint that 1sec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_10_SEC 9904 /* Long busy, hint that 10sec \ + is a good time to retry */ +#define H_LONG_BUSY_ORDER_100_SEC 9905 /* Long busy, hint that 100sec \ + is a good time to retry */ +#define H_LONG_BUSY_END_RANGE 9905 /* End of long busy range */ +#define H_HARDWARE -1 /* Hardware error */ +#define H_FUNCTION -2 /* Function not supported */ +#define H_PRIVILEGE -3 /* Caller not privileged */ +#define H_PARAMETER -4 /* Parameter invalid, out-of-range or conflicting */ +#define H_BAD_MODE -5 /* Illegal msr value */ +#define H_PTEG_FULL -6 /* PTEG is full */ +#define H_NOT_FOUND -7 /* PTE was not found" */ +#define H_RESERVED_DABR -8 /* DABR address is reserved by the hypervisor on this processor" */ +#define H_NO_MEM -9 +#define H_AUTHORITY -10 +#define H_PERMISSION -11 +#define H_DROPPED -12 +#define H_SOURCE_PARM -13 +#define H_DEST_PARM -14 +#define H_REMOTE_PARM -15 +#define H_RESOURCE -16 +#define H_ADAPTER_PARM -17 +#define H_RH_PARM -18 +#define H_RCQ_PARM -19 +#define H_SCQ_PARM -20 +#define H_EQ_PARM -21 +#define H_RT_PARM -22 +#define H_ST_PARM -23 +#define H_SIGT_PARM -24 +#define H_TOKEN_PARM -25 +#define H_MLENGTH_PARM -27 +#define H_MEM_PARM -28 +#define H_MEM_ACCESS_PARM -29 +#define H_ATTR_PARM -30 +#define H_PORT_PARM -31 +#define H_MCG_PARM -32 +#define H_VL_PARM -33 +#define H_TSIZE_PARM -34 +#define H_TRACE_PARM -35 + +#define H_MASK_PARM -37 +#define H_MCG_FULL -38 +#define H_ALIAS_EXIST -39 +#define H_P_COUNTER -40 +#define H_TABLE_FULL -41 +#define H_ALT_TABLE -42 +#define H_MR_CONDITION -43 +#define H_NOT_ENOUGH_RESOURCES -44 +#define H_R_STATE -45 +#define H_RESCINDEND -46 +#define H_MULTI_THREADS_ACTIVE -9005 + + +/* Long Busy is a condition that can be returned by the firmware + * when a call cannot be completed now, but the identical call + * should be retried later. This prevents calls blocking in the + * firmware for long periods of time. Annoyingly the firmware can return + * a range of return codes, hinting at how long we should wait before + * retrying. If you don't care for the hint, the macro below is a good + * way to check for the long_busy return codes + */ +#define H_IS_LONG_BUSY(x) ((x >= H_LONG_BUSY_START_RANGE) \ + && (x <= H_LONG_BUSY_END_RANGE)) + +/* Flags */ +#define H_LARGE_PAGE (1ULL<<(63-16)) +#define H_EXACT (1ULL<<(63-24)) /* Use exact PTE or return H_PTEG_FULL */ +#define H_R_XLATE (1ULL<<(63-25)) /* include a valid logical page num in the pte if the valid bit is set */ +#define H_READ_4 (1ULL<<(63-26)) /* Return 4 PTEs */ +#define H_PAGE_STATE_CHANGE (1ULL<<(63-28)) +#define H_PAGE_UNUSED ((1ULL<<(63-29)) | (1ULL<<(63-30))) +#define H_PAGE_SET_UNUSED (H_PAGE_STATE_CHANGE | H_PAGE_UNUSED) +#define H_PAGE_SET_LOANED (H_PAGE_SET_UNUSED | (1ULL<<(63-31))) +#define H_PAGE_SET_ACTIVE H_PAGE_STATE_CHANGE +#define H_AVPN (1ULL<<(63-32)) /* An avpn is provided as a sanity test */ +#define H_ANDCOND (1ULL<<(63-33)) +#define H_ICACHE_INVALIDATE (1ULL<<(63-40)) /* icbi, etc. (ignored for IO pages) */ +#define H_ICACHE_SYNCHRONIZE (1ULL<<(63-41)) /* dcbst, icbi, etc (ignored for IO pages */ +#define H_ZERO_PAGE (1ULL<<(63-48)) /* zero the page before mapping (ignored for IO pages) */ +#define H_COPY_PAGE (1ULL<<(63-49)) +#define H_N (1ULL<<(63-61)) +#define H_PP1 (1ULL<<(63-62)) +#define H_PP2 (1ULL<<(63-63)) + +/* VASI States */ +#define H_VASI_INVALID 0 +#define H_VASI_ENABLED 1 +#define H_VASI_ABORTED 2 +#define H_VASI_SUSPENDING 3 +#define H_VASI_SUSPENDED 4 +#define H_VASI_RESUMED 5 +#define H_VASI_COMPLETED 6 + +/* DABRX flags */ +#define H_DABRX_HYPERVISOR (1ULL<<(63-61)) +#define H_DABRX_KERNEL (1ULL<<(63-62)) +#define H_DABRX_USER (1ULL<<(63-63)) + +/* Each control block has to be on a 4K boundary */ +#define H_CB_ALIGNMENT 4096 + +/* pSeries hypervisor opcodes */ +#define H_REMOVE 0x04 +#define H_ENTER 0x08 +#define H_READ 0x0c +#define H_CLEAR_MOD 0x10 +#define H_CLEAR_REF 0x14 +#define H_PROTECT 0x18 +#define H_GET_TCE 0x1c +#define H_PUT_TCE 0x20 +#define H_SET_SPRG0 0x24 +#define H_SET_DABR 0x28 +#define H_PAGE_INIT 0x2c +#define H_SET_ASR 0x30 +#define H_ASR_ON 0x34 +#define H_ASR_OFF 0x38 +#define H_LOGICAL_CI_LOAD 0x3c +#define H_LOGICAL_CI_STORE 0x40 +#define H_LOGICAL_CACHE_LOAD 0x44 +#define H_LOGICAL_CACHE_STORE 0x48 +#define H_LOGICAL_ICBI 0x4c +#define H_LOGICAL_DCBF 0x50 +#define H_GET_TERM_CHAR 0x54 +#define H_PUT_TERM_CHAR 0x58 +#define H_REAL_TO_LOGICAL 0x5c +#define H_HYPERVISOR_DATA 0x60 +#define H_EOI 0x64 +#define H_CPPR 0x68 +#define H_IPI 0x6c +#define H_IPOLL 0x70 +#define H_XIRR 0x74 +#define H_PERFMON 0x7c +#define H_MIGRATE_DMA 0x78 +#define H_REGISTER_VPA 0xDC +#define H_CEDE 0xE0 +#define H_CONFER 0xE4 +#define H_PROD 0xE8 +#define H_GET_PPP 0xEC +#define H_SET_PPP 0xF0 +#define H_PURR 0xF4 +#define H_PIC 0xF8 +#define H_REG_CRQ 0xFC +#define H_FREE_CRQ 0x100 +#define H_VIO_SIGNAL 0x104 +#define H_SEND_CRQ 0x108 +#define H_COPY_RDMA 0x110 +#define H_REGISTER_LOGICAL_LAN 0x114 +#define H_FREE_LOGICAL_LAN 0x118 +#define H_ADD_LOGICAL_LAN_BUFFER 0x11C +#define H_SEND_LOGICAL_LAN 0x120 +#define H_BULK_REMOVE 0x124 +#define H_MULTICAST_CTRL 0x130 +#define H_SET_XDABR 0x134 +#define H_STUFF_TCE 0x138 +#define H_PUT_TCE_INDIRECT 0x13C +#define H_CHANGE_LOGICAL_LAN_MAC 0x14C +#define H_VTERM_PARTNER_INFO 0x150 +#define H_REGISTER_VTERM 0x154 +#define H_FREE_VTERM 0x158 +#define H_RESET_EVENTS 0x15C +#define H_ALLOC_RESOURCE 0x160 +#define H_FREE_RESOURCE 0x164 +#define H_MODIFY_QP 0x168 +#define H_QUERY_QP 0x16C +#define H_REREGISTER_PMR 0x170 +#define H_REGISTER_SMR 0x174 +#define H_QUERY_MR 0x178 +#define H_QUERY_MW 0x17C +#define H_QUERY_HCA 0x180 +#define H_QUERY_PORT 0x184 +#define H_MODIFY_PORT 0x188 +#define H_DEFINE_AQP1 0x18C +#define H_GET_TRACE_BUFFER 0x190 +#define H_DEFINE_AQP0 0x194 +#define H_RESIZE_MR 0x198 +#define H_ATTACH_MCQP 0x19C +#define H_DETACH_MCQP 0x1A0 +#define H_CREATE_RPT 0x1A4 +#define H_REMOVE_RPT 0x1A8 +#define H_REGISTER_RPAGES 0x1AC +#define H_DISABLE_AND_GETC 0x1B0 +#define H_ERROR_DATA 0x1B4 +#define H_GET_HCA_INFO 0x1B8 +#define H_GET_PERF_COUNT 0x1BC +#define H_MANAGE_TRACE 0x1C0 +#define H_FREE_LOGICAL_LAN_BUFFER 0x1D4 +#define H_QUERY_INT_STATE 0x1E4 +#define H_POLL_PENDING 0x1D8 +#define H_ILLAN_ATTRIBUTES 0x244 +#define H_MODIFY_HEA_QP 0x250 +#define H_QUERY_HEA_QP 0x254 +#define H_QUERY_HEA 0x258 +#define H_QUERY_HEA_PORT 0x25C +#define H_MODIFY_HEA_PORT 0x260 +#define H_REG_BCMC 0x264 +#define H_DEREG_BCMC 0x268 +#define H_REGISTER_HEA_RPAGES 0x26C +#define H_DISABLE_AND_GET_HEA 0x270 +#define H_GET_HEA_INFO 0x274 +#define H_ALLOC_HEA_RESOURCE 0x278 +#define H_ADD_CONN 0x284 +#define H_DEL_CONN 0x288 +#define H_JOIN 0x298 +#define H_VASI_STATE 0x2A4 +#define H_ENABLE_CRQ 0x2B0 +#define H_GET_EM_PARMS 0x2B8 +#define H_SET_MPP 0x2D0 +#define H_GET_MPP 0x2D4 +#define MAX_HCALL_OPCODE H_GET_MPP + +/* The hcalls above are standardized in PAPR and implemented by pHyp + * as well. + * + * We also need some hcalls which are specific to qemu / KVM-on-POWER. + * So far we just need one for H_RTAS, but in future we'll need more + * for extensions like virtio. We put those into the 0xf000-0xfffc + * range which is reserved by PAPR for "platform-specific" hcalls. + */ +#define KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) +#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1) +#define KVMPPC_HCALL_MAX KVMPPC_H_LOGICAL_MEMOP + +extern sPAPREnvironment *spapr; + +/*#define DEBUG_SPAPR_HCALLS*/ + +#ifdef DEBUG_SPAPR_HCALLS +#define hcall_dprintf(fmt, ...) \ + do { fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); } while (0) +#else +#define hcall_dprintf(fmt, ...) \ + do { } while (0) +#endif + +typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, + target_ulong *args); + +void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); +target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, + target_ulong *args); + +int spapr_allocate_irq(int hint, bool lsi); +int spapr_allocate_irq_block(int num, bool lsi); + +static inline int spapr_allocate_msi(int hint) +{ + return spapr_allocate_irq(hint, false); +} + +static inline int spapr_allocate_lsi(int hint) +{ + return spapr_allocate_irq(hint, true); +} + +static inline uint32_t rtas_ld(target_ulong phys, int n) +{ + return ldl_be_phys(phys + 4*n); +} + +static inline void rtas_st(target_ulong phys, int n, uint32_t val) +{ + stl_be_phys(phys + 4*n, val); +} + +typedef void (*spapr_rtas_fn)(sPAPREnvironment *spapr, uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets); +int spapr_rtas_register(const char *name, spapr_rtas_fn fn); +target_ulong spapr_rtas_call(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets); +int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, + hwaddr rtas_size); + +#define SPAPR_TCE_PAGE_SHIFT 12 +#define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT) +#define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1) + +typedef struct sPAPRTCE { + uint64_t tce; +} sPAPRTCE; + +#define SPAPR_VIO_BASE_LIOBN 0x00000000 +#define SPAPR_PCI_BASE_LIOBN 0x80000000 + +#define RTAS_ERROR_LOG_MAX 2048 + + +void spapr_iommu_init(void); +void spapr_events_init(sPAPREnvironment *spapr); +void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); +DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size); +void spapr_tce_free(DMAContext *dma); +void spapr_tce_reset(DMAContext *dma); +void spapr_tce_set_bypass(DMAContext *dma, bool bypass); +int spapr_dma_dt(void *fdt, int node_off, const char *propname, + uint32_t liobn, uint64_t window, uint32_t size); +int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, + DMAContext *dma); + +#endif /* !defined (__HW_SPAPR_H__) */ diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h new file mode 100644 index 0000000000..f98ec0a2e5 --- /dev/null +++ b/include/hw/ppc/spapr_vio.h @@ -0,0 +1,136 @@ +#ifndef _HW_SPAPR_VIO_H +#define _HW_SPAPR_VIO_H +/* + * QEMU sPAPR VIO bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus definitions: + * Copyright (c) 2009 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "sysemu/dma.h" + +#define TYPE_VIO_SPAPR_DEVICE "vio-spapr-device" +#define VIO_SPAPR_DEVICE(obj) \ + OBJECT_CHECK(VIOsPAPRDevice, (obj), TYPE_VIO_SPAPR_DEVICE) +#define VIO_SPAPR_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VIOsPAPRDeviceClass, (klass), TYPE_VIO_SPAPR_DEVICE) +#define VIO_SPAPR_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VIOsPAPRDeviceClass, (obj), TYPE_VIO_SPAPR_DEVICE) + +#define TYPE_SPAPR_VIO_BUS "spapr-vio-bus" +#define SPAPR_VIO_BUS(obj) OBJECT_CHECK(VIOsPAPRBus, (obj), TYPE_SPAPR_VIO_BUS) + +struct VIOsPAPRDevice; + +typedef struct VIOsPAPR_CRQ { + uint64_t qladdr; + uint32_t qsize; + uint32_t qnext; + int(*SendFunc)(struct VIOsPAPRDevice *vdev, uint8_t *crq); +} VIOsPAPR_CRQ; + +typedef struct VIOsPAPRDevice VIOsPAPRDevice; +typedef struct VIOsPAPRBus VIOsPAPRBus; + +typedef struct VIOsPAPRDeviceClass { + DeviceClass parent_class; + + const char *dt_name, *dt_type, *dt_compatible; + target_ulong signal_mask; + uint32_t rtce_window_size; + int (*init)(VIOsPAPRDevice *dev); + void (*reset)(VIOsPAPRDevice *dev); + int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); +} VIOsPAPRDeviceClass; + +struct VIOsPAPRDevice { + DeviceState qdev; + uint32_t reg; + uint32_t irq; + target_ulong signal_state; + VIOsPAPR_CRQ crq; + DMAContext *dma; +}; + +#define DEFINE_SPAPR_PROPERTIES(type, field) \ + DEFINE_PROP_UINT32("reg", type, field.reg, -1) + +struct VIOsPAPRBus { + BusState bus; + uint32_t next_reg; + int (*init)(VIOsPAPRDevice *dev); + int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); +}; + +extern VIOsPAPRBus *spapr_vio_bus_init(void); +extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg); +extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt); +extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus); + +extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode); + +static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev) +{ + return xics_get_qirq(spapr->icp, dev->irq); +} + +static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr, + uint32_t size, DMADirection dir) +{ + return dma_memory_valid(dev->dma, taddr, size, dir); +} + +static inline int spapr_vio_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, + void *buf, uint32_t size) +{ + return (dma_memory_read(dev->dma, taddr, buf, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +static inline int spapr_vio_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, + const void *buf, uint32_t size) +{ + return (dma_memory_write(dev->dma, taddr, buf, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +static inline int spapr_vio_dma_set(VIOsPAPRDevice *dev, uint64_t taddr, + uint8_t c, uint32_t size) +{ + return (dma_memory_set(dev->dma, taddr, c, size) != 0) ? + H_DEST_PARM : H_SUCCESS; +} + +#define vio_stb(_dev, _addr, _val) (stb_dma((_dev)->dma, (_addr), (_val))) +#define vio_sth(_dev, _addr, _val) (stw_be_dma((_dev)->dma, (_addr), (_val))) +#define vio_stl(_dev, _addr, _val) (stl_be_dma((_dev)->dma, (_addr), (_val))) +#define vio_stq(_dev, _addr, _val) (stq_be_dma((_dev)->dma, (_addr), (_val))) +#define vio_ldq(_dev, _addr) (ldq_be_dma((_dev)->dma, (_addr))) + +int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); + +VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg); +void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); +void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev); +void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd); +void spapr_vscsi_create(VIOsPAPRBus *bus); + +VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus); + +void spapr_vio_quiesce(void); + +#endif /* _HW_SPAPR_VIO_H */ diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h new file mode 100644 index 0000000000..6bce0424df --- /dev/null +++ b/include/hw/ppc/xics.h @@ -0,0 +1,41 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Copyright (c) 2010,2011 David Gibson, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#if !defined(__XICS_H__) +#define __XICS_H__ + +#define XICS_IPI 0x2 +#define XICS_IRQ_BASE 0x10 + +struct icp_state; + +qemu_irq xics_get_qirq(struct icp_state *icp, int irq); +void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi); + +struct icp_state *xics_system_init(int nr_servers, int nr_irqs); +void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu); + +#endif /* __XICS_H__ */ diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h new file mode 100644 index 0000000000..28fcaf17f8 --- /dev/null +++ b/include/hw/ptimer.h @@ -0,0 +1,39 @@ +/* + * General purpose implementation of a simple periodic countdown timer. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GNU LGPL. + */ +#ifndef PTIMER_H +#define PTIMER_H + +#include "qemu-common.h" +#include "qemu/timer.h" +#include "migration/vmstate.h" + +/* ptimer.c */ +typedef struct ptimer_state ptimer_state; +typedef void (*ptimer_cb)(void *opaque); + +ptimer_state *ptimer_init(QEMUBH *bh); +void ptimer_set_period(ptimer_state *s, int64_t period); +void ptimer_set_freq(ptimer_state *s, uint32_t freq); +void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload); +uint64_t ptimer_get_count(ptimer_state *s); +void ptimer_set_count(ptimer_state *s, uint64_t count); +void ptimer_run(ptimer_state *s, int oneshot); +void ptimer_stop(ptimer_state *s); + +extern const VMStateDescription vmstate_ptimer; + +#define VMSTATE_PTIMER(_field, _state) { \ + .name = (stringify(_field)), \ + .version_id = (1), \ + .vmsd = &vmstate_ptimer, \ + .size = sizeof(ptimer_state *), \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, ptimer_state), \ +} + +#endif diff --git a/include/hw/qdev-addr.h b/include/hw/qdev-addr.h new file mode 100644 index 0000000000..79708e6751 --- /dev/null +++ b/include/hw/qdev-addr.h @@ -0,0 +1,10 @@ +#ifndef HW_QDEV_ADDR_H +#define HW_QDEV_ADDR_H 1 + +#define DEFINE_PROP_TADDR(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_taddr, hwaddr) + +extern PropertyInfo qdev_prop_taddr; +void qdev_prop_set_taddr(DeviceState *dev, const char *name, hwaddr value); + +#endif diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h new file mode 100644 index 0000000000..547fbc7e0d --- /dev/null +++ b/include/hw/qdev-core.h @@ -0,0 +1,299 @@ +#ifndef QDEV_CORE_H +#define QDEV_CORE_H + +#include "qemu/queue.h" +#include "qemu/option.h" +#include "qemu/typedefs.h" +#include "qom/object.h" +#include "hw/irq.h" +#include "qapi/error.h" + +enum { + DEV_NVECTORS_UNSPECIFIED = -1, +}; + +#define TYPE_DEVICE "device" +#define DEVICE(obj) OBJECT_CHECK(DeviceState, (obj), TYPE_DEVICE) +#define DEVICE_CLASS(klass) OBJECT_CLASS_CHECK(DeviceClass, (klass), TYPE_DEVICE) +#define DEVICE_GET_CLASS(obj) OBJECT_GET_CLASS(DeviceClass, (obj), TYPE_DEVICE) + +typedef int (*qdev_initfn)(DeviceState *dev); +typedef int (*qdev_event)(DeviceState *dev); +typedef void (*qdev_resetfn)(DeviceState *dev); +typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); +typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp); + +struct VMStateDescription; + +/** + * DeviceClass: + * @props: Properties accessing state fields. + * @realize: Callback function invoked when the #DeviceState:realized + * property is changed to %true. The default invokes @init if not %NULL. + * @unrealize: Callback function invoked when the #DeviceState:realized + * property is changed to %false. + * @init: Callback function invoked when the #DeviceState::realized property + * is changed to %true. Deprecated, new types inheriting directly from + * TYPE_DEVICE should use @realize instead, new leaf types should consult + * their respective parent type. + * + * # Realization # + * Devices are constructed in two stages, + * 1) object instantiation via object_initialize() and + * 2) device realization via #DeviceState:realized property. + * The former may not fail (it might assert or exit), the latter may return + * error information to the caller and must be re-entrant. + * Trivial field initializations should go into #TypeInfo.instance_init. + * Operations depending on @props static properties should go into @realize. + * After successful realization, setting static properties will fail. + * + * As an interim step, the #DeviceState:realized property is set by deprecated + * functions qdev_init() and qdev_init_nofail(). + * In the future, devices will propagate this state change to their children + * and along busses they expose. + * The point in time will be deferred to machine creation, so that values + * set in @realize will not be introspectable beforehand. Therefore devices + * must not create children during @realize; they should initialize them via + * object_initialize() in their own #TypeInfo.instance_init and forward the + * realization events appropriately. + * + * The @init callback is considered private to a particular bus implementation + * (immediate abstract child types of TYPE_DEVICE). Derived leaf types set an + * "init" callback on their parent class instead. + * + * Any type may override the @realize and/or @unrealize callbacks but needs + * to call the parent type's implementation if keeping their functionality + * is desired. Refer to QOM documentation for further discussion and examples. + * + * + * + * If a type derived directly from TYPE_DEVICE implements @realize, it does + * not need to implement @init and therefore does not need to store and call + * #DeviceClass' default @realize callback. + * For other types consult the documentation and implementation of the + * respective parent types. + * + * + */ +typedef struct DeviceClass { + /*< private >*/ + ObjectClass parent_class; + /*< public >*/ + + const char *fw_name; + const char *desc; + Property *props; + int no_user; + + /* callbacks */ + void (*reset)(DeviceState *dev); + DeviceRealize realize; + DeviceUnrealize unrealize; + + /* device state */ + const struct VMStateDescription *vmsd; + + /* Private to qdev / bus. */ + qdev_initfn init; /* TODO remove, once users are converted to realize */ + qdev_event unplug; + qdev_event exit; + const char *bus_type; +} DeviceClass; + +/** + * DeviceState: + * @realized: Indicates whether the device has been fully constructed. + * + * This structure should not be accessed directly. We declare it here + * so that it can be embedded in individual device state structures. + */ +struct DeviceState { + /*< private >*/ + Object parent_obj; + /*< public >*/ + + const char *id; + bool realized; + QemuOpts *opts; + int hotplugged; + BusState *parent_bus; + int num_gpio_out; + qemu_irq *gpio_out; + int num_gpio_in; + qemu_irq *gpio_in; + QLIST_HEAD(, BusState) child_bus; + int num_child_bus; + int instance_id_alias; + int alias_required_for_version; +}; + +#define TYPE_BUS "bus" +#define BUS(obj) OBJECT_CHECK(BusState, (obj), TYPE_BUS) +#define BUS_CLASS(klass) OBJECT_CLASS_CHECK(BusClass, (klass), TYPE_BUS) +#define BUS_GET_CLASS(obj) OBJECT_GET_CLASS(BusClass, (obj), TYPE_BUS) + +struct BusClass { + ObjectClass parent_class; + + /* FIXME first arg should be BusState */ + void (*print_dev)(Monitor *mon, DeviceState *dev, int indent); + char *(*get_dev_path)(DeviceState *dev); + /* + * This callback is used to create Open Firmware device path in accordance + * with OF spec http://forthworks.com/standards/of1275.pdf. Individual bus + * bindings can be found at http://playground.sun.com/1275/bindings/. + */ + char *(*get_fw_dev_path)(DeviceState *dev); + int (*reset)(BusState *bus); + /* maximum devices allowed on the bus, 0: no limit. */ + int max_dev; +}; + +typedef struct BusChild { + DeviceState *child; + int index; + QTAILQ_ENTRY(BusChild) sibling; +} BusChild; + +/** + * BusState: + */ +struct BusState { + Object obj; + DeviceState *parent; + const char *name; + int allow_hotplug; + int max_index; + QTAILQ_HEAD(ChildrenHead, BusChild) children; + QLIST_ENTRY(BusState) sibling; +}; + +struct Property { + const char *name; + PropertyInfo *info; + int offset; + uint8_t bitnr; + uint8_t qtype; + int64_t defval; + int arrayoffset; + PropertyInfo *arrayinfo; + int arrayfieldsize; +}; + +struct PropertyInfo { + const char *name; + const char *legacy_name; + const char **enum_table; + int (*parse)(DeviceState *dev, Property *prop, const char *str); + int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len); + ObjectPropertyAccessor *get; + ObjectPropertyAccessor *set; + ObjectPropertyRelease *release; +}; + +typedef struct GlobalProperty { + const char *driver; + const char *property; + const char *value; + QTAILQ_ENTRY(GlobalProperty) next; +} GlobalProperty; + +/*** Board API. This should go away once we have a machine config file. ***/ + +DeviceState *qdev_create(BusState *bus, const char *name); +DeviceState *qdev_try_create(BusState *bus, const char *name); +int qdev_init(DeviceState *dev) QEMU_WARN_UNUSED_RESULT; +void qdev_init_nofail(DeviceState *dev); +void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, + int required_for_version); +void qdev_unplug(DeviceState *dev, Error **errp); +void qdev_free(DeviceState *dev); +int qdev_simple_unplug_cb(DeviceState *dev); +void qdev_machine_creation_done(void); +bool qdev_machine_modified(void); + +qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); +void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); + +BusState *qdev_get_child_bus(DeviceState *dev, const char *name); + +/*** Device API. ***/ + +/* Register device properties. */ +/* GPIO inputs also double as IRQ sinks. */ +void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n); +void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); + +BusState *qdev_get_parent_bus(DeviceState *dev); + +/*** BUS API. ***/ + +DeviceState *qdev_find_recursive(BusState *bus, const char *id); + +/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */ +typedef int (qbus_walkerfn)(BusState *bus, void *opaque); +typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); + +void qbus_create_inplace(void *bus, const char *typename, + DeviceState *parent, const char *name); +BusState *qbus_create(const char *typename, DeviceState *parent, const char *name); +/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion, + * < 0 if either devfn or busfn terminate walk somewhere in cursion, + * 0 otherwise. */ +int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque); +int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque); +void qdev_reset_all(DeviceState *dev); + +/** + * @qbus_reset_all: + * @bus: Bus to be reset. + * + * Reset @bus and perform a bus-level ("hard") reset of all devices connected + * to it, including recursive processing of all buses below @bus itself. A + * hard reset means that qbus_reset_all will reset all state of the device. + * For PCI devices, for example, this will include the base address registers + * or configuration space. + */ +void qbus_reset_all(BusState *bus); +void qbus_reset_all_fn(void *opaque); + +void qbus_free(BusState *bus); + +#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev) + +/* This should go away once we get rid of the NULL bus hack */ +BusState *sysbus_get_default(void); + +char *qdev_get_fw_dev_path(DeviceState *dev); + +/** + * @qdev_machine_init + * + * Initialize platform devices before machine init. This is a hack until full + * support for composition is added. + */ +void qdev_machine_init(void); + +/** + * @device_reset + * + * Reset a single device (by calling the reset method). + */ +void device_reset(DeviceState *dev); + +const struct VMStateDescription *qdev_get_vmsd(DeviceState *dev); + +const char *qdev_fw_name(DeviceState *dev); + +Object *qdev_get_machine(void); + +/* FIXME: make this a link<> */ +void qdev_set_parent_bus(DeviceState *dev, BusState *bus); + +extern int qdev_hotplug; + +char *qdev_get_dev_path(DeviceState *dev); + +#endif diff --git a/include/hw/qdev-dma.h b/include/hw/qdev-dma.h new file mode 100644 index 0000000000..6812735e3d --- /dev/null +++ b/include/hw/qdev-dma.h @@ -0,0 +1,10 @@ +/* + * Support for dma_addr_t typed properties + * + * Copyright (C) 2012 David Gibson, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#define DEFINE_PROP_DMAADDR(_n, _s, _f, _d) \ + DEFINE_PROP_HEX64(_n, _s, _f, _d) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h new file mode 100644 index 0000000000..a37933998a --- /dev/null +++ b/include/hw/qdev-properties.h @@ -0,0 +1,182 @@ +#ifndef QEMU_QDEV_PROPERTIES_H +#define QEMU_QDEV_PROPERTIES_H + +#include "hw/qdev-core.h" + +/*** qdev-properties.c ***/ + +extern PropertyInfo qdev_prop_bit; +extern PropertyInfo qdev_prop_uint8; +extern PropertyInfo qdev_prop_uint16; +extern PropertyInfo qdev_prop_uint32; +extern PropertyInfo qdev_prop_int32; +extern PropertyInfo qdev_prop_uint64; +extern PropertyInfo qdev_prop_hex8; +extern PropertyInfo qdev_prop_hex32; +extern PropertyInfo qdev_prop_hex64; +extern PropertyInfo qdev_prop_string; +extern PropertyInfo qdev_prop_chr; +extern PropertyInfo qdev_prop_ptr; +extern PropertyInfo qdev_prop_macaddr; +extern PropertyInfo qdev_prop_losttickpolicy; +extern PropertyInfo qdev_prop_bios_chs_trans; +extern PropertyInfo qdev_prop_drive; +extern PropertyInfo qdev_prop_netdev; +extern PropertyInfo qdev_prop_vlan; +extern PropertyInfo qdev_prop_pci_devfn; +extern PropertyInfo qdev_prop_blocksize; +extern PropertyInfo qdev_prop_pci_host_devaddr; +extern PropertyInfo qdev_prop_arraylen; + +#define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ + .name = (_name), \ + .info = &(_prop), \ + .offset = offsetof(_state, _field) \ + + type_check(_type, typeof_field(_state, _field)), \ + } +#define DEFINE_PROP_DEFAULT(_name, _state, _field, _defval, _prop, _type) { \ + .name = (_name), \ + .info = &(_prop), \ + .offset = offsetof(_state, _field) \ + + type_check(_type,typeof_field(_state, _field)), \ + .qtype = QTYPE_QINT, \ + .defval = (_type)_defval, \ + } +#define DEFINE_PROP_BIT(_name, _state, _field, _bit, _defval) { \ + .name = (_name), \ + .info = &(qdev_prop_bit), \ + .bitnr = (_bit), \ + .offset = offsetof(_state, _field) \ + + type_check(uint32_t,typeof_field(_state, _field)), \ + .qtype = QTYPE_QBOOL, \ + .defval = (bool)_defval, \ + } + +#define PROP_ARRAY_LEN_PREFIX "len-" + +/** + * DEFINE_PROP_ARRAY: + * @_name: name of the array + * @_state: name of the device state structure type + * @_field: uint32_t field in @_state to hold the array length + * @_arrayfield: field in @_state (of type '@_arraytype *') which + * will point to the array + * @_arrayprop: PropertyInfo defining what property the array elements have + * @_arraytype: C type of the array elements + * + * Define device properties for a variable-length array _name. A + * static property "len-arrayname" is defined. When the device creator + * sets this property to the desired length of array, further dynamic + * properties "arrayname[0]", "arrayname[1]", ... are defined so the + * device creator can set the array element values. Setting the + * "len-arrayname" property more than once is an error. + * + * When the array length is set, the @_field member of the device + * struct is set to the array length, and @_arrayfield is set to point + * to (zero-initialised) memory allocated for the array. For a zero + * length array, @_field will be set to 0 and @_arrayfield to NULL. + * It is the responsibility of the device deinit code to free the + * @_arrayfield memory. + */ +#define DEFINE_PROP_ARRAY(_name, _state, _field, \ + _arrayfield, _arrayprop, _arraytype) { \ + .name = (PROP_ARRAY_LEN_PREFIX _name), \ + .info = &(qdev_prop_arraylen), \ + .offset = offsetof(_state, _field) \ + + type_check(uint32_t, typeof_field(_state, _field)), \ + .qtype = QTYPE_QINT, \ + .arrayinfo = &(_arrayprop), \ + .arrayfieldsize = sizeof(_arraytype), \ + .arrayoffset = offsetof(_state, _arrayfield), \ + } + +#define DEFINE_PROP_UINT8(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t) +#define DEFINE_PROP_UINT16(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint16, uint16_t) +#define DEFINE_PROP_UINT32(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint32, uint32_t) +#define DEFINE_PROP_INT32(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_int32, int32_t) +#define DEFINE_PROP_UINT64(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint64, uint64_t) +#define DEFINE_PROP_HEX8(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex8, uint8_t) +#define DEFINE_PROP_HEX32(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex32, uint32_t) +#define DEFINE_PROP_HEX64(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex64, uint64_t) +#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) + +#define DEFINE_PROP_PTR(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, void*) +#define DEFINE_PROP_CHR(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharDriverState*) +#define DEFINE_PROP_STRING(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) +#define DEFINE_PROP_NETDEV(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers) +#define DEFINE_PROP_VLAN(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers) +#define DEFINE_PROP_DRIVE(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *) +#define DEFINE_PROP_MACADDR(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) +#define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ + LostTickPolicy) +#define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int) +#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t) +#define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress) + +#define DEFINE_PROP_END_OF_LIST() \ + {} + +/* Set properties between creation and init. */ +void *qdev_get_prop_ptr(DeviceState *dev, Property *prop); +int qdev_prop_parse(DeviceState *dev, const char *name, const char *value); +void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value); +void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value); +void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value); +void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value); +void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value); +void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value); +void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value); +void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value); +void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); +int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) QEMU_WARN_UNUSED_RESULT; +void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value); +void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); +void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); +/* FIXME: Remove opaque pointer properties. */ +void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); + +void qdev_prop_register_global(GlobalProperty *prop); +void qdev_prop_register_global_list(GlobalProperty *props); +void qdev_prop_set_globals(DeviceState *dev); +void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, + Property *prop, const char *value); + +/** + * @qdev_property_add_static - add a @Property to a device referencing a + * field in a struct. + */ +void qdev_property_add_static(DeviceState *dev, Property *prop, Error **errp); + +/** + * @qdev_prop_set_after_realize: + * @dev: device + * @name: name of property + * @errp: indirect pointer to Error to be set + * Set the Error object to report that an attempt was made to set a property + * on a device after it has already been realized. This is a utility function + * which allows property-setter functions to easily report the error in + * a friendly format identifying both the device and the property. + */ +void qdev_prop_set_after_realize(DeviceState *dev, const char *name, + Error **errp); +#endif diff --git a/include/hw/qdev.h b/include/hw/qdev.h new file mode 100644 index 0000000000..5cb8b080a6 --- /dev/null +++ b/include/hw/qdev.h @@ -0,0 +1,8 @@ +#ifndef QDEV_H +#define QDEV_H + +#include "hw/hw.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" + +#endif diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h new file mode 100644 index 0000000000..791ab2a6de --- /dev/null +++ b/include/hw/s390x/event-facility.h @@ -0,0 +1,96 @@ +/* + * SCLP + * Event Facility definitions + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Heinz Graalfs + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#ifndef HW_S390_SCLP_EVENT_FACILITY_H +#define HW_S390_SCLP_EVENT_FACILITY_H + +#include +#include "qemu/thread.h" + +/* SCLP event types */ +#define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a +#define SCLP_EVENT_SIGNAL_QUIESCE 0x1d + +/* SCLP event masks */ +#define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008 +#define SCLP_EVENT_MASK_MSG_ASCII 0x00000040 + +#define SCLP_UNCONDITIONAL_READ 0x00 +#define SCLP_SELECTIVE_READ 0x01 + +#define TYPE_SCLP_EVENT "s390-sclp-event-type" +#define SCLP_EVENT(obj) \ + OBJECT_CHECK(SCLPEvent, (obj), TYPE_SCLP_EVENT) +#define SCLP_EVENT_CLASS(klass) \ + OBJECT_CLASS_CHECK(SCLPEventClass, (klass), TYPE_SCLP_EVENT) +#define SCLP_EVENT_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SCLPEventClass, (obj), TYPE_SCLP_EVENT) + +typedef struct WriteEventMask { + SCCBHeader h; + uint16_t _reserved; + uint16_t mask_length; + uint32_t cp_receive_mask; + uint32_t cp_send_mask; + uint32_t send_mask; + uint32_t receive_mask; +} QEMU_PACKED WriteEventMask; + +typedef struct EventBufferHeader { + uint16_t length; + uint8_t type; + uint8_t flags; + uint16_t _reserved; +} QEMU_PACKED EventBufferHeader; + +typedef struct WriteEventData { + SCCBHeader h; + EventBufferHeader ebh; +} QEMU_PACKED WriteEventData; + +typedef struct ReadEventData { + SCCBHeader h; + EventBufferHeader ebh; + uint32_t mask; +} QEMU_PACKED ReadEventData; + +typedef struct SCLPEvent { + DeviceState qdev; + bool event_pending; + uint32_t event_type; + char *name; +} SCLPEvent; + +typedef struct SCLPEventClass { + DeviceClass parent_class; + int (*init)(SCLPEvent *event); + int (*exit)(SCLPEvent *event); + + /* get SCLP's send mask */ + unsigned int (*get_send_mask)(void); + + /* get SCLP's receive mask */ + unsigned int (*get_receive_mask)(void); + + int (*read_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, + int *slen); + + int (*write_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr); + + /* returns the supported event type */ + int (*event_type)(void); + +} SCLPEventClass; + +#endif diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h new file mode 100644 index 0000000000..231a38aa09 --- /dev/null +++ b/include/hw/s390x/sclp.h @@ -0,0 +1,118 @@ +/* + * SCLP Support + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Christian Borntraeger + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#ifndef HW_S390_SCLP_H +#define HW_S390_SCLP_H + +#include +#include + +/* SCLP command codes */ +#define SCLP_CMDW_READ_SCP_INFO 0x00020001 +#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 +#define SCLP_CMD_READ_EVENT_DATA 0x00770005 +#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005 +#define SCLP_CMD_READ_EVENT_DATA 0x00770005 +#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005 +#define SCLP_CMD_WRITE_EVENT_MASK 0x00780005 + +/* SCLP response codes */ +#define SCLP_RC_NORMAL_READ_COMPLETION 0x0010 +#define SCLP_RC_NORMAL_COMPLETION 0x0020 +#define SCLP_RC_INVALID_SCLP_COMMAND 0x01f0 +#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340 +#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300 +#define SCLP_RC_INVALID_FUNCTION 0x40f0 +#define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0 +#define SCLP_RC_INVALID_SELECTION_MASK 0x70f0 +#define SCLP_RC_INCONSISTENT_LENGTHS 0x72f0 +#define SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR 0x73f0 +#define SCLP_RC_INVALID_MASK_LENGTH 0x74f0 + + +/* Service Call Control Block (SCCB) and its elements */ + +#define SCCB_SIZE 4096 + +#define SCLP_VARIABLE_LENGTH_RESPONSE 0x80 +#define SCLP_EVENT_BUFFER_ACCEPTED 0x80 + +#define SCLP_FC_NORMAL_WRITE 0 + +/* + * Normally packed structures are not the right thing to do, since all code + * must take care of endianness. We cannot use ldl_phys and friends for two + * reasons, though: + * - some of the embedded structures below the SCCB can appear multiple times + * at different locations, so there is no fixed offset + * - we work on a private copy of the SCCB, since there are several length + * fields, that would cause a security nightmare if we allow the guest to + * alter the structure while we parse it. We cannot use ldl_p and friends + * either without doing pointer arithmetics + * So we have to double check that all users of sclp data structures use the + * right endianness wrappers. + */ +typedef struct SCCBHeader { + uint16_t length; + uint8_t function_code; + uint8_t control_mask[3]; + uint16_t response_code; +} QEMU_PACKED SCCBHeader; + +#define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader)) + +typedef struct ReadInfo { + SCCBHeader h; + uint16_t rnmax; + uint8_t rnsize; +} QEMU_PACKED ReadInfo; + +typedef struct SCCB { + SCCBHeader h; + char data[SCCB_DATA_LEN]; + } QEMU_PACKED SCCB; + +static inline int sccb_data_len(SCCB *sccb) +{ + return be16_to_cpu(sccb->h.length) - sizeof(sccb->h); +} + +#define TYPE_DEVICE_S390_SCLP "s390-sclp-device" +#define SCLP_S390_DEVICE(obj) \ + OBJECT_CHECK(S390SCLPDevice, (obj), TYPE_DEVICE_S390_SCLP) +#define SCLP_S390_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(S390SCLPDeviceClass, (klass), \ + TYPE_DEVICE_S390_SCLP) +#define SCLP_S390_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(S390SCLPDeviceClass, (obj), \ + TYPE_DEVICE_S390_SCLP) + +typedef struct SCLPEventFacility SCLPEventFacility; + +typedef struct S390SCLPDevice { + SysBusDevice busdev; + SCLPEventFacility *ef; + void (*sclp_command_handler)(SCLPEventFacility *ef, SCCB *sccb, + uint64_t code); + bool (*event_pending)(SCLPEventFacility *ef); +} S390SCLPDevice; + +typedef struct S390SCLPDeviceClass { + DeviceClass qdev; + int (*init)(S390SCLPDevice *sdev); +} S390SCLPDeviceClass; + +void s390_sclp_init(void); +void sclp_service_interrupt(uint32_t sccb); + +#endif diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h new file mode 100644 index 0000000000..e079fb8d16 --- /dev/null +++ b/include/hw/scsi/esp.h @@ -0,0 +1,132 @@ +#ifndef QEMU_HW_ESP_H +#define QEMU_HW_ESP_H + +#include "hw/scsi/scsi.h" + +/* esp.c */ +#define ESP_MAX_DEVS 7 +typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); +void esp_init(hwaddr espaddr, int it_shift, + ESPDMAMemoryReadWriteFunc dma_memory_read, + ESPDMAMemoryReadWriteFunc dma_memory_write, + void *dma_opaque, qemu_irq irq, qemu_irq *reset, + qemu_irq *dma_enable); + +#define ESP_REGS 16 +#define TI_BUFSZ 16 + +typedef struct ESPState ESPState; + +struct ESPState { + uint8_t rregs[ESP_REGS]; + uint8_t wregs[ESP_REGS]; + qemu_irq irq; + uint8_t chip_id; + int32_t ti_size; + uint32_t ti_rptr, ti_wptr; + uint32_t status; + uint32_t dma; + uint8_t ti_buf[TI_BUFSZ]; + SCSIBus bus; + SCSIDevice *current_dev; + SCSIRequest *current_req; + uint8_t cmdbuf[TI_BUFSZ]; + uint32_t cmdlen; + uint32_t do_cmd; + + /* The amount of data left in the current DMA transfer. */ + uint32_t dma_left; + /* The size of the current DMA transfer. Zero if no transfer is in + progress. */ + uint32_t dma_counter; + int dma_enabled; + + uint32_t async_len; + uint8_t *async_buf; + + ESPDMAMemoryReadWriteFunc dma_memory_read; + ESPDMAMemoryReadWriteFunc dma_memory_write; + void *dma_opaque; + void (*dma_cb)(ESPState *s); +}; + +#define ESP_TCLO 0x0 +#define ESP_TCMID 0x1 +#define ESP_FIFO 0x2 +#define ESP_CMD 0x3 +#define ESP_RSTAT 0x4 +#define ESP_WBUSID 0x4 +#define ESP_RINTR 0x5 +#define ESP_WSEL 0x5 +#define ESP_RSEQ 0x6 +#define ESP_WSYNTP 0x6 +#define ESP_RFLAGS 0x7 +#define ESP_WSYNO 0x7 +#define ESP_CFG1 0x8 +#define ESP_RRES1 0x9 +#define ESP_WCCF 0x9 +#define ESP_RRES2 0xa +#define ESP_WTEST 0xa +#define ESP_CFG2 0xb +#define ESP_CFG3 0xc +#define ESP_RES3 0xd +#define ESP_TCHI 0xe +#define ESP_RES4 0xf + +#define CMD_DMA 0x80 +#define CMD_CMD 0x7f + +#define CMD_NOP 0x00 +#define CMD_FLUSH 0x01 +#define CMD_RESET 0x02 +#define CMD_BUSRESET 0x03 +#define CMD_TI 0x10 +#define CMD_ICCS 0x11 +#define CMD_MSGACC 0x12 +#define CMD_PAD 0x18 +#define CMD_SATN 0x1a +#define CMD_RSTATN 0x1b +#define CMD_SEL 0x41 +#define CMD_SELATN 0x42 +#define CMD_SELATNS 0x43 +#define CMD_ENSEL 0x44 +#define CMD_DISSEL 0x45 + +#define STAT_DO 0x00 +#define STAT_DI 0x01 +#define STAT_CD 0x02 +#define STAT_ST 0x03 +#define STAT_MO 0x06 +#define STAT_MI 0x07 +#define STAT_PIO_MASK 0x06 + +#define STAT_TC 0x10 +#define STAT_PE 0x20 +#define STAT_GE 0x40 +#define STAT_INT 0x80 + +#define BUSID_DID 0x07 + +#define INTR_FC 0x08 +#define INTR_BS 0x10 +#define INTR_DC 0x20 +#define INTR_RST 0x80 + +#define SEQ_0 0x0 +#define SEQ_CD 0x4 + +#define CFG1_RESREPT 0x40 + +#define TCHI_FAS100A 0x4 +#define TCHI_AM53C974 0x12 + +void esp_dma_enable(ESPState *s, int irq, int level); +void esp_request_cancelled(SCSIRequest *req); +void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid); +void esp_transfer_data(SCSIRequest *req, uint32_t len); +void esp_hard_reset(ESPState *s); +uint64_t esp_reg_read(ESPState *s, uint32_t saddr); +void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val); +extern const VMStateDescription vmstate_esp; + +#endif diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h new file mode 100644 index 0000000000..3bda1c42c1 --- /dev/null +++ b/include/hw/scsi/scsi.h @@ -0,0 +1,256 @@ +#ifndef QEMU_HW_SCSI_H +#define QEMU_HW_SCSI_H + +#include "hw/qdev.h" +#include "block/block.h" +#include "hw/block/block.h" +#include "sysemu/sysemu.h" + +#define MAX_SCSI_DEVS 255 + +#define SCSI_CMD_BUF_SIZE 16 + +typedef struct SCSIBus SCSIBus; +typedef struct SCSIBusInfo SCSIBusInfo; +typedef struct SCSICommand SCSICommand; +typedef struct SCSIDevice SCSIDevice; +typedef struct SCSIRequest SCSIRequest; +typedef struct SCSIReqOps SCSIReqOps; + +enum SCSIXferMode { + SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ + SCSI_XFER_FROM_DEV, /* READ, INQUIRY, MODE_SENSE, ... */ + SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ +}; + +typedef struct SCSISense { + uint8_t key; + uint8_t asc; + uint8_t ascq; +} SCSISense; + +#define SCSI_SENSE_BUF_SIZE 96 + +struct SCSICommand { + uint8_t buf[SCSI_CMD_BUF_SIZE]; + int len; + size_t xfer; + uint64_t lba; + enum SCSIXferMode mode; +}; + +struct SCSIRequest { + SCSIBus *bus; + SCSIDevice *dev; + const SCSIReqOps *ops; + uint32_t refcount; + uint32_t tag; + uint32_t lun; + uint32_t status; + size_t resid; + SCSICommand cmd; + BlockDriverAIOCB *aiocb; + QEMUSGList *sg; + bool dma_started; + uint8_t sense[SCSI_SENSE_BUF_SIZE]; + uint32_t sense_len; + bool enqueued; + bool io_canceled; + bool retry; + void *hba_private; + QTAILQ_ENTRY(SCSIRequest) next; +}; + +#define TYPE_SCSI_DEVICE "scsi-device" +#define SCSI_DEVICE(obj) \ + OBJECT_CHECK(SCSIDevice, (obj), TYPE_SCSI_DEVICE) +#define SCSI_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SCSIDeviceClass, (klass), TYPE_SCSI_DEVICE) +#define SCSI_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SCSIDeviceClass, (obj), TYPE_SCSI_DEVICE) + +typedef struct SCSIDeviceClass { + DeviceClass parent_class; + int (*init)(SCSIDevice *dev); + void (*destroy)(SCSIDevice *s); + SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun, + uint8_t *buf, void *hba_private); + void (*unit_attention_reported)(SCSIDevice *s); +} SCSIDeviceClass; + +struct SCSIDevice +{ + DeviceState qdev; + VMChangeStateEntry *vmsentry; + QEMUBH *bh; + uint32_t id; + BlockConf conf; + SCSISense unit_attention; + bool sense_is_ua; + uint8_t sense[SCSI_SENSE_BUF_SIZE]; + uint32_t sense_len; + QTAILQ_HEAD(, SCSIRequest) requests; + uint32_t channel; + uint32_t lun; + int blocksize; + int type; + uint64_t max_lba; +}; + +extern const VMStateDescription vmstate_scsi_device; + +#define VMSTATE_SCSI_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SCSIDevice), \ + .vmsd = &vmstate_scsi_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SCSIDevice), \ +} + +/* cdrom.c */ +int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track); +int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num); + +/* scsi-bus.c */ +struct SCSIReqOps { + size_t size; + void (*free_req)(SCSIRequest *req); + int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); + void (*read_data)(SCSIRequest *req); + void (*write_data)(SCSIRequest *req); + void (*cancel_io)(SCSIRequest *req); + uint8_t *(*get_buf)(SCSIRequest *req); + + void (*save_request)(QEMUFile *f, SCSIRequest *req); + void (*load_request)(QEMUFile *f, SCSIRequest *req); +}; + +struct SCSIBusInfo { + int tcq; + int max_channel, max_target, max_lun; + void (*transfer_data)(SCSIRequest *req, uint32_t arg); + void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid); + void (*cancel)(SCSIRequest *req); + void (*hotplug)(SCSIBus *bus, SCSIDevice *dev); + void (*hot_unplug)(SCSIBus *bus, SCSIDevice *dev); + void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense); + QEMUSGList *(*get_sg_list)(SCSIRequest *req); + + void (*save_request)(QEMUFile *f, SCSIRequest *req); + void *(*load_request)(QEMUFile *f, SCSIRequest *req); + void (*free_request)(SCSIBus *bus, void *priv); +}; + +#define TYPE_SCSI_BUS "SCSI" +#define SCSI_BUS(obj) OBJECT_CHECK(SCSIBus, (obj), TYPE_SCSI_BUS) + +struct SCSIBus { + BusState qbus; + int busnr; + + SCSISense unit_attention; + const SCSIBusInfo *info; +}; + +void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info); + +static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) +{ + return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); +} + +SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, + int unit, bool removable, int bootindex, + const char *serial); +int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); + +/* + * Predefined sense codes + */ + +/* No sense data available */ +extern const struct SCSISense sense_code_NO_SENSE; +/* LUN not ready, Manual intervention required */ +extern const struct SCSISense sense_code_LUN_NOT_READY; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_NO_MEDIUM; +/* LUN not ready, medium removal prevented */ +extern const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED; +/* Hardware error, internal target failure */ +extern const struct SCSISense sense_code_TARGET_FAILURE; +/* Illegal request, invalid command operation code */ +extern const struct SCSISense sense_code_INVALID_OPCODE; +/* Illegal request, LBA out of range */ +extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; +/* Illegal request, Invalid field in CDB */ +extern const struct SCSISense sense_code_INVALID_FIELD; +/* Illegal request, Invalid field in parameter list */ +extern const struct SCSISense sense_code_INVALID_PARAM; +/* Illegal request, Parameter list length error */ +extern const struct SCSISense sense_code_INVALID_PARAM_LEN; +/* Illegal request, LUN not supported */ +extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED; +/* Illegal request, Saving parameters not supported */ +extern const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED; +/* Illegal request, Incompatible format */ +extern const struct SCSISense sense_code_INCOMPATIBLE_FORMAT; +/* Illegal request, medium removal prevented */ +extern const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED; +/* Command aborted, I/O process terminated */ +extern const struct SCSISense sense_code_IO_ERROR; +/* Command aborted, I_T Nexus loss occurred */ +extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; +/* Command aborted, Logical Unit failure */ +extern const struct SCSISense sense_code_LUN_FAILURE; +/* LUN not ready, Capacity data has changed */ +extern const struct SCSISense sense_code_CAPACITY_CHANGED; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM; +/* Unit attention, Power on, reset or bus device reset occurred */ +extern const struct SCSISense sense_code_RESET; +/* Unit attention, Medium may have changed*/ +extern const struct SCSISense sense_code_MEDIUM_CHANGED; +/* Unit attention, Reported LUNs data has changed */ +extern const struct SCSISense sense_code_REPORTED_LUNS_CHANGED; +/* Unit attention, Device internal reset */ +extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET; +/* Data Protection, Write Protected */ +extern const struct SCSISense sense_code_WRITE_PROTECTED; + +#define SENSE_CODE(x) sense_code_ ## x + +uint32_t scsi_data_cdb_length(uint8_t *buf); +uint32_t scsi_cdb_length(uint8_t *buf); +int scsi_sense_valid(SCSISense sense); +int scsi_build_sense(uint8_t *in_buf, int in_len, + uint8_t *buf, int len, bool fixed); + +SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, + uint32_t tag, uint32_t lun, void *hba_private); +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + uint8_t *buf, void *hba_private); +int32_t scsi_req_enqueue(SCSIRequest *req); +void scsi_req_free(SCSIRequest *req); +SCSIRequest *scsi_req_ref(SCSIRequest *req); +void scsi_req_unref(SCSIRequest *req); + +void scsi_req_build_sense(SCSIRequest *req, SCSISense sense); +void scsi_req_print(SCSIRequest *req); +void scsi_req_continue(SCSIRequest *req); +void scsi_req_data(SCSIRequest *req, int len); +void scsi_req_complete(SCSIRequest *req, int status); +uint8_t *scsi_req_get_buf(SCSIRequest *req); +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); +void scsi_req_abort(SCSIRequest *req, int status); +void scsi_req_cancel(SCSIRequest *req); +void scsi_req_retry(SCSIRequest *req); +void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); +void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); +void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); +int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); +SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); + +/* scsi-generic.c. */ +extern const SCSIReqOps scsi_generic_req_ops; + +#endif diff --git a/include/hw/sd.h b/include/hw/sd.h new file mode 100644 index 0000000000..d9b97e4466 --- /dev/null +++ b/include/hw/sd.h @@ -0,0 +1,80 @@ +/* + * SD Memory Card emulation. Mostly correct for MMC too. + * + * Copyright (c) 2006 Andrzej Zaborowski + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __hw_sd_h +#define __hw_sd_h 1 + +#define OUT_OF_RANGE (1 << 31) +#define ADDRESS_ERROR (1 << 30) +#define BLOCK_LEN_ERROR (1 << 29) +#define ERASE_SEQ_ERROR (1 << 28) +#define ERASE_PARAM (1 << 27) +#define WP_VIOLATION (1 << 26) +#define CARD_IS_LOCKED (1 << 25) +#define LOCK_UNLOCK_FAILED (1 << 24) +#define COM_CRC_ERROR (1 << 23) +#define ILLEGAL_COMMAND (1 << 22) +#define CARD_ECC_FAILED (1 << 21) +#define CC_ERROR (1 << 20) +#define SD_ERROR (1 << 19) +#define CID_CSD_OVERWRITE (1 << 16) +#define WP_ERASE_SKIP (1 << 15) +#define CARD_ECC_DISABLED (1 << 14) +#define ERASE_RESET (1 << 13) +#define CURRENT_STATE (7 << 9) +#define READY_FOR_DATA (1 << 8) +#define APP_CMD (1 << 5) +#define AKE_SEQ_ERROR (1 << 3) +#define OCR_CCS_BITN 30 + +typedef enum { + sd_none = -1, + sd_bc = 0, /* broadcast -- no response */ + sd_bcr, /* broadcast with response */ + sd_ac, /* addressed -- no data transfer */ + sd_adtc, /* addressed with data transfer */ +} sd_cmd_type_t; + +typedef struct { + uint8_t cmd; + uint32_t arg; + uint8_t crc; +} SDRequest; + +typedef struct SDState SDState; + +SDState *sd_init(BlockDriverState *bs, bool is_spi); +int sd_do_command(SDState *sd, SDRequest *req, + uint8_t *response); +void sd_write_data(SDState *sd, uint8_t value); +uint8_t sd_read_data(SDState *sd); +void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert); +bool sd_data_ready(SDState *sd); +void sd_enable(SDState *sd, bool enable); + +#endif /* __hw_sd_h */ diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h new file mode 100644 index 0000000000..87c378f1eb --- /dev/null +++ b/include/hw/sh4/sh.h @@ -0,0 +1,57 @@ +#ifndef QEMU_SH_H +#define QEMU_SH_H +/* Definitions for SH board emulation. */ + +#include "hw/sh4/sh_intc.h" + +#define A7ADDR(x) ((x) & 0x1fffffff) +#define P4ADDR(x) ((x) | 0xe0000000) + +/* sh7750.c */ +struct SH7750State; +struct MemoryRegion; + +struct SH7750State *sh7750_init(CPUSH4State * cpu, struct MemoryRegion *sysmem); + +typedef struct { + /* The callback will be triggered if any of the designated lines change */ + uint16_t portamask_trigger; + uint16_t portbmask_trigger; + /* Return 0 if no action was taken */ + int (*port_change_cb) (uint16_t porta, uint16_t portb, + uint16_t * periph_pdtra, + uint16_t * periph_portdira, + uint16_t * periph_pdtrb, + uint16_t * periph_portdirb); +} sh7750_io_device; + +int sh7750_register_io_device(struct SH7750State *s, + sh7750_io_device * device); +/* sh_timer.c */ +#define TMU012_FEAT_TOCR (1 << 0) +#define TMU012_FEAT_3CHAN (1 << 1) +#define TMU012_FEAT_EXTCLK (1 << 2) +void tmu012_init(struct MemoryRegion *sysmem, hwaddr base, + int feat, uint32_t freq, + qemu_irq ch0_irq, qemu_irq ch1_irq, + qemu_irq ch2_irq0, qemu_irq ch2_irq1); + + +/* sh_serial.c */ +#define SH_SERIAL_FEAT_SCIF (1 << 0) +void sh_serial_init(MemoryRegion *sysmem, + hwaddr base, int feat, + uint32_t freq, CharDriverState *chr, + qemu_irq eri_source, + qemu_irq rxi_source, + qemu_irq txi_source, + qemu_irq tei_source, + qemu_irq bri_source); + +/* sh7750.c */ +qemu_irq sh7750_irl(struct SH7750State *s); + +/* tc58128.c */ +int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2); + +#endif diff --git a/include/hw/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h new file mode 100644 index 0000000000..b7ddcb096a --- /dev/null +++ b/include/hw/sh4/sh_intc.h @@ -0,0 +1,83 @@ +#ifndef __SH_INTC_H__ +#define __SH_INTC_H__ + +#include "qemu-common.h" +#include "hw/irq.h" +#include "exec/address-spaces.h" + +typedef unsigned char intc_enum; + +struct intc_vect { + intc_enum enum_id; + unsigned short vect; +}; + +#define INTC_VECT(enum_id, vect) { enum_id, vect } + +struct intc_group { + intc_enum enum_id; + intc_enum enum_ids[32]; +}; + +#define INTC_GROUP(enum_id, ...) { enum_id, { __VA_ARGS__ } } + +struct intc_mask_reg { + unsigned long set_reg, clr_reg, reg_width; + intc_enum enum_ids[32]; + unsigned long value; +}; + +struct intc_prio_reg { + unsigned long set_reg, clr_reg, reg_width, field_width; + intc_enum enum_ids[16]; + unsigned long value; +}; + +#define _INTC_ARRAY(a) a, ARRAY_SIZE(a) + +struct intc_source { + unsigned short vect; + intc_enum next_enum_id; + + int asserted; /* emulates the interrupt signal line from device to intc */ + int enable_count; + int enable_max; + int pending; /* emulates the result of signal and masking */ + struct intc_desc *parent; +}; + +struct intc_desc { + MemoryRegion iomem; + MemoryRegion *iomem_aliases; + qemu_irq *irqs; + struct intc_source *sources; + int nr_sources; + struct intc_mask_reg *mask_regs; + int nr_mask_regs; + struct intc_prio_reg *prio_regs; + int nr_prio_regs; + int pending; /* number of interrupt sources that has pending set */ +}; + +int sh_intc_get_pending_vector(struct intc_desc *desc, int imask); +struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id); +void sh_intc_toggle_source(struct intc_source *source, + int enable_adj, int assert_adj); + +void sh_intc_register_sources(struct intc_desc *desc, + struct intc_vect *vectors, + int nr_vectors, + struct intc_group *groups, + int nr_groups); + +int sh_intc_init(MemoryRegion *sysmem, + struct intc_desc *desc, + int nr_sources, + struct intc_mask_reg *mask_regs, + int nr_mask_regs, + struct intc_prio_reg *prio_regs, + int nr_prio_regs); + +void sh_intc_set_irl(void *opaque, int n, int level); + +#endif /* __SH_INTC_H__ */ diff --git a/include/hw/sparc/firmware_abi.h b/include/hw/sparc/firmware_abi.h new file mode 100644 index 0000000000..5e6e5d4d34 --- /dev/null +++ b/include/hw/sparc/firmware_abi.h @@ -0,0 +1,73 @@ +#ifndef FIRMWARE_ABI_H +#define FIRMWARE_ABI_H + +/* OpenBIOS NVRAM partition */ +struct OpenBIOS_nvpart_v1 { + uint8_t signature; + uint8_t checksum; + uint16_t len; // BE, length divided by 16 + char name[12]; +}; + +#define OPENBIOS_PART_SYSTEM 0x70 +#define OPENBIOS_PART_FREE 0x7f + +static inline void +OpenBIOS_finish_partition(struct OpenBIOS_nvpart_v1 *header, uint32_t size) +{ + unsigned int i, sum; + uint8_t *tmpptr; + + // Length divided by 16 + header->len = cpu_to_be16(size >> 4); + + // Checksum + tmpptr = (uint8_t *)header; + sum = *tmpptr; + for (i = 0; i < 14; i++) { + sum += tmpptr[2 + i]; + sum = (sum + ((sum & 0xff00) >> 8)) & 0xff; + } + header->checksum = sum & 0xff; +} + +static inline uint32_t +OpenBIOS_set_var(uint8_t *nvram, uint32_t addr, const char *str) +{ + uint32_t len; + + len = strlen(str) + 1; + memcpy(&nvram[addr], str, len); + + return addr + len; +} + +/* Sun IDPROM structure at the end of NVRAM */ +/* from http://www.squirrel.com/squirrel/sun-nvram-hostid.faq.html */ +struct Sun_nvram { + uint8_t type; /* always 01 */ + uint8_t machine_id; /* first byte of host id (machine type) */ + uint8_t macaddr[6]; /* 6 byte ethernet address (first 3 bytes 08, 00, 20) */ + uint8_t date[4]; /* date of manufacture */ + uint8_t hostid[3]; /* remaining 3 bytes of host id (serial number) */ + uint8_t checksum; /* bitwise xor of previous bytes */ +}; + +static inline void +Sun_init_header(struct Sun_nvram *header, const uint8_t *macaddr, int machine_id) +{ + uint8_t tmp, *tmpptr; + unsigned int i; + + header->type = 1; + header->machine_id = machine_id & 0xff; + memcpy(&header->macaddr, macaddr, 6); + /* Calculate checksum */ + tmp = 0; + tmpptr = (uint8_t *)header; + for (i = 0; i < 15; i++) + tmp ^= tmpptr[i]; + + header->checksum = tmp; +} +#endif /* FIRMWARE_ABI_H */ diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h new file mode 100644 index 0000000000..470ce72250 --- /dev/null +++ b/include/hw/sparc/grlib.h @@ -0,0 +1,126 @@ +/* + * QEMU GRLIB Components + * + * Copyright (c) 2010-2011 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _GRLIB_H_ +#define _GRLIB_H_ + +#include "hw/qdev.h" +#include "hw/sysbus.h" + +/* Emulation of GrLib device is base on the GRLIB IP Core User's Manual: + * http://www.gaisler.com/products/grlib/grip.pdf + */ + +/* IRQMP */ + +typedef void (*set_pil_in_fn) (void *opaque, uint32_t pil_in); + +void grlib_irqmp_set_irq(void *opaque, int irq, int level); + +void grlib_irqmp_ack(DeviceState *dev, int intno); + +static inline +DeviceState *grlib_irqmp_create(hwaddr base, + CPUSPARCState *env, + qemu_irq **cpu_irqs, + uint32_t nr_irqs, + set_pil_in_fn set_pil_in) +{ + DeviceState *dev; + + assert(cpu_irqs != NULL); + + dev = qdev_create(NULL, "grlib,irqmp"); + qdev_prop_set_ptr(dev, "set_pil_in", set_pil_in); + qdev_prop_set_ptr(dev, "set_pil_in_opaque", env); + + if (qdev_init(dev)) { + return NULL; + } + + env->irq_manager = dev; + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq, + dev, + nr_irqs); + + return dev; +} + +/* GPTimer */ + +static inline +DeviceState *grlib_gptimer_create(hwaddr base, + uint32_t nr_timers, + uint32_t freq, + qemu_irq *cpu_irqs, + int base_irq) +{ + DeviceState *dev; + int i; + + dev = qdev_create(NULL, "grlib,gptimer"); + qdev_prop_set_uint32(dev, "nr-timers", nr_timers); + qdev_prop_set_uint32(dev, "frequency", freq); + qdev_prop_set_uint32(dev, "irq-line", base_irq); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + for (i = 0; i < nr_timers; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, cpu_irqs[base_irq + i]); + } + + return dev; +} + +/* APB UART */ + +static inline +DeviceState *grlib_apbuart_create(hwaddr base, + CharDriverState *serial, + qemu_irq irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "grlib,apbuart"); + qdev_prop_set_chr(dev, "chrdev", serial); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + + return dev; +} + +#endif /* ! _GRLIB_H_ */ diff --git a/include/hw/sparc/sparc32_dma.h b/include/hw/sparc/sparc32_dma.h new file mode 100644 index 0000000000..9497b13d34 --- /dev/null +++ b/include/hw/sparc/sparc32_dma.h @@ -0,0 +1,12 @@ +#ifndef SPARC32_DMA_H +#define SPARC32_DMA_H + +/* sparc32_dma.c */ +void ledma_memory_read(void *opaque, hwaddr addr, + uint8_t *buf, int len, int do_bswap); +void ledma_memory_write(void *opaque, hwaddr addr, + uint8_t *buf, int len, int do_bswap); +void espdma_memory_read(void *opaque, uint8_t *buf, int len); +void espdma_memory_write(void *opaque, uint8_t *buf, int len); + +#endif diff --git a/include/hw/sparc/sun4m.h b/include/hw/sparc/sun4m.h new file mode 100644 index 0000000000..e984671129 --- /dev/null +++ b/include/hw/sparc/sun4m.h @@ -0,0 +1,36 @@ +#ifndef SUN4M_H +#define SUN4M_H + +#include "qemu-common.h" + +/* Devices used by sparc32 system. */ + +/* iommu.c */ +void sparc_iommu_memory_rw(void *opaque, hwaddr addr, + uint8_t *buf, int len, int is_write); +static inline void sparc_iommu_memory_read(void *opaque, + hwaddr addr, + uint8_t *buf, int len) +{ + sparc_iommu_memory_rw(opaque, addr, buf, len, 0); +} + +static inline void sparc_iommu_memory_write(void *opaque, + hwaddr addr, + uint8_t *buf, int len) +{ + sparc_iommu_memory_rw(opaque, addr, buf, len, 1); +} + +/* slavio_intctl.c */ +void slavio_pic_info(Monitor *mon, DeviceState *dev); +void slavio_irq_info(Monitor *mon, DeviceState *dev); + +/* sun4m.c */ +void sun4m_pic_info(Monitor *mon, const QDict *qdict); +void sun4m_irq_info(Monitor *mon, const QDict *qdict); + +/* sparc32_dma.c */ +#include "hw/sparc/sparc32_dma.h" + +#endif diff --git a/include/hw/ssi.h b/include/hw/ssi.h new file mode 100644 index 0000000000..fdae317295 --- /dev/null +++ b/include/hw/ssi.h @@ -0,0 +1,93 @@ +/* QEMU Synchronous Serial Interface support. */ + +/* In principle SSI is a point-point interface. As such the qemu + implementation has a single slave device on a "bus". + However it is fairly common for boards to have multiple slaves + connected to a single master, and select devices with an external + chip select. This is implemented in qemu by having an explicit mux device. + It is assumed that master and slave are both using the same transfer width. + */ + +#ifndef QEMU_SSI_H +#define QEMU_SSI_H + +#include "hw/qdev.h" + +typedef struct SSISlave SSISlave; + +#define TYPE_SSI_SLAVE "ssi-slave" +#define SSI_SLAVE(obj) \ + OBJECT_CHECK(SSISlave, (obj), TYPE_SSI_SLAVE) +#define SSI_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SSISlaveClass, (klass), TYPE_SSI_SLAVE) +#define SSI_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SSISlaveClass, (obj), TYPE_SSI_SLAVE) + +typedef enum { + SSI_CS_NONE = 0, + SSI_CS_LOW, + SSI_CS_HIGH, +} SSICSMode; + +/* Slave devices. */ +typedef struct SSISlaveClass { + DeviceClass parent_class; + + int (*init)(SSISlave *dev); + + /* if you have standard or no CS behaviour, just override transfer. + * This is called when the device cs is active (true by default). + */ + uint32_t (*transfer)(SSISlave *dev, uint32_t val); + /* called when the CS line changes. Optional, devices only need to implement + * this if they have side effects associated with the cs line (beyond + * tristating the txrx lines). + */ + int (*set_cs)(SSISlave *dev, bool select); + /* define whether or not CS exists and is active low/high */ + SSICSMode cs_polarity; + + /* if you have non-standard CS behaviour override this to take control + * of the CS behaviour at the device level. transfer, set_cs, and + * cs_polarity are unused if this is overwritten. Transfer_raw will + * always be called for the device for every txrx access to the parent bus + */ + uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val); +} SSISlaveClass; + +struct SSISlave { + DeviceState qdev; + + /* Chip select state */ + bool cs; +}; + +#define SSI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SSISlave, qdev, dev) +#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev) + +extern const VMStateDescription vmstate_ssi_slave; + +#define VMSTATE_SSI_SLAVE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SSISlave), \ + .vmsd = &vmstate_ssi_slave, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SSISlave), \ +} + +DeviceState *ssi_create_slave(SSIBus *bus, const char *name); +DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name); + +/* Master interface. */ +SSIBus *ssi_create_bus(DeviceState *parent, const char *name); + +uint32_t ssi_transfer(SSIBus *bus, uint32_t val); + +/* Automatically connect all children nodes a spi controller as slaves */ +void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_lines, + SSIBus *bus); + +/* max111x.c */ +void max111x_set_input(DeviceState *dev, int line, uint8_t value); + +#endif diff --git a/include/hw/stream.h b/include/hw/stream.h new file mode 100644 index 0000000000..f6137d6e25 --- /dev/null +++ b/include/hw/stream.h @@ -0,0 +1,31 @@ +#ifndef STREAM_H +#define STREAM_H 1 + +#include "qemu-common.h" +#include "qom/object.h" + +/* stream slave. Used until qdev provides a generic way. */ +#define TYPE_STREAM_SLAVE "stream-slave" + +#define STREAM_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(StreamSlaveClass, (klass), TYPE_STREAM_SLAVE) +#define STREAM_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(StreamSlaveClass, (obj), TYPE_STREAM_SLAVE) +#define STREAM_SLAVE(obj) \ + INTERFACE_CHECK(StreamSlave, (obj), TYPE_STREAM_SLAVE) + +typedef struct StreamSlave { + Object Parent; +} StreamSlave; + +typedef struct StreamSlaveClass { + InterfaceClass parent; + + void (*push)(StreamSlave *obj, unsigned char *buf, size_t len, + uint32_t *app); +} StreamSlaveClass; + +void +stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app); + +#endif /* STREAM_H */ diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h new file mode 100644 index 0000000000..7c2e3163fb --- /dev/null +++ b/include/hw/sysbus.h @@ -0,0 +1,85 @@ +#ifndef HW_SYSBUS_H +#define HW_SYSBUS_H 1 + +/* Devices attached directly to the main system bus. */ + +#include "hw/qdev.h" +#include "exec/memory.h" + +#define QDEV_MAX_MMIO 32 +#define QDEV_MAX_PIO 32 +#define QDEV_MAX_IRQ 512 + +#define TYPE_SYSTEM_BUS "System" +#define SYSTEM_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS) + +typedef struct SysBusDevice SysBusDevice; + +#define TYPE_SYS_BUS_DEVICE "sys-bus-device" +#define SYS_BUS_DEVICE(obj) \ + OBJECT_CHECK(SysBusDevice, (obj), TYPE_SYS_BUS_DEVICE) +#define SYS_BUS_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SysBusDeviceClass, (klass), TYPE_SYS_BUS_DEVICE) +#define SYS_BUS_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SysBusDeviceClass, (obj), TYPE_SYS_BUS_DEVICE) + +typedef struct SysBusDeviceClass { + DeviceClass parent_class; + + int (*init)(SysBusDevice *dev); +} SysBusDeviceClass; + +struct SysBusDevice { + DeviceState qdev; + int num_irq; + qemu_irq irqs[QDEV_MAX_IRQ]; + qemu_irq *irqp[QDEV_MAX_IRQ]; + int num_mmio; + struct { + hwaddr addr; + MemoryRegion *memory; + } mmio[QDEV_MAX_MMIO]; + int num_pio; + pio_addr_t pio[QDEV_MAX_PIO]; +}; + +/* Macros to compensate for lack of type inheritance in C. */ +#define FROM_SYSBUS(type, dev) DO_UPCAST(type, busdev, dev) + +void *sysbus_new(void); +void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory); +MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n); +void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p); +void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target); +void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); + + +void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); +void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); +void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, + unsigned priority); +void sysbus_add_io(SysBusDevice *dev, hwaddr addr, + MemoryRegion *mem); +void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem); +MemoryRegion *sysbus_address_space(SysBusDevice *dev); + +/* Legacy helper function for creating devices. */ +DeviceState *sysbus_create_varargs(const char *name, + hwaddr addr, ...); +DeviceState *sysbus_try_create_varargs(const char *name, + hwaddr addr, ...); +static inline DeviceState *sysbus_create_simple(const char *name, + hwaddr addr, + qemu_irq irq) +{ + return sysbus_create_varargs(name, addr, irq, NULL); +} + +static inline DeviceState *sysbus_try_create_simple(const char *name, + hwaddr addr, + qemu_irq irq) +{ + return sysbus_try_create_varargs(name, addr, irq, NULL); +} + +#endif /* !HW_SYSBUS_H */ diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h new file mode 100644 index 0000000000..757f79fdd2 --- /dev/null +++ b/include/hw/timer/hpet.h @@ -0,0 +1,74 @@ +/* + * QEMU Emulated HPET support + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Beth Kon + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef QEMU_HPET_EMUL_H +#define QEMU_HPET_EMUL_H + +#define HPET_BASE 0xfed00000 +#define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/ + +#define FS_PER_NS 1000000 +#define HPET_MIN_TIMERS 3 +#define HPET_MAX_TIMERS 32 + +#define HPET_NUM_IRQ_ROUTES 32 + +#define HPET_LEGACY_PIT_INT 0 +#define HPET_LEGACY_RTC_INT 1 + +#define HPET_CFG_ENABLE 0x001 +#define HPET_CFG_LEGACY 0x002 + +#define HPET_ID 0x000 +#define HPET_PERIOD 0x004 +#define HPET_CFG 0x010 +#define HPET_STATUS 0x020 +#define HPET_COUNTER 0x0f0 +#define HPET_TN_CFG 0x000 +#define HPET_TN_CMP 0x008 +#define HPET_TN_ROUTE 0x010 +#define HPET_CFG_WRITE_MASK 0x3 + +#define HPET_ID_NUM_TIM_SHIFT 8 +#define HPET_ID_NUM_TIM_MASK 0x1f00 + +#define HPET_TN_TYPE_LEVEL 0x002 +#define HPET_TN_ENABLE 0x004 +#define HPET_TN_PERIODIC 0x008 +#define HPET_TN_PERIODIC_CAP 0x010 +#define HPET_TN_SIZE_CAP 0x020 +#define HPET_TN_SETVAL 0x040 +#define HPET_TN_32BIT 0x100 +#define HPET_TN_INT_ROUTE_MASK 0x3e00 +#define HPET_TN_FSB_ENABLE 0x4000 +#define HPET_TN_FSB_CAP 0x8000 +#define HPET_TN_CFG_WRITE_MASK 0x7f4e +#define HPET_TN_INT_ROUTE_SHIFT 9 +#define HPET_TN_INT_ROUTE_CAP_SHIFT 32 +#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U + +struct hpet_fw_entry +{ + uint32_t event_timer_block_id; + uint64_t address; + uint16_t min_tick; + uint8_t page_prot; +} QEMU_PACKED; + +struct hpet_fw_config +{ + uint8_t count; + struct hpet_fw_entry hpet[8]; +} QEMU_PACKED; + +extern struct hpet_fw_config hpet_cfg; +#endif diff --git a/include/hw/timer/i8254.h b/include/hw/timer/i8254.h new file mode 100644 index 0000000000..75bb530ad9 --- /dev/null +++ b/include/hw/timer/i8254.h @@ -0,0 +1,68 @@ +/* + * QEMU 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_I8254_H +#define HW_I8254_H + +#include "hw/hw.h" +#include "hw/isa/isa.h" + +#define PIT_FREQ 1193182 + +typedef struct PITChannelInfo { + int gate; + int mode; + int initial_count; + int out; +} PITChannelInfo; + +static inline ISADevice *pit_init(ISABus *bus, int base, int isa_irq, + qemu_irq alt_irq) +{ + ISADevice *dev; + + dev = isa_create(bus, "isa-pit"); + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_init_nofail(&dev->qdev); + qdev_connect_gpio_out(&dev->qdev, 0, + isa_irq >= 0 ? isa_get_irq(dev, isa_irq) : alt_irq); + + return dev; +} + +static inline ISADevice *kvm_pit_init(ISABus *bus, int base) +{ + ISADevice *dev; + + dev = isa_create(bus, "kvm-pit"); + qdev_prop_set_uint32(&dev->qdev, "iobase", base); + qdev_init_nofail(&dev->qdev); + + return dev; +} + +void pit_set_gate(ISADevice *dev, int channel, int val); +void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info); + +#endif /* !HW_I8254_H */ diff --git a/include/hw/timer/i8254_internal.h b/include/hw/timer/i8254_internal.h new file mode 100644 index 0000000000..e0cff0cf5a --- /dev/null +++ b/include/hw/timer/i8254_internal.h @@ -0,0 +1,85 @@ +/* + * QEMU 8253/8254 - internal interfaces + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_I8254_INTERNAL_H +#define QEMU_I8254_INTERNAL_H + +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/isa/isa.h" + +typedef struct PITChannelState { + int count; /* can be 65536 */ + uint16_t latched_count; + uint8_t count_latched; + uint8_t status_latched; + uint8_t status; + uint8_t read_state; + uint8_t write_state; + uint8_t write_latch; + uint8_t rw_mode; + uint8_t mode; + uint8_t bcd; /* not supported */ + uint8_t gate; /* timer start */ + int64_t count_load_time; + /* irq handling */ + int64_t next_transition_time; + QEMUTimer *irq_timer; + qemu_irq irq; + uint32_t irq_disabled; +} PITChannelState; + +typedef struct PITCommonState { + ISADevice dev; + MemoryRegion ioports; + uint32_t iobase; + PITChannelState channels[3]; +} PITCommonState; + +#define TYPE_PIT_COMMON "pit-common" +#define PIT_COMMON(obj) \ + OBJECT_CHECK(PITCommonState, (obj), TYPE_PIT_COMMON) +#define PIT_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(PITCommonClass, (klass), TYPE_PIT_COMMON) +#define PIT_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PITCommonClass, (obj), TYPE_PIT_COMMON) + +typedef struct PITCommonClass { + ISADeviceClass parent_class; + + int (*init)(PITCommonState *s); + void (*set_channel_gate)(PITCommonState *s, PITChannelState *sc, int val); + void (*get_channel_info)(PITCommonState *s, PITChannelState *sc, + PITChannelInfo *info); + void (*pre_save)(PITCommonState *s); + void (*post_load)(PITCommonState *s); +} PITCommonClass; + +int pit_get_out(PITChannelState *s, int64_t current_time); +int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time); +void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc, + PITChannelInfo *info); +void pit_reset_common(PITCommonState *s); + +#endif /* !QEMU_I8254_INTERNAL_H */ diff --git a/include/hw/timer/m48t59.h b/include/hw/timer/m48t59.h new file mode 100644 index 0000000000..59337faaad --- /dev/null +++ b/include/hw/timer/m48t59.h @@ -0,0 +1,34 @@ +#ifndef NVRAM_H +#define NVRAM_H + +/* NVRAM helpers */ +typedef uint32_t (*nvram_read_t)(void *private, uint32_t addr); +typedef void (*nvram_write_t)(void *private, uint32_t addr, uint32_t val); +typedef struct nvram_t { + void *opaque; + nvram_read_t read_fn; + nvram_write_t write_fn; +} nvram_t; + +uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr); +int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max); + +int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, + const char *arch, + uint32_t RAM_size, int boot_device, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth); +typedef struct M48t59State M48t59State; + +void m48t59_write (void *private, uint32_t addr, uint32_t val); +uint32_t m48t59_read (void *private, uint32_t addr); +void m48t59_toggle_lock (void *private, int lock); +M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int type); +M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base, + uint32_t io_base, uint16_t size, int type); + +#endif /* !NVRAM_H */ diff --git a/include/hw/timer/mc146818rtc.h b/include/hw/timer/mc146818rtc.h new file mode 100644 index 0000000000..854ea3f5b5 --- /dev/null +++ b/include/hw/timer/mc146818rtc.h @@ -0,0 +1,11 @@ +#ifndef MC146818RTC_H +#define MC146818RTC_H + +#include "hw/isa/isa.h" +#include "hw/timer/mc146818rtc_regs.h" + +ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); +void rtc_set_memory(ISADevice *dev, int addr, int val); +void rtc_set_date(ISADevice *dev, const struct tm *tm); + +#endif /* !MC146818RTC_H */ diff --git a/include/hw/timer/mc146818rtc_regs.h b/include/hw/timer/mc146818rtc_regs.h new file mode 100644 index 0000000000..ccdee42b3c --- /dev/null +++ b/include/hw/timer/mc146818rtc_regs.h @@ -0,0 +1,67 @@ +/* + * QEMU MC146818 RTC emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef RTC_REGS_H +#define RTC_REGS_H + +#define RTC_ISA_IRQ 8 + +#define RTC_SECONDS 0 +#define RTC_SECONDS_ALARM 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +#define RTC_ALARM_DONT_CARE 0xC0 + +#define RTC_DAY_OF_WEEK 6 +#define RTC_DAY_OF_MONTH 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 + +#define RTC_REG_A 10 +#define RTC_REG_B 11 +#define RTC_REG_C 12 +#define RTC_REG_D 13 + +/* PC cmos mappings */ +#define RTC_CENTURY 0x32 +#define RTC_IBM_PS2_CENTURY_BYTE 0x37 + +#define REG_A_UIP 0x80 + +#define REG_B_SET 0x80 +#define REG_B_PIE 0x40 +#define REG_B_AIE 0x20 +#define REG_B_UIE 0x10 +#define REG_B_SQWE 0x08 +#define REG_B_DM 0x04 +#define REG_B_24H 0x02 + +#define REG_C_UF 0x10 +#define REG_C_IRQF 0x80 +#define REG_C_PF 0x40 +#define REG_C_AF 0x20 +#define REG_C_MASK 0x70 + +#endif diff --git a/include/hw/unicore32/puv3.h b/include/hw/unicore32/puv3.h new file mode 100644 index 0000000000..f37adcb665 --- /dev/null +++ b/include/hw/unicore32/puv3.h @@ -0,0 +1,49 @@ +/* + * Misc PKUnity SoC declarations + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * 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, or any later version. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_HW_PUV3_H +#define QEMU_HW_PUV3_H + +#define PUV3_REGS_OFFSET (0x1000) /* 4K is reasonable */ + +/* PKUnity System bus (AHB): 0xc0000000 - 0xedffffff (640MB) */ +#define PUV3_DMA_BASE (0xc0200000) /* AHB-4 */ + +/* PKUnity Peripheral bus (APB): 0xee000000 - 0xefffffff (128MB) */ +#define PUV3_GPIO_BASE (0xee500000) /* APB-5 */ +#define PUV3_INTC_BASE (0xee600000) /* APB-6 */ +#define PUV3_OST_BASE (0xee800000) /* APB-8 */ +#define PUV3_PM_BASE (0xeea00000) /* APB-10 */ +#define PUV3_PS2_BASE (0xeeb00000) /* APB-11 */ + +/* Hardware interrupts */ +#define PUV3_IRQS_NR (32) + +#define PUV3_IRQS_GPIOLOW0 (0) +#define PUV3_IRQS_GPIOLOW1 (1) +#define PUV3_IRQS_GPIOLOW2 (2) +#define PUV3_IRQS_GPIOLOW3 (3) +#define PUV3_IRQS_GPIOLOW4 (4) +#define PUV3_IRQS_GPIOLOW5 (5) +#define PUV3_IRQS_GPIOLOW6 (6) +#define PUV3_IRQS_GPIOLOW7 (7) +#define PUV3_IRQS_GPIOHIGH (8) +#define PUV3_IRQS_PS2_KBD (22) +#define PUV3_IRQS_PS2_AUX (23) +#define PUV3_IRQS_OST0 (26) + +/* All puv3_*.c use DPRINTF for debug. */ +#ifdef DEBUG_PUV3 +#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#endif /* !QEMU_HW_PUV3_H */ diff --git a/include/hw/usb.h b/include/hw/usb.h new file mode 100644 index 0000000000..1b10684dde --- /dev/null +++ b/include/hw/usb.h @@ -0,0 +1,570 @@ +#ifndef QEMU_USB_H +#define QEMU_USB_H + +/* + * QEMU USB API + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/qdev.h" +#include "qemu/queue.h" + +/* Constants related to the USB / PCI interaction */ +#define USB_SBRN 0x60 /* Serial Bus Release Number Register */ +#define USB_RELEASE_1 0x10 /* USB 1.0 */ +#define USB_RELEASE_2 0x20 /* USB 2.0 */ +#define USB_RELEASE_3 0x30 /* USB 3.0 */ + +#define USB_TOKEN_SETUP 0x2d +#define USB_TOKEN_IN 0x69 /* device -> host */ +#define USB_TOKEN_OUT 0xe1 /* host -> device */ + +#define USB_RET_SUCCESS (0) +#define USB_RET_NODEV (-1) +#define USB_RET_NAK (-2) +#define USB_RET_STALL (-3) +#define USB_RET_BABBLE (-4) +#define USB_RET_IOERROR (-5) +#define USB_RET_ASYNC (-6) +#define USB_RET_ADD_TO_QUEUE (-7) +#define USB_RET_REMOVE_FROM_QUEUE (-8) + +#define USB_SPEED_LOW 0 +#define USB_SPEED_FULL 1 +#define USB_SPEED_HIGH 2 +#define USB_SPEED_SUPER 3 + +#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW) +#define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL) +#define USB_SPEED_MASK_HIGH (1 << USB_SPEED_HIGH) +#define USB_SPEED_MASK_SUPER (1 << USB_SPEED_SUPER) + +#define USB_STATE_NOTATTACHED 0 +#define USB_STATE_ATTACHED 1 +//#define USB_STATE_POWERED 2 +#define USB_STATE_DEFAULT 3 +//#define USB_STATE_ADDRESS 4 +//#define USB_STATE_CONFIGURED 5 +#define USB_STATE_SUSPENDED 6 + +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b +#define USB_CLASS_CONTENT_SEC 0x0d +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +#define USB_SUBCLASS_UNDEFINED 0 +#define USB_SUBCLASS_AUDIO_CONTROL 1 +#define USB_SUBCLASS_AUDIO_STREAMING 2 +#define USB_SUBCLASS_AUDIO_MIDISTREAMING 3 + +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define InterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define InterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define ClassInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define ClassInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_DEVICE_SELF_POWERED 0 +#define USB_DEVICE_REMOTE_WAKEUP 1 + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_DEBUG 0x0A +#define USB_DT_INTERFACE_ASSOC 0x0B +#define USB_DT_BOS 0x0F +#define USB_DT_DEVICE_CAPABILITY 0x10 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 +#define USB_DT_ENDPOINT_COMPANION 0x30 + +#define USB_DEV_CAP_WIRELESS 0x01 +#define USB_DEV_CAP_USB2_EXT 0x02 +#define USB_DEV_CAP_SUPERSPEED 0x03 + +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_XFER_INVALID 255 + +#define USB_INTERFACE_INVALID 255 + +typedef struct USBBus USBBus; +typedef struct USBBusOps USBBusOps; +typedef struct USBPort USBPort; +typedef struct USBDevice USBDevice; +typedef struct USBPacket USBPacket; +typedef struct USBCombinedPacket USBCombinedPacket; +typedef struct USBEndpoint USBEndpoint; + +typedef struct USBDesc USBDesc; +typedef struct USBDescID USBDescID; +typedef struct USBDescDevice USBDescDevice; +typedef struct USBDescConfig USBDescConfig; +typedef struct USBDescIfaceAssoc USBDescIfaceAssoc; +typedef struct USBDescIface USBDescIface; +typedef struct USBDescEndpoint USBDescEndpoint; +typedef struct USBDescOther USBDescOther; +typedef struct USBDescString USBDescString; + +struct USBDescString { + uint8_t index; + char *str; + QLIST_ENTRY(USBDescString) next; +}; + +#define USB_MAX_ENDPOINTS 15 +#define USB_MAX_INTERFACES 16 + +struct USBEndpoint { + uint8_t nr; + uint8_t pid; + uint8_t type; + uint8_t ifnum; + int max_packet_size; + bool pipeline; + bool halted; + USBDevice *dev; + QTAILQ_HEAD(, USBPacket) queue; +}; + +enum USBDeviceFlags { + USB_DEV_FLAG_FULL_PATH, + USB_DEV_FLAG_IS_HOST, +}; + +/* definition of a USB device */ +struct USBDevice { + DeviceState qdev; + USBPort *port; + char *port_path; + void *opaque; + uint32_t flags; + + /* Actual connected speed */ + int speed; + /* Supported speeds, not in info because it may be variable (hostdevs) */ + int speedmask; + uint8_t addr; + char product_desc[32]; + int auto_attach; + int attached; + + int32_t state; + uint8_t setup_buf[8]; + uint8_t data_buf[4096]; + int32_t remote_wakeup; + int32_t setup_state; + int32_t setup_len; + int32_t setup_index; + + USBEndpoint ep_ctl; + USBEndpoint ep_in[USB_MAX_ENDPOINTS]; + USBEndpoint ep_out[USB_MAX_ENDPOINTS]; + + QLIST_HEAD(, USBDescString) strings; + const USBDesc *usb_desc; /* Overrides class usb_desc if not NULL */ + const USBDescDevice *device; + + int configuration; + int ninterfaces; + int altsetting[USB_MAX_INTERFACES]; + const USBDescConfig *config; + const USBDescIface *ifaces[USB_MAX_INTERFACES]; +}; + +#define TYPE_USB_DEVICE "usb-device" +#define USB_DEVICE(obj) \ + OBJECT_CHECK(USBDevice, (obj), TYPE_USB_DEVICE) +#define USB_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(USBDeviceClass, (klass), TYPE_USB_DEVICE) +#define USB_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(USBDeviceClass, (obj), TYPE_USB_DEVICE) + +typedef struct USBDeviceClass { + DeviceClass parent_class; + + int (*init)(USBDevice *dev); + + /* + * Walk (enabled) downstream ports, check for a matching device. + * Only hubs implement this. + */ + USBDevice *(*find_device)(USBDevice *dev, uint8_t addr); + + /* + * Called when a packet is canceled. + */ + void (*cancel_packet)(USBDevice *dev, USBPacket *p); + + /* + * Called when device is destroyed. + */ + void (*handle_destroy)(USBDevice *dev); + + /* + * Attach the device + */ + void (*handle_attach)(USBDevice *dev); + + /* + * Reset the device + */ + void (*handle_reset)(USBDevice *dev); + + /* + * Process control request. + * Called from handle_packet(). + * + * Status gets stored in p->status, and if p->status == USB_RET_SUCCESS + * then the number of bytes transferred is stored in p->actual_length + */ + void (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value, + int index, int length, uint8_t *data); + + /* + * Process data transfers (both BULK and ISOC). + * Called from handle_packet(). + * + * Status gets stored in p->status, and if p->status == USB_RET_SUCCESS + * then the number of bytes transferred is stored in p->actual_length + */ + void (*handle_data)(USBDevice *dev, USBPacket *p); + + void (*set_interface)(USBDevice *dev, int interface, + int alt_old, int alt_new); + + /* + * Called when the hcd is done queuing packets for an endpoint, only + * necessary for devices which can return USB_RET_ADD_TO_QUEUE. + */ + void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep); + + /* + * Called by the hcd to let the device know the queue for an endpoint + * has been unlinked / stopped. Optional may be NULL. + */ + void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep); + + const char *product_desc; + const USBDesc *usb_desc; +} USBDeviceClass; + +typedef struct USBPortOps { + void (*attach)(USBPort *port); + void (*detach)(USBPort *port); + /* + * This gets called when a device downstream from the device attached to + * the port (iow attached through a hub) gets detached. + */ + void (*child_detach)(USBPort *port, USBDevice *child); + void (*wakeup)(USBPort *port); + /* + * Note that port->dev will be different then the device from which + * the packet originated when a hub is involved. + */ + void (*complete)(USBPort *port, USBPacket *p); +} USBPortOps; + +/* USB port on which a device can be connected */ +struct USBPort { + USBDevice *dev; + int speedmask; + char path[16]; + USBPortOps *ops; + void *opaque; + int index; /* internal port index, may be used with the opaque */ + QTAILQ_ENTRY(USBPort) next; +}; + +typedef void USBCallback(USBPacket * packet, void *opaque); + +typedef enum USBPacketState { + USB_PACKET_UNDEFINED = 0, + USB_PACKET_SETUP, + USB_PACKET_QUEUED, + USB_PACKET_ASYNC, + USB_PACKET_COMPLETE, + USB_PACKET_CANCELED, +} USBPacketState; + +/* Structure used to hold information about an active USB packet. */ +struct USBPacket { + /* Data fields for use by the driver. */ + int pid; + uint64_t id; + USBEndpoint *ep; + unsigned int stream; + QEMUIOVector iov; + uint64_t parameter; /* control transfers */ + bool short_not_ok; + bool int_req; + int status; /* USB_RET_* status code */ + int actual_length; /* Number of bytes actually transferred */ + /* Internal use by the USB layer. */ + USBPacketState state; + USBCombinedPacket *combined; + QTAILQ_ENTRY(USBPacket) queue; + QTAILQ_ENTRY(USBPacket) combined_entry; +}; + +struct USBCombinedPacket { + USBPacket *first; + QTAILQ_HEAD(packets_head, USBPacket) packets; + QEMUIOVector iov; +}; + +void usb_packet_init(USBPacket *p); +void usb_packet_set_state(USBPacket *p, USBPacketState state); +void usb_packet_check_state(USBPacket *p, USBPacketState expected); +void usb_packet_setup(USBPacket *p, int pid, + USBEndpoint *ep, unsigned int stream, + uint64_t id, bool short_not_ok, bool int_req); +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); +int usb_packet_map(USBPacket *p, QEMUSGList *sgl); +void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl); +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); +void usb_packet_skip(USBPacket *p, size_t bytes); +size_t usb_packet_size(USBPacket *p); +void usb_packet_cleanup(USBPacket *p); + +static inline bool usb_packet_is_inflight(USBPacket *p) +{ + return (p->state == USB_PACKET_QUEUED || + p->state == USB_PACKET_ASYNC); +} + +USBDevice *usb_find_device(USBPort *port, uint8_t addr); + +void usb_handle_packet(USBDevice *dev, USBPacket *p); +void usb_packet_complete(USBDevice *dev, USBPacket *p); +void usb_packet_complete_one(USBDevice *dev, USBPacket *p); +void usb_cancel_packet(USBPacket * p); + +void usb_ep_init(USBDevice *dev); +void usb_ep_reset(USBDevice *dev); +void usb_ep_dump(USBDevice *dev); +struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep); +uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep); +uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep); +void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type); +void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum); +void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, + uint16_t raw); +int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); +void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); +void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted); +USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, + uint64_t id); + +void usb_ep_combine_input_packets(USBEndpoint *ep); +void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p); +void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p); + +void usb_attach(USBPort *port); +void usb_detach(USBPort *port); +void usb_port_reset(USBPort *port); +void usb_device_reset(USBDevice *dev); +void usb_wakeup(USBEndpoint *ep, unsigned int stream); +void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); +int set_usb_string(uint8_t *buf, const char *str); + +/* usb-linux.c */ +USBDevice *usb_host_device_open(USBBus *bus, const char *devname); +void usb_host_info(Monitor *mon, const QDict *qdict); + +/* usb-bt.c */ +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci); + +/* usb ports of the VM */ + +#define VM_USB_HUB_SIZE 8 + +/* usb-musb.c */ +enum musb_irq_source_e { + musb_irq_suspend = 0, + musb_irq_resume, + musb_irq_rst_babble, + musb_irq_sof, + musb_irq_connect, + musb_irq_disconnect, + musb_irq_vbus_request, + musb_irq_vbus_error, + musb_irq_rx, + musb_irq_tx, + musb_set_vbus, + musb_set_session, + /* Add new interrupts here */ + musb_irq_max, /* total number of interrupts defined */ +}; + +typedef struct MUSBState MUSBState; +MUSBState *musb_init(DeviceState *parent_device, int gpio_base); +void musb_reset(MUSBState *s); +uint32_t musb_core_intr_get(MUSBState *s); +void musb_core_intr_clear(MUSBState *s, uint32_t mask); +void musb_set_size(MUSBState *s, int epnum, int size, int is_tx); + +/* usb-bus.c */ + +#define TYPE_USB_BUS "usb-bus" +#define USB_BUS(obj) OBJECT_CHECK(USBBus, (obj), TYPE_USB_BUS) + +struct USBBus { + BusState qbus; + USBBusOps *ops; + int busnr; + int nfree; + int nused; + QTAILQ_HEAD(, USBPort) free; + QTAILQ_HEAD(, USBPort) used; + QTAILQ_ENTRY(USBBus) next; +}; + +struct USBBusOps { + int (*register_companion)(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport); + void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep, unsigned int stream); +}; + +void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); +USBBus *usb_bus_find(int busnr); +void usb_legacy_register(const char *typename, const char *usbdevice_name, + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)); +USBDevice *usb_create(USBBus *bus, const char *name); +USBDevice *usb_create_simple(USBBus *bus, const char *name); +USBDevice *usbdevice_create(const char *cmdline); +void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask); +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask); +void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); +void usb_unregister_port(USBBus *bus, USBPort *port); +int usb_claim_port(USBDevice *dev); +void usb_release_port(USBDevice *dev); +int usb_device_attach(USBDevice *dev); +int usb_device_detach(USBDevice *dev); +int usb_device_delete_addr(int busnr, int addr); + +static inline USBBus *usb_bus_from_device(USBDevice *d) +{ + return DO_UPCAST(USBBus, qbus, d->qdev.parent_bus); +} + +extern const VMStateDescription vmstate_usb_device; + +#define VMSTATE_USB_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(USBDevice), \ + .vmsd = &vmstate_usb_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, USBDevice), \ +} + +USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr); + +void usb_device_cancel_packet(USBDevice *dev, USBPacket *p); + +void usb_device_handle_attach(USBDevice *dev); + +void usb_device_handle_reset(USBDevice *dev); + +void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, + int val, int index, int length, uint8_t *data); + +void usb_device_handle_data(USBDevice *dev, USBPacket *p); + +void usb_device_set_interface(USBDevice *dev, int interface, + int alt_old, int alt_new); + +void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep); + +void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep); + +const char *usb_device_get_product_desc(USBDevice *dev); + +const USBDesc *usb_device_get_usb_desc(USBDevice *dev); + +int ehci_create_ich9_with_companions(PCIBus *bus, int slot); + +/* quirks.c */ + +/* In bulk endpoints are streaming data sources (iow behave like isoc eps) */ +#define USB_QUIRK_BUFFER_BULK_IN 0x01 +/* Bulk pkts in FTDI format, need special handling when combining packets */ +#define USB_QUIRK_IS_FTDI 0x02 + +int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, + uint8_t interface_class, uint8_t interface_subclass, + uint8_t interface_protocol); + +#endif diff --git a/include/hw/virtio/dataplane/hostmem.h b/include/hw/virtio/dataplane/hostmem.h new file mode 100644 index 0000000000..b2cf09333f --- /dev/null +++ b/include/hw/virtio/dataplane/hostmem.h @@ -0,0 +1,57 @@ +/* + * Thread-safe guest to host memory mapping + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HOSTMEM_H +#define HOSTMEM_H + +#include "exec/memory.h" +#include "qemu/thread.h" + +typedef struct { + void *host_addr; + hwaddr guest_addr; + uint64_t size; + bool readonly; +} HostMemRegion; + +typedef struct { + /* The listener is invoked when regions change and a new list of regions is + * built up completely before they are installed. + */ + MemoryListener listener; + HostMemRegion *new_regions; + size_t num_new_regions; + + /* Current regions are accessed from multiple threads either to lookup + * addresses or to install a new list of regions. The lock protects the + * pointer and the regions. + */ + QemuMutex current_regions_lock; + HostMemRegion *current_regions; + size_t num_current_regions; +} HostMem; + +void hostmem_init(HostMem *hostmem); +void hostmem_finalize(HostMem *hostmem); + +/** + * Map a guest physical address to a pointer + * + * Note that there is map/unmap mechanism here. The caller must ensure that + * mapped memory is no longer used across events like hot memory unplug. This + * can be done with other mechanisms like bdrv_drain_all() that quiesce + * in-flight I/O. + */ +void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write); + +#endif /* HOSTMEM_H */ diff --git a/include/hw/virtio/dataplane/vring.h b/include/hw/virtio/dataplane/vring.h new file mode 100644 index 0000000000..9380cb5413 --- /dev/null +++ b/include/hw/virtio/dataplane/vring.h @@ -0,0 +1,62 @@ +/* Copyright 2012 Red Hat, Inc. and/or its affiliates + * Copyright IBM, Corp. 2012 + * + * Based on Linux 2.6.39 vhost code: + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2006 Rusty Russell IBM Corporation + * + * Author: Michael S. Tsirkin + * Stefan Hajnoczi + * + * Inspiration, some code, and most witty comments come from + * Documentation/virtual/lguest/lguest.c, by Rusty Russell + * + * This work is licensed under the terms of the GNU GPL, version 2. + */ + +#ifndef VRING_H +#define VRING_H + +#include +#include "qemu-common.h" +#include "hostmem.h" +#include "hw/virtio/virtio.h" + +typedef struct { + HostMem hostmem; /* guest memory mapper */ + struct vring vr; /* virtqueue vring mapped to host memory */ + uint16_t last_avail_idx; /* last processed avail ring index */ + uint16_t last_used_idx; /* last processed used ring index */ + uint16_t signalled_used; /* EVENT_IDX state */ + bool signalled_used_valid; + bool broken; /* was there a fatal error? */ +} Vring; + +static inline unsigned int vring_get_num(Vring *vring) +{ + return vring->vr.num; +} + +/* Are there more descriptors available? */ +static inline bool vring_more_avail(Vring *vring) +{ + return vring->vr.avail->idx != vring->last_avail_idx; +} + +/* Fail future vring_pop() and vring_push() calls until reset */ +static inline void vring_set_broken(Vring *vring) +{ + vring->broken = true; +} + +bool vring_setup(Vring *vring, VirtIODevice *vdev, int n); +void vring_teardown(Vring *vring); +void vring_disable_notification(VirtIODevice *vdev, Vring *vring); +bool vring_enable_notification(VirtIODevice *vdev, Vring *vring); +bool vring_should_notify(VirtIODevice *vdev, Vring *vring); +int vring_pop(VirtIODevice *vdev, Vring *vring, + struct iovec iov[], struct iovec *iov_end, + unsigned int *out_num, unsigned int *in_num); +void vring_push(Vring *vring, unsigned int head, int len); + +#endif /* VRING_H */ diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h new file mode 100644 index 0000000000..b373be0387 --- /dev/null +++ b/include/hw/virtio/vhost.h @@ -0,0 +1,68 @@ +#ifndef VHOST_H +#define VHOST_H + +#include "hw/hw.h" +#include "hw/virtio/virtio.h" +#include "exec/memory.h" + +/* Generic structures common for any vhost based device. */ +struct vhost_virtqueue { + int kick; + int call; + void *desc; + void *avail; + void *used; + int num; + unsigned long long used_phys; + unsigned used_size; + void *ring; + unsigned long long ring_phys; + unsigned ring_size; + EventNotifier masked_notifier; +}; + +typedef unsigned long vhost_log_chunk_t; +#define VHOST_LOG_PAGE 0x1000 +#define VHOST_LOG_BITS (8 * sizeof(vhost_log_chunk_t)) +#define VHOST_LOG_CHUNK (VHOST_LOG_PAGE * VHOST_LOG_BITS) + +struct vhost_memory; +struct vhost_dev { + MemoryListener memory_listener; + int control; + struct vhost_memory *mem; + int n_mem_sections; + MemoryRegionSection *mem_sections; + struct vhost_virtqueue *vqs; + int nvqs; + /* the first virtuque which would be used by this vhost dev */ + int vq_index; + unsigned long long features; + unsigned long long acked_features; + unsigned long long backend_features; + bool started; + bool log_enabled; + vhost_log_chunk_t *log; + unsigned long long log_size; + bool force; +}; + +int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath, + bool force); +void vhost_dev_cleanup(struct vhost_dev *hdev); +bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev); +int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); +void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); +int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); +void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); + +/* Test and clear masked event pending status. + * Should be called after unmask to avoid losing events. + */ +bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n); + +/* Mask/unmask events from this vq. + */ +void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, + bool mask); +#endif diff --git a/include/hw/virtio/virtio-9p.h b/include/hw/virtio/virtio-9p.h new file mode 100644 index 0000000000..65789db131 --- /dev/null +++ b/include/hw/virtio/virtio-9p.h @@ -0,0 +1,24 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_VIRTIO_9P_DEVICE_H +#define QEMU_VIRTIO_9P_DEVICE_H + +typedef struct V9fsConf +{ + /* tag name for the device */ + char *tag; + char *fsdev_id; +} V9fsConf; + +#endif diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h new file mode 100644 index 0000000000..3b459bbdd7 --- /dev/null +++ b/include/hw/virtio/virtio-balloon.h @@ -0,0 +1,72 @@ +/* + * Virtio Support + * + * Copyright IBM, Corp. 2007-2008 + * + * Authors: + * Anthony Liguori + * Rusty Russell + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_BALLOON_H +#define _QEMU_VIRTIO_BALLOON_H + +#include "hw/virtio/virtio.h" +#include "hw/pci/pci.h" + +#define TYPE_VIRTIO_BALLOON "virtio-balloon" +#define VIRTIO_BALLOON(obj) \ + OBJECT_CHECK(VirtIOBalloon, (obj), TYPE_VIRTIO_BALLOON) + +/* from Linux's linux/virtio_balloon.h */ + +/* The ID for virtio_balloon */ +#define VIRTIO_ID_BALLOON 5 + +/* The feature bitmap for virtio balloon */ +#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory stats virtqueue */ + +/* Size of a PFN in the balloon interface. */ +#define VIRTIO_BALLOON_PFN_SHIFT 12 + +struct virtio_balloon_config +{ + /* Number of pages host wants Guest to give up. */ + uint32_t num_pages; + /* Number of pages we've actually got in balloon. */ + uint32_t actual; +}; + +/* Memory Statistics */ +#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ +#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ +#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ +#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ +#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ +#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ +#define VIRTIO_BALLOON_S_NR 6 + +typedef struct VirtIOBalloonStat { + uint16_t tag; + uint64_t val; +} QEMU_PACKED VirtIOBalloonStat; + +typedef struct VirtIOBalloon { + VirtIODevice parent_obj; + VirtQueue *ivq, *dvq, *svq; + uint32_t num_pages; + uint32_t actual; + uint64_t stats[VIRTIO_BALLOON_S_NR]; + VirtQueueElement stats_vq_elem; + size_t stats_vq_offset; + QEMUTimer *stats_timer; + int64_t stats_last_update; + int64_t stats_poll_interval; +} VirtIOBalloon; + +#endif diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h new file mode 100644 index 0000000000..c10d069b25 --- /dev/null +++ b/include/hw/virtio/virtio-blk.h @@ -0,0 +1,152 @@ +/* + * Virtio Block Device + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_BLK_H +#define _QEMU_VIRTIO_BLK_H + +#include "hw/virtio/virtio.h" +#include "hw/block/block.h" + +#define TYPE_VIRTIO_BLK "virtio-blk" +#define VIRTIO_BLK(obj) \ + OBJECT_CHECK(VirtIOBlock, (obj), TYPE_VIRTIO_BLK) + +/* from Linux's linux/virtio_blk.h */ + +/* The ID for virtio_block */ +#define VIRTIO_ID_BLOCK 2 + +/* Feature bits */ +#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ +#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ +#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ +#define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */ +#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ +/* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ +#define VIRTIO_BLK_F_WCE 9 /* write cache enabled */ +#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */ + +#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ + +struct virtio_blk_config +{ + uint64_t capacity; + uint32_t size_max; + uint32_t seg_max; + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + uint32_t blk_size; + uint8_t physical_block_exp; + uint8_t alignment_offset; + uint16_t min_io_size; + uint32_t opt_io_size; + uint8_t wce; +} QEMU_PACKED; + +/* These two define direction. */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +/* This bit says it's a scsi command, not an actual read or write. */ +#define VIRTIO_BLK_T_SCSI_CMD 2 + +/* Flush the volatile write cache */ +#define VIRTIO_BLK_T_FLUSH 4 + +/* return the device ID string */ +#define VIRTIO_BLK_T_GET_ID 8 + +/* Barrier before this op. */ +#define VIRTIO_BLK_T_BARRIER 0x80000000 + +/* This is the first element of the read scatter-gather list. */ +struct virtio_blk_outhdr +{ + /* VIRTIO_BLK_T* */ + uint32_t type; + /* io priority. */ + uint32_t ioprio; + /* Sector (ie. 512 byte offset) */ + uint64_t sector; +}; + +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +/* This is the last element of the write scatter-gather list */ +struct virtio_blk_inhdr +{ + unsigned char status; +}; + +/* SCSI pass-through header */ +struct virtio_scsi_inhdr +{ + uint32_t errors; + uint32_t data_len; + uint32_t sense_len; + uint32_t residual; +}; + +struct VirtIOBlkConf +{ + BlockConf conf; + char *serial; + uint32_t scsi; + uint32_t config_wce; + uint32_t data_plane; +}; + +struct VirtIOBlockDataPlane; + +typedef struct VirtIOBlock { + VirtIODevice parent_obj; + BlockDriverState *bs; + VirtQueue *vq; + void *rq; + QEMUBH *bh; + BlockConf *conf; + VirtIOBlkConf blk; + unsigned short sector_mask; + VMChangeStateEntry *change; +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + struct VirtIOBlockDataPlane *dataplane; +#endif +} VirtIOBlock; + +#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) + +#ifdef __linux__ +#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \ + DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \ + DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \ + DEFINE_PROP_STRING("serial", _state, _field.serial), \ + DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \ + DEFINE_PROP_BIT("scsi", _state, _field.scsi, 0, true) +#else +#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \ + DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \ + DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \ + DEFINE_PROP_STRING("serial", _state, _field.serial), \ + DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true) +#endif /* __linux__ */ + +void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk); + +#endif diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h new file mode 100644 index 0000000000..311e8c78bd --- /dev/null +++ b/include/hw/virtio/virtio-bus.h @@ -0,0 +1,94 @@ +/* + * VirtioBus + * + * Copyright (C) 2012 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + */ + +#ifndef VIRTIO_BUS_H +#define VIRTIO_BUS_H + +#include "hw/qdev.h" +#include "sysemu/sysemu.h" +#include "hw/virtio/virtio.h" + +#define TYPE_VIRTIO_BUS "virtio-bus" +#define VIRTIO_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioBusClass, obj, TYPE_VIRTIO_BUS) +#define VIRTIO_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioBusClass, klass, TYPE_VIRTIO_BUS) +#define VIRTIO_BUS(obj) OBJECT_CHECK(VirtioBusState, (obj), TYPE_VIRTIO_BUS) + +typedef struct VirtioBusState VirtioBusState; + +typedef struct VirtioBusClass { + /* This is what a VirtioBus must implement */ + BusClass parent; + void (*notify)(DeviceState *d, uint16_t vector); + void (*save_config)(DeviceState *d, QEMUFile *f); + void (*save_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_config)(DeviceState *d, QEMUFile *f); + int (*load_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_done)(DeviceState *d, QEMUFile *f); + unsigned (*get_features)(DeviceState *d); + bool (*query_guest_notifiers)(DeviceState *d); + int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); + int (*set_host_notifier)(DeviceState *d, int n, bool assigned); + void (*vmstate_change)(DeviceState *d, bool running); + /* + * transport independent init function. + * This is called by virtio-bus just after the device is plugged. + */ + void (*device_plugged)(DeviceState *d); + /* + * transport independent exit function. + * This is called by virtio-bus just before the device is unplugged. + */ + void (*device_unplug)(DeviceState *d); +} VirtioBusClass; + +struct VirtioBusState { + BusState parent_obj; + /* + * Only one VirtIODevice can be plugged on the bus. + */ + VirtIODevice *vdev; + /* + * This will be removed at the end of the series. + */ + VirtIOBindings bindings; +}; + +int virtio_bus_plug_device(VirtIODevice *vdev); +void virtio_bus_reset(VirtioBusState *bus); +void virtio_bus_destroy_device(VirtioBusState *bus); +/* Get the device id of the plugged device. */ +uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus); +/* Get the config_len field of the plugged device. */ +size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus); +/* Get the features of the plugged device. */ +uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, + uint32_t requested_features); +/* Get bad features of the plugged device. */ +uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus); +/* Get config of the plugged device. */ +void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config); + +#endif /* VIRTIO_BUS_H */ diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h new file mode 100644 index 0000000000..d2cc996872 --- /dev/null +++ b/include/hw/virtio/virtio-net.h @@ -0,0 +1,246 @@ +/* + * Virtio Network Device + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_NET_H +#define _QEMU_VIRTIO_NET_H + +#include "hw/virtio/virtio.h" +#include "hw/pci/pci.h" + +#define ETH_ALEN 6 + +/* from Linux's virtio_net.h */ + +/* The ID for virtio_net */ +#define VIRTIO_ID_NET 1 + +/* The feature bitmap for virtio net */ +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow + * Steering */ + +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ + +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ + +#define TX_TIMER_INTERVAL 150000 /* 150 us */ + +/* Limit the number of packets that can be sent via a single flush + * of the TX queue. This gives us a guaranteed exit condition and + * ensures fairness in the io path. 256 conveniently matches the + * length of the TX queue and shows a good balance of performance + * and latency. */ +#define TX_BURST 256 + +typedef struct virtio_net_conf +{ + uint32_t txtimer; + int32_t txburst; + char *tx; +} virtio_net_conf; + +/* Maximum packet size we can receive from tap device: header + 64k */ +#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10)) + +struct virtio_net_config +{ + /* The config defining mac address ($ETH_ALEN bytes) */ + uint8_t mac[ETH_ALEN]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + uint16_t status; + /* Max virtqueue pairs supported by the device */ + uint16_t max_virtqueue_pairs; +} QEMU_PACKED; + +/* + * Control virtqueue data structures + * + * The control virtqueue expects a header in the first sg entry + * and an ack/status response in the last entry. Data for the + * command goes in between. + */ +struct virtio_net_ctrl_hdr { + uint8_t class; + uint8_t cmd; +}; + +typedef uint8_t virtio_net_ctrl_ack; + +#define VIRTIO_NET_OK 0 +#define VIRTIO_NET_ERR 1 + +/* + * Control the RX mode, ie. promisucous, allmulti, etc... + * All commands require an "out" sg entry containing a 1 byte + * state value, zero = disable, non-zero = enable. Commands + * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. + * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. + */ +#define VIRTIO_NET_CTRL_RX 0 + #define VIRTIO_NET_CTRL_RX_PROMISC 0 + #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 + #define VIRTIO_NET_CTRL_RX_ALLUNI 2 + #define VIRTIO_NET_CTRL_RX_NOMULTI 3 + #define VIRTIO_NET_CTRL_RX_NOUNI 4 + #define VIRTIO_NET_CTRL_RX_NOBCAST 5 + +/* + * Control the MAC + * + * The MAC filter table is managed by the hypervisor, the guest should + * assume the size is infinite. Filtering should be considered + * non-perfect, ie. based on hypervisor resources, the guest may + * received packets from sources not specified in the filter list. + * + * In addition to the class/cmd header, the TABLE_SET command requires + * two out scatterlists. Each contains a 4 byte count of entries followed + * by a concatenated byte stream of the ETH_ALEN MAC addresses. The + * first sg list contains unicast addresses, the second is for multicast. + * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature + * is available. + * + * The ADDR_SET command requests one out scatterlist, it contains a + * 6 bytes MAC address. This functionality is present if the + * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. + */ +struct virtio_net_ctrl_mac { + uint32_t entries; + uint8_t macs[][ETH_ALEN]; +}; + +typedef struct VirtIONetQueue { + VirtQueue *rx_vq; + VirtQueue *tx_vq; + QEMUTimer *tx_timer; + QEMUBH *tx_bh; + int tx_waiting; + struct { + VirtQueueElement elem; + ssize_t len; + } async_tx; + struct VirtIONet *n; +} VirtIONetQueue; + +typedef struct VirtIONet { + VirtIODevice vdev; + uint8_t mac[ETH_ALEN]; + uint16_t status; + VirtIONetQueue *vqs; + VirtQueue *ctrl_vq; + NICState *nic; + uint32_t tx_timeout; + int32_t tx_burst; + uint32_t has_vnet_hdr; + size_t host_hdr_len; + size_t guest_hdr_len; + uint8_t has_ufo; + int mergeable_rx_bufs; + uint8_t promisc; + uint8_t allmulti; + uint8_t alluni; + uint8_t nomulti; + uint8_t nouni; + uint8_t nobcast; + uint8_t vhost_started; + struct { + int in_use; + int first_multi; + uint8_t multi_overflow; + uint8_t uni_overflow; + uint8_t *macs; + } mac_table; + uint32_t *vlans; + DeviceState *qdev; + int multiqueue; + uint16_t max_queues; + uint16_t curr_queues; + size_t config_size; +} VirtIONet; + +#define VIRTIO_NET_CTRL_MAC 1 + #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 + #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 + +/* + * Control VLAN filtering + * + * The VLAN filter table is controlled via a simple ADD/DEL interface. + * VLAN IDs not added may be filterd by the hypervisor. Del is the + * opposite of add. Both commands expect an out entry containing a 2 + * byte VLAN ID. VLAN filterting is available with the + * VIRTIO_NET_F_CTRL_VLAN feature bit. + */ +#define VIRTIO_NET_CTRL_VLAN 2 + #define VIRTIO_NET_CTRL_VLAN_ADD 0 + #define VIRTIO_NET_CTRL_VLAN_DEL 1 + +/* + * Control Multiqueue + * + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + * enables multiqueue, specifying the number of the transmit and + * receive queues that will be used. After the command is consumed and acked by + * the device, the device will not steer new packets on receive virtqueues + * other than specified nor read from transmit virtqueues other than specified. + * Accordingly, driver should not transmit new packets on virtqueues other than + * specified. + */ +struct virtio_net_ctrl_mq { + uint16_t virtqueue_pairs; +}; + +#define VIRTIO_NET_CTRL_MQ 4 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 + +#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ + DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ + DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \ + DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \ + DEFINE_PROP_BIT("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \ + DEFINE_PROP_BIT("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \ + DEFINE_PROP_BIT("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \ + DEFINE_PROP_BIT("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \ + DEFINE_PROP_BIT("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \ + DEFINE_PROP_BIT("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \ + DEFINE_PROP_BIT("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \ + DEFINE_PROP_BIT("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \ + DEFINE_PROP_BIT("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \ + DEFINE_PROP_BIT("status", _state, _field, VIRTIO_NET_F_STATUS, true), \ + DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ + DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ + DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ + DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true), \ + DEFINE_PROP_BIT("ctrl_mac_addr", _state, _field, VIRTIO_NET_F_CTRL_MAC_ADDR, true), \ + DEFINE_PROP_BIT("mq", _state, _field, VIRTIO_NET_F_MQ, false) + +#endif diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h new file mode 100644 index 0000000000..3711c97a70 --- /dev/null +++ b/include/hw/virtio/virtio-rng.h @@ -0,0 +1,47 @@ +/* + * Virtio RNG Support + * + * Copyright Red Hat, Inc. 2012 + * Copyright Amit Shah + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef _QEMU_VIRTIO_RNG_H +#define _QEMU_VIRTIO_RNG_H + +#include "qemu/rng.h" +#include "qemu/rng-random.h" + +/* The Virtio ID for the virtio rng device */ +#define VIRTIO_ID_RNG 4 + +struct VirtIORNGConf { + RngBackend *rng; + uint64_t max_bytes; + uint32_t period_ms; + RndRandom *default_backend; +}; + +typedef struct VirtIORNG { + VirtIODevice vdev; + + DeviceState *qdev; + + /* Only one vq - guest puts buffer(s) on it when it needs entropy */ + VirtQueue *vq; + + VirtIORNGConf *conf; + + RngBackend *rng; + + /* We purposefully don't migrate this state. The quota will reset on the + * destination as a result. Rate limiting is host state, not guest state. + */ + QEMUTimer *rate_limit_timer; + int64_t quota_remaining; +} VirtIORNG; + +#endif diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h new file mode 100644 index 0000000000..c9d92ca2e8 --- /dev/null +++ b/include/hw/virtio/virtio-scsi.h @@ -0,0 +1,66 @@ +/* + * Virtio SCSI HBA + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_SCSI_H +#define _QEMU_VIRTIO_SCSI_H + +#include "hw/virtio/virtio.h" +#include "hw/pci/pci.h" +#include "hw/scsi/scsi.h" + +#define TYPE_VIRTIO_SCSI "virtio-scsi" +#define VIRTIO_SCSI(obj) \ + OBJECT_CHECK(VirtIOSCSI, (obj), TYPE_VIRTIO_SCSI) + + +/* The ID for virtio_scsi */ +#define VIRTIO_ID_SCSI 8 + +/* Feature Bits */ +#define VIRTIO_SCSI_F_INOUT 0 +#define VIRTIO_SCSI_F_HOTPLUG 1 +#define VIRTIO_SCSI_F_CHANGE 2 + +struct VirtIOSCSIConf { + uint32_t num_queues; + uint32_t max_sectors; + uint32_t cmd_per_lun; +}; + +typedef struct VirtIOSCSI { + VirtIODevice parent_obj; + VirtIOSCSIConf conf; + + SCSIBus bus; + uint32_t sense_size; + uint32_t cdb_size; + int resetting; + bool events_dropped; + VirtQueue *ctrl_vq; + VirtQueue *event_vq; + VirtQueue **cmd_vqs; +} VirtIOSCSI; + +#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _conf_field) \ + DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ + DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF),\ + DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128) + +#define DEFINE_VIRTIO_SCSI_FEATURES(_state, _feature_field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _feature_field), \ + DEFINE_PROP_BIT("hotplug", _state, _feature_field, VIRTIO_SCSI_F_HOTPLUG, \ + true), \ + DEFINE_PROP_BIT("param_change", _state, _feature_field, \ + VIRTIO_SCSI_F_CHANGE, true) + +#endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/include/hw/virtio/virtio-serial.h b/include/hw/virtio/virtio-serial.h new file mode 100644 index 0000000000..098deeac4a --- /dev/null +++ b/include/hw/virtio/virtio-serial.h @@ -0,0 +1,247 @@ +/* + * Virtio Serial / Console Support + * + * Copyright IBM, Corp. 2008 + * Copyright Red Hat, Inc. 2009, 2010 + * + * Authors: + * Christian Ehrhardt + * Amit Shah + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef _QEMU_VIRTIO_SERIAL_H +#define _QEMU_VIRTIO_SERIAL_H + +#include "hw/qdev.h" +#include "hw/virtio/virtio.h" + +/* == Interface shared between the guest kernel and qemu == */ + +/* The Virtio ID for virtio console / serial ports */ +#define VIRTIO_ID_CONSOLE 3 + +/* Features supported */ +#define VIRTIO_CONSOLE_F_MULTIPORT 1 + +#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) + +struct virtio_console_config { + /* + * These two fields are used by VIRTIO_CONSOLE_F_SIZE which + * isn't implemented here yet + */ + uint16_t cols; + uint16_t rows; + + uint32_t max_nr_ports; +} QEMU_PACKED; + +struct virtio_console_control { + uint32_t id; /* Port number */ + uint16_t event; /* The kind of control event (see below) */ + uint16_t value; /* Extra information for the key */ +}; + +struct virtio_serial_conf { + /* Max. number of ports we can have for a virtio-serial device */ + uint32_t max_virtserial_ports; +}; + +/* Some events for the internal messages (control packets) */ +#define VIRTIO_CONSOLE_DEVICE_READY 0 +#define VIRTIO_CONSOLE_PORT_ADD 1 +#define VIRTIO_CONSOLE_PORT_REMOVE 2 +#define VIRTIO_CONSOLE_PORT_READY 3 +#define VIRTIO_CONSOLE_CONSOLE_PORT 4 +#define VIRTIO_CONSOLE_RESIZE 5 +#define VIRTIO_CONSOLE_PORT_OPEN 6 +#define VIRTIO_CONSOLE_PORT_NAME 7 + +/* == In-qemu interface == */ + +#define TYPE_VIRTIO_SERIAL_PORT "virtio-serial-port" +#define VIRTIO_SERIAL_PORT(obj) \ + OBJECT_CHECK(VirtIOSerialPort, (obj), TYPE_VIRTIO_SERIAL_PORT) +#define VIRTIO_SERIAL_PORT_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtIOSerialPortClass, (klass), TYPE_VIRTIO_SERIAL_PORT) +#define VIRTIO_SERIAL_PORT_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOSerialPortClass, (obj), TYPE_VIRTIO_SERIAL_PORT) + +typedef struct VirtIOSerial VirtIOSerial; +typedef struct VirtIOSerialBus VirtIOSerialBus; +typedef struct VirtIOSerialPort VirtIOSerialPort; + +typedef struct VirtIOSerialPortClass { + DeviceClass parent_class; + + /* Is this a device that binds with hvc in the guest? */ + bool is_console; + + /* + * The per-port (or per-app) init function that's called when a + * new device is found on the bus. + */ + int (*init)(VirtIOSerialPort *port); + /* + * Per-port exit function that's called when a port gets + * hot-unplugged or removed. + */ + int (*exit)(VirtIOSerialPort *port); + + /* Callbacks for guest events */ + /* Guest opened/closed device. */ + void (*set_guest_connected)(VirtIOSerialPort *port, int guest_connected); + + /* Guest is now ready to accept data (virtqueues set up). */ + void (*guest_ready)(VirtIOSerialPort *port); + + /* + * Guest wrote some data to the port. This data is handed over to + * the app via this callback. The app can return a size less than + * 'len'. In this case, throttling will be enabled for this port. + */ + ssize_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, + size_t len); +} VirtIOSerialPortClass; + +/* + * This is the state that's shared between all the ports. Some of the + * state is configurable via command-line options. Some of it can be + * set by individual devices in their initfn routines. Some of the + * state is set by the generic qdev device init routine. + */ +struct VirtIOSerialPort { + DeviceState dev; + + QTAILQ_ENTRY(VirtIOSerialPort) next; + + /* + * This field gives us the virtio device as well as the qdev bus + * that we are associated with + */ + VirtIOSerial *vser; + + VirtQueue *ivq, *ovq; + + /* + * This name is sent to the guest and exported via sysfs. + * The guest could create symlinks based on this information. + * The name is in the reverse fqdn format, like org.qemu.console.0 + */ + char *name; + + /* + * This id helps identify ports between the guest and the host. + * The guest sends a "header" with this id with each data packet + * that it sends and the host can then find out which associated + * device to send out this data to + */ + uint32_t id; + + /* + * This is the elem that we pop from the virtqueue. A slow + * backend that consumes guest data (e.g. the file backend for + * qemu chardevs) can cause the guest to block till all the output + * is flushed. This isn't desired, so we keep a note of the last + * element popped and continue consuming it once the backend + * becomes writable again. + */ + VirtQueueElement elem; + + /* + * The index and the offset into the iov buffer that was popped in + * elem above. + */ + uint32_t iov_idx; + uint64_t iov_offset; + + /* + * When unthrottling we use a bottom-half to call flush_queued_data. + */ + QEMUBH *bh; + + /* Is the corresponding guest device open? */ + bool guest_connected; + /* Is this device open for IO on the host? */ + bool host_connected; + /* Do apps not want to receive data? */ + bool throttled; +}; + +/* The virtio-serial bus on top of which the ports will ride as devices */ +struct VirtIOSerialBus { + BusState qbus; + + /* This is the parent device that provides the bus for ports. */ + VirtIOSerial *vser; + + /* The maximum number of ports that can ride on top of this bus */ + uint32_t max_nr_ports; +}; + +typedef struct VirtIOSerialPostLoad { + QEMUTimer *timer; + uint32_t nr_active_ports; + struct { + VirtIOSerialPort *port; + uint8_t host_connected; + } *connected; +} VirtIOSerialPostLoad; + +struct VirtIOSerial { + VirtIODevice vdev; + + VirtQueue *c_ivq, *c_ovq; + /* Arrays of ivqs and ovqs: one per port */ + VirtQueue **ivqs, **ovqs; + + VirtIOSerialBus bus; + + DeviceState *qdev; + + QTAILQ_HEAD(, VirtIOSerialPort) ports; + + /* bitmap for identifying active ports */ + uint32_t *ports_map; + + struct virtio_console_config config; + + struct VirtIOSerialPostLoad *post_load; +}; + +/* Interface to the virtio-serial bus */ + +/* + * Open a connection to the port + * Returns 0 on success (always). + */ +int virtio_serial_open(VirtIOSerialPort *port); + +/* + * Close the connection to the port + * Returns 0 on success (always). + */ +int virtio_serial_close(VirtIOSerialPort *port); + +/* + * Send data to Guest + */ +ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, + size_t size); + +/* + * Query whether a guest is ready to receive data. + */ +size_t virtio_serial_guest_ready(VirtIOSerialPort *port); + +/* + * Flow control: Ports can signal to the virtio-serial core to stop + * sending data or re-start sending data, depending on the 'throttle' + * value here. + */ +void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle); + +#endif diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h new file mode 100644 index 0000000000..7e24b2b69a --- /dev/null +++ b/include/hw/virtio/virtio.h @@ -0,0 +1,292 @@ +/* + * Virtio Support + * + * Copyright IBM, Corp. 2007 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_H +#define _QEMU_VIRTIO_H + +#include "hw/hw.h" +#include "net/net.h" +#include "hw/qdev.h" +#include "sysemu/sysemu.h" +#include "qemu/event_notifier.h" +#ifdef CONFIG_VIRTFS +#include "hw/virtio/virtio-9p.h" +#endif + +/* from Linux's linux/virtio_config.h */ + +/* Status byte for guest to report progress, and synchronize features. */ +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +/* We have found a driver for the device. */ +#define VIRTIO_CONFIG_S_DRIVER 2 +/* Driver has used its parts of the config, and is happy */ +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +/* Some virtio feature bits (currently bits 28 through 31) are reserved for the + * transport being used (eg. virtio_ring), the rest are per-device feature bits. */ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 32 + +/* We notify when the ring is completely used, even if the guest is suppressing + * callbacks */ +#define VIRTIO_F_NOTIFY_ON_EMPTY 24 +/* We support indirect buffer descriptors */ +#define VIRTIO_RING_F_INDIRECT_DESC 28 +/* The Guest publishes the used index for which it expects an interrupt + * at the end of the avail ring. Host should ignore the avail->flags field. */ +/* The Host publishes the avail index for which it expects a kick + * at the end of the used ring. Guest should ignore the used->flags field. */ +#define VIRTIO_RING_F_EVENT_IDX 29 +/* A guest should never accept this. It implies negotiation is broken. */ +#define VIRTIO_F_BAD_FEATURE 30 + +/* from Linux's linux/virtio_ring.h */ + +/* This marks a buffer as continuing via the next field. */ +#define VRING_DESC_F_NEXT 1 +/* This marks a buffer as write-only (otherwise read-only). */ +#define VRING_DESC_F_WRITE 2 +/* This means the buffer contains a list of buffer descriptors. */ +#define VRING_DESC_F_INDIRECT 4 + +/* This means don't notify other side when buffer added. */ +#define VRING_USED_F_NO_NOTIFY 1 +/* This means don't interrupt guest when buffer consumed. */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +struct VirtQueue; + +static inline hwaddr vring_align(hwaddr addr, + unsigned long align) +{ + return (addr + align - 1) & ~(align - 1); +} + +typedef struct VirtQueue VirtQueue; + +#define VIRTQUEUE_MAX_SIZE 1024 + +typedef struct VirtQueueElement +{ + unsigned int index; + unsigned int out_num; + unsigned int in_num; + hwaddr in_addr[VIRTQUEUE_MAX_SIZE]; + hwaddr out_addr[VIRTQUEUE_MAX_SIZE]; + struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; + struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; +} VirtQueueElement; + +typedef struct { + void (*notify)(DeviceState *d, uint16_t vector); + void (*save_config)(DeviceState *d, QEMUFile *f); + void (*save_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_config)(DeviceState *d, QEMUFile *f); + int (*load_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_done)(DeviceState *d, QEMUFile *f); + unsigned (*get_features)(DeviceState *d); + bool (*query_guest_notifiers)(DeviceState *d); + int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assigned); + int (*set_host_notifier)(DeviceState *d, int n, bool assigned); + void (*vmstate_change)(DeviceState *d, bool running); +} VirtIOBindings; + +#define VIRTIO_PCI_QUEUE_MAX 64 + +#define VIRTIO_NO_VECTOR 0xffff + +#define TYPE_VIRTIO_DEVICE "virtio-device" +#define VIRTIO_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioDeviceClass, obj, TYPE_VIRTIO_DEVICE) +#define VIRTIO_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioDeviceClass, klass, TYPE_VIRTIO_DEVICE) +#define VIRTIO_DEVICE(obj) \ + OBJECT_CHECK(VirtIODevice, (obj), TYPE_VIRTIO_DEVICE) + +struct VirtIODevice +{ + DeviceState parent_obj; + const char *name; + uint8_t status; + uint8_t isr; + uint16_t queue_sel; + uint32_t guest_features; + size_t config_len; + void *config; + uint16_t config_vector; + int nvectors; + /* + * Function pointers will be removed at the end of the series as they are in + * VirtioDeviceClass. + */ + uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); + uint32_t (*bad_features)(VirtIODevice *vdev); + void (*set_features)(VirtIODevice *vdev, uint32_t val); + void (*get_config)(VirtIODevice *vdev, uint8_t *config); + void (*set_config)(VirtIODevice *vdev, const uint8_t *config); + void (*reset)(VirtIODevice *vdev); + void (*set_status)(VirtIODevice *vdev, uint8_t val); + /* Test and clear event pending status. + * Should be called after unmask to avoid losing events. + * If backend does not support masking, + * must check in frontend instead. + */ + bool (*guest_notifier_pending)(VirtIODevice *vdev, int n); + /* Mask/unmask events from this vq. Any events reported + * while masked will become pending. + * If backend does not support masking, + * must mask in frontend instead. + */ + void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); + + VirtQueue *vq; + const VirtIOBindings *binding; + DeviceState *binding_opaque; + uint16_t device_id; + bool vm_running; + VMChangeStateEntry *vmstate; +}; + +typedef struct VirtioDeviceClass { + /* This is what a VirtioDevice must implement */ + DeviceClass parent; + int (*init)(VirtIODevice *vdev); + uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); + uint32_t (*bad_features)(VirtIODevice *vdev); + void (*set_features)(VirtIODevice *vdev, uint32_t val); + void (*get_config)(VirtIODevice *vdev, uint8_t *config); + void (*set_config)(VirtIODevice *vdev, const uint8_t *config); + void (*reset)(VirtIODevice *vdev); + void (*set_status)(VirtIODevice *vdev, uint8_t val); +} VirtioDeviceClass; + +void virtio_init(VirtIODevice *vdev, const char *name, + uint16_t device_id, size_t config_size); +void virtio_common_cleanup(VirtIODevice *vdev); + +VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, + void (*handle_output)(VirtIODevice *, + VirtQueue *)); + +void virtio_del_queue(VirtIODevice *vdev, int n); + +void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len); +void virtqueue_flush(VirtQueue *vq, unsigned int count); +void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len, unsigned int idx); + +void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, + size_t num_sg, int is_write); +int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem); +int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, + unsigned int out_bytes); +void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, + unsigned max_in_bytes, unsigned max_out_bytes); + +void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); + +void virtio_save(VirtIODevice *vdev, QEMUFile *f); + +int virtio_load(VirtIODevice *vdev, QEMUFile *f); + +void virtio_cleanup(VirtIODevice *vdev); + +void virtio_notify_config(VirtIODevice *vdev); + +void virtio_queue_set_notification(VirtQueue *vq, int enable); + +int virtio_queue_ready(VirtQueue *vq); + +int virtio_queue_empty(VirtQueue *vq); + +/* Host binding interface. */ + +VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, + size_t config_size, size_t struct_size); +uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr); +uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr); +uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr); +void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data); +void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data); +void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data); +void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr); +hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n); +int virtio_queue_get_num(VirtIODevice *vdev, int n); +void virtio_queue_notify(VirtIODevice *vdev, int n); +uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); +void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); +void virtio_set_status(VirtIODevice *vdev, uint8_t val); +void virtio_reset(void *opaque); +void virtio_update_irq(VirtIODevice *vdev); +int virtio_set_features(VirtIODevice *vdev, uint32_t val); + +void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, + DeviceState *opaque); + +/* Base devices. */ +typedef struct VirtIOBlkConf VirtIOBlkConf; +struct virtio_net_conf; +VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, + struct virtio_net_conf *net, + uint32_t host_features); +typedef struct virtio_serial_conf virtio_serial_conf; +VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); +VirtIODevice *virtio_balloon_init(DeviceState *dev); +typedef struct VirtIOSCSIConf VirtIOSCSIConf; +VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); +typedef struct VirtIORNGConf VirtIORNGConf; +VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf); +#ifdef CONFIG_VIRTFS +VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); +#endif + + +void virtio_net_exit(VirtIODevice *vdev); +void virtio_serial_exit(VirtIODevice *vdev); +void virtio_balloon_exit(VirtIODevice *vdev); +void virtio_scsi_exit(VirtIODevice *vdev); +void virtio_rng_exit(VirtIODevice *vdev); + +#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ + DEFINE_PROP_BIT("indirect_desc", _state, _field, \ + VIRTIO_RING_F_INDIRECT_DESC, true), \ + DEFINE_PROP_BIT("event_idx", _state, _field, \ + VIRTIO_RING_F_EVENT_IDX, true) + +hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_avail_addr(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_used_addr(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_ring_addr(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_desc_size(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_avail_size(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_used_size(VirtIODevice *vdev, int n); +hwaddr virtio_queue_get_ring_size(VirtIODevice *vdev, int n); +uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); +void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); +VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); +uint16_t virtio_get_queue_index(VirtQueue *vq); +int virtio_queue_get_id(VirtQueue *vq); +EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); +void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, + bool with_irqfd); +EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); +void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, + bool set_handler); +void virtio_queue_notify_vq(VirtQueue *vq); +void virtio_irq(VirtQueue *vq); +#endif diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h new file mode 100644 index 0000000000..6235f91fe0 --- /dev/null +++ b/include/hw/xen/xen.h @@ -0,0 +1,62 @@ +#ifndef QEMU_HW_XEN_H +#define QEMU_HW_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ +#include + +#include "hw/irq.h" +#include "qemu-common.h" + +/* xen-machine.c */ +enum xen_mode { + XEN_EMULATE = 0, // xen emulation, using xenner (default) + XEN_CREATE, // create xen domain + XEN_ATTACH // attach to xen domain created by xend +}; + +extern uint32_t xen_domid; +extern enum xen_mode xen_mode; + +extern bool xen_allowed; + +static inline bool xen_enabled(void) +{ +#if defined(CONFIG_XEN_BACKEND) && !defined(CONFIG_NO_XEN) + return xen_allowed; +#else + return 0; +#endif +} + +int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); +void xen_piix3_set_irq(void *opaque, int irq_num, int level); +void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); +void xen_hvm_inject_msi(uint64_t addr, uint32_t data); +void xen_cmos_set_s3_resume(void *opaque, int irq, int level); + +qemu_irq *xen_interrupt_controller_init(void); + +int xen_init(void); +int xen_hvm_init(void); +void xen_vcpu_init(void); +void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); + +#if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) +struct MemoryRegion; +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, + struct MemoryRegion *mr); +void xen_modified_memory(ram_addr_t start, ram_addr_t length); +#endif + +struct MemoryRegion; +void xen_register_framebuffer(struct MemoryRegion *mr); + +#if defined(CONFIG_XEN) && CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 +# define HVM_MAX_VCPUS 32 +#endif + +#endif /* QEMU_HW_XEN_H */ diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h new file mode 100644 index 0000000000..3b7d96d7a5 --- /dev/null +++ b/include/hw/xen/xen_backend.h @@ -0,0 +1,109 @@ +#ifndef QEMU_HW_XEN_BACKEND_H +#define QEMU_HW_XEN_BACKEND_H 1 + +#include "hw/xen/xen_common.h" +#include "sysemu/sysemu.h" +#include "net/net.h" + +/* ------------------------------------------------------------- */ + +#define XEN_BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don't expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*initialise)(struct XenDevice *xendev); + void (*connected)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + const char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[XEN_BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + XenEvtchn evtchndev; + XenGnttab gnttabdev; + + struct XenDevOps *ops; + QTAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern XenXC xen_xc; +extern struct xs_handle *xenstore; +extern const char *xen_protocol; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +int xenstore_write_int64(const char *base, const char *node, int64_t ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(const char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + GCC_FMT_ATTR(3, 4); + +/* actual backend drivers */ +extern struct XenDevOps xen_console_ops; /* xen_console.c */ +extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ + +void xen_init_display(int domid); + +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); +int xen_config_dev_vfb(int vdev, const char *type); +int xen_config_dev_vkbd(int vdev); +int xen_config_dev_console(int vdev); + +#endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h new file mode 100644 index 0000000000..2d5a25bf40 --- /dev/null +++ b/include/hw/xen/xen_common.h @@ -0,0 +1,160 @@ +#ifndef QEMU_HW_XEN_COMMON_H +#define QEMU_HW_XEN_COMMON_H 1 + +#include "config-host.h" + +#include +#include + +#include +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 420 +# include +#else +# include +#endif +#include + +#include "hw/hw.h" +#include "hw/xen/xen.h" +#include "qemu/queue.h" + +/* + * We don't support Xen prior to 3.3.0. + */ + +/* Xen before 4.0 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 +static inline void *xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int *err, + unsigned int num) +{ + return xc_map_foreign_batch(xc_handle, dom, prot, arr, num); +} +#endif + + +/* Xen before 4.1 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 410 + +typedef int XenXC; +typedef int XenEvtchn; +typedef int XenGnttab; + +# define XC_INTERFACE_FMT "%i" +# define XC_HANDLER_INITIAL_VALUE -1 + +static inline XenEvtchn xen_xc_evtchn_open(void *logger, + unsigned int open_flags) +{ + return xc_evtchn_open(); +} + +static inline XenGnttab xen_xc_gnttab_open(void *logger, + unsigned int open_flags) +{ + return xc_gnttab_open(); +} + +static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, + unsigned int open_flags) +{ + return xc_interface_open(); +} + +static inline int xc_fd(int xen_xc) +{ + return xen_xc; +} + + +static inline int xc_domain_populate_physmap_exact + (XenXC xc_handle, uint32_t domid, unsigned long nr_extents, + unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) +{ + return xc_domain_memory_populate_physmap + (xc_handle, domid, nr_extents, extent_order, mem_flags, extent_start); +} + +static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid, + unsigned int space, unsigned long idx, + xen_pfn_t gpfn) +{ + struct xen_add_to_physmap xatp = { + .domid = domid, + .space = space, + .idx = idx, + .gpfn = gpfn, + }; + + return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); +} + +static inline struct xs_handle *xs_open(unsigned long flags) +{ + return xs_daemon_open(); +} + +static inline void xs_close(struct xs_handle *xsh) +{ + if (xsh != NULL) { + xs_daemon_close(xsh); + } +} + + +/* Xen 4.1 */ +#else + +typedef xc_interface *XenXC; +typedef xc_evtchn *XenEvtchn; +typedef xc_gnttab *XenGnttab; + +# define XC_INTERFACE_FMT "%p" +# define XC_HANDLER_INITIAL_VALUE NULL + +static inline XenEvtchn xen_xc_evtchn_open(void *logger, + unsigned int open_flags) +{ + return xc_evtchn_open(logger, open_flags); +} + +static inline XenGnttab xen_xc_gnttab_open(void *logger, + unsigned int open_flags) +{ + return xc_gnttab_open(logger, open_flags); +} + +static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, + unsigned int open_flags) +{ + return xc_interface_open(logger, dombuild_logger, open_flags); +} + +/* FIXME There is now way to have the xen fd */ +static inline int xc_fd(xc_interface *xen_xc) +{ + return -1; +} +#endif + +/* Xen before 4.2 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 420 +static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, + uint64_t addr, uint32_t data) +{ + return -ENOSYS; +} +#else +static inline int xen_xc_hvm_inject_msi(XenXC xen_xc, domid_t dom, + uint64_t addr, uint32_t data) +{ + return xc_hvm_inject_msi(xen_xc, dom, addr, data); +} +#endif + +void destroy_hvm_domain(bool reboot); + +/* shutdown/destroy current domain because of an error */ +void xen_shutdown_fatal_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + +#endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/include/hw/xilinx.h b/include/hw/xilinx.h new file mode 100644 index 0000000000..6c1ee21c54 --- /dev/null +++ b/include/hw/xilinx.h @@ -0,0 +1,91 @@ +#ifndef HW_XILINX_H +#define HW_XILINX_H 1 + + +#include "qemu-common.h" +#include "qapi/qmp/qerror.h" +#include "hw/stream.h" +#include "net/net.h" + +static inline DeviceState * +xilinx_intc_create(hwaddr base, qemu_irq irq, int kind_of_intr) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", kind_of_intr); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + return dev; +} + +/* OPB Timer/Counter. */ +static inline DeviceState * +xilinx_timer_create(hwaddr base, qemu_irq irq, int oto, int freq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", oto); + qdev_prop_set_uint32(dev, "clock-frequency", freq); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + return dev; +} + +/* XPS Ethernet Lite MAC. */ +static inline DeviceState * +xilinx_ethlite_create(NICInfo *nd, hwaddr base, qemu_irq irq, + int txpingpong, int rxpingpong) +{ + DeviceState *dev; + + qemu_check_nic_model(nd, "xlnx.xps-ethernetlite"); + + dev = qdev_create(NULL, "xlnx.xps-ethernetlite"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint32(dev, "tx-ping-pong", txpingpong); + qdev_prop_set_uint32(dev, "rx-ping-pong", rxpingpong); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + return dev; +} + +static inline void +xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *peer, + hwaddr base, qemu_irq irq, int txmem, int rxmem) +{ + Error *errp = NULL; + + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint32(dev, "rxmem", rxmem); + qdev_prop_set_uint32(dev, "txmem", txmem); + object_property_set_link(OBJECT(dev), OBJECT(peer), "axistream-connected", + &errp); + assert_no_error(errp); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); +} + +static inline void +xilinx_axidma_init(DeviceState *dev, StreamSlave *peer, hwaddr base, + qemu_irq irq, qemu_irq irq2, int freqhz) +{ + Error *errp = NULL; + + qdev_prop_set_uint32(dev, "freqhz", freqhz); + object_property_set_link(OBJECT(dev), OBJECT(peer), "axistream-connected", + &errp); + assert_no_error(errp); + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, irq2); +} + +#endif diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h new file mode 100644 index 0000000000..2d936bb5f5 --- /dev/null +++ b/include/net/vhost_net.h @@ -0,0 +1,23 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "net/net.h" + +struct vhost_net; +typedef struct vhost_net VHostNetState; + +VHostNetState *vhost_net_init(NetClientState *backend, int devfd, bool force); + +bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues); +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues); + +void vhost_net_cleanup(VHostNetState *net); + +unsigned vhost_net_get_features(VHostNetState *net, unsigned features); +void vhost_net_ack_features(VHostNetState *net, unsigned features); + +bool vhost_net_virtqueue_pending(VHostNetState *net, int n); +void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, + int idx, bool mask); +#endif diff --git a/include/sysemu/watchdog.h b/include/sysemu/watchdog.h new file mode 100644 index 0000000000..3e9a970686 --- /dev/null +++ b/include/sysemu/watchdog.h @@ -0,0 +1,43 @@ +/* + * Virtual hardware watchdog. + * + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * By Richard W.M. Jones (rjones@redhat.com). + */ + +#ifndef QEMU_WATCHDOG_H +#define QEMU_WATCHDOG_H + +#include "qemu/queue.h" + +struct WatchdogTimerModel { + QLIST_ENTRY(WatchdogTimerModel) entry; + + /* Short name of the device - used to select it on the command line. */ + const char *wdt_name; + /* Longer description (eg. manufacturer and full model number). */ + const char *wdt_description; +}; +typedef struct WatchdogTimerModel WatchdogTimerModel; + +/* in hw/watchdog.c */ +int select_watchdog(const char *p); +int select_watchdog_action(const char *action); +void watchdog_add_model(WatchdogTimerModel *model); +void watchdog_perform_action(void); + +#endif /* QEMU_WATCHDOG_H */ -- cgit v1.2.3-55-g7522 From 3bd884511f8dc44a01e32878b2972443a16db70d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 14:38:25 +0100 Subject: hw: move timer devices to hw/timer/, configure with default-configs/ Signed-off-by: Paolo Bonzini --- default-configs/arm-softmmu.mak | 4 + default-configs/sparc-softmmu.mak | 1 + hw/arm/Makefile.objs | 17 +- hw/arm_mptimer.c | 309 -------- hw/cris/Makefile.objs | 1 - hw/etraxfs_timer.c | 351 --------- hw/exynos4210_mct.c | 1482 ------------------------------------- hw/exynos4210_pwm.c | 422 ----------- hw/exynos4210_rtc.c | 592 --------------- hw/grlib_gptimer.c | 404 ---------- hw/imx_timer.c | 689 ----------------- hw/input/Makefile.objs | 4 + hw/input/milkymist-softusb.c | 334 +++++++++ hw/input/pxa2xx_keypad.c | 335 +++++++++ hw/input/tsc210x.c | 1293 ++++++++++++++++++++++++++++++++ hw/lm32/Makefile.objs | 3 - hw/lm32_timer.c | 230 ------ hw/milkymist-softusb.c | 334 --------- hw/milkymist-sysctl.c | 338 --------- hw/omap_gptimer.c | 488 ------------ hw/omap_synctimer.c | 102 --- hw/pxa2xx_keypad.c | 335 --------- hw/pxa2xx_timer.c | 583 --------------- hw/sh4/Makefile.objs | 2 +- hw/sh_timer.c | 333 --------- hw/slavio_timer.c | 435 ----------- hw/sparc/Makefile.objs | 4 +- hw/timer/Makefile.objs | 16 + hw/timer/arm_mptimer.c | 309 ++++++++ hw/timer/etraxfs_timer.c | 351 +++++++++ hw/timer/exynos4210_mct.c | 1482 +++++++++++++++++++++++++++++++++++++ hw/timer/exynos4210_pwm.c | 422 +++++++++++ hw/timer/exynos4210_rtc.c | 592 +++++++++++++++ hw/timer/grlib_gptimer.c | 404 ++++++++++ hw/timer/imx_timer.c | 689 +++++++++++++++++ hw/timer/lm32_timer.c | 230 ++++++ hw/timer/milkymist-sysctl.c | 338 +++++++++ hw/timer/omap_gptimer.c | 488 ++++++++++++ hw/timer/omap_synctimer.c | 102 +++ hw/timer/pxa2xx_timer.c | 583 +++++++++++++++ hw/timer/sh_timer.c | 333 +++++++++ hw/timer/slavio_timer.c | 435 +++++++++++ hw/timer/tusb6010.c | 813 ++++++++++++++++++++ hw/tsc210x.c | 1293 -------------------------------- hw/tusb6010.c | 813 -------------------- include/hw/sparc/sun4m.h | 2 + 46 files changed, 9570 insertions(+), 9550 deletions(-) delete mode 100644 hw/arm_mptimer.c delete mode 100644 hw/etraxfs_timer.c delete mode 100644 hw/exynos4210_mct.c delete mode 100644 hw/exynos4210_pwm.c delete mode 100644 hw/exynos4210_rtc.c delete mode 100644 hw/grlib_gptimer.c delete mode 100644 hw/imx_timer.c create mode 100644 hw/input/milkymist-softusb.c create mode 100644 hw/input/pxa2xx_keypad.c create mode 100644 hw/input/tsc210x.c delete mode 100644 hw/lm32_timer.c delete mode 100644 hw/milkymist-softusb.c delete mode 100644 hw/milkymist-sysctl.c delete mode 100644 hw/omap_gptimer.c delete mode 100644 hw/omap_synctimer.c delete mode 100644 hw/pxa2xx_keypad.c delete mode 100644 hw/pxa2xx_timer.c delete mode 100644 hw/sh_timer.c delete mode 100644 hw/slavio_timer.c create mode 100644 hw/timer/arm_mptimer.c create mode 100644 hw/timer/etraxfs_timer.c create mode 100644 hw/timer/exynos4210_mct.c create mode 100644 hw/timer/exynos4210_pwm.c create mode 100644 hw/timer/exynos4210_rtc.c create mode 100644 hw/timer/grlib_gptimer.c create mode 100644 hw/timer/imx_timer.c create mode 100644 hw/timer/lm32_timer.c create mode 100644 hw/timer/milkymist-sysctl.c create mode 100644 hw/timer/omap_gptimer.c create mode 100644 hw/timer/omap_synctimer.c create mode 100644 hw/timer/pxa2xx_timer.c create mode 100644 hw/timer/sh_timer.c create mode 100644 hw/timer/slavio_timer.c create mode 100644 hw/timer/tusb6010.c delete mode 100644 hw/tsc210x.c delete mode 100644 hw/tusb6010.c (limited to 'include') diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 8da5ec857e..94b52da3c8 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -34,6 +34,7 @@ CONFIG_MICRODRIVE=y CONFIG_USB_MUSB=y CONFIG_ARM_TIMER=y +CONFIG_ARM_MPTIMER=y CONFIG_PL011=y CONFIG_PL022=y CONFIG_PL031=y @@ -53,10 +54,13 @@ CONFIG_PXA2XX=y CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_XILINX_SPIPS=y + CONFIG_MARVELL_88W8618=y CONFIG_OMAP=y +CONFIG_TSC210X=y CONFIG_BLIZZARD=y CONFIG_ONENAND=y +CONFIG_TUSB6010=y CONFIG_IMX=y CONFIG_ZAURUS=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index da5b02d405..6a2bad3962 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -10,5 +10,6 @@ CONFIG_EMPTY_SLOT=y CONFIG_PCNET_COMMON=y CONFIG_LANCE=y CONFIG_TCX=y +CONFIG_SLAVIO=y CONFIG_CS4231=y CONFIG_GRLIB=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 26e107f93c..7691540297 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -3,23 +3,20 @@ obj-y += arm_gic.o arm_gic_common.o obj-y += a9scu.o obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o -obj-y += exynos4210_pwm.o -obj-y += exynos4210_pmu.o exynos4210_mct.o -obj-y += exynos4210_rtc.o -obj-y += arm_mptimer.o a15mpcore.o +obj-y += exynos4210_pmu.o +obj-y += a15mpcore.o obj-y += armv7m_nvic.o -obj-y += pxa2xx_timer.o pxa2xx_dma.o -obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o +obj-y += pxa2xx_dma.o +obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o obj-y += zaurus.o obj-y += omap_dma.o omap_clk.o omap_mmc.o \ omap_gpio.o omap_intc.o -obj-y += soc_dma.o omap_gptimer.o omap_synctimer.o \ +obj-y += soc_dma.o \ omap_gpmc.o omap_sdrc.o omap_tap.o omap_l4.o -obj-y += tsc210x.o -obj-y += cbus.o tusb6010.o +obj-y += cbus.o obj-y += mst_fpga.o obj-y += strongarm.o -obj-y += imx_ccm.o imx_timer.o imx_avic.o +obj-y += imx_ccm.o imx_avic.o obj-$(CONFIG_KVM) += kvm/arm_gic.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c deleted file mode 100644 index 317f5e43ed..0000000000 --- a/hw/arm_mptimer.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP - * - * Copyright (c) 2006-2007 CodeSourcery. - * Copyright (c) 2011 Linaro Limited - * Written by Paul Brook, Peter Maydell - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/sysbus.h" -#include "qemu/timer.h" - -/* This device implements the per-cpu private timer and watchdog block - * which is used in both the ARM11MPCore and Cortex-A9MP. - */ - -#define MAX_CPUS 4 - -/* State of a single timer or watchdog block */ -typedef struct { - uint32_t count; - uint32_t load; - uint32_t control; - uint32_t status; - int64_t tick; - QEMUTimer *timer; - qemu_irq irq; - MemoryRegion iomem; -} TimerBlock; - -typedef struct { - SysBusDevice busdev; - uint32_t num_cpu; - TimerBlock timerblock[MAX_CPUS]; - MemoryRegion iomem; -} ARMMPTimerState; - -static inline int get_current_cpu(ARMMPTimerState *s) -{ - CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env); - - if (cpu_single_cpu->cpu_index >= s->num_cpu) { - hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", - s->num_cpu, cpu_single_cpu->cpu_index); - } - return cpu_single_cpu->cpu_index; -} - -static inline void timerblock_update_irq(TimerBlock *tb) -{ - qemu_set_irq(tb->irq, tb->status); -} - -/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t timerblock_scale(TimerBlock *tb) -{ - return (((tb->control >> 8) & 0xff) + 1) * 10; -} - -static void timerblock_reload(TimerBlock *tb, int restart) -{ - if (tb->count == 0) { - return; - } - if (restart) { - tb->tick = qemu_get_clock_ns(vm_clock); - } - tb->tick += (int64_t)tb->count * timerblock_scale(tb); - qemu_mod_timer(tb->timer, tb->tick); -} - -static void timerblock_tick(void *opaque) -{ - TimerBlock *tb = (TimerBlock *)opaque; - tb->status = 1; - if (tb->control & 2) { - tb->count = tb->load; - timerblock_reload(tb, 0); - } else { - tb->count = 0; - } - timerblock_update_irq(tb); -} - -static uint64_t timerblock_read(void *opaque, hwaddr addr, - unsigned size) -{ - TimerBlock *tb = (TimerBlock *)opaque; - int64_t val; - switch (addr) { - case 0: /* Load */ - return tb->load; - case 4: /* Counter. */ - if (((tb->control & 1) == 0) || (tb->count == 0)) { - return 0; - } - /* Slow and ugly, but hopefully won't happen too often. */ - val = tb->tick - qemu_get_clock_ns(vm_clock); - val /= timerblock_scale(tb); - if (val < 0) { - val = 0; - } - return val; - case 8: /* Control. */ - return tb->control; - case 12: /* Interrupt status. */ - return tb->status; - default: - return 0; - } -} - -static void timerblock_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - TimerBlock *tb = (TimerBlock *)opaque; - int64_t old; - switch (addr) { - case 0: /* Load */ - tb->load = value; - /* Fall through. */ - case 4: /* Counter. */ - if ((tb->control & 1) && tb->count) { - /* Cancel the previous timer. */ - qemu_del_timer(tb->timer); - } - tb->count = value; - if (tb->control & 1) { - timerblock_reload(tb, 1); - } - break; - case 8: /* Control. */ - old = tb->control; - tb->control = value; - if (((old & 1) == 0) && (value & 1)) { - if (tb->count == 0 && (tb->control & 2)) { - tb->count = tb->load; - } - timerblock_reload(tb, 1); - } - break; - case 12: /* Interrupt status. */ - tb->status &= ~value; - timerblock_update_irq(tb); - break; - } -} - -/* Wrapper functions to implement the "read timer/watchdog for - * the current CPU" memory regions. - */ -static uint64_t arm_thistimer_read(void *opaque, hwaddr addr, - unsigned size) -{ - ARMMPTimerState *s = (ARMMPTimerState *)opaque; - int id = get_current_cpu(s); - return timerblock_read(&s->timerblock[id], addr, size); -} - -static void arm_thistimer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - ARMMPTimerState *s = (ARMMPTimerState *)opaque; - int id = get_current_cpu(s); - timerblock_write(&s->timerblock[id], addr, value, size); -} - -static const MemoryRegionOps arm_thistimer_ops = { - .read = arm_thistimer_read, - .write = arm_thistimer_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps timerblock_ops = { - .read = timerblock_read, - .write = timerblock_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void timerblock_reset(TimerBlock *tb) -{ - tb->count = 0; - tb->load = 0; - tb->control = 0; - tb->status = 0; - tb->tick = 0; - if (tb->timer) { - qemu_del_timer(tb->timer); - } -} - -static void arm_mptimer_reset(DeviceState *dev) -{ - ARMMPTimerState *s = - FROM_SYSBUS(ARMMPTimerState, SYS_BUS_DEVICE(dev)); - int i; - for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { - timerblock_reset(&s->timerblock[i]); - } -} - -static int arm_mptimer_init(SysBusDevice *dev) -{ - ARMMPTimerState *s = FROM_SYSBUS(ARMMPTimerState, dev); - int i; - if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) { - hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS); - } - /* We implement one timer block per CPU, and expose multiple MMIO regions: - * * region 0 is "timer for this core" - * * region 1 is "timer for core 0" - * * region 2 is "timer for core 1" - * and so on. - * The outgoing interrupt lines are - * * timer for core 0 - * * timer for core 1 - * and so on. - */ - memory_region_init_io(&s->iomem, &arm_thistimer_ops, s, - "arm_mptimer_timer", 0x20); - sysbus_init_mmio(dev, &s->iomem); - for (i = 0; i < s->num_cpu; i++) { - TimerBlock *tb = &s->timerblock[i]; - tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb); - sysbus_init_irq(dev, &tb->irq); - memory_region_init_io(&tb->iomem, &timerblock_ops, tb, - "arm_mptimer_timerblock", 0x20); - sysbus_init_mmio(dev, &tb->iomem); - } - - return 0; -} - -static const VMStateDescription vmstate_timerblock = { - .name = "arm_mptimer_timerblock", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(count, TimerBlock), - VMSTATE_UINT32(load, TimerBlock), - VMSTATE_UINT32(control, TimerBlock), - VMSTATE_UINT32(status, TimerBlock), - VMSTATE_INT64(tick, TimerBlock), - VMSTATE_TIMER(timer, TimerBlock), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_arm_mptimer = { - .name = "arm_mptimer", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, - 2, vmstate_timerblock, TimerBlock), - VMSTATE_END_OF_LIST() - } -}; - -static Property arm_mptimer_properties[] = { - DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void arm_mptimer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - sbc->init = arm_mptimer_init; - dc->vmsd = &vmstate_arm_mptimer; - dc->reset = arm_mptimer_reset; - dc->no_user = 1; - dc->props = arm_mptimer_properties; -} - -static const TypeInfo arm_mptimer_info = { - .name = "arm_mptimer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARMMPTimerState), - .class_init = arm_mptimer_class_init, -}; - -static void arm_mptimer_register_types(void) -{ - type_register_static(&arm_mptimer_info); -} - -type_init(arm_mptimer_register_types) diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs index c4d5189fc0..a8a4a9efcf 100644 --- a/hw/cris/Makefile.objs +++ b/hw/cris/Makefile.objs @@ -1,7 +1,6 @@ # IO blocks obj-y += etraxfs_dma.o obj-y += etraxfs_pic.o -obj-y += etraxfs_timer.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/etraxfs_timer.c b/hw/etraxfs_timer.c deleted file mode 100644 index 3cd9476bb1..0000000000 --- a/hw/etraxfs_timer.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * QEMU ETRAX Timers - * - * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" - -#define D(x) - -#define RW_TMR0_DIV 0x00 -#define R_TMR0_DATA 0x04 -#define RW_TMR0_CTRL 0x08 -#define RW_TMR1_DIV 0x10 -#define R_TMR1_DATA 0x14 -#define RW_TMR1_CTRL 0x18 -#define R_TIME 0x38 -#define RW_WD_CTRL 0x40 -#define R_WD_STAT 0x44 -#define RW_INTR_MASK 0x48 -#define RW_ACK_INTR 0x4c -#define R_INTR 0x50 -#define R_MASKED_INTR 0x54 - -struct etrax_timer { - SysBusDevice busdev; - MemoryRegion mmio; - qemu_irq irq; - qemu_irq nmi; - - QEMUBH *bh_t0; - QEMUBH *bh_t1; - QEMUBH *bh_wd; - ptimer_state *ptimer_t0; - ptimer_state *ptimer_t1; - ptimer_state *ptimer_wd; - - int wd_hits; - - /* Control registers. */ - uint32_t rw_tmr0_div; - uint32_t r_tmr0_data; - uint32_t rw_tmr0_ctrl; - - uint32_t rw_tmr1_div; - uint32_t r_tmr1_data; - uint32_t rw_tmr1_ctrl; - - uint32_t rw_wd_ctrl; - - uint32_t rw_intr_mask; - uint32_t rw_ack_intr; - uint32_t r_intr; - uint32_t r_masked_intr; -}; - -static uint64_t -timer_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct etrax_timer *t = opaque; - uint32_t r = 0; - - switch (addr) { - case R_TMR0_DATA: - r = ptimer_get_count(t->ptimer_t0); - break; - case R_TMR1_DATA: - r = ptimer_get_count(t->ptimer_t1); - break; - case R_TIME: - r = qemu_get_clock_ns(vm_clock) / 10; - break; - case RW_INTR_MASK: - r = t->rw_intr_mask; - break; - case R_MASKED_INTR: - r = t->r_intr & t->rw_intr_mask; - break; - default: - D(printf ("%s %x\n", __func__, addr)); - break; - } - return r; -} - -static void update_ctrl(struct etrax_timer *t, int tnum) -{ - unsigned int op; - unsigned int freq; - unsigned int freq_hz; - unsigned int div; - uint32_t ctrl; - - ptimer_state *timer; - - if (tnum == 0) { - ctrl = t->rw_tmr0_ctrl; - div = t->rw_tmr0_div; - timer = t->ptimer_t0; - } else { - ctrl = t->rw_tmr1_ctrl; - div = t->rw_tmr1_div; - timer = t->ptimer_t1; - } - - - op = ctrl & 3; - freq = ctrl >> 2; - freq_hz = 32000000; - - switch (freq) - { - case 0: - case 1: - D(printf ("extern or disabled timer clock?\n")); - break; - case 4: freq_hz = 29493000; break; - case 5: freq_hz = 32000000; break; - case 6: freq_hz = 32768000; break; - case 7: freq_hz = 100000000; break; - default: - abort(); - break; - } - - D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); - ptimer_set_freq(timer, freq_hz); - ptimer_set_limit(timer, div, 0); - - switch (op) - { - case 0: - /* Load. */ - ptimer_set_limit(timer, div, 1); - break; - case 1: - /* Hold. */ - ptimer_stop(timer); - break; - case 2: - /* Run. */ - ptimer_run(timer, 0); - break; - default: - abort(); - break; - } -} - -static void timer_update_irq(struct etrax_timer *t) -{ - t->r_intr &= ~(t->rw_ack_intr); - t->r_masked_intr = t->r_intr & t->rw_intr_mask; - - D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr)); - qemu_set_irq(t->irq, !!t->r_masked_intr); -} - -static void timer0_hit(void *opaque) -{ - struct etrax_timer *t = opaque; - t->r_intr |= 1; - timer_update_irq(t); -} - -static void timer1_hit(void *opaque) -{ - struct etrax_timer *t = opaque; - t->r_intr |= 2; - timer_update_irq(t); -} - -static void watchdog_hit(void *opaque) -{ - struct etrax_timer *t = opaque; - if (t->wd_hits == 0) { - /* real hw gives a single tick before reseting but we are - a bit friendlier to compensate for our slower execution. */ - ptimer_set_count(t->ptimer_wd, 10); - ptimer_run(t->ptimer_wd, 1); - qemu_irq_raise(t->nmi); - } - else - qemu_system_reset_request(); - - t->wd_hits++; -} - -static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value) -{ - unsigned int wd_en = t->rw_wd_ctrl & (1 << 8); - unsigned int wd_key = t->rw_wd_ctrl >> 9; - unsigned int wd_cnt = t->rw_wd_ctrl & 511; - unsigned int new_key = value >> 9 & ((1 << 7) - 1); - unsigned int new_cmd = (value >> 8) & 1; - - /* If the watchdog is enabled, they written key must match the - complement of the previous. */ - wd_key = ~wd_key & ((1 << 7) - 1); - - if (wd_en && wd_key != new_key) - return; - - D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", - wd_en, new_key, wd_key, new_cmd, wd_cnt)); - - if (t->wd_hits) - qemu_irq_lower(t->nmi); - - t->wd_hits = 0; - - ptimer_set_freq(t->ptimer_wd, 760); - if (wd_cnt == 0) - wd_cnt = 256; - ptimer_set_count(t->ptimer_wd, wd_cnt); - if (new_cmd) - ptimer_run(t->ptimer_wd, 1); - else - ptimer_stop(t->ptimer_wd); - - t->rw_wd_ctrl = value; -} - -static void -timer_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct etrax_timer *t = opaque; - uint32_t value = val64; - - switch (addr) - { - case RW_TMR0_DIV: - t->rw_tmr0_div = value; - break; - case RW_TMR0_CTRL: - D(printf ("RW_TMR0_CTRL=%x\n", value)); - t->rw_tmr0_ctrl = value; - update_ctrl(t, 0); - break; - case RW_TMR1_DIV: - t->rw_tmr1_div = value; - break; - case RW_TMR1_CTRL: - D(printf ("RW_TMR1_CTRL=%x\n", value)); - t->rw_tmr1_ctrl = value; - update_ctrl(t, 1); - break; - case RW_INTR_MASK: - D(printf ("RW_INTR_MASK=%x\n", value)); - t->rw_intr_mask = value; - timer_update_irq(t); - break; - case RW_WD_CTRL: - timer_watchdog_update(t, value); - break; - case RW_ACK_INTR: - t->rw_ack_intr = value; - timer_update_irq(t); - t->rw_ack_intr = 0; - break; - default: - printf ("%s " TARGET_FMT_plx " %x\n", - __func__, addr, value); - break; - } -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void etraxfs_timer_reset(void *opaque) -{ - struct etrax_timer *t = opaque; - - ptimer_stop(t->ptimer_t0); - ptimer_stop(t->ptimer_t1); - ptimer_stop(t->ptimer_wd); - t->rw_wd_ctrl = 0; - t->r_intr = 0; - t->rw_intr_mask = 0; - qemu_irq_lower(t->irq); -} - -static int etraxfs_timer_init(SysBusDevice *dev) -{ - struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev); - - t->bh_t0 = qemu_bh_new(timer0_hit, t); - t->bh_t1 = qemu_bh_new(timer1_hit, t); - t->bh_wd = qemu_bh_new(watchdog_hit, t); - t->ptimer_t0 = ptimer_init(t->bh_t0); - t->ptimer_t1 = ptimer_init(t->bh_t1); - t->ptimer_wd = ptimer_init(t->bh_wd); - - sysbus_init_irq(dev, &t->irq); - sysbus_init_irq(dev, &t->nmi); - - memory_region_init_io(&t->mmio, &timer_ops, t, "etraxfs-timer", 0x5c); - sysbus_init_mmio(dev, &t->mmio); - qemu_register_reset(etraxfs_timer_reset, t); - return 0; -} - -static void etraxfs_timer_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = etraxfs_timer_init; -} - -static const TypeInfo etraxfs_timer_info = { - .name = "etraxfs,timer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof (struct etrax_timer), - .class_init = etraxfs_timer_class_init, -}; - -static void etraxfs_timer_register_types(void) -{ - type_register_static(&etraxfs_timer_info); -} - -type_init(etraxfs_timer_register_types) diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c deleted file mode 100644 index 87ce75b643..0000000000 --- a/hw/exynos4210_mct.c +++ /dev/null @@ -1,1482 +0,0 @@ -/* - * Samsung exynos4210 Multi Core timer - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * - * Evgeny Voevodin - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -/* - * Global Timer: - * - * Consists of two timers. First represents Free Running Counter and second - * is used to measure interval from FRC to nearest comparator. - * - * 0 UINT64_MAX - * | timer0 | - * | <-------------------------------------------------------------- | - * | --------------------------------------------frc---------------> | - * |______________________________________________|__________________| - * CMP0 CMP1 CMP2 | CMP3 - * __| |_ - * | timer1 | - * | -------------> | - * frc CMPx - * - * Problem: when implementing global timer as is, overflow arises. - * next_time = cur_time + period * count; - * period and count are 64 bits width. - * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT - * register during each event. - * - * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because - * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--. - * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0 - * generates IRQs suffers from too frequently events. Better to have one - * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT, - * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values, - * there is no way to avoid frequently events). - */ - -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "qemu-common.h" -#include "hw/ptimer.h" - -#include "hw/arm/exynos4210.h" - -//#define DEBUG_MCT - -#ifdef DEBUG_MCT -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define MCT_CFG 0x000 -#define G_CNT_L 0x100 -#define G_CNT_U 0x104 -#define G_CNT_WSTAT 0x110 -#define G_COMP0_L 0x200 -#define G_COMP0_U 0x204 -#define G_COMP0_ADD_INCR 0x208 -#define G_COMP1_L 0x210 -#define G_COMP1_U 0x214 -#define G_COMP1_ADD_INCR 0x218 -#define G_COMP2_L 0x220 -#define G_COMP2_U 0x224 -#define G_COMP2_ADD_INCR 0x228 -#define G_COMP3_L 0x230 -#define G_COMP3_U 0x234 -#define G_COMP3_ADD_INCR 0x238 -#define G_TCON 0x240 -#define G_INT_CSTAT 0x244 -#define G_INT_ENB 0x248 -#define G_WSTAT 0x24C -#define L0_TCNTB 0x300 -#define L0_TCNTO 0x304 -#define L0_ICNTB 0x308 -#define L0_ICNTO 0x30C -#define L0_FRCNTB 0x310 -#define L0_FRCNTO 0x314 -#define L0_TCON 0x320 -#define L0_INT_CSTAT 0x330 -#define L0_INT_ENB 0x334 -#define L0_WSTAT 0x340 -#define L1_TCNTB 0x400 -#define L1_TCNTO 0x404 -#define L1_ICNTB 0x408 -#define L1_ICNTO 0x40C -#define L1_FRCNTB 0x410 -#define L1_FRCNTO 0x414 -#define L1_TCON 0x420 -#define L1_INT_CSTAT 0x430 -#define L1_INT_ENB 0x434 -#define L1_WSTAT 0x440 - -#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF) -#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7)) - -#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10) -#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10) - -#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10) -#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10) - -#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10) - -/* MCT bits */ -#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x)) -#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1)) -#define G_TCON_TIMER_ENABLE (1 << 8) - -#define G_INT_ENABLE(x) (1 << (x)) -#define G_INT_CSTAT_COMP(x) (1 << (x)) - -#define G_CNT_WSTAT_L 1 -#define G_CNT_WSTAT_U 2 - -#define G_WSTAT_COMP_L(x) (1 << 4 * (x)) -#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1)) -#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2)) -#define G_WSTAT_TCON_WRITE (1 << 16) - -#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100) -#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \ - (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2) - -#define L_ICNTB_MANUAL_UPDATE (1 << 31) - -#define L_TCON_TICK_START (1) -#define L_TCON_INT_START (1 << 1) -#define L_TCON_INTERVAL_MODE (1 << 2) -#define L_TCON_FRC_START (1 << 3) - -#define L_INT_CSTAT_INTCNT (1 << 0) -#define L_INT_CSTAT_FRCCNT (1 << 1) - -#define L_INT_INTENB_ICNTEIE (1 << 0) -#define L_INT_INTENB_FRCEIE (1 << 1) - -#define L_WSTAT_TCNTB_WRITE (1 << 0) -#define L_WSTAT_ICNTB_WRITE (1 << 1) -#define L_WSTAT_FRCCNTB_WRITE (1 << 2) -#define L_WSTAT_TCON_WRITE (1 << 3) - -enum LocalTimerRegCntIndexes { - L_REG_CNT_TCNTB, - L_REG_CNT_TCNTO, - L_REG_CNT_ICNTB, - L_REG_CNT_ICNTO, - L_REG_CNT_FRCCNTB, - L_REG_CNT_FRCCNTO, - - L_REG_CNT_AMOUNT -}; - -#define MCT_NIRQ 6 -#define MCT_SFR_SIZE 0x444 - -#define MCT_GT_CMP_NUM 4 - -#define MCT_GT_MAX_VAL UINT64_MAX - -#define MCT_GT_COUNTER_STEP 0x100000000ULL -#define MCT_LT_COUNTER_STEP 0x100000000ULL -#define MCT_LT_CNT_LOW_LIMIT 0x100 - -/* global timer */ -typedef struct { - qemu_irq irq[MCT_GT_CMP_NUM]; - - struct gregs { - uint64_t cnt; - uint32_t cnt_wstat; - uint32_t tcon; - uint32_t int_cstat; - uint32_t int_enb; - uint32_t wstat; - uint64_t comp[MCT_GT_CMP_NUM]; - uint32_t comp_add_incr[MCT_GT_CMP_NUM]; - } reg; - - uint64_t count; /* Value FRC was armed with */ - int32_t curr_comp; /* Current comparator FRC is running to */ - - ptimer_state *ptimer_frc; /* FRC timer */ - -} Exynos4210MCTGT; - -/* local timer */ -typedef struct { - int id; /* timer id */ - qemu_irq irq; /* local timer irq */ - - struct tick_timer { - uint32_t cnt_run; /* cnt timer is running */ - uint32_t int_run; /* int timer is running */ - - uint32_t last_icnto; - uint32_t last_tcnto; - uint32_t tcntb; /* initial value for TCNTB */ - uint32_t icntb; /* initial value for ICNTB */ - - /* for step mode */ - uint64_t distance; /* distance to count to the next event */ - uint64_t progress; /* progress when counting by steps */ - uint64_t count; /* count to arm timer with */ - - ptimer_state *ptimer_tick; /* timer for tick counter */ - } tick_timer; - - /* use ptimer.c to represent count down timer */ - - ptimer_state *ptimer_frc; /* timer for free running counter */ - - /* registers */ - struct lregs { - uint32_t cnt[L_REG_CNT_AMOUNT]; - uint32_t tcon; - uint32_t int_cstat; - uint32_t int_enb; - uint32_t wstat; - } reg; - -} Exynos4210MCTLT; - -typedef struct Exynos4210MCTState { - SysBusDevice busdev; - MemoryRegion iomem; - - /* Registers */ - uint32_t reg_mct_cfg; - - Exynos4210MCTLT l_timer[2]; - Exynos4210MCTGT g_timer; - - uint32_t freq; /* all timers tick frequency, TCLK */ -} Exynos4210MCTState; - -/*** VMState ***/ -static const VMStateDescription vmstate_tick_timer = { - .name = "exynos4210.mct.tick_timer", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cnt_run, struct tick_timer), - VMSTATE_UINT32(int_run, struct tick_timer), - VMSTATE_UINT32(last_icnto, struct tick_timer), - VMSTATE_UINT32(last_tcnto, struct tick_timer), - VMSTATE_UINT32(tcntb, struct tick_timer), - VMSTATE_UINT32(icntb, struct tick_timer), - VMSTATE_UINT64(distance, struct tick_timer), - VMSTATE_UINT64(progress, struct tick_timer), - VMSTATE_UINT64(count, struct tick_timer), - VMSTATE_PTIMER(ptimer_tick, struct tick_timer), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_lregs = { - .name = "exynos4210.mct.lregs", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT), - VMSTATE_UINT32(tcon, struct lregs), - VMSTATE_UINT32(int_cstat, struct lregs), - VMSTATE_UINT32(int_enb, struct lregs), - VMSTATE_UINT32(wstat, struct lregs), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_mct_lt = { - .name = "exynos4210.mct.lt", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(id, Exynos4210MCTLT), - VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0, - vmstate_tick_timer, - struct tick_timer), - VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT), - VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0, - vmstate_lregs, - struct lregs), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_gregs = { - .name = "exynos4210.mct.lregs", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(cnt, struct gregs), - VMSTATE_UINT32(cnt_wstat, struct gregs), - VMSTATE_UINT32(tcon, struct gregs), - VMSTATE_UINT32(int_cstat, struct gregs), - VMSTATE_UINT32(int_enb, struct gregs), - VMSTATE_UINT32(wstat, struct gregs), - VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM), - VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs, - MCT_GT_CMP_NUM), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_mct_gt = { - .name = "exynos4210.mct.lt", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs, - struct gregs), - VMSTATE_UINT64(count, Exynos4210MCTGT), - VMSTATE_INT32(curr_comp, Exynos4210MCTGT), - VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_mct_state = { - .name = "exynos4210.mct", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState), - VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0, - vmstate_exynos4210_mct_lt, Exynos4210MCTLT), - VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0, - vmstate_exynos4210_mct_gt, Exynos4210MCTGT), - VMSTATE_UINT32(freq, Exynos4210MCTState), - VMSTATE_END_OF_LIST() - } -}; - -static void exynos4210_mct_update_freq(Exynos4210MCTState *s); - -/* - * Set counter of FRC global timer. - */ -static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count) -{ - s->count = count; - DPRINTF("global timer frc set count 0x%llx\n", count); - ptimer_set_count(s->ptimer_frc, count); -} - -/* - * Get counter of FRC global timer. - */ -static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s) -{ - uint64_t count = 0; - count = ptimer_get_count(s->ptimer_frc); - count = s->count - count; - return s->reg.cnt + count; -} - -/* - * Stop global FRC timer - */ -static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) -{ - DPRINTF("global timer frc stop\n"); - - ptimer_stop(s->ptimer_frc); -} - -/* - * Start global FRC timer - */ -static void exynos4210_gfrc_start(Exynos4210MCTGT *s) -{ - DPRINTF("global timer frc start\n"); - - ptimer_run(s->ptimer_frc, 1); -} - -/* - * Find next nearest Comparator. If current Comparator value equals to other - * Comparator value, skip them both - */ -static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s) -{ - int res; - int i; - int enabled; - uint64_t min; - int min_comp_i; - uint64_t gfrc; - uint64_t distance; - uint64_t distance_min; - int comp_i; - - /* get gfrc count */ - gfrc = exynos4210_gfrc_get_count(&s->g_timer); - - min = UINT64_MAX; - distance_min = UINT64_MAX; - comp_i = MCT_GT_CMP_NUM; - min_comp_i = MCT_GT_CMP_NUM; - enabled = 0; - - /* lookup for nearest comparator */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - - if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) { - - enabled = 1; - - if (s->g_timer.reg.comp[i] > gfrc) { - /* Comparator is upper then FRC */ - distance = s->g_timer.reg.comp[i] - gfrc; - - if (distance <= distance_min) { - distance_min = distance; - comp_i = i; - } - } else { - /* Comparator is below FRC, find the smallest */ - - if (s->g_timer.reg.comp[i] <= min) { - min = s->g_timer.reg.comp[i]; - min_comp_i = i; - } - } - } - } - - if (!enabled) { - /* All Comparators disabled */ - res = -1; - } else if (comp_i < MCT_GT_CMP_NUM) { - /* Found upper Comparator */ - res = comp_i; - } else { - /* All Comparators are below or equal to FRC */ - res = min_comp_i; - } - - DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", - res, - s->g_timer.reg.comp[res], - distance_min, - gfrc); - - return res; -} - -/* - * Get distance to nearest Comparator - */ -static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id) -{ - if (id == -1) { - /* no enabled Comparators, choose max distance */ - return MCT_GT_COUNTER_STEP; - } - if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) { - return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt; - } else { - return MCT_GT_COUNTER_STEP; - } -} - -/* - * Restart global FRC timer - */ -static void exynos4210_gfrc_restart(Exynos4210MCTState *s) -{ - uint64_t distance; - - exynos4210_gfrc_stop(&s->g_timer); - - s->g_timer.curr_comp = exynos4210_gcomp_find(s); - - distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); - - if (distance > MCT_GT_COUNTER_STEP || !distance) { - distance = MCT_GT_COUNTER_STEP; - } - - exynos4210_gfrc_set_count(&s->g_timer, distance); - exynos4210_gfrc_start(&s->g_timer); -} - -/* - * Raise global timer CMP IRQ - */ -static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id) -{ - Exynos4210MCTGT *s = opaque; - - /* If CSTAT is pending and IRQ is enabled */ - if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) && - (s->reg.int_enb & G_INT_ENABLE(id))) { - DPRINTF("gcmp timer[%d] IRQ\n", id); - qemu_irq_raise(s->irq[id]); - } -} - -/* - * Lower global timer CMP IRQ - */ -static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id) -{ - Exynos4210MCTGT *s = opaque; - qemu_irq_lower(s->irq[id]); -} - -/* - * Global timer FRC event handler. - * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP - * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value - */ -static void exynos4210_gfrc_event(void *opaque) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; - int i; - uint64_t distance; - - DPRINTF("\n"); - - s->g_timer.reg.cnt += s->g_timer.count; - - /* Process all comparators */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - - if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) { - /* reached nearest comparator */ - - s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i); - - /* Auto increment */ - if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) { - s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i]; - } - - /* IRQ */ - exynos4210_gcomp_raise_irq(&s->g_timer, i); - } - } - - /* Reload FRC to reach nearest comparator */ - s->g_timer.curr_comp = exynos4210_gcomp_find(s); - distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); - if (distance > MCT_GT_COUNTER_STEP || !distance) { - distance = MCT_GT_COUNTER_STEP; - } - exynos4210_gfrc_set_count(&s->g_timer, distance); - - exynos4210_gfrc_start(&s->g_timer); -} - -/* - * Get counter of FRC local timer. - */ -static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s) -{ - return ptimer_get_count(s->ptimer_frc); -} - -/* - * Set counter of FRC local timer. - */ -static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) -{ - if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) { - ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP); - } else { - ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]); - } -} - -/* - * Start local FRC timer - */ -static void exynos4210_lfrc_start(Exynos4210MCTLT *s) -{ - ptimer_run(s->ptimer_frc, 1); -} - -/* - * Stop local FRC timer - */ -static void exynos4210_lfrc_stop(Exynos4210MCTLT *s) -{ - ptimer_stop(s->ptimer_frc); -} - -/* - * Local timer free running counter tick handler - */ -static void exynos4210_lfrc_event(void *opaque) -{ - Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; - - /* local frc expired */ - - DPRINTF("\n"); - - s->reg.int_cstat |= L_INT_CSTAT_FRCCNT; - - /* update frc counter */ - exynos4210_lfrc_update_count(s); - - /* raise irq */ - if (s->reg.int_enb & L_INT_INTENB_FRCEIE) { - qemu_irq_raise(s->irq); - } - - /* we reached here, this means that timer is enabled */ - exynos4210_lfrc_start(s); -} - -static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s); -static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s); -static void exynos4210_ltick_recalc_count(struct tick_timer *s); - -/* - * Action on enabling local tick int timer - */ -static void exynos4210_ltick_int_start(struct tick_timer *s) -{ - if (!s->int_run) { - s->int_run = 1; - } -} - -/* - * Action on disabling local tick int timer - */ -static void exynos4210_ltick_int_stop(struct tick_timer *s) -{ - if (s->int_run) { - s->last_icnto = exynos4210_ltick_int_get_cnto(s); - s->int_run = 0; - } -} - -/* - * Get count for INT timer - */ -static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s) -{ - uint32_t icnto; - uint64_t remain; - uint64_t count; - uint64_t counted; - uint64_t cur_progress; - - count = ptimer_get_count(s->ptimer_tick); - if (count) { - /* timer is still counting, called not from event */ - counted = s->count - ptimer_get_count(s->ptimer_tick); - cur_progress = s->progress + counted; - } else { - /* timer expired earlier */ - cur_progress = s->progress; - } - - remain = s->distance - cur_progress; - - if (!s->int_run) { - /* INT is stopped. */ - icnto = s->last_icnto; - } else { - /* Both are counting */ - icnto = remain / s->tcntb; - } - - return icnto; -} - -/* - * Start local tick cnt timer. - */ -static void exynos4210_ltick_cnt_start(struct tick_timer *s) -{ - if (!s->cnt_run) { - - exynos4210_ltick_recalc_count(s); - ptimer_set_count(s->ptimer_tick, s->count); - ptimer_run(s->ptimer_tick, 1); - - s->cnt_run = 1; - } -} - -/* - * Stop local tick cnt timer. - */ -static void exynos4210_ltick_cnt_stop(struct tick_timer *s) -{ - if (s->cnt_run) { - - s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s); - - if (s->int_run) { - exynos4210_ltick_int_stop(s); - } - - ptimer_stop(s->ptimer_tick); - - s->cnt_run = 0; - } -} - -/* - * Get counter for CNT timer - */ -static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) -{ - uint32_t tcnto; - uint32_t icnto; - uint64_t remain; - uint64_t counted; - uint64_t count; - uint64_t cur_progress; - - count = ptimer_get_count(s->ptimer_tick); - if (count) { - /* timer is still counting, called not from event */ - counted = s->count - ptimer_get_count(s->ptimer_tick); - cur_progress = s->progress + counted; - } else { - /* timer expired earlier */ - cur_progress = s->progress; - } - - remain = s->distance - cur_progress; - - if (!s->cnt_run) { - /* Both are stopped. */ - tcnto = s->last_tcnto; - } else if (!s->int_run) { - /* INT counter is stopped, progress is by CNT timer */ - tcnto = remain % s->tcntb; - } else { - /* Both are counting */ - icnto = remain / s->tcntb; - if (icnto) { - tcnto = remain % (icnto * s->tcntb); - } else { - tcnto = remain % s->tcntb; - } - } - - return tcnto; -} - -/* - * Set new values of counters for CNT and INT timers - */ -static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt, - uint32_t new_int) -{ - uint32_t cnt_stopped = 0; - uint32_t int_stopped = 0; - - if (s->cnt_run) { - exynos4210_ltick_cnt_stop(s); - cnt_stopped = 1; - } - - if (s->int_run) { - exynos4210_ltick_int_stop(s); - int_stopped = 1; - } - - s->tcntb = new_cnt + 1; - s->icntb = new_int + 1; - - if (cnt_stopped) { - exynos4210_ltick_cnt_start(s); - } - if (int_stopped) { - exynos4210_ltick_int_start(s); - } - -} - -/* - * Calculate new counter value for tick timer - */ -static void exynos4210_ltick_recalc_count(struct tick_timer *s) -{ - uint64_t to_count; - - if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) { - /* - * one or both timers run and not counted to the end; - * distance is not passed, recalculate with last_tcnto * last_icnto - */ - - if (s->last_tcnto) { - to_count = s->last_tcnto * s->last_icnto; - } else { - to_count = s->last_icnto; - } - } else { - /* distance is passed, recalculate with tcnto * icnto */ - if (s->icntb) { - s->distance = s->tcntb * s->icntb; - } else { - s->distance = s->tcntb; - } - - to_count = s->distance; - s->progress = 0; - } - - if (to_count > MCT_LT_COUNTER_STEP) { - /* count by step */ - s->count = MCT_LT_COUNTER_STEP; - } else { - s->count = to_count; - } -} - -/* - * Initialize tick_timer - */ -static void exynos4210_ltick_timer_init(struct tick_timer *s) -{ - exynos4210_ltick_int_stop(s); - exynos4210_ltick_cnt_stop(s); - - s->count = 0; - s->distance = 0; - s->progress = 0; - s->icntb = 0; - s->tcntb = 0; -} - -/* - * tick_timer event. - * Raises when abstract tick_timer expires. - */ -static void exynos4210_ltick_timer_event(struct tick_timer *s) -{ - s->progress += s->count; -} - -/* - * Local timer tick counter handler. - * Don't use reloaded timers. If timer counter = zero - * then handler called but after handler finished no - * timer reload occurs. - */ -static void exynos4210_ltick_event(void *opaque) -{ - Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; - uint32_t tcnto; - uint32_t icnto; -#ifdef DEBUG_MCT - static uint64_t time1[2] = {0}; - static uint64_t time2[2] = {0}; -#endif - - /* Call tick_timer event handler, it will update its tcntb and icntb. */ - exynos4210_ltick_timer_event(&s->tick_timer); - - /* get tick_timer cnt */ - tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer); - - /* get tick_timer int */ - icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer); - - /* raise IRQ if needed */ - if (!icnto && s->reg.tcon & L_TCON_INT_START) { - /* INT counter enabled and expired */ - - s->reg.int_cstat |= L_INT_CSTAT_INTCNT; - - /* raise interrupt if enabled */ - if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) { -#ifdef DEBUG_MCT - time2[s->id] = qemu_get_clock_ns(vm_clock); - DPRINTF("local timer[%d] IRQ: %llx\n", s->id, - time2[s->id] - time1[s->id]); - time1[s->id] = time2[s->id]; -#endif - qemu_irq_raise(s->irq); - } - - /* reload ICNTB */ - if (s->reg.tcon & L_TCON_INTERVAL_MODE) { - exynos4210_ltick_set_cntb(&s->tick_timer, - s->reg.cnt[L_REG_CNT_TCNTB], - s->reg.cnt[L_REG_CNT_ICNTB]); - } - } else { - /* reload TCNTB */ - if (!tcnto) { - exynos4210_ltick_set_cntb(&s->tick_timer, - s->reg.cnt[L_REG_CNT_TCNTB], - icnto); - } - } - - /* start tick_timer cnt */ - exynos4210_ltick_cnt_start(&s->tick_timer); - - /* start tick_timer int */ - exynos4210_ltick_int_start(&s->tick_timer); -} - -/* update timer frequency */ -static void exynos4210_mct_update_freq(Exynos4210MCTState *s) -{ - uint32_t freq = s->freq; - s->freq = 24000000 / - ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) * - MCT_CFG_GET_DIVIDER(s->reg_mct_cfg)); - - if (freq != s->freq) { - DPRINTF("freq=%dHz\n", s->freq); - - /* global timer */ - ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); - - /* local timer */ - ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); - ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); - ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); - ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); - } -} - -/* set defaul_timer values for all fields */ -static void exynos4210_mct_reset(DeviceState *d) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)d; - uint32_t i; - - s->reg_mct_cfg = 0; - - /* global timer */ - memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg)); - exynos4210_gfrc_stop(&s->g_timer); - - /* local timer */ - memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt)); - memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt)); - for (i = 0; i < 2; i++) { - s->l_timer[i].reg.int_cstat = 0; - s->l_timer[i].reg.int_enb = 0; - s->l_timer[i].reg.tcon = 0; - s->l_timer[i].reg.wstat = 0; - s->l_timer[i].tick_timer.count = 0; - s->l_timer[i].tick_timer.distance = 0; - s->l_timer[i].tick_timer.progress = 0; - ptimer_stop(s->l_timer[i].ptimer_frc); - - exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer); - } - - exynos4210_mct_update_freq(s); - -} - -/* Multi Core Timer read */ -static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; - int index; - int shift; - uint64_t count; - uint32_t value; - int lt_i; - - switch (offset) { - - case MCT_CFG: - value = s->reg_mct_cfg; - break; - - case G_CNT_L: case G_CNT_U: - shift = 8 * (offset & 0x4); - count = exynos4210_gfrc_get_count(&s->g_timer); - value = UINT32_MAX & (count >> shift); - DPRINTF("read FRC=0x%llx\n", count); - break; - - case G_CNT_WSTAT: - value = s->g_timer.reg.cnt_wstat; - break; - - case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): - case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): - index = GET_G_COMP_IDX(offset); - shift = 8 * (offset & 0x4); - value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); - break; - - case G_TCON: - value = s->g_timer.reg.tcon; - break; - - case G_INT_CSTAT: - value = s->g_timer.reg.int_cstat; - break; - - case G_INT_ENB: - value = s->g_timer.reg.int_enb; - break; - break; - case G_WSTAT: - value = s->g_timer.reg.wstat; - break; - - case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: - case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: - value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)]; - break; - - /* Local timers */ - case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB: - case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB: - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - value = s->l_timer[lt_i].reg.cnt[index]; - break; - - case L0_TCNTO: case L1_TCNTO: - lt_i = GET_L_TIMER_IDX(offset); - - value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer); - DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value); - break; - - case L0_ICNTO: case L1_ICNTO: - lt_i = GET_L_TIMER_IDX(offset); - - value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer); - DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value); - break; - - case L0_FRCNTO: case L1_FRCNTO: - lt_i = GET_L_TIMER_IDX(offset); - - value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]); - - break; - - case L0_TCON: case L1_TCON: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.tcon; - break; - - case L0_INT_CSTAT: case L1_INT_CSTAT: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.int_cstat; - break; - - case L0_INT_ENB: case L1_INT_ENB: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.int_enb; - break; - - case L0_WSTAT: case L1_WSTAT: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.wstat; - break; - - default: - hw_error("exynos4210.mct: bad read offset " - TARGET_FMT_plx "\n", offset); - break; - } - return value; -} - -/* MCT write */ -static void exynos4210_mct_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; - int index; /* index in buffer which represents register set */ - int shift; - int lt_i; - uint64_t new_frc; - uint32_t i; - uint32_t old_val; -#ifdef DEBUG_MCT - static uint32_t icntb_max[2] = {0}; - static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX}; - static uint32_t tcntb_max[2] = {0}; - static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX}; -#endif - - new_frc = s->g_timer.reg.cnt; - - switch (offset) { - - case MCT_CFG: - s->reg_mct_cfg = value; - exynos4210_mct_update_freq(s); - break; - - case G_CNT_L: - case G_CNT_U: - if (offset == G_CNT_L) { - - DPRINTF("global timer write to reg.cntl %llx\n", value); - - new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value; - s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L; - } - if (offset == G_CNT_U) { - - DPRINTF("global timer write to reg.cntu %llx\n", value); - - new_frc = (s->g_timer.reg.cnt & UINT32_MAX) + - ((uint64_t)value << 32); - s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U; - } - - s->g_timer.reg.cnt = new_frc; - exynos4210_gfrc_restart(s); - break; - - case G_CNT_WSTAT: - s->g_timer.reg.cnt_wstat &= ~(value); - break; - - case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): - case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): - index = GET_G_COMP_IDX(offset); - shift = 8 * (offset & 0x4); - s->g_timer.reg.comp[index] = - (s->g_timer.reg.comp[index] & - (((uint64_t)UINT32_MAX << 32) >> shift)) + - (value << shift); - - DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); - - if (offset&0x4) { - s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); - } else { - s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); - } - - exynos4210_gfrc_restart(s); - break; - - case G_TCON: - old_val = s->g_timer.reg.tcon; - s->g_timer.reg.tcon = value; - s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE; - - DPRINTF("global timer write to reg.g_tcon %llx\n", value); - - /* Start FRC if transition from disabled to enabled */ - if ((value & G_TCON_TIMER_ENABLE) > (old_val & - G_TCON_TIMER_ENABLE)) { - exynos4210_gfrc_start(&s->g_timer); - } - if ((value & G_TCON_TIMER_ENABLE) < (old_val & - G_TCON_TIMER_ENABLE)) { - exynos4210_gfrc_stop(&s->g_timer); - } - - /* Start CMP if transition from disabled to enabled */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - if ((value & G_TCON_COMP_ENABLE(i)) != (old_val & - G_TCON_COMP_ENABLE(i))) { - exynos4210_gfrc_restart(s); - } - } - break; - - case G_INT_CSTAT: - s->g_timer.reg.int_cstat &= ~(value); - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - if (value & G_INT_CSTAT_COMP(i)) { - exynos4210_gcomp_lower_irq(&s->g_timer, i); - } - } - break; - - case G_INT_ENB: - - /* Raise IRQ if transition from disabled to enabled and CSTAT pending */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon & - G_INT_ENABLE(i))) { - if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) { - exynos4210_gcomp_raise_irq(&s->g_timer, i); - } - } - - if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon & - G_INT_ENABLE(i))) { - exynos4210_gcomp_lower_irq(&s->g_timer, i); - } - } - - DPRINTF("global timer INT enable %llx\n", value); - s->g_timer.reg.int_enb = value; - break; - - case G_WSTAT: - s->g_timer.reg.wstat &= ~(value); - break; - - case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: - case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: - index = GET_G_COMP_ADD_INCR_IDX(offset); - s->g_timer.reg.comp_add_incr[index] = value; - s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index); - break; - - /* Local timers */ - case L0_TCON: case L1_TCON: - lt_i = GET_L_TIMER_IDX(offset); - old_val = s->l_timer[lt_i].reg.tcon; - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE; - s->l_timer[lt_i].reg.tcon = value; - - /* Stop local CNT */ - if ((value & L_TCON_TICK_START) < - (old_val & L_TCON_TICK_START)) { - DPRINTF("local timer[%d] stop cnt\n", lt_i); - exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer); - } - - /* Stop local INT */ - if ((value & L_TCON_INT_START) < - (old_val & L_TCON_INT_START)) { - DPRINTF("local timer[%d] stop int\n", lt_i); - exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer); - } - - /* Start local CNT */ - if ((value & L_TCON_TICK_START) > - (old_val & L_TCON_TICK_START)) { - DPRINTF("local timer[%d] start cnt\n", lt_i); - exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer); - } - - /* Start local INT */ - if ((value & L_TCON_INT_START) > - (old_val & L_TCON_INT_START)) { - DPRINTF("local timer[%d] start int\n", lt_i); - exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer); - } - - /* Start or Stop local FRC if TCON changed */ - if ((value & L_TCON_FRC_START) > - (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { - DPRINTF("local timer[%d] start frc\n", lt_i); - exynos4210_lfrc_start(&s->l_timer[lt_i]); - } - if ((value & L_TCON_FRC_START) < - (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { - DPRINTF("local timer[%d] stop frc\n", lt_i); - exynos4210_lfrc_stop(&s->l_timer[lt_i]); - } - break; - - case L0_TCNTB: case L1_TCNTB: - - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - - /* - * TCNTB is updated to internal register only after CNT expired. - * Due to this we should reload timer to nearest moment when CNT is - * expired and then in event handler update tcntb to new TCNTB value. - */ - exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value, - s->l_timer[lt_i].tick_timer.icntb); - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE; - s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value; - -#ifdef DEBUG_MCT - if (tcntb_min[lt_i] > value) { - tcntb_min[lt_i] = value; - } - if (tcntb_max[lt_i] < value) { - tcntb_max[lt_i] = value; - } - DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n", - lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]); -#endif - break; - - case L0_ICNTB: case L1_ICNTB: - - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE; - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value & - ~L_ICNTB_MANUAL_UPDATE; - - /* - * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event - * could raise too fast disallowing QEMU to execute target code. - */ - if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] * - s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) { - if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) { - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = - MCT_LT_CNT_LOW_LIMIT; - } else { - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = - MCT_LT_CNT_LOW_LIMIT / - s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]; - } - } - - if (value & L_ICNTB_MANUAL_UPDATE) { - exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, - s->l_timer[lt_i].tick_timer.tcntb, - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]); - } - -#ifdef DEBUG_MCT - if (icntb_min[lt_i] > value) { - icntb_min[lt_i] = value; - } - if (icntb_max[lt_i] < value) { - icntb_max[lt_i] = value; - } -DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", - lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); -#endif -break; - - case L0_FRCNTB: case L1_FRCNTB: - - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - - DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value); - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE; - s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value; - - break; - - case L0_TCNTO: case L1_TCNTO: - case L0_ICNTO: case L1_ICNTO: - case L0_FRCNTO: case L1_FRCNTO: - fprintf(stderr, "\n[exynos4210.mct: write to RO register " - TARGET_FMT_plx "]\n\n", offset); - break; - - case L0_INT_CSTAT: case L1_INT_CSTAT: - lt_i = GET_L_TIMER_IDX(offset); - - DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value); - - s->l_timer[lt_i].reg.int_cstat &= ~value; - if (!s->l_timer[lt_i].reg.int_cstat) { - qemu_irq_lower(s->l_timer[lt_i].irq); - } - break; - - case L0_INT_ENB: case L1_INT_ENB: - lt_i = GET_L_TIMER_IDX(offset); - old_val = s->l_timer[lt_i].reg.int_enb; - - /* Raise Local timer IRQ if cstat is pending */ - if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) { - if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) { - qemu_irq_raise(s->l_timer[lt_i].irq); - } - } - - s->l_timer[lt_i].reg.int_enb = value; - - break; - - case L0_WSTAT: case L1_WSTAT: - lt_i = GET_L_TIMER_IDX(offset); - - s->l_timer[lt_i].reg.wstat &= ~value; - break; - - default: - hw_error("exynos4210.mct: bad write offset " - TARGET_FMT_plx "\n", offset); - break; - } -} - -static const MemoryRegionOps exynos4210_mct_ops = { - .read = exynos4210_mct_read, - .write = exynos4210_mct_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* MCT init */ -static int exynos4210_mct_init(SysBusDevice *dev) -{ - int i; - Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev); - QEMUBH *bh[2]; - - /* Global timer */ - bh[0] = qemu_bh_new(exynos4210_gfrc_event, s); - s->g_timer.ptimer_frc = ptimer_init(bh[0]); - memset(&s->g_timer.reg, 0, sizeof(struct gregs)); - - /* Local timers */ - for (i = 0; i < 2; i++) { - bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]); - bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]); - s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]); - s->l_timer[i].ptimer_frc = ptimer_init(bh[1]); - s->l_timer[i].id = i; - } - - /* IRQs */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - sysbus_init_irq(dev, &s->g_timer.irq[i]); - } - for (i = 0; i < 2; i++) { - sysbus_init_irq(dev, &s->l_timer[i].irq); - } - - memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct", - MCT_SFR_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void exynos4210_mct_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_mct_init; - dc->reset = exynos4210_mct_reset; - dc->vmsd = &vmstate_exynos4210_mct_state; -} - -static const TypeInfo exynos4210_mct_info = { - .name = "exynos4210.mct", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210MCTState), - .class_init = exynos4210_mct_class_init, -}; - -static void exynos4210_mct_register_types(void) -{ - type_register_static(&exynos4210_mct_info); -} - -type_init(exynos4210_mct_register_types) diff --git a/hw/exynos4210_pwm.c b/hw/exynos4210_pwm.c deleted file mode 100644 index 185ccb9a74..0000000000 --- a/hw/exynos4210_pwm.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Samsung exynos4210 Pulse Width Modulation Timer - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * - * Evgeny Voevodin - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "qemu-common.h" -#include "hw/ptimer.h" - -#include "hw/arm/exynos4210.h" - -//#define DEBUG_PWM - -#ifdef DEBUG_PWM -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define EXYNOS4210_PWM_TIMERS_NUM 5 -#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50 - -#define TCFG0 0x0000 -#define TCFG1 0x0004 -#define TCON 0x0008 -#define TCNTB0 0x000C -#define TCMPB0 0x0010 -#define TCNTO0 0x0014 -#define TCNTB1 0x0018 -#define TCMPB1 0x001C -#define TCNTO1 0x0020 -#define TCNTB2 0x0024 -#define TCMPB2 0x0028 -#define TCNTO2 0x002C -#define TCNTB3 0x0030 -#define TCMPB3 0x0034 -#define TCNTO3 0x0038 -#define TCNTB4 0x003C -#define TCNTO4 0x0040 -#define TINT_CSTAT 0x0044 - -#define TCNTB(x) (0xC * (x)) -#define TCMPB(x) (0xC * (x) + 1) -#define TCNTO(x) (0xC * (x) + 2) - -#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x)) -#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x)))) - -/* - * Attention! Timer4 doesn't have OUTPUT_INVERTER, - * so Auto Reload bit is not accessible by macros! - */ -#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x)) -#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0)) -#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1)) -#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2)) -#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3)) -#define TCON_TIMER4_AUTO_RELOAD (1 << 22) - -#define TINT_CSTAT_STATUS(x) (1 << (5 + (x))) -#define TINT_CSTAT_ENABLE(x) (1 << (x)) - -/* timer struct */ -typedef struct { - uint32_t id; /* timer id */ - qemu_irq irq; /* local timer irq */ - uint32_t freq; /* timer frequency */ - - /* use ptimer.c to represent count down timer */ - ptimer_state *ptimer; /* timer */ - - /* registers */ - uint32_t reg_tcntb; /* counter register buffer */ - uint32_t reg_tcmpb; /* compare register buffer */ - - struct Exynos4210PWMState *parent; - -} Exynos4210PWM; - - -typedef struct Exynos4210PWMState { - SysBusDevice busdev; - MemoryRegion iomem; - - uint32_t reg_tcfg[2]; - uint32_t reg_tcon; - uint32_t reg_tint_cstat; - - Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM]; - -} Exynos4210PWMState; - -/*** VMState ***/ -static const VMStateDescription vmstate_exynos4210_pwm = { - .name = "exynos4210.pwm.pwm", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(id, Exynos4210PWM), - VMSTATE_UINT32(freq, Exynos4210PWM), - VMSTATE_PTIMER(ptimer, Exynos4210PWM), - VMSTATE_UINT32(reg_tcntb, Exynos4210PWM), - VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_pwm_state = { - .name = "exynos4210.pwm", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2), - VMSTATE_UINT32(reg_tcon, Exynos4210PWMState), - VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState), - VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState, - EXYNOS4210_PWM_TIMERS_NUM, 0, - vmstate_exynos4210_pwm, Exynos4210PWM), - VMSTATE_END_OF_LIST() - } -}; - -/* - * PWM update frequency - */ -static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id) -{ - uint32_t freq; - freq = s->timer[id].freq; - if (id > 1) { - s->timer[id].freq = 24000000 / - ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) * - (GET_DIVIDER(s->reg_tcfg[1], id))); - } else { - s->timer[id].freq = 24000000 / - ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) * - (GET_DIVIDER(s->reg_tcfg[1], id))); - } - - if (freq != s->timer[id].freq) { - ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq); - DPRINTF("freq=%dHz\n", s->timer[id].freq); - } -} - -/* - * Counter tick handler - */ -static void exynos4210_pwm_tick(void *opaque) -{ - Exynos4210PWM *s = (Exynos4210PWM *)opaque; - Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent; - uint32_t id = s->id; - bool cmp; - - DPRINTF("timer %d tick\n", id); - - /* set irq status */ - p->reg_tint_cstat |= TINT_CSTAT_STATUS(id); - - /* raise IRQ */ - if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) { - DPRINTF("timer %d IRQ\n", id); - qemu_irq_raise(p->timer[id].irq); - } - - /* reload timer */ - if (id != 4) { - cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id); - } else { - cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD; - } - - if (cmp) { - DPRINTF("auto reload timer %d count to %x\n", id, - p->timer[id].reg_tcntb); - ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb); - ptimer_run(p->timer[id].ptimer, 1); - } else { - /* stop timer, set status to STOP, see Basic Timer Operation */ - p->reg_tcon &= ~TCON_TIMER_START(id); - ptimer_stop(p->timer[id].ptimer); - } -} - -/* - * PWM Read - */ -static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; - uint32_t value = 0; - int index; - - switch (offset) { - case TCFG0: case TCFG1: - index = (offset - TCFG0) >> 2; - value = s->reg_tcfg[index]; - break; - - case TCON: - value = s->reg_tcon; - break; - - case TCNTB0: case TCNTB1: - case TCNTB2: case TCNTB3: case TCNTB4: - index = (offset - TCNTB0) / 0xC; - value = s->timer[index].reg_tcntb; - break; - - case TCMPB0: case TCMPB1: - case TCMPB2: case TCMPB3: - index = (offset - TCMPB0) / 0xC; - value = s->timer[index].reg_tcmpb; - break; - - case TCNTO0: case TCNTO1: - case TCNTO2: case TCNTO3: case TCNTO4: - index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC; - value = ptimer_get_count(s->timer[index].ptimer); - break; - - case TINT_CSTAT: - value = s->reg_tint_cstat; - break; - - default: - fprintf(stderr, - "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n", - offset); - break; - } - return value; -} - -/* - * PWM Write - */ -static void exynos4210_pwm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; - int index; - uint32_t new_val; - int i; - - switch (offset) { - case TCFG0: case TCFG1: - index = (offset - TCFG0) >> 2; - s->reg_tcfg[index] = value; - - /* update timers frequencies */ - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - exynos4210_pwm_update_freq(s, s->timer[i].id); - } - break; - - case TCON: - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - if ((value & TCON_TIMER_MANUAL_UPD(i)) > - (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) { - /* - * TCNTB and TCMPB are loaded into TCNT and TCMP. - * Update timers. - */ - - /* this will start timer to run, this ok, because - * during processing start bit timer will be stopped - * if needed */ - ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb); - DPRINTF("set timer %d count to %x\n", i, - s->timer[i].reg_tcntb); - } - - if ((value & TCON_TIMER_START(i)) > - (s->reg_tcon & TCON_TIMER_START(i))) { - /* changed to start */ - ptimer_run(s->timer[i].ptimer, 1); - DPRINTF("run timer %d\n", i); - } - - if ((value & TCON_TIMER_START(i)) < - (s->reg_tcon & TCON_TIMER_START(i))) { - /* changed to stop */ - ptimer_stop(s->timer[i].ptimer); - DPRINTF("stop timer %d\n", i); - } - } - s->reg_tcon = value; - break; - - case TCNTB0: case TCNTB1: - case TCNTB2: case TCNTB3: case TCNTB4: - index = (offset - TCNTB0) / 0xC; - s->timer[index].reg_tcntb = value; - break; - - case TCMPB0: case TCMPB1: - case TCMPB2: case TCMPB3: - index = (offset - TCMPB0) / 0xC; - s->timer[index].reg_tcmpb = value; - break; - - case TINT_CSTAT: - new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value); - new_val &= ~(0x3E0 & value); - - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - if ((new_val & TINT_CSTAT_STATUS(i)) < - (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) { - qemu_irq_lower(s->timer[i].irq); - } - } - - s->reg_tint_cstat = new_val; - break; - - default: - fprintf(stderr, - "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n", - offset); - break; - - } -} - -/* - * Set default values to timer fields and registers - */ -static void exynos4210_pwm_reset(DeviceState *d) -{ - Exynos4210PWMState *s = (Exynos4210PWMState *)d; - int i; - s->reg_tcfg[0] = 0x0101; - s->reg_tcfg[1] = 0x0; - s->reg_tcon = 0; - s->reg_tint_cstat = 0; - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - s->timer[i].reg_tcmpb = 0; - s->timer[i].reg_tcntb = 0; - - exynos4210_pwm_update_freq(s, s->timer[i].id); - ptimer_stop(s->timer[i].ptimer); - } -} - -static const MemoryRegionOps exynos4210_pwm_ops = { - .read = exynos4210_pwm_read, - .write = exynos4210_pwm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * PWM timer initialization - */ -static int exynos4210_pwm_init(SysBusDevice *dev) -{ - Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev); - int i; - QEMUBH *bh; - - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]); - sysbus_init_irq(dev, &s->timer[i].irq); - s->timer[i].ptimer = ptimer_init(bh); - s->timer[i].id = i; - s->timer[i].parent = s; - } - - memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm", - EXYNOS4210_PWM_REG_MEM_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_pwm_init; - dc->reset = exynos4210_pwm_reset; - dc->vmsd = &vmstate_exynos4210_pwm_state; -} - -static const TypeInfo exynos4210_pwm_info = { - .name = "exynos4210.pwm", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210PWMState), - .class_init = exynos4210_pwm_class_init, -}; - -static void exynos4210_pwm_register_types(void) -{ - type_register_static(&exynos4210_pwm_info); -} - -type_init(exynos4210_pwm_register_types) diff --git a/hw/exynos4210_rtc.c b/hw/exynos4210_rtc.c deleted file mode 100644 index bceee44cb2..0000000000 --- a/hw/exynos4210_rtc.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Samsung exynos4210 Real Time Clock - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Ogurtsov Oleg - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -/* Description: - * Register RTCCON: - * CLKSEL Bit[1] not used - * CLKOUTEN Bit[9] not used - */ - -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "qemu-common.h" -#include "hw/ptimer.h" - -#include "hw/hw.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" - -#include "hw/arm/exynos4210.h" - -#define DEBUG_RTC 0 - -#if DEBUG_RTC -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100 - -#define INTP 0x0030 -#define RTCCON 0x0040 -#define TICCNT 0x0044 -#define RTCALM 0x0050 -#define ALMSEC 0x0054 -#define ALMMIN 0x0058 -#define ALMHOUR 0x005C -#define ALMDAY 0x0060 -#define ALMMON 0x0064 -#define ALMYEAR 0x0068 -#define BCDSEC 0x0070 -#define BCDMIN 0x0074 -#define BCDHOUR 0x0078 -#define BCDDAY 0x007C -#define BCDDAYWEEK 0x0080 -#define BCDMON 0x0084 -#define BCDYEAR 0x0088 -#define CURTICNT 0x0090 - -#define TICK_TIMER_ENABLE 0x0100 -#define TICNT_THRESHHOLD 2 - - -#define RTC_ENABLE 0x0001 - -#define INTP_TICK_ENABLE 0x0001 -#define INTP_ALM_ENABLE 0x0002 - -#define ALARM_INT_ENABLE 0x0040 - -#define RTC_BASE_FREQ 32768 - -typedef struct Exynos4210RTCState { - SysBusDevice busdev; - MemoryRegion iomem; - - /* registers */ - uint32_t reg_intp; - uint32_t reg_rtccon; - uint32_t reg_ticcnt; - uint32_t reg_rtcalm; - uint32_t reg_almsec; - uint32_t reg_almmin; - uint32_t reg_almhour; - uint32_t reg_almday; - uint32_t reg_almmon; - uint32_t reg_almyear; - uint32_t reg_curticcnt; - - ptimer_state *ptimer; /* tick timer */ - ptimer_state *ptimer_1Hz; /* clock timer */ - uint32_t freq; - - qemu_irq tick_irq; /* Time Tick Generator irq */ - qemu_irq alm_irq; /* alarm irq */ - - struct tm current_tm; /* current time */ -} Exynos4210RTCState; - -#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4) - -/*** VMState ***/ -static const VMStateDescription vmstate_exynos4210_rtc_state = { - .name = "exynos4210.rtc", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(reg_intp, Exynos4210RTCState), - VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState), - VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState), - VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState), - VMSTATE_UINT32(reg_almsec, Exynos4210RTCState), - VMSTATE_UINT32(reg_almmin, Exynos4210RTCState), - VMSTATE_UINT32(reg_almhour, Exynos4210RTCState), - VMSTATE_UINT32(reg_almday, Exynos4210RTCState), - VMSTATE_UINT32(reg_almmon, Exynos4210RTCState), - VMSTATE_UINT32(reg_almyear, Exynos4210RTCState), - VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState), - VMSTATE_PTIMER(ptimer, Exynos4210RTCState), - VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState), - VMSTATE_UINT32(freq, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState), - VMSTATE_END_OF_LIST() - } -}; - -#define BCD3DIGITS(x) \ - ((uint32_t)to_bcd((uint8_t)(x % 100)) + \ - ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8)) - -static void check_alarm_raise(Exynos4210RTCState *s) -{ - unsigned int alarm_raise = 0; - struct tm stm = s->current_tm; - - if ((s->reg_rtcalm & 0x01) && - (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x02) && - (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x04) && - (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x08) && - (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x10) && - (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x20) && - (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) { - alarm_raise = 1; - } - - if (alarm_raise) { - DPRINTF("ALARM IRQ\n"); - /* set irq status */ - s->reg_intp |= INTP_ALM_ENABLE; - qemu_irq_raise(s->alm_irq); - } -} - -/* - * RTC update frequency - * Parameters: - * reg_value - current RTCCON register or his new value - */ -static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, - uint32_t reg_value) -{ - uint32_t freq; - - freq = s->freq; - /* set frequncy for time generator */ - s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value)); - - if (freq != s->freq) { - ptimer_set_freq(s->ptimer, s->freq); - DPRINTF("freq=%dHz\n", s->freq); - } -} - -/* month is between 0 and 11. */ -static int get_days_in_month(int month, int year) -{ - static const int days_tab[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - int d; - if ((unsigned)month >= 12) { - return 31; - } - d = days_tab[month]; - if (month == 1) { - if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) { - d++; - } - } - return d; -} - -/* update 'tm' to the next second */ -static void rtc_next_second(struct tm *tm) -{ - int days_in_month; - - tm->tm_sec++; - if ((unsigned)tm->tm_sec >= 60) { - tm->tm_sec = 0; - tm->tm_min++; - if ((unsigned)tm->tm_min >= 60) { - tm->tm_min = 0; - tm->tm_hour++; - if ((unsigned)tm->tm_hour >= 24) { - tm->tm_hour = 0; - /* next day */ - tm->tm_wday++; - if ((unsigned)tm->tm_wday >= 7) { - tm->tm_wday = 0; - } - days_in_month = get_days_in_month(tm->tm_mon, - tm->tm_year + 1900); - tm->tm_mday++; - if (tm->tm_mday < 1) { - tm->tm_mday = 1; - } else if (tm->tm_mday > days_in_month) { - tm->tm_mday = 1; - tm->tm_mon++; - if (tm->tm_mon >= 12) { - tm->tm_mon = 0; - tm->tm_year++; - } - } - } - } - } -} - -/* - * tick handler - */ -static void exynos4210_rtc_tick(void *opaque) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - DPRINTF("TICK IRQ\n"); - /* set irq status */ - s->reg_intp |= INTP_TICK_ENABLE; - /* raise IRQ */ - qemu_irq_raise(s->tick_irq); - - /* restart timer */ - ptimer_set_count(s->ptimer, s->reg_ticcnt); - ptimer_run(s->ptimer, 1); -} - -/* - * 1Hz clock handler - */ -static void exynos4210_rtc_1Hz_tick(void *opaque) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - rtc_next_second(&s->current_tm); - /* DPRINTF("1Hz tick\n"); */ - - /* raise IRQ */ - if (s->reg_rtcalm & ALARM_INT_ENABLE) { - check_alarm_raise(s); - } - - ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); - ptimer_run(s->ptimer_1Hz, 1); -} - -/* - * RTC Read - */ -static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t value = 0; - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - switch (offset) { - case INTP: - value = s->reg_intp; - break; - case RTCCON: - value = s->reg_rtccon; - break; - case TICCNT: - value = s->reg_ticcnt; - break; - case RTCALM: - value = s->reg_rtcalm; - break; - case ALMSEC: - value = s->reg_almsec; - break; - case ALMMIN: - value = s->reg_almmin; - break; - case ALMHOUR: - value = s->reg_almhour; - break; - case ALMDAY: - value = s->reg_almday; - break; - case ALMMON: - value = s->reg_almmon; - break; - case ALMYEAR: - value = s->reg_almyear; - break; - - case BCDSEC: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec); - break; - case BCDMIN: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min); - break; - case BCDHOUR: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour); - break; - case BCDDAYWEEK: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday); - break; - case BCDDAY: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday); - break; - case BCDMON: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1); - break; - case BCDYEAR: - value = BCD3DIGITS(s->current_tm.tm_year); - break; - - case CURTICNT: - s->reg_curticcnt = ptimer_get_count(s->ptimer); - value = s->reg_curticcnt; - break; - - default: - fprintf(stderr, - "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n", - offset); - break; - } - return value; -} - -/* - * RTC Write - */ -static void exynos4210_rtc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - switch (offset) { - case INTP: - if (value & INTP_ALM_ENABLE) { - qemu_irq_lower(s->alm_irq); - s->reg_intp &= (~INTP_ALM_ENABLE); - } - if (value & INTP_TICK_ENABLE) { - qemu_irq_lower(s->tick_irq); - s->reg_intp &= (~INTP_TICK_ENABLE); - } - break; - case RTCCON: - if (value & RTC_ENABLE) { - exynos4210_rtc_update_freq(s, value); - } - if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) { - /* clock timer */ - ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); - ptimer_run(s->ptimer_1Hz, 1); - DPRINTF("run clock timer\n"); - } - if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) { - /* tick timer */ - ptimer_stop(s->ptimer); - /* clock timer */ - ptimer_stop(s->ptimer_1Hz); - DPRINTF("stop all timers\n"); - } - if (value & RTC_ENABLE) { - if ((value & TICK_TIMER_ENABLE) > - (s->reg_rtccon & TICK_TIMER_ENABLE) && - (s->reg_ticcnt)) { - ptimer_set_count(s->ptimer, s->reg_ticcnt); - ptimer_run(s->ptimer, 1); - DPRINTF("run tick timer\n"); - } - if ((value & TICK_TIMER_ENABLE) < - (s->reg_rtccon & TICK_TIMER_ENABLE)) { - ptimer_stop(s->ptimer); - } - } - s->reg_rtccon = value; - break; - case TICCNT: - if (value > TICNT_THRESHHOLD) { - s->reg_ticcnt = value; - } else { - fprintf(stderr, - "[exynos4210.rtc: bad TICNT value %u ]\n", - (uint32_t)value); - } - break; - - case RTCALM: - s->reg_rtcalm = value; - break; - case ALMSEC: - s->reg_almsec = (value & 0x7f); - break; - case ALMMIN: - s->reg_almmin = (value & 0x7f); - break; - case ALMHOUR: - s->reg_almhour = (value & 0x3f); - break; - case ALMDAY: - s->reg_almday = (value & 0x3f); - break; - case ALMMON: - s->reg_almmon = (value & 0x1f); - break; - case ALMYEAR: - s->reg_almyear = (value & 0x0fff); - break; - - case BCDSEC: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_sec = (int)from_bcd((uint8_t)value); - } - break; - case BCDMIN: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_min = (int)from_bcd((uint8_t)value); - } - break; - case BCDHOUR: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_hour = (int)from_bcd((uint8_t)value); - } - break; - case BCDDAYWEEK: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_wday = (int)from_bcd((uint8_t)value); - } - break; - case BCDDAY: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_mday = (int)from_bcd((uint8_t)value); - } - break; - case BCDMON: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1; - } - break; - case BCDYEAR: - if (s->reg_rtccon & RTC_ENABLE) { - /* 3 digits */ - s->current_tm.tm_year = (int)from_bcd((uint8_t)value) + - (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100; - } - break; - - default: - fprintf(stderr, - "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n", - offset); - break; - - } -} - -/* - * Set default values to timer fields and registers - */ -static void exynos4210_rtc_reset(DeviceState *d) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)d; - - qemu_get_timedate(&s->current_tm, 0); - - DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n", - s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday, - s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec); - - s->reg_intp = 0; - s->reg_rtccon = 0; - s->reg_ticcnt = 0; - s->reg_rtcalm = 0; - s->reg_almsec = 0; - s->reg_almmin = 0; - s->reg_almhour = 0; - s->reg_almday = 0; - s->reg_almmon = 0; - s->reg_almyear = 0; - - s->reg_curticcnt = 0; - - exynos4210_rtc_update_freq(s, s->reg_rtccon); - ptimer_stop(s->ptimer); - ptimer_stop(s->ptimer_1Hz); -} - -static const MemoryRegionOps exynos4210_rtc_ops = { - .read = exynos4210_rtc_read, - .write = exynos4210_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * RTC timer initialization - */ -static int exynos4210_rtc_init(SysBusDevice *dev) -{ - Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev); - QEMUBH *bh; - - bh = qemu_bh_new(exynos4210_rtc_tick, s); - s->ptimer = ptimer_init(bh); - ptimer_set_freq(s->ptimer, RTC_BASE_FREQ); - exynos4210_rtc_update_freq(s, 0); - - bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s); - s->ptimer_1Hz = ptimer_init(bh); - ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ); - - sysbus_init_irq(dev, &s->alm_irq); - sysbus_init_irq(dev, &s->tick_irq); - - memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc", - EXYNOS4210_RTC_REG_MEM_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_rtc_init; - dc->reset = exynos4210_rtc_reset; - dc->vmsd = &vmstate_exynos4210_rtc_state; -} - -static const TypeInfo exynos4210_rtc_info = { - .name = "exynos4210.rtc", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210RTCState), - .class_init = exynos4210_rtc_class_init, -}; - -static void exynos4210_rtc_register_types(void) -{ - type_register_static(&exynos4210_rtc_info); -} - -type_init(exynos4210_rtc_register_types) diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c deleted file mode 100644 index 7043a34684..0000000000 --- a/hw/grlib_gptimer.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * QEMU GRLIB GPTimer Emulator - * - * Copyright (c) 2010-2011 AdaCore - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" - -#include "trace.h" - -#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ -#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ - -#define GPTIMER_MAX_TIMERS 8 - -/* GPTimer Config register fields */ -#define GPTIMER_ENABLE (1 << 0) -#define GPTIMER_RESTART (1 << 1) -#define GPTIMER_LOAD (1 << 2) -#define GPTIMER_INT_ENABLE (1 << 3) -#define GPTIMER_INT_PENDING (1 << 4) -#define GPTIMER_CHAIN (1 << 5) /* Not supported */ -#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ - -/* Memory mapped register offsets */ -#define SCALER_OFFSET 0x00 -#define SCALER_RELOAD_OFFSET 0x04 -#define CONFIG_OFFSET 0x08 -#define COUNTER_OFFSET 0x00 -#define COUNTER_RELOAD_OFFSET 0x04 -#define TIMER_BASE 0x10 - -typedef struct GPTimer GPTimer; -typedef struct GPTimerUnit GPTimerUnit; - -struct GPTimer { - QEMUBH *bh; - struct ptimer_state *ptimer; - - qemu_irq irq; - int id; - GPTimerUnit *unit; - - /* registers */ - uint32_t counter; - uint32_t reload; - uint32_t config; -}; - -struct GPTimerUnit { - SysBusDevice busdev; - MemoryRegion iomem; - - uint32_t nr_timers; /* Number of timers available */ - uint32_t freq_hz; /* System frequency */ - uint32_t irq_line; /* Base irq line */ - - GPTimer *timers; - - /* registers */ - uint32_t scaler; - uint32_t reload; - uint32_t config; -}; - -static void grlib_gptimer_enable(GPTimer *timer) -{ - assert(timer != NULL); - - - ptimer_stop(timer->ptimer); - - if (!(timer->config & GPTIMER_ENABLE)) { - /* Timer disabled */ - trace_grlib_gptimer_disabled(timer->id, timer->config); - return; - } - - /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at - underflow. Set count + 1 to simulate the GPTimer behavior. */ - - trace_grlib_gptimer_enable(timer->id, timer->counter + 1); - - ptimer_set_count(timer->ptimer, timer->counter + 1); - ptimer_run(timer->ptimer, 1); -} - -static void grlib_gptimer_restart(GPTimer *timer) -{ - assert(timer != NULL); - - trace_grlib_gptimer_restart(timer->id, timer->reload); - - timer->counter = timer->reload; - grlib_gptimer_enable(timer); -} - -static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) -{ - int i = 0; - uint32_t value = 0; - - assert(unit != NULL); - - if (scaler > 0) { - value = unit->freq_hz / (scaler + 1); - } else { - value = unit->freq_hz; - } - - trace_grlib_gptimer_set_scaler(scaler, value); - - for (i = 0; i < unit->nr_timers; i++) { - ptimer_set_freq(unit->timers[i].ptimer, value); - } -} - -static void grlib_gptimer_hit(void *opaque) -{ - GPTimer *timer = opaque; - assert(timer != NULL); - - trace_grlib_gptimer_hit(timer->id); - - /* Timer expired */ - - if (timer->config & GPTIMER_INT_ENABLE) { - /* Set the pending bit (only unset by write in the config register) */ - timer->config |= GPTIMER_INT_PENDING; - qemu_irq_pulse(timer->irq); - } - - if (timer->config & GPTIMER_RESTART) { - grlib_gptimer_restart(timer); - } -} - -static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, - unsigned size) -{ - GPTimerUnit *unit = opaque; - hwaddr timer_addr; - int id; - uint32_t value = 0; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case SCALER_OFFSET: - trace_grlib_gptimer_readl(-1, addr, unit->scaler); - return unit->scaler; - - case SCALER_RELOAD_OFFSET: - trace_grlib_gptimer_readl(-1, addr, unit->reload); - return unit->reload; - - case CONFIG_OFFSET: - trace_grlib_gptimer_readl(-1, addr, unit->config); - return unit->config; - - default: - break; - } - - timer_addr = (addr % TIMER_BASE); - id = (addr - TIMER_BASE) / TIMER_BASE; - - if (id >= 0 && id < unit->nr_timers) { - - /* GPTimer registers */ - switch (timer_addr) { - case COUNTER_OFFSET: - value = ptimer_get_count(unit->timers[id].ptimer); - trace_grlib_gptimer_readl(id, addr, value); - return value; - - case COUNTER_RELOAD_OFFSET: - value = unit->timers[id].reload; - trace_grlib_gptimer_readl(id, addr, value); - return value; - - case CONFIG_OFFSET: - trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); - return unit->timers[id].config; - - default: - break; - } - - } - - trace_grlib_gptimer_readl(-1, addr, 0); - return 0; -} - -static void grlib_gptimer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - GPTimerUnit *unit = opaque; - hwaddr timer_addr; - int id; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case SCALER_OFFSET: - value &= 0xFFFF; /* clean up the value */ - unit->scaler = value; - trace_grlib_gptimer_writel(-1, addr, unit->scaler); - return; - - case SCALER_RELOAD_OFFSET: - value &= 0xFFFF; /* clean up the value */ - unit->reload = value; - trace_grlib_gptimer_writel(-1, addr, unit->reload); - grlib_gptimer_set_scaler(unit, value); - return; - - case CONFIG_OFFSET: - /* Read Only (disable timer freeze not supported) */ - trace_grlib_gptimer_writel(-1, addr, 0); - return; - - default: - break; - } - - timer_addr = (addr % TIMER_BASE); - id = (addr - TIMER_BASE) / TIMER_BASE; - - if (id >= 0 && id < unit->nr_timers) { - - /* GPTimer registers */ - switch (timer_addr) { - case COUNTER_OFFSET: - trace_grlib_gptimer_writel(id, addr, value); - unit->timers[id].counter = value; - grlib_gptimer_enable(&unit->timers[id]); - return; - - case COUNTER_RELOAD_OFFSET: - trace_grlib_gptimer_writel(id, addr, value); - unit->timers[id].reload = value; - return; - - case CONFIG_OFFSET: - trace_grlib_gptimer_writel(id, addr, value); - - if (value & GPTIMER_INT_PENDING) { - /* clear pending bit */ - value &= ~GPTIMER_INT_PENDING; - } else { - /* keep pending bit */ - value |= unit->timers[id].config & GPTIMER_INT_PENDING; - } - - unit->timers[id].config = value; - - /* gptimer_restart calls gptimer_enable, so if "enable" and "load" - bits are present, we just have to call restart. */ - - if (value & GPTIMER_LOAD) { - grlib_gptimer_restart(&unit->timers[id]); - } else if (value & GPTIMER_ENABLE) { - grlib_gptimer_enable(&unit->timers[id]); - } - - /* These fields must always be read as 0 */ - value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); - - unit->timers[id].config = value; - return; - - default: - break; - } - - } - - trace_grlib_gptimer_writel(-1, addr, value); -} - -static const MemoryRegionOps grlib_gptimer_ops = { - .read = grlib_gptimer_read, - .write = grlib_gptimer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void grlib_gptimer_reset(DeviceState *d) -{ - GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev); - int i = 0; - - assert(unit != NULL); - - unit->scaler = 0; - unit->reload = 0; - unit->config = 0; - - unit->config = unit->nr_timers; - unit->config |= unit->irq_line << 3; - unit->config |= 1 << 8; /* separate interrupt */ - unit->config |= 1 << 9; /* Disable timer freeze */ - - - for (i = 0; i < unit->nr_timers; i++) { - GPTimer *timer = &unit->timers[i]; - - timer->counter = 0; - timer->reload = 0; - timer->config = 0; - ptimer_stop(timer->ptimer); - ptimer_set_count(timer->ptimer, 0); - ptimer_set_freq(timer->ptimer, unit->freq_hz); - } -} - -static int grlib_gptimer_init(SysBusDevice *dev) -{ - GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev); - unsigned int i; - - assert(unit->nr_timers > 0); - assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); - - unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); - - for (i = 0; i < unit->nr_timers; i++) { - GPTimer *timer = &unit->timers[i]; - - timer->unit = unit; - timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); - timer->ptimer = ptimer_init(timer->bh); - timer->id = i; - - /* One IRQ line for each timer */ - sysbus_init_irq(dev, &timer->irq); - - ptimer_set_freq(timer->ptimer, unit->freq_hz); - } - - memory_region_init_io(&unit->iomem, &grlib_gptimer_ops, unit, "gptimer", - UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); - - sysbus_init_mmio(dev, &unit->iomem); - return 0; -} - -static Property grlib_gptimer_properties[] = { - DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), - DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), - DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void grlib_gptimer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = grlib_gptimer_init; - dc->reset = grlib_gptimer_reset; - dc->props = grlib_gptimer_properties; -} - -static const TypeInfo grlib_gptimer_info = { - .name = "grlib,gptimer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GPTimerUnit), - .class_init = grlib_gptimer_class_init, -}; - -static void grlib_gptimer_register_types(void) -{ - type_register_static(&grlib_gptimer_info); -} - -type_init(grlib_gptimer_register_types) diff --git a/hw/imx_timer.c b/hw/imx_timer.c deleted file mode 100644 index 03197e3f54..0000000000 --- a/hw/imx_timer.c +++ /dev/null @@ -1,689 +0,0 @@ -/* - * IMX31 Timer - * - * Copyright (c) 2008 OK Labs - * Copyright (c) 2011 NICTA Pty Ltd - * Originally written by Hans Jiang - * Updated by Peter Chubb - * - * This code is licensed under GPL version 2 or later. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "hw/sysbus.h" -#include "hw/arm/imx.h" - -//#define DEBUG_TIMER 1 -#ifdef DEBUG_TIMER -# define DPRINTF(fmt, args...) \ - do { printf("imx_timer: " fmt , ##args); } while (0) -#else -# define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -#define DEBUG_IMPLEMENTATION 1 -#if DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * GPT : General purpose timer - * - * This timer counts up continuously while it is enabled, resetting itself - * to 0 when it reaches TIMER_MAX (in freerun mode) or when it - * reaches the value of ocr1 (in periodic mode). WE simulate this using a - * QEMU ptimer counting down from ocr1 and reloading from ocr1 in - * periodic mode, or counting from ocr1 to zero, then TIMER_MAX - ocr1. - * waiting_rov is set when counting from TIMER_MAX. - * - * In the real hardware, there are three comparison registers that can - * trigger interrupts, and compare channel 1 can be used to - * force-reset the timer. However, this is a `bare-bones' - * implementation: only what Linux 3.x uses has been implemented - * (free-running timer from 0 to OCR1 or TIMER_MAX) . - */ - - -#define TIMER_MAX 0XFFFFFFFFUL - -/* Control register. Not all of these bits have any effect (yet) */ -#define GPT_CR_EN (1 << 0) /* GPT Enable */ -#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */ -#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ -#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ -#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ -#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ -#define GPT_CR_CLKSRC_SHIFT (6) -#define GPT_CR_CLKSRC_MASK (0x7) - -#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ -#define GPT_CR_SWR (1 << 15) /* Software Reset */ -#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ -#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ -#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ -#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ -#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ -#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ -#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ -#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ - -#define GPT_SR_OF1 (1 << 0) -#define GPT_SR_ROV (1 << 5) - -#define GPT_IR_OF1IE (1 << 0) -#define GPT_IR_ROVIE (1 << 5) - -typedef struct { - SysBusDevice busdev; - ptimer_state *timer; - MemoryRegion iomem; - DeviceState *ccm; - - uint32_t cr; - uint32_t pr; - uint32_t sr; - uint32_t ir; - uint32_t ocr1; - uint32_t cnt; - - uint32_t waiting_rov; - qemu_irq irq; -} IMXTimerGState; - -static const VMStateDescription vmstate_imx_timerg = { - .name = "imx-timerg", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cr, IMXTimerGState), - VMSTATE_UINT32(pr, IMXTimerGState), - VMSTATE_UINT32(sr, IMXTimerGState), - VMSTATE_UINT32(ir, IMXTimerGState), - VMSTATE_UINT32(ocr1, IMXTimerGState), - VMSTATE_UINT32(cnt, IMXTimerGState), - VMSTATE_UINT32(waiting_rov, IMXTimerGState), - VMSTATE_PTIMER(timer, IMXTimerGState), - VMSTATE_END_OF_LIST() - } -}; - -static const IMXClk imx_timerg_clocks[] = { - NOCLK, /* 000 No clock source */ - IPG, /* 001 ipg_clk, 532MHz*/ - IPG, /* 010 ipg_clk_highfreq */ - NOCLK, /* 011 not defined */ - CLK_32k, /* 100 ipg_clk_32k */ - NOCLK, /* 101 not defined */ - NOCLK, /* 110 not defined */ - NOCLK, /* 111 not defined */ -}; - - -static void imx_timerg_set_freq(IMXTimerGState *s) -{ - int clksrc; - uint32_t freq; - - clksrc = (s->cr >> GPT_CR_CLKSRC_SHIFT) & GPT_CR_CLKSRC_MASK; - freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc]) / (1 + s->pr); - - DPRINTF("Setting gtimer clksrc %d to frequency %d\n", clksrc, freq); - if (freq) { - ptimer_set_freq(s->timer, freq); - } -} - -static void imx_timerg_update(IMXTimerGState *s) -{ - uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV); - - DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n", - s->sr & GPT_SR_OF1 ? "OF1" : "", - s->sr & GPT_SR_ROV ? "ROV" : "", - s->ir & GPT_SR_OF1 ? "OF1" : "", - s->ir & GPT_SR_ROV ? "ROV" : "", - s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled"); - - - qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags); -} - -static uint32_t imx_timerg_update_counts(IMXTimerGState *s) -{ - uint64_t target = s->waiting_rov ? TIMER_MAX : s->ocr1; - uint64_t cnt = ptimer_get_count(s->timer); - s->cnt = target - cnt; - return s->cnt; -} - -static void imx_timerg_reload(IMXTimerGState *s, uint32_t timeout) -{ - uint64_t diff_cnt; - - if (!(s->cr & GPT_CR_FRR)) { - IPRINTF("IMX_timerg_reload --- called in reset-mode\n"); - return; - } - - /* - * For small timeouts, qemu sometimes runs too slow. - * Better deliver a late interrupt than none. - * - * In Reset mode (FRR bit clear) - * the ptimer reloads itself from OCR1; - * in free-running mode we need to fake - * running from 0 to ocr1 to TIMER_MAX - */ - if (timeout > s->cnt) { - diff_cnt = timeout - s->cnt; - } else { - diff_cnt = 0; - } - ptimer_set_count(s->timer, diff_cnt); -} - -static uint64_t imx_timerg_read(void *opaque, hwaddr offset, - unsigned size) -{ - IMXTimerGState *s = (IMXTimerGState *)opaque; - - DPRINTF("g-read(offset=%x)", offset >> 2); - switch (offset >> 2) { - case 0: /* Control Register */ - DPRINTF(" cr = %x\n", s->cr); - return s->cr; - - case 1: /* prescaler */ - DPRINTF(" pr = %x\n", s->pr); - return s->pr; - - case 2: /* Status Register */ - DPRINTF(" sr = %x\n", s->sr); - return s->sr; - - case 3: /* Interrupt Register */ - DPRINTF(" ir = %x\n", s->ir); - return s->ir; - - case 4: /* Output Compare Register 1 */ - DPRINTF(" ocr1 = %x\n", s->ocr1); - return s->ocr1; - - - case 9: /* cnt */ - imx_timerg_update_counts(s); - DPRINTF(" cnt = %x\n", s->cnt); - return s->cnt; - } - - IPRINTF("imx_timerg_read: Bad offset %x\n", - (int)offset >> 2); - return 0; -} - -static void imx_timerg_reset(DeviceState *dev) -{ - IMXTimerGState *s = container_of(dev, IMXTimerGState, busdev.qdev); - - /* - * Soft reset doesn't touch some bits; hard reset clears them - */ - s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); - s->sr = 0; - s->pr = 0; - s->ir = 0; - s->cnt = 0; - s->ocr1 = TIMER_MAX; - ptimer_stop(s->timer); - ptimer_set_limit(s->timer, TIMER_MAX, 1); - imx_timerg_set_freq(s); -} - -static void imx_timerg_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - IMXTimerGState *s = (IMXTimerGState *)opaque; - DPRINTF("g-write(offset=%x, value = 0x%x)\n", (unsigned int)offset >> 2, - (unsigned int)value); - - switch (offset >> 2) { - case 0: { - uint32_t oldcr = s->cr; - /* CR */ - if (value & GPT_CR_SWR) { /* force reset */ - value &= ~GPT_CR_SWR; - imx_timerg_reset(&s->busdev.qdev); - imx_timerg_update(s); - } - - s->cr = value & ~0x7c00; - imx_timerg_set_freq(s); - if ((oldcr ^ value) & GPT_CR_EN) { - if (value & GPT_CR_EN) { - if (value & GPT_CR_ENMOD) { - ptimer_set_count(s->timer, s->ocr1); - s->cnt = 0; - } - ptimer_run(s->timer, - (value & GPT_CR_FRR) && (s->ocr1 != TIMER_MAX)); - } else { - ptimer_stop(s->timer); - }; - } - return; - } - - case 1: /* Prescaler */ - s->pr = value & 0xfff; - imx_timerg_set_freq(s); - return; - - case 2: /* SR */ - /* - * No point in implementing the status register bits to do with - * external interrupt sources. - */ - value &= GPT_SR_OF1 | GPT_SR_ROV; - s->sr &= ~value; - imx_timerg_update(s); - return; - - case 3: /* IR -- interrupt register */ - s->ir = value & 0x3f; - imx_timerg_update(s); - return; - - case 4: /* OCR1 -- output compare register */ - /* In non-freerun mode, reset count when this register is written */ - if (!(s->cr & GPT_CR_FRR)) { - s->waiting_rov = 0; - ptimer_set_limit(s->timer, value, 1); - } else { - imx_timerg_update_counts(s); - if (value > s->cnt) { - s->waiting_rov = 0; - imx_timerg_reload(s, value); - } else { - s->waiting_rov = 1; - imx_timerg_reload(s, TIMER_MAX - s->cnt); - } - } - s->ocr1 = value; - return; - - default: - IPRINTF("imx_timerg_write: Bad offset %x\n", - (int)offset >> 2); - } -} - -static void imx_timerg_timeout(void *opaque) -{ - IMXTimerGState *s = (IMXTimerGState *)opaque; - - DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov); - if (s->cr & GPT_CR_FRR) { - /* - * Free running timer from 0 -> TIMERMAX - * Generates interrupt at TIMER_MAX and at cnt==ocr1 - * If ocr1 == TIMER_MAX, then no need to reload timer. - */ - if (s->ocr1 == TIMER_MAX) { - DPRINTF("s->ocr1 == TIMER_MAX, FRR\n"); - s->sr |= GPT_SR_OF1 | GPT_SR_ROV; - imx_timerg_update(s); - return; - } - - if (s->waiting_rov) { - /* - * We were waiting for cnt==TIMER_MAX - */ - s->sr |= GPT_SR_ROV; - s->waiting_rov = 0; - s->cnt = 0; - imx_timerg_reload(s, s->ocr1); - } else { - /* Must have got a cnt==ocr1 timeout. */ - s->sr |= GPT_SR_OF1; - s->cnt = s->ocr1; - s->waiting_rov = 1; - imx_timerg_reload(s, TIMER_MAX); - } - imx_timerg_update(s); - return; - } - - s->sr |= GPT_SR_OF1; - imx_timerg_update(s); -} - -static const MemoryRegionOps imx_timerg_ops = { - .read = imx_timerg_read, - .write = imx_timerg_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - - -static int imx_timerg_init(SysBusDevice *dev) -{ - IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev); - QEMUBH *bh; - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &imx_timerg_ops, - s, "imxg-timer", - 0x00001000); - sysbus_init_mmio(dev, &s->iomem); - - bh = qemu_bh_new(imx_timerg_timeout, s); - s->timer = ptimer_init(bh); - - /* Hard reset resets extra bits in CR */ - s->cr = 0; - return 0; -} - - - -/* - * EPIT: Enhanced periodic interrupt timer - */ - -#define CR_EN (1 << 0) -#define CR_ENMOD (1 << 1) -#define CR_OCIEN (1 << 2) -#define CR_RLD (1 << 3) -#define CR_PRESCALE_SHIFT (4) -#define CR_PRESCALE_MASK (0xfff) -#define CR_SWR (1 << 16) -#define CR_IOVW (1 << 17) -#define CR_DBGEN (1 << 18) -#define CR_EPIT (1 << 19) -#define CR_DOZEN (1 << 20) -#define CR_STOPEN (1 << 21) -#define CR_CLKSRC_SHIFT (24) -#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT) - - -/* - * Exact clock frequencies vary from board to board. - * These are typical. - */ -static const IMXClk imx_timerp_clocks[] = { - 0, /* disabled */ - IPG, /* ipg_clk, ~532MHz */ - IPG, /* ipg_clk_highfreq */ - CLK_32k, /* ipg_clk_32k -- ~32kHz */ -}; - -typedef struct { - SysBusDevice busdev; - ptimer_state *timer; - MemoryRegion iomem; - DeviceState *ccm; - - uint32_t cr; - uint32_t lr; - uint32_t cmp; - - uint32_t freq; - int int_level; - qemu_irq irq; -} IMXTimerPState; - -/* - * Update interrupt status - */ -static void imx_timerp_update(IMXTimerPState *s) -{ - if (s->int_level && (s->cr & CR_OCIEN)) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static void imx_timerp_reset(DeviceState *dev) -{ - IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev); - - s->cr = 0; - s->lr = TIMER_MAX; - s->int_level = 0; - s->cmp = 0; - ptimer_stop(s->timer); - ptimer_set_count(s->timer, TIMER_MAX); -} - -static uint64_t imx_timerp_read(void *opaque, hwaddr offset, - unsigned size) -{ - IMXTimerPState *s = (IMXTimerPState *)opaque; - - DPRINTF("p-read(offset=%x)", offset >> 2); - switch (offset >> 2) { - case 0: /* Control Register */ - DPRINTF("cr %x\n", s->cr); - return s->cr; - - case 1: /* Status Register */ - DPRINTF("int_level %x\n", s->int_level); - return s->int_level; - - case 2: /* LR - ticks*/ - DPRINTF("lr %x\n", s->lr); - return s->lr; - - case 3: /* CMP */ - DPRINTF("cmp %x\n", s->cmp); - return s->cmp; - - case 4: /* CNT */ - return ptimer_get_count(s->timer); - } - IPRINTF("imx_timerp_read: Bad offset %x\n", - (int)offset >> 2); - return 0; -} - -static void set_timerp_freq(IMXTimerPState *s) -{ - int clksrc; - unsigned prescaler; - uint32_t freq; - - clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT; - prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK); - freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler; - - s->freq = freq; - DPRINTF("Setting ptimer frequency to %u\n", freq); - - if (freq) { - ptimer_set_freq(s->timer, freq); - } -} - -static void imx_timerp_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - IMXTimerPState *s = (IMXTimerPState *)opaque; - DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, - (unsigned int)value); - - switch (offset >> 2) { - case 0: /* CR */ - if (value & CR_SWR) { - imx_timerp_reset(&s->busdev.qdev); - value &= ~CR_SWR; - } - s->cr = value & 0x03ffffff; - set_timerp_freq(s); - - if (s->freq && (s->cr & CR_EN)) { - if (!(s->cr & CR_ENMOD)) { - ptimer_set_count(s->timer, s->lr); - } - ptimer_run(s->timer, 0); - } else { - ptimer_stop(s->timer); - } - break; - - case 1: /* SR - ACK*/ - s->int_level = 0; - imx_timerp_update(s); - break; - - case 2: /* LR - set ticks */ - s->lr = value; - ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW)); - break; - - case 3: /* CMP */ - s->cmp = value; - if (value) { - IPRINTF( - "Values for EPIT comparison other than zero not supported\n" - ); - } - break; - - default: - IPRINTF("imx_timerp_write: Bad offset %x\n", - (int)offset >> 2); - } -} - -static void imx_timerp_tick(void *opaque) -{ - IMXTimerPState *s = (IMXTimerPState *)opaque; - - DPRINTF("imxp tick\n"); - if (!(s->cr & CR_RLD)) { - ptimer_set_count(s->timer, TIMER_MAX); - } - s->int_level = 1; - imx_timerp_update(s); -} - -void imx_timerp_create(const hwaddr addr, - qemu_irq irq, - DeviceState *ccm) -{ - IMXTimerPState *pp; - DeviceState *dev; - - dev = sysbus_create_simple("imx_timerp", addr, irq); - pp = container_of(dev, IMXTimerPState, busdev.qdev); - pp->ccm = ccm; -} - -static const MemoryRegionOps imx_timerp_ops = { - .read = imx_timerp_read, - .write = imx_timerp_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_imx_timerp = { - .name = "imx-timerp", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cr, IMXTimerPState), - VMSTATE_UINT32(lr, IMXTimerPState), - VMSTATE_UINT32(cmp, IMXTimerPState), - VMSTATE_UINT32(freq, IMXTimerPState), - VMSTATE_INT32(int_level, IMXTimerPState), - VMSTATE_PTIMER(timer, IMXTimerPState), - VMSTATE_END_OF_LIST() - } -}; - -static int imx_timerp_init(SysBusDevice *dev) -{ - IMXTimerPState *s = FROM_SYSBUS(IMXTimerPState, dev); - QEMUBH *bh; - - DPRINTF("imx_timerp_init\n"); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &imx_timerp_ops, - s, "imxp-timer", - 0x00001000); - sysbus_init_mmio(dev, &s->iomem); - - bh = qemu_bh_new(imx_timerp_tick, s); - s->timer = ptimer_init(bh); - - return 0; -} - - -void imx_timerg_create(const hwaddr addr, - qemu_irq irq, - DeviceState *ccm) -{ - IMXTimerGState *pp; - DeviceState *dev; - - dev = sysbus_create_simple("imx_timerg", addr, irq); - pp = container_of(dev, IMXTimerGState, busdev.qdev); - pp->ccm = ccm; -} - -static void imx_timerg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = imx_timerg_init; - dc->vmsd = &vmstate_imx_timerg; - dc->reset = imx_timerg_reset; - dc->desc = "i.MX general timer"; -} - -static void imx_timerp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = imx_timerp_init; - dc->vmsd = &vmstate_imx_timerp; - dc->reset = imx_timerp_reset; - dc->desc = "i.MX periodic timer"; -} - -static const TypeInfo imx_timerp_info = { - .name = "imx_timerp", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXTimerPState), - .class_init = imx_timerp_class_init, -}; - -static const TypeInfo imx_timerg_info = { - .name = "imx_timerg", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXTimerGState), - .class_init = imx_timerg_class_init, -}; - -static void imx_timer_register_types(void) -{ - type_register_static(&imx_timerp_info); - type_register_static(&imx_timerg_info); -} - -type_init(imx_timer_register_types) diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index 824997e367..e8c80b9de2 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -7,3 +7,7 @@ common-obj-y += ps2.o common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o common-obj-$(CONFIG_TSC2005) += tsc2005.o common-obj-$(CONFIG_VMMOUSE) += vmmouse.o + +obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o +obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o +obj-$(CONFIG_TSC210X) += tsc210x.o diff --git a/hw/input/milkymist-softusb.c b/hw/input/milkymist-softusb.c new file mode 100644 index 0000000000..3edab4ff0b --- /dev/null +++ b/hw/input/milkymist-softusb.c @@ -0,0 +1,334 @@ +/* + * QEMU model of the Milkymist SoftUSB block. + * + * Copyright (c) 2010 Michael Walle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * Specification available at: + * not available yet + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "ui/console.h" +#include "hw/input/hid.h" +#include "qemu/error-report.h" + +enum { + R_CTRL = 0, + R_MAX +}; + +enum { + CTRL_RESET = (1<<0), +}; + +#define COMLOC_DEBUG_PRODUCE 0x1000 +#define COMLOC_DEBUG_BASE 0x1001 +#define COMLOC_MEVT_PRODUCE 0x1101 +#define COMLOC_MEVT_BASE 0x1102 +#define COMLOC_KEVT_PRODUCE 0x1142 +#define COMLOC_KEVT_BASE 0x1143 + +struct MilkymistSoftUsbState { + SysBusDevice busdev; + HIDState hid_kbd; + HIDState hid_mouse; + + MemoryRegion regs_region; + MemoryRegion pmem; + MemoryRegion dmem; + qemu_irq irq; + + void *pmem_ptr; + void *dmem_ptr; + + /* device properties */ + uint32_t pmem_size; + uint32_t dmem_size; + + /* device registers */ + uint32_t regs[R_MAX]; + + /* mouse state */ + uint8_t mouse_hid_buffer[4]; + + /* keyboard state */ + uint8_t kbd_hid_buffer[8]; +}; +typedef struct MilkymistSoftUsbState MilkymistSoftUsbState; + +static uint64_t softusb_read(void *opaque, hwaddr addr, + unsigned size) +{ + MilkymistSoftUsbState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTRL: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_softusb: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_softusb_memory_read(addr << 2, r); + + return r; +} + +static void +softusb_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MilkymistSoftUsbState *s = opaque; + + trace_milkymist_softusb_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTRL: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_softusb: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static const MemoryRegionOps softusb_mmio_ops = { + .read = softusb_read, + .write = softusb_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static inline void softusb_read_dmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->dmem_size) { + error_report("milkymist_softusb: read dmem out of bounds " + "at offset 0x%x, len %d", offset, len); + memset(buf, 0, len); + return; + } + + memcpy(buf, s->dmem_ptr + offset, len); +} + +static inline void softusb_write_dmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->dmem_size) { + error_report("milkymist_softusb: write dmem out of bounds " + "at offset 0x%x, len %d", offset, len); + return; + } + + memcpy(s->dmem_ptr + offset, buf, len); +} + +static inline void softusb_read_pmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->pmem_size) { + error_report("milkymist_softusb: read pmem out of bounds " + "at offset 0x%x, len %d", offset, len); + memset(buf, 0, len); + return; + } + + memcpy(buf, s->pmem_ptr + offset, len); +} + +static inline void softusb_write_pmem(MilkymistSoftUsbState *s, + uint32_t offset, uint8_t *buf, uint32_t len) +{ + if (offset + len >= s->pmem_size) { + error_report("milkymist_softusb: write pmem out of bounds " + "at offset 0x%x, len %d", offset, len); + return; + } + + memcpy(s->pmem_ptr + offset, buf, len); +} + +static void softusb_mouse_changed(MilkymistSoftUsbState *s) +{ + uint8_t m; + + softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); + trace_milkymist_softusb_mevt(m); + softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4); + m = (m + 1) & 0xf; + softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); + + trace_milkymist_softusb_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static void softusb_kbd_changed(MilkymistSoftUsbState *s) +{ + uint8_t m; + + softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); + trace_milkymist_softusb_kevt(m); + softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8); + m = (m + 1) & 0x7; + softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); + + trace_milkymist_softusb_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static void softusb_kbd_hid_datain(HIDState *hs) +{ + MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd); + int len; + + /* if device is in reset, do nothing */ + if (s->regs[R_CTRL] & CTRL_RESET) { + return; + } + + len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer)); + + if (len == 8) { + softusb_kbd_changed(s); + } +} + +static void softusb_mouse_hid_datain(HIDState *hs) +{ + MilkymistSoftUsbState *s = + container_of(hs, MilkymistSoftUsbState, hid_mouse); + int len; + + /* if device is in reset, do nothing */ + if (s->regs[R_CTRL] & CTRL_RESET) { + return; + } + + len = hid_pointer_poll(hs, s->mouse_hid_buffer, + sizeof(s->mouse_hid_buffer)); + + if (len == 4) { + softusb_mouse_changed(s); + } +} + +static void milkymist_softusb_reset(DeviceState *d) +{ + MilkymistSoftUsbState *s = + container_of(d, MilkymistSoftUsbState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer)); + memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer)); + + hid_reset(&s->hid_kbd); + hid_reset(&s->hid_mouse); + + /* defaults */ + s->regs[R_CTRL] = CTRL_RESET; +} + +static int milkymist_softusb_init(SysBusDevice *dev) +{ + MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev); + + sysbus_init_irq(dev, &s->irq); + + memory_region_init_io(&s->regs_region, &softusb_mmio_ops, s, + "milkymist-softusb", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); + + /* register pmem and dmem */ + memory_region_init_ram(&s->pmem, "milkymist-softusb.pmem", + s->pmem_size); + vmstate_register_ram_global(&s->pmem); + s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem); + sysbus_init_mmio(dev, &s->pmem); + memory_region_init_ram(&s->dmem, "milkymist-softusb.dmem", + s->dmem_size); + vmstate_register_ram_global(&s->dmem); + s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem); + sysbus_init_mmio(dev, &s->dmem); + + hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain); + hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_softusb = { + .name = "milkymist-softusb", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX), + VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState), + VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState), + VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState), + VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState), + VMSTATE_END_OF_LIST() + } +}; + +static Property milkymist_softusb_properties[] = { + DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000), + DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void milkymist_softusb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = milkymist_softusb_init; + dc->reset = milkymist_softusb_reset; + dc->vmsd = &vmstate_milkymist_softusb; + dc->props = milkymist_softusb_properties; +} + +static const TypeInfo milkymist_softusb_info = { + .name = "milkymist-softusb", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MilkymistSoftUsbState), + .class_init = milkymist_softusb_class_init, +}; + +static void milkymist_softusb_register_types(void) +{ + type_register_static(&milkymist_softusb_info); +} + +type_init(milkymist_softusb_register_types) diff --git a/hw/input/pxa2xx_keypad.c b/hw/input/pxa2xx_keypad.c new file mode 100644 index 0000000000..1fd5f2076a --- /dev/null +++ b/hw/input/pxa2xx_keypad.c @@ -0,0 +1,335 @@ +/* + * Intel PXA27X Keypad Controller emulation. + * + * Copyright (c) 2007 MontaVista Software, Inc + * Written by Armin Kuster + * or + * + * This code is licensed under the GPLv2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw/hw.h" +#include "hw/arm/pxa.h" +#include "ui/console.h" + +/* + * Keypad + */ +#define KPC 0x00 /* Keypad Interface Control register */ +#define KPDK 0x08 /* Keypad Interface Direct Key register */ +#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */ +#define KPMK 0x18 /* Keypad Interface Matrix Key register */ +#define KPAS 0x20 /* Keypad Interface Automatic Scan register */ +#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple + Key Presser register 0 */ +#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple + Key Presser register 1 */ +#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple + Key Presser register 2 */ +#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple + Key Presser register 3 */ +#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval + register */ + +/* Keypad defines */ +#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ +#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ +#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ +#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ +#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ +#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ +#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ +#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ +#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ +#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ +#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ +#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ +#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ +#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ +#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ +#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ +#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ +#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ +#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ +#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ +#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ + +#define KPDK_DKP (0x1 << 31) +#define KPDK_DK7 (0x1 << 7) +#define KPDK_DK6 (0x1 << 6) +#define KPDK_DK5 (0x1 << 5) +#define KPDK_DK4 (0x1 << 4) +#define KPDK_DK3 (0x1 << 3) +#define KPDK_DK2 (0x1 << 2) +#define KPDK_DK1 (0x1 << 1) +#define KPDK_DK0 (0x1 << 0) + +#define KPREC_OF1 (0x1 << 31) +#define KPREC_UF1 (0x1 << 30) +#define KPREC_OF0 (0x1 << 15) +#define KPREC_UF0 (0x1 << 14) + +#define KPMK_MKP (0x1 << 31) +#define KPAS_SO (0x1 << 31) +#define KPASMKPx_SO (0x1 << 31) + + +#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) + +#define PXAKBD_MAXROW 8 +#define PXAKBD_MAXCOL 8 + +struct PXA2xxKeyPadState { + MemoryRegion iomem; + qemu_irq irq; + struct keymap *map; + int pressed_cnt; + int alt_code; + + uint32_t kpc; + uint32_t kpdk; + uint32_t kprec; + uint32_t kpmk; + uint32_t kpas; + uint32_t kpasmkp[4]; + uint32_t kpkdi; +}; + +static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col) +{ + int i; + for (i = 0; i < 4; i++) + { + *col = i * 2; + for (*row = 0; *row < 8; (*row)++) { + if (kp->kpasmkp[i] & (1 << *row)) + return; + } + *col = i * 2 + 1; + for (*row = 0; *row < 8; (*row)++) { + if (kp->kpasmkp[i] & (1 << (*row + 16))) + return; + } + } +} + +static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode) +{ + int row, col, rel, assert_irq = 0; + uint32_t val; + + if (keycode == 0xe0) { + kp->alt_code = 1; + return; + } + + if(!(kp->kpc & KPC_ME)) /* skip if not enabled */ + return; + + rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */ + keycode &= ~0x80; /* strip qemu key release bit */ + if (kp->alt_code) { + keycode |= 0x80; + kp->alt_code = 0; + } + + row = kp->map[keycode].row; + col = kp->map[keycode].column; + if (row == -1 || col == -1) { + return; + } + + val = KPASMKPx_MKC(row, col); + if (rel) { + if (kp->kpasmkp[col / 2] & val) { + kp->kpasmkp[col / 2] &= ~val; + kp->pressed_cnt--; + assert_irq = 1; + } + } else { + if (!(kp->kpasmkp[col / 2] & val)) { + kp->kpasmkp[col / 2] |= val; + kp->pressed_cnt++; + assert_irq = 1; + } + } + kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf; + if (kp->pressed_cnt == 1) { + kp->kpas &= ~((0xf << 4) | 0xf); + if (rel) { + pxa27x_keypad_find_pressed_key(kp, &row, &col); + } + kp->kpas |= ((row & 0xf) << 4) | (col & 0xf); + } + + if (!(kp->kpc & (KPC_AS | KPC_ASACT))) + assert_irq = 0; + + if (assert_irq && (kp->kpc & KPC_MIE)) { + kp->kpc |= KPC_MI; + qemu_irq_raise(kp->irq); + } +} + +static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset, + unsigned size) +{ + PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; + uint32_t tmp; + + switch (offset) { + case KPC: + tmp = s->kpc; + if(tmp & KPC_MI) + s->kpc &= ~(KPC_MI); + if(tmp & KPC_DI) + s->kpc &= ~(KPC_DI); + qemu_irq_lower(s->irq); + return tmp; + break; + case KPDK: + return s->kpdk; + break; + case KPREC: + tmp = s->kprec; + if(tmp & KPREC_OF1) + s->kprec &= ~(KPREC_OF1); + if(tmp & KPREC_UF1) + s->kprec &= ~(KPREC_UF1); + if(tmp & KPREC_OF0) + s->kprec &= ~(KPREC_OF0); + if(tmp & KPREC_UF0) + s->kprec &= ~(KPREC_UF0); + return tmp; + break; + case KPMK: + tmp = s->kpmk; + if(tmp & KPMK_MKP) + s->kpmk &= ~(KPMK_MKP); + return tmp; + break; + case KPAS: + return s->kpas; + break; + case KPASMKP0: + return s->kpasmkp[0]; + break; + case KPASMKP1: + return s->kpasmkp[1]; + break; + case KPASMKP2: + return s->kpasmkp[2]; + break; + case KPASMKP3: + return s->kpasmkp[3]; + break; + case KPKDI: + return s->kpkdi; + break; + default: + hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); + } + + return 0; +} + +static void pxa2xx_keypad_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; + + switch (offset) { + case KPC: + s->kpc = value; + if (s->kpc & KPC_AS) { + s->kpc &= ~(KPC_AS); + } + break; + case KPDK: + s->kpdk = value; + break; + case KPREC: + s->kprec = value; + break; + case KPMK: + s->kpmk = value; + break; + case KPAS: + s->kpas = value; + break; + case KPASMKP0: + s->kpasmkp[0] = value; + break; + case KPASMKP1: + s->kpasmkp[1] = value; + break; + case KPASMKP2: + s->kpasmkp[2] = value; + break; + case KPASMKP3: + s->kpasmkp[3] = value; + break; + case KPKDI: + s->kpkdi = value; + break; + + default: + hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); + } +} + +static const MemoryRegionOps pxa2xx_keypad_ops = { + .read = pxa2xx_keypad_read, + .write = pxa2xx_keypad_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_pxa2xx_keypad = { + .name = "pxa2xx_keypad", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(kpc, PXA2xxKeyPadState), + VMSTATE_UINT32(kpdk, PXA2xxKeyPadState), + VMSTATE_UINT32(kprec, PXA2xxKeyPadState), + VMSTATE_UINT32(kpmk, PXA2xxKeyPadState), + VMSTATE_UINT32(kpas, PXA2xxKeyPadState), + VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4), + VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState), + VMSTATE_END_OF_LIST() + } +}; + +PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq) +{ + PXA2xxKeyPadState *s; + + s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState)); + s->irq = irq; + + memory_region_init_io(&s->iomem, &pxa2xx_keypad_ops, s, + "pxa2xx-keypad", 0x00100000); + memory_region_add_subregion(sysmem, base, &s->iomem); + + vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s); + + return s; +} + +void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map, + int size) +{ + if(!map || size < 0x80) { + fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__); + exit(-1); + } + + kp->map = map; + qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp); +} diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c new file mode 100644 index 0000000000..e6c217c8db --- /dev/null +++ b/hw/input/tsc210x.c @@ -0,0 +1,1293 @@ +/* + * TI TSC2102 (touchscreen/sensors/audio controller) emulator. + * TI TSC2301 (touchscreen/sensors/keypad). + * + * Copyright (c) 2006 Andrzej Zaborowski + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/hw.h" +#include "audio/audio.h" +#include "qemu/timer.h" +#include "ui/console.h" +#include "hw/arm/omap.h" /* For I2SCodec and uWireSlave */ +#include "hw/arm/devices.h" + +#define TSC_DATA_REGISTERS_PAGE 0x0 +#define TSC_CONTROL_REGISTERS_PAGE 0x1 +#define TSC_AUDIO_REGISTERS_PAGE 0x2 + +#define TSC_VERBOSE + +#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p])) + +typedef struct { + qemu_irq pint; + qemu_irq kbint; + qemu_irq davint; + QEMUTimer *timer; + QEMUSoundCard card; + uWireSlave chip; + I2SCodec codec; + uint8_t in_fifo[16384]; + uint8_t out_fifo[16384]; + uint16_t model; + + int x, y; + int pressure; + + int state, page, offset, irq; + uint16_t command, dav; + + int busy; + int enabled; + int host_mode; + int function; + int nextfunction; + int precision; + int nextprecision; + int filter; + int pin_func; + int ref; + int timing; + int noise; + + uint16_t audio_ctrl1; + uint16_t audio_ctrl2; + uint16_t audio_ctrl3; + uint16_t pll[3]; + uint16_t volume; + int64_t volume_change; + int softstep; + uint16_t dac_power; + int64_t powerdown; + uint16_t filter_data[0x14]; + + const char *name; + SWVoiceIn *adc_voice[1]; + SWVoiceOut *dac_voice[1]; + int i2s_rx_rate; + int i2s_tx_rate; + + int tr[8]; + + struct { + uint16_t down; + uint16_t mask; + int scan; + int debounce; + int mode; + int intr; + } kb; +} TSC210xState; + +static const int resolution[4] = { 12, 8, 10, 12 }; + +#define TSC_MODE_NO_SCAN 0x0 +#define TSC_MODE_XY_SCAN 0x1 +#define TSC_MODE_XYZ_SCAN 0x2 +#define TSC_MODE_X 0x3 +#define TSC_MODE_Y 0x4 +#define TSC_MODE_Z 0x5 +#define TSC_MODE_BAT1 0x6 +#define TSC_MODE_BAT2 0x7 +#define TSC_MODE_AUX 0x8 +#define TSC_MODE_AUX_SCAN 0x9 +#define TSC_MODE_TEMP1 0xa +#define TSC_MODE_PORT_SCAN 0xb +#define TSC_MODE_TEMP2 0xc +#define TSC_MODE_XX_DRV 0xd +#define TSC_MODE_YY_DRV 0xe +#define TSC_MODE_YX_DRV 0xf + +static const uint16_t mode_regs[16] = { + 0x0000, /* No scan */ + 0x0600, /* X, Y scan */ + 0x0780, /* X, Y, Z scan */ + 0x0400, /* X */ + 0x0200, /* Y */ + 0x0180, /* Z */ + 0x0040, /* BAT1 */ + 0x0030, /* BAT2 */ + 0x0010, /* AUX */ + 0x0010, /* AUX scan */ + 0x0004, /* TEMP1 */ + 0x0070, /* Port scan */ + 0x0002, /* TEMP2 */ + 0x0000, /* X+, X- drivers */ + 0x0000, /* Y+, Y- drivers */ + 0x0000, /* Y+, X- drivers */ +}; + +#define X_TRANSFORM(s) \ + ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) +#define Y_TRANSFORM(s) \ + ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) +#define Z1_TRANSFORM(s) \ + ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) +#define Z2_TRANSFORM(s) \ + ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) + +#define BAT1_VAL 0x8660 +#define BAT2_VAL 0x0000 +#define AUX1_VAL 0x35c0 +#define AUX2_VAL 0xffff +#define TEMP1_VAL 0x8c70 +#define TEMP2_VAL 0xa5b0 + +#define TSC_POWEROFF_DELAY 50 +#define TSC_SOFTSTEP_DELAY 50 + +static void tsc210x_reset(TSC210xState *s) +{ + s->state = 0; + s->pin_func = 2; + s->enabled = 0; + s->busy = 0; + s->nextfunction = 0; + s->ref = 0; + s->timing = 0; + s->irq = 0; + s->dav = 0; + + s->audio_ctrl1 = 0x0000; + s->audio_ctrl2 = 0x4410; + s->audio_ctrl3 = 0x0000; + s->pll[0] = 0x1004; + s->pll[1] = 0x0000; + s->pll[2] = 0x1fff; + s->volume = 0xffff; + s->dac_power = 0x8540; + s->softstep = 1; + s->volume_change = 0; + s->powerdown = 0; + s->filter_data[0x00] = 0x6be3; + s->filter_data[0x01] = 0x9666; + s->filter_data[0x02] = 0x675d; + s->filter_data[0x03] = 0x6be3; + s->filter_data[0x04] = 0x9666; + s->filter_data[0x05] = 0x675d; + s->filter_data[0x06] = 0x7d83; + s->filter_data[0x07] = 0x84ee; + s->filter_data[0x08] = 0x7d83; + s->filter_data[0x09] = 0x84ee; + s->filter_data[0x0a] = 0x6be3; + s->filter_data[0x0b] = 0x9666; + s->filter_data[0x0c] = 0x675d; + s->filter_data[0x0d] = 0x6be3; + s->filter_data[0x0e] = 0x9666; + s->filter_data[0x0f] = 0x675d; + s->filter_data[0x10] = 0x7d83; + s->filter_data[0x11] = 0x84ee; + s->filter_data[0x12] = 0x7d83; + s->filter_data[0x13] = 0x84ee; + + s->i2s_tx_rate = 0; + s->i2s_rx_rate = 0; + + s->kb.scan = 1; + s->kb.debounce = 0; + s->kb.mask = 0x0000; + s->kb.mode = 3; + s->kb.intr = 0; + + qemu_set_irq(s->pint, !s->irq); + qemu_set_irq(s->davint, !s->dav); + qemu_irq_raise(s->kbint); +} + +typedef struct { + int rate; + int dsor; + int fsref; +} TSC210xRateInfo; + +/* { rate, dsor, fsref } */ +static const TSC210xRateInfo tsc2101_rates[] = { + /* Fsref / 6.0 */ + { 7350, 7, 1 }, + { 8000, 7, 0 }, + /* Fsref / 5.5 */ + { 8018, 6, 1 }, + { 8727, 6, 0 }, + /* Fsref / 5.0 */ + { 8820, 5, 1 }, + { 9600, 5, 0 }, + /* Fsref / 4.0 */ + { 11025, 4, 1 }, + { 12000, 4, 0 }, + /* Fsref / 3.0 */ + { 14700, 3, 1 }, + { 16000, 3, 0 }, + /* Fsref / 2.0 */ + { 22050, 2, 1 }, + { 24000, 2, 0 }, + /* Fsref / 1.5 */ + { 29400, 1, 1 }, + { 32000, 1, 0 }, + /* Fsref */ + { 44100, 0, 1 }, + { 48000, 0, 0 }, + + { 0, 0, 0 }, +}; + +/* { rate, dsor, fsref } */ +static const TSC210xRateInfo tsc2102_rates[] = { + /* Fsref / 6.0 */ + { 7350, 63, 1 }, + { 8000, 63, 0 }, + /* Fsref / 6.0 */ + { 7350, 54, 1 }, + { 8000, 54, 0 }, + /* Fsref / 5.0 */ + { 8820, 45, 1 }, + { 9600, 45, 0 }, + /* Fsref / 4.0 */ + { 11025, 36, 1 }, + { 12000, 36, 0 }, + /* Fsref / 3.0 */ + { 14700, 27, 1 }, + { 16000, 27, 0 }, + /* Fsref / 2.0 */ + { 22050, 18, 1 }, + { 24000, 18, 0 }, + /* Fsref / 1.5 */ + { 29400, 9, 1 }, + { 32000, 9, 0 }, + /* Fsref */ + { 44100, 0, 1 }, + { 48000, 0, 0 }, + + { 0, 0, 0 }, +}; + +static inline void tsc210x_out_flush(TSC210xState *s, int len) +{ + uint8_t *data = s->codec.out.fifo + s->codec.out.start; + uint8_t *end = data + len; + + while (data < end) + data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); + + s->codec.out.len -= len; + if (s->codec.out.len) + memmove(s->codec.out.fifo, end, s->codec.out.len); + s->codec.out.start = 0; +} + +static void tsc210x_audio_out_cb(TSC210xState *s, int free_b) +{ + if (s->codec.out.len >= free_b) { + tsc210x_out_flush(s, free_b); + return; + } + + s->codec.out.size = MIN(free_b, 16384); + qemu_irq_raise(s->codec.tx_start); +} + +static void tsc2102_audio_rate_update(TSC210xState *s) +{ + const TSC210xRateInfo *rate; + + s->codec.tx_rate = 0; + s->codec.rx_rate = 0; + if (s->dac_power & (1 << 15)) /* PWDNC */ + return; + + for (rate = tsc2102_rates; rate->rate; rate ++) + if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ + rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ + break; + if (!rate->rate) { + printf("%s: unknown sampling rate configured\n", __FUNCTION__); + return; + } + + s->codec.tx_rate = rate->rate; +} + +static void tsc2102_audio_output_update(TSC210xState *s) +{ + int enable; + struct audsettings fmt; + + if (s->dac_voice[0]) { + tsc210x_out_flush(s, s->codec.out.len); + s->codec.out.size = 0; + AUD_set_active_out(s->dac_voice[0], 0); + AUD_close_out(&s->card, s->dac_voice[0]); + s->dac_voice[0] = NULL; + } + s->codec.cts = 0; + + enable = + (~s->dac_power & (1 << 15)) && /* PWDNC */ + (~s->dac_power & (1 << 10)); /* DAPWDN */ + if (!enable || !s->codec.tx_rate) + return; + + /* Force our own sampling rate even in slave DAC mode */ + fmt.endianness = 0; + fmt.nchannels = 2; + fmt.freq = s->codec.tx_rate; + fmt.fmt = AUD_FMT_S16; + + s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], + "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); + if (s->dac_voice[0]) { + s->codec.cts = 1; + AUD_set_active_out(s->dac_voice[0], 1); + } +} + +static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg) +{ + switch (reg) { + case 0x00: /* X */ + s->dav &= 0xfbff; + return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + + (s->noise & 3); + + case 0x01: /* Y */ + s->noise ++; + s->dav &= 0xfdff; + return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ + (s->noise & 3); + + case 0x02: /* Z1 */ + s->dav &= 0xfeff; + return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - + (s->noise & 3); + + case 0x03: /* Z2 */ + s->dav &= 0xff7f; + return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | + (s->noise & 3); + + case 0x04: /* KPData */ + if ((s->model & 0xff00) == 0x2300) { + if (s->kb.intr && (s->kb.mode & 2)) { + s->kb.intr = 0; + qemu_irq_raise(s->kbint); + } + return s->kb.down; + } + + return 0xffff; + + case 0x05: /* BAT1 */ + s->dav &= 0xffbf; + return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) + + (s->noise & 6); + + case 0x06: /* BAT2 */ + s->dav &= 0xffdf; + return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision); + + case 0x07: /* AUX1 */ + s->dav &= 0xffef; + return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision); + + case 0x08: /* AUX2 */ + s->dav &= 0xfff7; + return 0xffff; + + case 0x09: /* TEMP1 */ + s->dav &= 0xfffb; + return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - + (s->noise & 5); + + case 0x0a: /* TEMP2 */ + s->dav &= 0xfffd; + return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ + (s->noise & 3); + + case 0x0b: /* DAC */ + s->dav &= 0xfffe; + return 0xffff; + + default: +#ifdef TSC_VERBOSE + fprintf(stderr, "tsc2102_data_register_read: " + "no such register: 0x%02x\n", reg); +#endif + return 0xffff; + } +} + +static uint16_t tsc2102_control_register_read( + TSC210xState *s, int reg) +{ + switch (reg) { + case 0x00: /* TSC ADC */ + return (s->pressure << 15) | ((!s->busy) << 14) | + (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; + + case 0x01: /* Status / Keypad Control */ + if ((s->model & 0xff00) == 0x2100) + return (s->pin_func << 14) | ((!s->enabled) << 13) | + (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav; + else + return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) | + (s->kb.debounce << 11); + + case 0x02: /* DAC Control */ + if ((s->model & 0xff00) == 0x2300) + return s->dac_power & 0x8000; + else + goto bad_reg; + + case 0x03: /* Reference */ + return s->ref; + + case 0x04: /* Reset */ + return 0xffff; + + case 0x05: /* Configuration */ + return s->timing; + + case 0x06: /* Secondary configuration */ + if ((s->model & 0xff00) == 0x2100) + goto bad_reg; + return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2]; + + case 0x10: /* Keypad Mask */ + if ((s->model & 0xff00) == 0x2100) + goto bad_reg; + return s->kb.mask; + + default: + bad_reg: +#ifdef TSC_VERBOSE + fprintf(stderr, "tsc2102_control_register_read: " + "no such register: 0x%02x\n", reg); +#endif + return 0xffff; + } +} + +static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg) +{ + int l_ch, r_ch; + uint16_t val; + + switch (reg) { + case 0x00: /* Audio Control 1 */ + return s->audio_ctrl1; + + case 0x01: + return 0xff00; + + case 0x02: /* DAC Volume Control */ + return s->volume; + + case 0x03: + return 0x8b00; + + case 0x04: /* Audio Control 2 */ + l_ch = 1; + r_ch = 1; + if (s->softstep && !(s->dac_power & (1 << 10))) { + l_ch = (qemu_get_clock_ns(vm_clock) > + s->volume_change + TSC_SOFTSTEP_DELAY); + r_ch = (qemu_get_clock_ns(vm_clock) > + s->volume_change + TSC_SOFTSTEP_DELAY); + } + + return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2); + + case 0x05: /* Stereo DAC Power Control */ + return 0x2aa0 | s->dac_power | + (((s->dac_power & (1 << 10)) && + (qemu_get_clock_ns(vm_clock) > + s->powerdown + TSC_POWEROFF_DELAY)) << 6); + + case 0x06: /* Audio Control 3 */ + val = s->audio_ctrl3 | 0x0001; + s->audio_ctrl3 &= 0xff3f; + return val; + + case 0x07: /* LCH_BASS_BOOST_N0 */ + case 0x08: /* LCH_BASS_BOOST_N1 */ + case 0x09: /* LCH_BASS_BOOST_N2 */ + case 0x0a: /* LCH_BASS_BOOST_N3 */ + case 0x0b: /* LCH_BASS_BOOST_N4 */ + case 0x0c: /* LCH_BASS_BOOST_N5 */ + case 0x0d: /* LCH_BASS_BOOST_D1 */ + case 0x0e: /* LCH_BASS_BOOST_D2 */ + case 0x0f: /* LCH_BASS_BOOST_D4 */ + case 0x10: /* LCH_BASS_BOOST_D5 */ + case 0x11: /* RCH_BASS_BOOST_N0 */ + case 0x12: /* RCH_BASS_BOOST_N1 */ + case 0x13: /* RCH_BASS_BOOST_N2 */ + case 0x14: /* RCH_BASS_BOOST_N3 */ + case 0x15: /* RCH_BASS_BOOST_N4 */ + case 0x16: /* RCH_BASS_BOOST_N5 */ + case 0x17: /* RCH_BASS_BOOST_D1 */ + case 0x18: /* RCH_BASS_BOOST_D2 */ + case 0x19: /* RCH_BASS_BOOST_D4 */ + case 0x1a: /* RCH_BASS_BOOST_D5 */ + return s->filter_data[reg - 0x07]; + + case 0x1b: /* PLL Programmability 1 */ + return s->pll[0]; + + case 0x1c: /* PLL Programmability 2 */ + return s->pll[1]; + + case 0x1d: /* Audio Control 4 */ + return (!s->softstep) << 14; + + default: +#ifdef TSC_VERBOSE + fprintf(stderr, "tsc2102_audio_register_read: " + "no such register: 0x%02x\n", reg); +#endif + return 0xffff; + } +} + +static void tsc2102_data_register_write( + TSC210xState *s, int reg, uint16_t value) +{ + switch (reg) { + case 0x00: /* X */ + case 0x01: /* Y */ + case 0x02: /* Z1 */ + case 0x03: /* Z2 */ + case 0x05: /* BAT1 */ + case 0x06: /* BAT2 */ + case 0x07: /* AUX1 */ + case 0x08: /* AUX2 */ + case 0x09: /* TEMP1 */ + case 0x0a: /* TEMP2 */ + return; + + default: +#ifdef TSC_VERBOSE + fprintf(stderr, "tsc2102_data_register_write: " + "no such register: 0x%02x\n", reg); +#endif + } +} + +static void tsc2102_control_register_write( + TSC210xState *s, int reg, uint16_t value) +{ + switch (reg) { + case 0x00: /* TSC ADC */ + s->host_mode = value >> 15; + s->enabled = !(value & 0x4000); + if (s->busy && !s->enabled) + qemu_del_timer(s->timer); + s->busy &= s->enabled; + s->nextfunction = (value >> 10) & 0xf; + s->nextprecision = (value >> 8) & 3; + s->filter = value & 0xff; + return; + + case 0x01: /* Status / Keypad Control */ + if ((s->model & 0xff00) == 0x2100) + s->pin_func = value >> 14; + else { + s->kb.scan = (value >> 14) & 1; + s->kb.debounce = (value >> 11) & 7; + if (s->kb.intr && s->kb.scan) { + s->kb.intr = 0; + qemu_irq_raise(s->kbint); + } + } + return; + + case 0x02: /* DAC Control */ + if ((s->model & 0xff00) == 0x2300) { + s->dac_power &= 0x7fff; + s->dac_power |= 0x8000 & value; + } else + goto bad_reg; + break; + + case 0x03: /* Reference */ + s->ref = value & 0x1f; + return; + + case 0x04: /* Reset */ + if (value == 0xbb00) { + if (s->busy) + qemu_del_timer(s->timer); + tsc210x_reset(s); +#ifdef TSC_VERBOSE + } else { + fprintf(stderr, "tsc2102_control_register_write: " + "wrong value written into RESET\n"); +#endif + } + return; + + case 0x05: /* Configuration */ + s->timing = value & 0x3f; +#ifdef TSC_VERBOSE + if (value & ~0x3f) + fprintf(stderr, "tsc2102_control_register_write: " + "wrong value written into CONFIG\n"); +#endif + return; + + case 0x06: /* Secondary configuration */ + if ((s->model & 0xff00) == 0x2100) + goto bad_reg; + s->kb.mode = value >> 14; + s->pll[2] = value & 0x3ffff; + return; + + case 0x10: /* Keypad Mask */ + if ((s->model & 0xff00) == 0x2100) + goto bad_reg; + s->kb.mask = value; + return; + + default: + bad_reg: +#ifdef TSC_VERBOSE + fprintf(stderr, "tsc2102_control_register_write: " + "no such register: 0x%02x\n", reg); +#endif + } +} + +static void tsc2102_audio_register_write( + TSC210xState *s, int reg, uint16_t value) +{ + switch (reg) { + case 0x00: /* Audio Control 1 */ + s->audio_ctrl1 = value & 0x0f3f; +#ifdef TSC_VERBOSE + if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7))) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into Audio 1\n"); +#endif + tsc2102_audio_rate_update(s); + tsc2102_audio_output_update(s); + return; + + case 0x01: +#ifdef TSC_VERBOSE + if (value != 0xff00) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into reg 0x01\n"); +#endif + return; + + case 0x02: /* DAC Volume Control */ + s->volume = value; + s->volume_change = qemu_get_clock_ns(vm_clock); + return; + + case 0x03: +#ifdef TSC_VERBOSE + if (value != 0x8b00) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into reg 0x03\n"); +#endif + return; + + case 0x04: /* Audio Control 2 */ + s->audio_ctrl2 = value & 0xf7f2; +#ifdef TSC_VERBOSE + if (value & ~0xf7fd) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into Audio 2\n"); +#endif + return; + + case 0x05: /* Stereo DAC Power Control */ + if ((value & ~s->dac_power) & (1 << 10)) + s->powerdown = qemu_get_clock_ns(vm_clock); + + s->dac_power = value & 0x9543; +#ifdef TSC_VERBOSE + if ((value & ~0x9543) != 0x2aa0) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into Power\n"); +#endif + tsc2102_audio_rate_update(s); + tsc2102_audio_output_update(s); + return; + + case 0x06: /* Audio Control 3 */ + s->audio_ctrl3 &= 0x00c0; + s->audio_ctrl3 |= value & 0xf800; +#ifdef TSC_VERBOSE + if (value & ~0xf8c7) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into Audio 3\n"); +#endif + tsc2102_audio_output_update(s); + return; + + case 0x07: /* LCH_BASS_BOOST_N0 */ + case 0x08: /* LCH_BASS_BOOST_N1 */ + case 0x09: /* LCH_BASS_BOOST_N2 */ + case 0x0a: /* LCH_BASS_BOOST_N3 */ + case 0x0b: /* LCH_BASS_BOOST_N4 */ + case 0x0c: /* LCH_BASS_BOOST_N5 */ + case 0x0d: /* LCH_BASS_BOOST_D1 */ + case 0x0e: /* LCH_BASS_BOOST_D2 */ + case 0x0f: /* LCH_BASS_BOOST_D4 */ + case 0x10: /* LCH_BASS_BOOST_D5 */ + case 0x11: /* RCH_BASS_BOOST_N0 */ + case 0x12: /* RCH_BASS_BOOST_N1 */ + case 0x13: /* RCH_BASS_BOOST_N2 */ + case 0x14: /* RCH_BASS_BOOST_N3 */ + case 0x15: /* RCH_BASS_BOOST_N4 */ + case 0x16: /* RCH_BASS_BOOST_N5 */ + case 0x17: /* RCH_BASS_BOOST_D1 */ + case 0x18: /* RCH_BASS_BOOST_D2 */ + case 0x19: /* RCH_BASS_BOOST_D4 */ + case 0x1a: /* RCH_BASS_BOOST_D5 */ + s->filter_data[reg - 0x07] = value; + return; + + case 0x1b: /* PLL Programmability 1 */ + s->pll[0] = value & 0xfffc; +#ifdef TSC_VERBOSE + if (value & ~0xfffc) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into PLL 1\n"); +#endif + return; + + case 0x1c: /* PLL Programmability 2 */ + s->pll[1] = value & 0xfffc; +#ifdef TSC_VERBOSE + if (value & ~0xfffc) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into PLL 2\n"); +#endif + return; + + case 0x1d: /* Audio Control 4 */ + s->softstep = !(value & 0x4000); +#ifdef TSC_VERBOSE + if (value & ~0x4000) + fprintf(stderr, "tsc2102_audio_register_write: " + "wrong value written into Audio 4\n"); +#endif + return; + + default: +#ifdef TSC_VERBOSE + fprintf(stderr, "tsc2102_audio_register_write: " + "no such register: 0x%02x\n", reg); +#endif + } +} + +/* This handles most of the chip logic. */ +static void tsc210x_pin_update(TSC210xState *s) +{ + int64_t expires; + int pin_state; + + switch (s->pin_func) { + case 0: + pin_state = s->pressure; + break; + case 1: + pin_state = !!s->dav; + break; + case 2: + default: + pin_state = s->pressure && !s->dav; + } + + if (!s->enabled) + pin_state = 0; + + if (pin_state != s->irq) { + s->irq = pin_state; + qemu_set_irq(s->pint, !s->irq); + } + + switch (s->nextfunction) { + case TSC_MODE_XY_SCAN: + case TSC_MODE_XYZ_SCAN: + if (!s->pressure) + return; + break; + + case TSC_MODE_X: + case TSC_MODE_Y: + case TSC_MODE_Z: + if (!s->pressure) + return; + /* Fall through */ + case TSC_MODE_BAT1: + case TSC_MODE_BAT2: + case TSC_MODE_AUX: + case TSC_MODE_TEMP1: + case TSC_MODE_TEMP2: + if (s->dav) + s->enabled = 0; + break; + + case TSC_MODE_AUX_SCAN: + case TSC_MODE_PORT_SCAN: + break; + + case TSC_MODE_NO_SCAN: + case TSC_MODE_XX_DRV: + case TSC_MODE_YY_DRV: + case TSC_MODE_YX_DRV: + default: + return; + } + + if (!s->enabled || s->busy || s->dav) + return; + + s->busy = 1; + s->precision = s->nextprecision; + s->function = s->nextfunction; + expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 10); + qemu_mod_timer(s->timer, expires); +} + +static uint16_t tsc210x_read(TSC210xState *s) +{ + uint16_t ret = 0x0000; + + if (!s->command) + fprintf(stderr, "tsc210x_read: SPI underrun!\n"); + + switch (s->page) { + case TSC_DATA_REGISTERS_PAGE: + ret = tsc2102_data_register_read(s, s->offset); + if (!s->dav) + qemu_irq_raise(s->davint); + break; + case TSC_CONTROL_REGISTERS_PAGE: + ret = tsc2102_control_register_read(s, s->offset); + break; + case TSC_AUDIO_REGISTERS_PAGE: + ret = tsc2102_audio_register_read(s, s->offset); + break; + default: + hw_error("tsc210x_read: wrong memory page\n"); + } + + tsc210x_pin_update(s); + + /* Allow sequential reads. */ + s->offset ++; + s->state = 0; + return ret; +} + +static void tsc210x_write(TSC210xState *s, uint16_t value) +{ + /* + * This is a two-state state machine for reading + * command and data every second time. + */ + if (!s->state) { + s->command = value >> 15; + s->page = (value >> 11) & 0x0f; + s->offset = (value >> 5) & 0x3f; + s->state = 1; + } else { + if (s->command) + fprintf(stderr, "tsc210x_write: SPI overrun!\n"); + else + switch (s->page) { + case TSC_DATA_REGISTERS_PAGE: + tsc2102_data_register_write(s, s->offset, value); + break; + case TSC_CONTROL_REGISTERS_PAGE: + tsc2102_control_register_write(s, s->offset, value); + break; + case TSC_AUDIO_REGISTERS_PAGE: + tsc2102_audio_register_write(s, s->offset, value); + break; + default: + hw_error("tsc210x_write: wrong memory page\n"); + } + + tsc210x_pin_update(s); + s->state = 0; + } +} + +uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len) +{ + TSC210xState *s = opaque; + uint32_t ret = 0; + + if (len != 16) + hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len); + + /* TODO: sequential reads etc - how do we make sure the host doesn't + * unintentionally read out a conversion result from a register while + * transmitting the command word of the next command? */ + if (!value || (s->state && s->command)) + ret = tsc210x_read(s); + if (value || (s->state && !s->command)) + tsc210x_write(s, value); + + return ret; +} + +static void tsc210x_timer_tick(void *opaque) +{ + TSC210xState *s = opaque; + + /* Timer ticked -- a set of conversions has been finished. */ + + if (!s->busy) + return; + + s->busy = 0; + s->dav |= mode_regs[s->function]; + tsc210x_pin_update(s); + qemu_irq_lower(s->davint); +} + +static void tsc210x_touchscreen_event(void *opaque, + int x, int y, int z, int buttons_state) +{ + TSC210xState *s = opaque; + int p = s->pressure; + + if (buttons_state) { + s->x = x; + s->y = y; + } + s->pressure = !!buttons_state; + + /* + * Note: We would get better responsiveness in the guest by + * signaling TS events immediately, but for now we simulate + * the first conversion delay for sake of correctness. + */ + if (p != s->pressure) + tsc210x_pin_update(s); +} + +static void tsc210x_i2s_swallow(TSC210xState *s) +{ + if (s->dac_voice[0]) + tsc210x_out_flush(s, s->codec.out.len); + else + s->codec.out.len = 0; +} + +static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out) +{ + s->i2s_tx_rate = out; + s->i2s_rx_rate = in; +} + +static void tsc210x_save(QEMUFile *f, void *opaque) +{ + TSC210xState *s = (TSC210xState *) opaque; + int64_t now = qemu_get_clock_ns(vm_clock); + int i; + + qemu_put_be16(f, s->x); + qemu_put_be16(f, s->y); + qemu_put_byte(f, s->pressure); + + qemu_put_byte(f, s->state); + qemu_put_byte(f, s->page); + qemu_put_byte(f, s->offset); + qemu_put_byte(f, s->command); + + qemu_put_byte(f, s->irq); + qemu_put_be16s(f, &s->dav); + + qemu_put_timer(f, s->timer); + qemu_put_byte(f, s->enabled); + qemu_put_byte(f, s->host_mode); + qemu_put_byte(f, s->function); + qemu_put_byte(f, s->nextfunction); + qemu_put_byte(f, s->precision); + qemu_put_byte(f, s->nextprecision); + qemu_put_byte(f, s->filter); + qemu_put_byte(f, s->pin_func); + qemu_put_byte(f, s->ref); + qemu_put_byte(f, s->timing); + qemu_put_be32(f, s->noise); + + qemu_put_be16s(f, &s->audio_ctrl1); + qemu_put_be16s(f, &s->audio_ctrl2); + qemu_put_be16s(f, &s->audio_ctrl3); + qemu_put_be16s(f, &s->pll[0]); + qemu_put_be16s(f, &s->pll[1]); + qemu_put_be16s(f, &s->volume); + qemu_put_sbe64(f, (s->volume_change - now)); + qemu_put_sbe64(f, (s->powerdown - now)); + qemu_put_byte(f, s->softstep); + qemu_put_be16s(f, &s->dac_power); + + for (i = 0; i < 0x14; i ++) + qemu_put_be16s(f, &s->filter_data[i]); +} + +static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) +{ + TSC210xState *s = (TSC210xState *) opaque; + int64_t now = qemu_get_clock_ns(vm_clock); + int i; + + s->x = qemu_get_be16(f); + s->y = qemu_get_be16(f); + s->pressure = qemu_get_byte(f); + + s->state = qemu_get_byte(f); + s->page = qemu_get_byte(f); + s->offset = qemu_get_byte(f); + s->command = qemu_get_byte(f); + + s->irq = qemu_get_byte(f); + qemu_get_be16s(f, &s->dav); + + qemu_get_timer(f, s->timer); + s->enabled = qemu_get_byte(f); + s->host_mode = qemu_get_byte(f); + s->function = qemu_get_byte(f); + s->nextfunction = qemu_get_byte(f); + s->precision = qemu_get_byte(f); + s->nextprecision = qemu_get_byte(f); + s->filter = qemu_get_byte(f); + s->pin_func = qemu_get_byte(f); + s->ref = qemu_get_byte(f); + s->timing = qemu_get_byte(f); + s->noise = qemu_get_be32(f); + + qemu_get_be16s(f, &s->audio_ctrl1); + qemu_get_be16s(f, &s->audio_ctrl2); + qemu_get_be16s(f, &s->audio_ctrl3); + qemu_get_be16s(f, &s->pll[0]); + qemu_get_be16s(f, &s->pll[1]); + qemu_get_be16s(f, &s->volume); + s->volume_change = qemu_get_sbe64(f) + now; + s->powerdown = qemu_get_sbe64(f) + now; + s->softstep = qemu_get_byte(f); + qemu_get_be16s(f, &s->dac_power); + + for (i = 0; i < 0x14; i ++) + qemu_get_be16s(f, &s->filter_data[i]); + + s->busy = qemu_timer_pending(s->timer); + qemu_set_irq(s->pint, !s->irq); + qemu_set_irq(s->davint, !s->dav); + + return 0; +} + +uWireSlave *tsc2102_init(qemu_irq pint) +{ + TSC210xState *s; + + s = (TSC210xState *) + g_malloc0(sizeof(TSC210xState)); + memset(s, 0, sizeof(TSC210xState)); + s->x = 160; + s->y = 160; + s->pressure = 0; + s->precision = s->nextprecision = 0; + s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s); + s->pint = pint; + s->model = 0x2102; + s->name = "tsc2102"; + + s->tr[0] = 0; + s->tr[1] = 1; + s->tr[2] = 1; + s->tr[3] = 0; + s->tr[4] = 1; + s->tr[5] = 0; + s->tr[6] = 1; + s->tr[7] = 0; + + s->chip.opaque = s; + s->chip.send = (void *) tsc210x_write; + s->chip.receive = (void *) tsc210x_read; + + s->codec.opaque = s; + s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; + s->codec.set_rate = (void *) tsc210x_i2s_set_rate; + s->codec.in.fifo = s->in_fifo; + s->codec.out.fifo = s->out_fifo; + + tsc210x_reset(s); + + qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, + "QEMU TSC2102-driven Touchscreen"); + + AUD_register_card(s->name, &s->card); + + qemu_register_reset((void *) tsc210x_reset, s); + register_savevm(NULL, s->name, -1, 0, + tsc210x_save, tsc210x_load, s); + + return &s->chip; +} + +uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) +{ + TSC210xState *s; + + s = (TSC210xState *) + g_malloc0(sizeof(TSC210xState)); + memset(s, 0, sizeof(TSC210xState)); + s->x = 400; + s->y = 240; + s->pressure = 0; + s->precision = s->nextprecision = 0; + s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s); + s->pint = penirq; + s->kbint = kbirq; + s->davint = dav; + s->model = 0x2301; + s->name = "tsc2301"; + + s->tr[0] = 0; + s->tr[1] = 1; + s->tr[2] = 1; + s->tr[3] = 0; + s->tr[4] = 1; + s->tr[5] = 0; + s->tr[6] = 1; + s->tr[7] = 0; + + s->chip.opaque = s; + s->chip.send = (void *) tsc210x_write; + s->chip.receive = (void *) tsc210x_read; + + s->codec.opaque = s; + s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; + s->codec.set_rate = (void *) tsc210x_i2s_set_rate; + s->codec.in.fifo = s->in_fifo; + s->codec.out.fifo = s->out_fifo; + + tsc210x_reset(s); + + qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, + "QEMU TSC2301-driven Touchscreen"); + + AUD_register_card(s->name, &s->card); + + qemu_register_reset((void *) tsc210x_reset, s); + register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s); + + return &s->chip; +} + +I2SCodec *tsc210x_codec(uWireSlave *chip) +{ + TSC210xState *s = (TSC210xState *) chip->opaque; + + return &s->codec; +} + +/* + * Use tslib generated calibration data to generate ADC input values + * from the touchscreen. Assuming 12-bit precision was used during + * tslib calibration. + */ +void tsc210x_set_transform(uWireSlave *chip, + MouseTransformInfo *info) +{ + TSC210xState *s = (TSC210xState *) chip->opaque; +#if 0 + int64_t ltr[8]; + + ltr[0] = (int64_t) info->a[1] * info->y; + ltr[1] = (int64_t) info->a[4] * info->x; + ltr[2] = (int64_t) info->a[1] * info->a[3] - + (int64_t) info->a[4] * info->a[0]; + ltr[3] = (int64_t) info->a[2] * info->a[4] - + (int64_t) info->a[5] * info->a[1]; + ltr[4] = (int64_t) info->a[0] * info->y; + ltr[5] = (int64_t) info->a[3] * info->x; + ltr[6] = (int64_t) info->a[4] * info->a[0] - + (int64_t) info->a[1] * info->a[3]; + ltr[7] = (int64_t) info->a[2] * info->a[3] - + (int64_t) info->a[5] * info->a[0]; + + /* Avoid integer overflow */ + s->tr[0] = ltr[0] >> 11; + s->tr[1] = ltr[1] >> 11; + s->tr[2] = muldiv64(ltr[2], 1, info->a[6]); + s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]); + s->tr[4] = ltr[4] >> 11; + s->tr[5] = ltr[5] >> 11; + s->tr[6] = muldiv64(ltr[6], 1, info->a[6]); + s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]); +#else + + /* This version assumes touchscreen X & Y axis are parallel or + * perpendicular to LCD's X & Y axis in some way. */ + if (abs(info->a[0]) > abs(info->a[1])) { + s->tr[0] = 0; + s->tr[1] = -info->a[6] * info->x; + s->tr[2] = info->a[0]; + s->tr[3] = -info->a[2] / info->a[0]; + s->tr[4] = info->a[6] * info->y; + s->tr[5] = 0; + s->tr[6] = info->a[4]; + s->tr[7] = -info->a[5] / info->a[4]; + } else { + s->tr[0] = info->a[6] * info->y; + s->tr[1] = 0; + s->tr[2] = info->a[1]; + s->tr[3] = -info->a[2] / info->a[1]; + s->tr[4] = 0; + s->tr[5] = -info->a[6] * info->x; + s->tr[6] = info->a[3]; + s->tr[7] = -info->a[5] / info->a[3]; + } + + s->tr[0] >>= 11; + s->tr[1] >>= 11; + s->tr[3] <<= 4; + s->tr[4] >>= 11; + s->tr[5] >>= 11; + s->tr[7] <<= 4; +#endif +} + +void tsc210x_key_event(uWireSlave *chip, int key, int down) +{ + TSC210xState *s = (TSC210xState *) chip->opaque; + + if (down) + s->kb.down |= 1 << key; + else + s->kb.down &= ~(1 << key); + + if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) { + s->kb.intr = 1; + qemu_irq_lower(s->kbint); + } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) && + !(s->kb.mode & 1)) { + s->kb.intr = 0; + qemu_irq_raise(s->kbint); + } +} diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index e1161566ad..f911ac61d1 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -1,12 +1,9 @@ # LM32 peripherals obj-y += lm32_pic.o -obj-y += lm32_timer.o obj-y += lm32_sys.o obj-y += milkymist-hpdmc.o obj-y += milkymist-memcard.o obj-y += milkymist-pfpu.o -obj-y += milkymist-softusb.o -obj-y += milkymist-sysctl.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/lm32_timer.c b/hw/lm32_timer.c deleted file mode 100644 index e06fac7082..0000000000 --- a/hw/lm32_timer.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * QEMU model of the LatticeMico32 timer block. - * - * Copyright (c) 2010 Michael Walle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * - * Specification available at: - * http://www.latticesemi.com/documents/mico32timer.pdf - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/error-report.h" - -#define DEFAULT_FREQUENCY (50*1000000) - -enum { - R_SR = 0, - R_CR, - R_PERIOD, - R_SNAPSHOT, - R_MAX -}; - -enum { - SR_TO = (1 << 0), - SR_RUN = (1 << 1), -}; - -enum { - CR_ITO = (1 << 0), - CR_CONT = (1 << 1), - CR_START = (1 << 2), - CR_STOP = (1 << 3), -}; - -struct LM32TimerState { - SysBusDevice busdev; - MemoryRegion iomem; - - QEMUBH *bh; - ptimer_state *ptimer; - - qemu_irq irq; - uint32_t freq_hz; - - uint32_t regs[R_MAX]; -}; -typedef struct LM32TimerState LM32TimerState; - -static void timer_update_irq(LM32TimerState *s) -{ - int state = (s->regs[R_SR] & SR_TO) && (s->regs[R_CR] & CR_ITO); - - trace_lm32_timer_irq_state(state); - qemu_set_irq(s->irq, state); -} - -static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) -{ - LM32TimerState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_SR: - case R_CR: - case R_PERIOD: - r = s->regs[addr]; - break; - case R_SNAPSHOT: - r = (uint32_t)ptimer_get_count(s->ptimer); - break; - default: - error_report("lm32_timer: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_lm32_timer_memory_read(addr << 2, r); - return r; -} - -static void timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - LM32TimerState *s = opaque; - - trace_lm32_timer_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_SR: - s->regs[R_SR] &= ~SR_TO; - break; - case R_CR: - s->regs[R_CR] = value; - if (s->regs[R_CR] & CR_START) { - ptimer_run(s->ptimer, 1); - } - if (s->regs[R_CR] & CR_STOP) { - ptimer_stop(s->ptimer); - } - break; - case R_PERIOD: - s->regs[R_PERIOD] = value; - ptimer_set_count(s->ptimer, value); - break; - case R_SNAPSHOT: - error_report("lm32_timer: write access to read only register 0x" - TARGET_FMT_plx, addr << 2); - break; - default: - error_report("lm32_timer: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - timer_update_irq(s); -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void timer_hit(void *opaque) -{ - LM32TimerState *s = opaque; - - trace_lm32_timer_hit(); - - s->regs[R_SR] |= SR_TO; - - if (s->regs[R_CR] & CR_CONT) { - ptimer_set_count(s->ptimer, s->regs[R_PERIOD]); - ptimer_run(s->ptimer, 1); - } - timer_update_irq(s); -} - -static void timer_reset(DeviceState *d) -{ - LM32TimerState *s = container_of(d, LM32TimerState, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - ptimer_stop(s->ptimer); -} - -static int lm32_timer_init(SysBusDevice *dev) -{ - LM32TimerState *s = FROM_SYSBUS(typeof(*s), dev); - - sysbus_init_irq(dev, &s->irq); - - s->bh = qemu_bh_new(timer_hit, s); - s->ptimer = ptimer_init(s->bh); - ptimer_set_freq(s->ptimer, s->freq_hz); - - memory_region_init_io(&s->iomem, &timer_ops, s, "timer", R_MAX * 4); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static const VMStateDescription vmstate_lm32_timer = { - .name = "lm32-timer", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_PTIMER(ptimer, LM32TimerState), - VMSTATE_UINT32(freq_hz, LM32TimerState), - VMSTATE_UINT32_ARRAY(regs, LM32TimerState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static Property lm32_timer_properties[] = { - DEFINE_PROP_UINT32("frequency", LM32TimerState, freq_hz, DEFAULT_FREQUENCY), - DEFINE_PROP_END_OF_LIST(), -}; - -static void lm32_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_timer_init; - dc->reset = timer_reset; - dc->vmsd = &vmstate_lm32_timer; - dc->props = lm32_timer_properties; -} - -static const TypeInfo lm32_timer_info = { - .name = "lm32-timer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32TimerState), - .class_init = lm32_timer_class_init, -}; - -static void lm32_timer_register_types(void) -{ - type_register_static(&lm32_timer_info); -} - -type_init(lm32_timer_register_types) diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c deleted file mode 100644 index 3edab4ff0b..0000000000 --- a/hw/milkymist-softusb.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * QEMU model of the Milkymist SoftUSB block. - * - * Copyright (c) 2010 Michael Walle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * - * Specification available at: - * not available yet - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "ui/console.h" -#include "hw/input/hid.h" -#include "qemu/error-report.h" - -enum { - R_CTRL = 0, - R_MAX -}; - -enum { - CTRL_RESET = (1<<0), -}; - -#define COMLOC_DEBUG_PRODUCE 0x1000 -#define COMLOC_DEBUG_BASE 0x1001 -#define COMLOC_MEVT_PRODUCE 0x1101 -#define COMLOC_MEVT_BASE 0x1102 -#define COMLOC_KEVT_PRODUCE 0x1142 -#define COMLOC_KEVT_BASE 0x1143 - -struct MilkymistSoftUsbState { - SysBusDevice busdev; - HIDState hid_kbd; - HIDState hid_mouse; - - MemoryRegion regs_region; - MemoryRegion pmem; - MemoryRegion dmem; - qemu_irq irq; - - void *pmem_ptr; - void *dmem_ptr; - - /* device properties */ - uint32_t pmem_size; - uint32_t dmem_size; - - /* device registers */ - uint32_t regs[R_MAX]; - - /* mouse state */ - uint8_t mouse_hid_buffer[4]; - - /* keyboard state */ - uint8_t kbd_hid_buffer[8]; -}; -typedef struct MilkymistSoftUsbState MilkymistSoftUsbState; - -static uint64_t softusb_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistSoftUsbState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTRL: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_softusb: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_softusb_memory_read(addr << 2, r); - - return r; -} - -static void -softusb_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistSoftUsbState *s = opaque; - - trace_milkymist_softusb_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTRL: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_softusb: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps softusb_mmio_ops = { - .read = softusb_read, - .write = softusb_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static inline void softusb_read_dmem(MilkymistSoftUsbState *s, - uint32_t offset, uint8_t *buf, uint32_t len) -{ - if (offset + len >= s->dmem_size) { - error_report("milkymist_softusb: read dmem out of bounds " - "at offset 0x%x, len %d", offset, len); - memset(buf, 0, len); - return; - } - - memcpy(buf, s->dmem_ptr + offset, len); -} - -static inline void softusb_write_dmem(MilkymistSoftUsbState *s, - uint32_t offset, uint8_t *buf, uint32_t len) -{ - if (offset + len >= s->dmem_size) { - error_report("milkymist_softusb: write dmem out of bounds " - "at offset 0x%x, len %d", offset, len); - return; - } - - memcpy(s->dmem_ptr + offset, buf, len); -} - -static inline void softusb_read_pmem(MilkymistSoftUsbState *s, - uint32_t offset, uint8_t *buf, uint32_t len) -{ - if (offset + len >= s->pmem_size) { - error_report("milkymist_softusb: read pmem out of bounds " - "at offset 0x%x, len %d", offset, len); - memset(buf, 0, len); - return; - } - - memcpy(buf, s->pmem_ptr + offset, len); -} - -static inline void softusb_write_pmem(MilkymistSoftUsbState *s, - uint32_t offset, uint8_t *buf, uint32_t len) -{ - if (offset + len >= s->pmem_size) { - error_report("milkymist_softusb: write pmem out of bounds " - "at offset 0x%x, len %d", offset, len); - return; - } - - memcpy(s->pmem_ptr + offset, buf, len); -} - -static void softusb_mouse_changed(MilkymistSoftUsbState *s) -{ - uint8_t m; - - softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); - trace_milkymist_softusb_mevt(m); - softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4); - m = (m + 1) & 0xf; - softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); - - trace_milkymist_softusb_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static void softusb_kbd_changed(MilkymistSoftUsbState *s) -{ - uint8_t m; - - softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); - trace_milkymist_softusb_kevt(m); - softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8); - m = (m + 1) & 0x7; - softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); - - trace_milkymist_softusb_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static void softusb_kbd_hid_datain(HIDState *hs) -{ - MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd); - int len; - - /* if device is in reset, do nothing */ - if (s->regs[R_CTRL] & CTRL_RESET) { - return; - } - - len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer)); - - if (len == 8) { - softusb_kbd_changed(s); - } -} - -static void softusb_mouse_hid_datain(HIDState *hs) -{ - MilkymistSoftUsbState *s = - container_of(hs, MilkymistSoftUsbState, hid_mouse); - int len; - - /* if device is in reset, do nothing */ - if (s->regs[R_CTRL] & CTRL_RESET) { - return; - } - - len = hid_pointer_poll(hs, s->mouse_hid_buffer, - sizeof(s->mouse_hid_buffer)); - - if (len == 4) { - softusb_mouse_changed(s); - } -} - -static void milkymist_softusb_reset(DeviceState *d) -{ - MilkymistSoftUsbState *s = - container_of(d, MilkymistSoftUsbState, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer)); - memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer)); - - hid_reset(&s->hid_kbd); - hid_reset(&s->hid_mouse); - - /* defaults */ - s->regs[R_CTRL] = CTRL_RESET; -} - -static int milkymist_softusb_init(SysBusDevice *dev) -{ - MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, &softusb_mmio_ops, s, - "milkymist-softusb", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - /* register pmem and dmem */ - memory_region_init_ram(&s->pmem, "milkymist-softusb.pmem", - s->pmem_size); - vmstate_register_ram_global(&s->pmem); - s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem); - sysbus_init_mmio(dev, &s->pmem); - memory_region_init_ram(&s->dmem, "milkymist-softusb.dmem", - s->dmem_size); - vmstate_register_ram_global(&s->dmem); - s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem); - sysbus_init_mmio(dev, &s->dmem); - - hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain); - hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_softusb = { - .name = "milkymist-softusb", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX), - VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState), - VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState), - VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState), - VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_softusb_properties[] = { - DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000), - DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_softusb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_softusb_init; - dc->reset = milkymist_softusb_reset; - dc->vmsd = &vmstate_milkymist_softusb; - dc->props = milkymist_softusb_properties; -} - -static const TypeInfo milkymist_softusb_info = { - .name = "milkymist-softusb", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistSoftUsbState), - .class_init = milkymist_softusb_class_init, -}; - -static void milkymist_softusb_register_types(void) -{ - type_register_static(&milkymist_softusb_info); -} - -type_init(milkymist_softusb_register_types) diff --git a/hw/milkymist-sysctl.c b/hw/milkymist-sysctl.c deleted file mode 100644 index e083a280c4..0000000000 --- a/hw/milkymist-sysctl.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * QEMU model of the Milkymist System Controller. - * - * Copyright (c) 2010-2012 Michael Walle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/sysctl.pdf - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "trace.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/error-report.h" - -enum { - CTRL_ENABLE = (1<<0), - CTRL_AUTORESTART = (1<<1), -}; - -enum { - ICAP_READY = (1<<0), -}; - -enum { - R_GPIO_IN = 0, - R_GPIO_OUT, - R_GPIO_INTEN, - R_TIMER0_CONTROL = 4, - R_TIMER0_COMPARE, - R_TIMER0_COUNTER, - R_TIMER1_CONTROL = 8, - R_TIMER1_COMPARE, - R_TIMER1_COUNTER, - R_ICAP = 16, - R_DBG_SCRATCHPAD = 20, - R_DBG_WRITE_LOCK, - R_CLK_FREQUENCY = 29, - R_CAPABILITIES, - R_SYSTEM_ID, - R_MAX -}; - -struct MilkymistSysctlState { - SysBusDevice busdev; - MemoryRegion regs_region; - - QEMUBH *bh0; - QEMUBH *bh1; - ptimer_state *ptimer0; - ptimer_state *ptimer1; - - uint32_t freq_hz; - uint32_t capabilities; - uint32_t systemid; - uint32_t strappings; - - uint32_t regs[R_MAX]; - - qemu_irq gpio_irq; - qemu_irq timer0_irq; - qemu_irq timer1_irq; -}; -typedef struct MilkymistSysctlState MilkymistSysctlState; - -static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value) -{ - trace_milkymist_sysctl_icap_write(value); - switch (value & 0xffff) { - case 0x000e: - qemu_system_shutdown_request(); - break; - } -} - -static uint64_t sysctl_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistSysctlState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_TIMER0_COUNTER: - r = (uint32_t)ptimer_get_count(s->ptimer0); - /* milkymist timer counts up */ - r = s->regs[R_TIMER0_COMPARE] - r; - break; - case R_TIMER1_COUNTER: - r = (uint32_t)ptimer_get_count(s->ptimer1); - /* milkymist timer counts up */ - r = s->regs[R_TIMER1_COMPARE] - r; - break; - case R_GPIO_IN: - case R_GPIO_OUT: - case R_GPIO_INTEN: - case R_TIMER0_CONTROL: - case R_TIMER0_COMPARE: - case R_TIMER1_CONTROL: - case R_TIMER1_COMPARE: - case R_ICAP: - case R_DBG_SCRATCHPAD: - case R_DBG_WRITE_LOCK: - case R_CLK_FREQUENCY: - case R_CAPABILITIES: - case R_SYSTEM_ID: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_sysctl: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_sysctl_memory_read(addr << 2, r); - - return r; -} - -static void sysctl_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistSysctlState *s = opaque; - - trace_milkymist_sysctl_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_GPIO_OUT: - case R_GPIO_INTEN: - case R_TIMER0_COUNTER: - case R_TIMER1_COUNTER: - case R_DBG_SCRATCHPAD: - s->regs[addr] = value; - break; - case R_TIMER0_COMPARE: - ptimer_set_limit(s->ptimer0, value, 0); - s->regs[addr] = value; - break; - case R_TIMER1_COMPARE: - ptimer_set_limit(s->ptimer1, value, 0); - s->regs[addr] = value; - break; - case R_TIMER0_CONTROL: - s->regs[addr] = value; - if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) { - trace_milkymist_sysctl_start_timer0(); - ptimer_set_count(s->ptimer0, - s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]); - ptimer_run(s->ptimer0, 0); - } else { - trace_milkymist_sysctl_stop_timer0(); - ptimer_stop(s->ptimer0); - } - break; - case R_TIMER1_CONTROL: - s->regs[addr] = value; - if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) { - trace_milkymist_sysctl_start_timer1(); - ptimer_set_count(s->ptimer1, - s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]); - ptimer_run(s->ptimer1, 0); - } else { - trace_milkymist_sysctl_stop_timer1(); - ptimer_stop(s->ptimer1); - } - break; - case R_ICAP: - sysctl_icap_write(s, value); - break; - case R_DBG_WRITE_LOCK: - s->regs[addr] = 1; - break; - case R_SYSTEM_ID: - qemu_system_reset_request(); - break; - - case R_GPIO_IN: - case R_CLK_FREQUENCY: - case R_CAPABILITIES: - error_report("milkymist_sysctl: write to read-only register 0x" - TARGET_FMT_plx, addr << 2); - break; - - default: - error_report("milkymist_sysctl: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps sysctl_mmio_ops = { - .read = sysctl_read, - .write = sysctl_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void timer0_hit(void *opaque) -{ - MilkymistSysctlState *s = opaque; - - if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) { - s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE; - trace_milkymist_sysctl_stop_timer0(); - ptimer_stop(s->ptimer0); - } - - trace_milkymist_sysctl_pulse_irq_timer0(); - qemu_irq_pulse(s->timer0_irq); -} - -static void timer1_hit(void *opaque) -{ - MilkymistSysctlState *s = opaque; - - if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) { - s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE; - trace_milkymist_sysctl_stop_timer1(); - ptimer_stop(s->ptimer1); - } - - trace_milkymist_sysctl_pulse_irq_timer1(); - qemu_irq_pulse(s->timer1_irq); -} - -static void milkymist_sysctl_reset(DeviceState *d) -{ - MilkymistSysctlState *s = - container_of(d, MilkymistSysctlState, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - ptimer_stop(s->ptimer0); - ptimer_stop(s->ptimer1); - - /* defaults */ - s->regs[R_ICAP] = ICAP_READY; - s->regs[R_SYSTEM_ID] = s->systemid; - s->regs[R_CLK_FREQUENCY] = s->freq_hz; - s->regs[R_CAPABILITIES] = s->capabilities; - s->regs[R_GPIO_IN] = s->strappings; -} - -static int milkymist_sysctl_init(SysBusDevice *dev) -{ - MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev); - - sysbus_init_irq(dev, &s->gpio_irq); - sysbus_init_irq(dev, &s->timer0_irq); - sysbus_init_irq(dev, &s->timer1_irq); - - s->bh0 = qemu_bh_new(timer0_hit, s); - s->bh1 = qemu_bh_new(timer1_hit, s); - s->ptimer0 = ptimer_init(s->bh0); - s->ptimer1 = ptimer_init(s->bh1); - ptimer_set_freq(s->ptimer0, s->freq_hz); - ptimer_set_freq(s->ptimer1, s->freq_hz); - - memory_region_init_io(&s->regs_region, &sysctl_mmio_ops, s, - "milkymist-sysctl", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_sysctl = { - .name = "milkymist-sysctl", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX), - VMSTATE_PTIMER(ptimer0, MilkymistSysctlState), - VMSTATE_PTIMER(ptimer1, MilkymistSysctlState), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_sysctl_properties[] = { - DEFINE_PROP_UINT32("frequency", MilkymistSysctlState, - freq_hz, 80000000), - DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState, - capabilities, 0x00000000), - DEFINE_PROP_UINT32("systemid", MilkymistSysctlState, - systemid, 0x10014d31), - DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState, - strappings, 0x00000001), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_sysctl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_sysctl_init; - dc->reset = milkymist_sysctl_reset; - dc->vmsd = &vmstate_milkymist_sysctl; - dc->props = milkymist_sysctl_properties; -} - -static const TypeInfo milkymist_sysctl_info = { - .name = "milkymist-sysctl", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistSysctlState), - .class_init = milkymist_sysctl_class_init, -}; - -static void milkymist_sysctl_register_types(void) -{ - type_register_static(&milkymist_sysctl_info); -} - -type_init(milkymist_sysctl_register_types) diff --git a/hw/omap_gptimer.c b/hw/omap_gptimer.c deleted file mode 100644 index 9b0e9dd567..0000000000 --- a/hw/omap_gptimer.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * TI OMAP2 general purpose timers emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" - -/* GP timers */ -struct omap_gp_timer_s { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq wkup; - qemu_irq in; - qemu_irq out; - omap_clk clk; - QEMUTimer *timer; - QEMUTimer *match; - struct omap_target_agent_s *ta; - - int in_val; - int out_val; - int64_t time; - int64_t rate; - int64_t ticks_per_sec; - - int16_t config; - int status; - int it_ena; - int wu_ena; - int enable; - int inout; - int capt2; - int pt; - enum { - gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both - } trigger; - enum { - gpt_capture_none, gpt_capture_rising, - gpt_capture_falling, gpt_capture_both - } capture; - int scpwm; - int ce; - int pre; - int ptv; - int ar; - int st; - int posted; - uint32_t val; - uint32_t load_val; - uint32_t capture_val[2]; - uint32_t match_val; - int capt_num; - - uint16_t writeh; /* LSB */ - uint16_t readh; /* MSB */ -}; - -#define GPT_TCAR_IT (1 << 2) -#define GPT_OVF_IT (1 << 1) -#define GPT_MAT_IT (1 << 0) - -static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) -{ - if (timer->it_ena & it) { - if (!timer->status) - qemu_irq_raise(timer->irq); - - timer->status |= it; - /* Or are the status bits set even when masked? - * i.e. is masking applied before or after the status register? */ - } - - if (timer->wu_ena & it) - qemu_irq_pulse(timer->wkup); -} - -static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) -{ - if (!timer->inout && timer->out_val != level) { - timer->out_val = level; - qemu_set_irq(timer->out, level); - } -} - -static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) -{ - uint64_t distance; - - if (timer->st && timer->rate) { - distance = qemu_get_clock_ns(vm_clock) - timer->time; - distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); - - if (distance >= 0xffffffff - timer->val) - return 0xffffffff; - else - return timer->val + distance; - } else - return timer->val; -} - -static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) -{ - if (timer->st) { - timer->val = omap_gp_timer_read(timer); - timer->time = qemu_get_clock_ns(vm_clock); - } -} - -static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) -{ - int64_t expires, matches; - - if (timer->st && timer->rate) { - expires = muldiv64(0x100000000ll - timer->val, - timer->ticks_per_sec, timer->rate); - qemu_mod_timer(timer->timer, timer->time + expires); - - if (timer->ce && timer->match_val >= timer->val) { - matches = muldiv64(timer->match_val - timer->val, - timer->ticks_per_sec, timer->rate); - qemu_mod_timer(timer->match, timer->time + matches); - } else - qemu_del_timer(timer->match); - } else { - qemu_del_timer(timer->timer); - qemu_del_timer(timer->match); - omap_gp_timer_out(timer, timer->scpwm); - } -} - -static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) -{ - if (timer->pt) - /* TODO in overflow-and-match mode if the first event to - * occur is the match, don't toggle. */ - omap_gp_timer_out(timer, !timer->out_val); - else - /* TODO inverted pulse on timer->out_val == 1? */ - qemu_irq_pulse(timer->out); -} - -static void omap_gp_timer_tick(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (!timer->ar) { - timer->st = 0; - timer->val = 0; - } else { - timer->val = timer->load_val; - timer->time = qemu_get_clock_ns(vm_clock); - } - - if (timer->trigger == gpt_trigger_overflow || - timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_OVF_IT); - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_match(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_MAT_IT); -} - -static void omap_gp_timer_input(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - int trigger; - - switch (s->capture) { - default: - case gpt_capture_none: - trigger = 0; - break; - case gpt_capture_rising: - trigger = !s->in_val && on; - break; - case gpt_capture_falling: - trigger = s->in_val && !on; - break; - case gpt_capture_both: - trigger = (s->in_val == !on); - break; - } - s->in_val = on; - - if (s->inout && trigger && s->capt_num < 2) { - s->capture_val[s->capt_num] = omap_gp_timer_read(s); - - if (s->capt2 == s->capt_num ++) - omap_gp_timer_intr(s, GPT_TCAR_IT); - } -} - -static void omap_gp_timer_clk_update(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - omap_gp_timer_sync(timer); - timer->rate = on ? omap_clk_getrate(timer->clk) : 0; - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) -{ - omap_clk_adduser(timer->clk, - qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); - timer->rate = omap_clk_getrate(timer->clk); -} - -void omap_gp_timer_reset(struct omap_gp_timer_s *s) -{ - s->config = 0x000; - s->status = 0; - s->it_ena = 0; - s->wu_ena = 0; - s->inout = 0; - s->capt2 = 0; - s->capt_num = 0; - s->pt = 0; - s->trigger = gpt_trigger_none; - s->capture = gpt_capture_none; - s->scpwm = 0; - s->ce = 0; - s->pre = 0; - s->ptv = 0; - s->ar = 0; - s->st = 0; - s->posted = 1; - s->val = 0x00000000; - s->load_val = 0x00000000; - s->capture_val[0] = 0x00000000; - s->capture_val[1] = 0x00000000; - s->match_val = 0x00000000; - omap_gp_timer_update(s); -} - -static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - return 0x21; - - case 0x10: /* TIOCP_CFG */ - return s->config; - - case 0x14: /* TISTAT */ - /* ??? When's this bit reset? */ - return 1; /* RESETDONE */ - - case 0x18: /* TISR */ - return s->status; - - case 0x1c: /* TIER */ - return s->it_ena; - - case 0x20: /* TWER */ - return s->wu_ena; - - case 0x24: /* TCLR */ - return (s->inout << 14) | - (s->capt2 << 13) | - (s->pt << 12) | - (s->trigger << 10) | - (s->capture << 8) | - (s->scpwm << 7) | - (s->ce << 6) | - (s->pre << 5) | - (s->ptv << 2) | - (s->ar << 1) | - (s->st << 0); - - case 0x28: /* TCRR */ - return omap_gp_timer_read(s); - - case 0x2c: /* TLDR */ - return s->load_val; - - case 0x30: /* TTGR */ - return 0xffffffff; - - case 0x34: /* TWPS */ - return 0x00000000; /* No posted writes pending. */ - - case 0x38: /* TMAR */ - return s->match_val; - - case 0x3c: /* TCAR1 */ - return s->capture_val[0]; - - case 0x40: /* TSICR */ - return s->posted << 2; - - case 0x44: /* TCAR2 */ - return s->capture_val[1]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_gp_timer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static void omap_gp_timer_write(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - case 0x14: /* TISTAT */ - case 0x34: /* TWPS */ - case 0x3c: /* TCAR1 */ - case 0x44: /* TCAR2 */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* TIOCP_CFG */ - s->config = value & 0x33d; - if (((value >> 3) & 3) == 3) /* IDLEMODE */ - fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", - __FUNCTION__); - if (value & 2) /* SOFTRESET */ - omap_gp_timer_reset(s); - break; - - case 0x18: /* TISR */ - if (value & GPT_TCAR_IT) - s->capt_num = 0; - if (s->status && !(s->status &= ~value)) - qemu_irq_lower(s->irq); - break; - - case 0x1c: /* TIER */ - s->it_ena = value & 7; - break; - - case 0x20: /* TWER */ - s->wu_ena = value & 7; - break; - - case 0x24: /* TCLR */ - omap_gp_timer_sync(s); - s->inout = (value >> 14) & 1; - s->capt2 = (value >> 13) & 1; - s->pt = (value >> 12) & 1; - s->trigger = (value >> 10) & 3; - if (s->capture == gpt_capture_none && - ((value >> 8) & 3) != gpt_capture_none) - s->capt_num = 0; - s->capture = (value >> 8) & 3; - s->scpwm = (value >> 7) & 1; - s->ce = (value >> 6) & 1; - s->pre = (value >> 5) & 1; - s->ptv = (value >> 2) & 7; - s->ar = (value >> 1) & 1; - s->st = (value >> 0) & 1; - if (s->inout && s->trigger != gpt_trigger_none) - fprintf(stderr, "%s: GP timer pin must be an output " - "for this trigger mode\n", __FUNCTION__); - if (!s->inout && s->capture != gpt_capture_none) - fprintf(stderr, "%s: GP timer pin must be an input " - "for this capture mode\n", __FUNCTION__); - if (s->trigger == gpt_trigger_none) - omap_gp_timer_out(s, s->scpwm); - /* TODO: make sure this doesn't overflow 32-bits */ - s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); - omap_gp_timer_update(s); - break; - - case 0x28: /* TCRR */ - s->time = qemu_get_clock_ns(vm_clock); - s->val = value; - omap_gp_timer_update(s); - break; - - case 0x2c: /* TLDR */ - s->load_val = value; - break; - - case 0x30: /* TTGR */ - s->time = qemu_get_clock_ns(vm_clock); - s->val = s->load_val; - omap_gp_timer_update(s); - break; - - case 0x38: /* TMAR */ - omap_gp_timer_sync(s); - s->match_val = value; - omap_gp_timer_update(s); - break; - - case 0x40: /* TSICR */ - s->posted = (value >> 2) & 1; - if (value & 2) /* How much exactly are we supposed to reset? */ - omap_gp_timer_reset(s); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static void omap_gp_timer_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - if (addr & 2) - return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); - else - s->writeh = (uint16_t) value; -} - -static const MemoryRegionOps omap_gp_timer_ops = { - .old_mmio = { - .read = { - omap_badwidth_read32, - omap_gp_timer_readh, - omap_gp_timer_readw, - }, - .write = { - omap_badwidth_write32, - omap_gp_timer_writeh, - omap_gp_timer_write, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) - g_malloc0(sizeof(struct omap_gp_timer_s)); - - s->ta = ta; - s->irq = irq; - s->clk = fclk; - s->timer = qemu_new_timer_ns(vm_clock, omap_gp_timer_tick, s); - s->match = qemu_new_timer_ns(vm_clock, omap_gp_timer_match, s); - s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; - omap_gp_timer_reset(s); - omap_gp_timer_clk_setup(s); - - memory_region_init_io(&s->iomem, &omap_gp_timer_ops, s, "omap.gptimer", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} diff --git a/hw/omap_synctimer.c b/hw/omap_synctimer.c deleted file mode 100644 index a24f35c277..0000000000 --- a/hw/omap_synctimer.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * TI OMAP2 32kHz sync timer emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" -struct omap_synctimer_s { - MemoryRegion iomem; - uint32_t val; - uint16_t readh; -}; - -/* 32-kHz Sync Timer of the OMAP2 */ -static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { - return muldiv64(qemu_get_clock_ns(vm_clock), 0x8000, get_ticks_per_sec()); -} - -void omap_synctimer_reset(struct omap_synctimer_s *s) -{ - s->val = omap_synctimer_read(s); -} - -static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - - switch (addr) { - case 0x00: /* 32KSYNCNT_REV */ - return 0x21; - - case 0x10: /* CR */ - return omap_synctimer_read(s) - s->val; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_synctimer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static void omap_synctimer_write(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_synctimer_ops = { - .old_mmio = { - .read = { - omap_badwidth_read32, - omap_synctimer_readh, - omap_synctimer_readw, - }, - .write = { - omap_badwidth_write32, - omap_synctimer_write, - omap_synctimer_write, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk) -{ - struct omap_synctimer_s *s = g_malloc0(sizeof(*s)); - - omap_synctimer_reset(s); - memory_region_init_io(&s->iomem, &omap_synctimer_ops, s, "omap.synctimer", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} diff --git a/hw/pxa2xx_keypad.c b/hw/pxa2xx_keypad.c deleted file mode 100644 index 1fd5f2076a..0000000000 --- a/hw/pxa2xx_keypad.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Intel PXA27X Keypad Controller emulation. - * - * Copyright (c) 2007 MontaVista Software, Inc - * Written by Armin Kuster - * or - * - * This code is licensed under the GPLv2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "hw/hw.h" -#include "hw/arm/pxa.h" -#include "ui/console.h" - -/* - * Keypad - */ -#define KPC 0x00 /* Keypad Interface Control register */ -#define KPDK 0x08 /* Keypad Interface Direct Key register */ -#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */ -#define KPMK 0x18 /* Keypad Interface Matrix Key register */ -#define KPAS 0x20 /* Keypad Interface Automatic Scan register */ -#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple - Key Presser register 0 */ -#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple - Key Presser register 1 */ -#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple - Key Presser register 2 */ -#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple - Key Presser register 3 */ -#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval - register */ - -/* Keypad defines */ -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ -#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ -#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ -#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ -#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ -#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ -#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ -#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ -#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ -#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ -#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ -#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ - -#define KPDK_DKP (0x1 << 31) -#define KPDK_DK7 (0x1 << 7) -#define KPDK_DK6 (0x1 << 6) -#define KPDK_DK5 (0x1 << 5) -#define KPDK_DK4 (0x1 << 4) -#define KPDK_DK3 (0x1 << 3) -#define KPDK_DK2 (0x1 << 2) -#define KPDK_DK1 (0x1 << 1) -#define KPDK_DK0 (0x1 << 0) - -#define KPREC_OF1 (0x1 << 31) -#define KPREC_UF1 (0x1 << 30) -#define KPREC_OF0 (0x1 << 15) -#define KPREC_UF0 (0x1 << 14) - -#define KPMK_MKP (0x1 << 31) -#define KPAS_SO (0x1 << 31) -#define KPASMKPx_SO (0x1 << 31) - - -#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) - -#define PXAKBD_MAXROW 8 -#define PXAKBD_MAXCOL 8 - -struct PXA2xxKeyPadState { - MemoryRegion iomem; - qemu_irq irq; - struct keymap *map; - int pressed_cnt; - int alt_code; - - uint32_t kpc; - uint32_t kpdk; - uint32_t kprec; - uint32_t kpmk; - uint32_t kpas; - uint32_t kpasmkp[4]; - uint32_t kpkdi; -}; - -static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col) -{ - int i; - for (i = 0; i < 4; i++) - { - *col = i * 2; - for (*row = 0; *row < 8; (*row)++) { - if (kp->kpasmkp[i] & (1 << *row)) - return; - } - *col = i * 2 + 1; - for (*row = 0; *row < 8; (*row)++) { - if (kp->kpasmkp[i] & (1 << (*row + 16))) - return; - } - } -} - -static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode) -{ - int row, col, rel, assert_irq = 0; - uint32_t val; - - if (keycode == 0xe0) { - kp->alt_code = 1; - return; - } - - if(!(kp->kpc & KPC_ME)) /* skip if not enabled */ - return; - - rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */ - keycode &= ~0x80; /* strip qemu key release bit */ - if (kp->alt_code) { - keycode |= 0x80; - kp->alt_code = 0; - } - - row = kp->map[keycode].row; - col = kp->map[keycode].column; - if (row == -1 || col == -1) { - return; - } - - val = KPASMKPx_MKC(row, col); - if (rel) { - if (kp->kpasmkp[col / 2] & val) { - kp->kpasmkp[col / 2] &= ~val; - kp->pressed_cnt--; - assert_irq = 1; - } - } else { - if (!(kp->kpasmkp[col / 2] & val)) { - kp->kpasmkp[col / 2] |= val; - kp->pressed_cnt++; - assert_irq = 1; - } - } - kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf; - if (kp->pressed_cnt == 1) { - kp->kpas &= ~((0xf << 4) | 0xf); - if (rel) { - pxa27x_keypad_find_pressed_key(kp, &row, &col); - } - kp->kpas |= ((row & 0xf) << 4) | (col & 0xf); - } - - if (!(kp->kpc & (KPC_AS | KPC_ASACT))) - assert_irq = 0; - - if (assert_irq && (kp->kpc & KPC_MIE)) { - kp->kpc |= KPC_MI; - qemu_irq_raise(kp->irq); - } -} - -static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - uint32_t tmp; - - switch (offset) { - case KPC: - tmp = s->kpc; - if(tmp & KPC_MI) - s->kpc &= ~(KPC_MI); - if(tmp & KPC_DI) - s->kpc &= ~(KPC_DI); - qemu_irq_lower(s->irq); - return tmp; - break; - case KPDK: - return s->kpdk; - break; - case KPREC: - tmp = s->kprec; - if(tmp & KPREC_OF1) - s->kprec &= ~(KPREC_OF1); - if(tmp & KPREC_UF1) - s->kprec &= ~(KPREC_UF1); - if(tmp & KPREC_OF0) - s->kprec &= ~(KPREC_OF0); - if(tmp & KPREC_UF0) - s->kprec &= ~(KPREC_UF0); - return tmp; - break; - case KPMK: - tmp = s->kpmk; - if(tmp & KPMK_MKP) - s->kpmk &= ~(KPMK_MKP); - return tmp; - break; - case KPAS: - return s->kpas; - break; - case KPASMKP0: - return s->kpasmkp[0]; - break; - case KPASMKP1: - return s->kpasmkp[1]; - break; - case KPASMKP2: - return s->kpasmkp[2]; - break; - case KPASMKP3: - return s->kpasmkp[3]; - break; - case KPKDI: - return s->kpkdi; - break; - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } - - return 0; -} - -static void pxa2xx_keypad_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - - switch (offset) { - case KPC: - s->kpc = value; - if (s->kpc & KPC_AS) { - s->kpc &= ~(KPC_AS); - } - break; - case KPDK: - s->kpdk = value; - break; - case KPREC: - s->kprec = value; - break; - case KPMK: - s->kpmk = value; - break; - case KPAS: - s->kpas = value; - break; - case KPASMKP0: - s->kpasmkp[0] = value; - break; - case KPASMKP1: - s->kpasmkp[1] = value; - break; - case KPASMKP2: - s->kpasmkp[2] = value; - break; - case KPASMKP3: - s->kpasmkp[3] = value; - break; - case KPKDI: - s->kpkdi = value; - break; - - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_keypad_ops = { - .read = pxa2xx_keypad_read, - .write = pxa2xx_keypad_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_keypad = { - .name = "pxa2xx_keypad", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(kpc, PXA2xxKeyPadState), - VMSTATE_UINT32(kpdk, PXA2xxKeyPadState), - VMSTATE_UINT32(kprec, PXA2xxKeyPadState), - VMSTATE_UINT32(kpmk, PXA2xxKeyPadState), - VMSTATE_UINT32(kpas, PXA2xxKeyPadState), - VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4), - VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState), - VMSTATE_END_OF_LIST() - } -}; - -PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq) -{ - PXA2xxKeyPadState *s; - - s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState)); - s->irq = irq; - - memory_region_init_io(&s->iomem, &pxa2xx_keypad_ops, s, - "pxa2xx-keypad", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s); - - return s; -} - -void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map, - int size) -{ - if(!map || size < 0x80) { - fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__); - exit(-1); - } - - kp->map = map; - qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp); -} diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c deleted file mode 100644 index 8ea2416dd7..0000000000 --- a/hw/pxa2xx_timer.c +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Intel XScale PXA255/270 OS Timers. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * - * This code is licensed under the GPL. - */ - -#include "hw/hw.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/arm/pxa.h" -#include "hw/sysbus.h" - -#define OSMR0 0x00 -#define OSMR1 0x04 -#define OSMR2 0x08 -#define OSMR3 0x0c -#define OSMR4 0x80 -#define OSMR5 0x84 -#define OSMR6 0x88 -#define OSMR7 0x8c -#define OSMR8 0x90 -#define OSMR9 0x94 -#define OSMR10 0x98 -#define OSMR11 0x9c -#define OSCR 0x10 /* OS Timer Count */ -#define OSCR4 0x40 -#define OSCR5 0x44 -#define OSCR6 0x48 -#define OSCR7 0x4c -#define OSCR8 0x50 -#define OSCR9 0x54 -#define OSCR10 0x58 -#define OSCR11 0x5c -#define OSSR 0x14 /* Timer status register */ -#define OWER 0x18 -#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ -#define OMCR4 0xc0 /* OS Match Control registers */ -#define OMCR5 0xc4 -#define OMCR6 0xc8 -#define OMCR7 0xcc -#define OMCR8 0xd0 -#define OMCR9 0xd4 -#define OMCR10 0xd8 -#define OMCR11 0xdc -#define OSNR 0x20 - -#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ -#define PXA27X_FREQ 3250000 /* 3.25 MHz */ - -static int pxa2xx_timer4_freq[8] = { - [0] = 0, - [1] = 32768, - [2] = 1000, - [3] = 1, - [4] = 1000000, - /* [5] is the "Externally supplied clock". Assign if necessary. */ - [5 ... 7] = 0, -}; - -typedef struct PXA2xxTimerInfo PXA2xxTimerInfo; - -typedef struct { - uint32_t value; - qemu_irq irq; - QEMUTimer *qtimer; - int num; - PXA2xxTimerInfo *info; -} PXA2xxTimer0; - -typedef struct { - PXA2xxTimer0 tm; - int32_t oldclock; - int32_t clock; - uint64_t lastload; - uint32_t freq; - uint32_t control; -} PXA2xxTimer4; - -struct PXA2xxTimerInfo { - SysBusDevice busdev; - MemoryRegion iomem; - uint32_t flags; - - int32_t clock; - int32_t oldclock; - uint64_t lastload; - uint32_t freq; - PXA2xxTimer0 timer[4]; - uint32_t events; - uint32_t irq_enabled; - uint32_t reset3; - uint32_t snapshot; - - qemu_irq irq4; - PXA2xxTimer4 tm4[8]; -}; - -#define PXA2XX_TIMER_HAVE_TM4 0 - -static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s) -{ - return s->flags & (1 << PXA2XX_TIMER_HAVE_TM4); -} - -static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - int i; - uint32_t now_vm; - uint64_t new_qemu; - - now_vm = s->clock + - muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec()); - - for (i = 0; i < 4; i ++) { - new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm), - get_ticks_per_sec(), s->freq); - qemu_mod_timer(s->timer[i].qtimer, new_qemu); - } -} - -static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - uint32_t now_vm; - uint64_t new_qemu; - static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 }; - int counter; - - if (s->tm4[n].control & (1 << 7)) - counter = n; - else - counter = counters[n]; - - if (!s->tm4[counter].freq) { - qemu_del_timer(s->tm4[n].tm.qtimer); - return; - } - - now_vm = s->tm4[counter].clock + muldiv64(now_qemu - - s->tm4[counter].lastload, - s->tm4[counter].freq, get_ticks_per_sec()); - - new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm), - get_ticks_per_sec(), s->tm4[counter].freq); - qemu_mod_timer(s->tm4[n].tm.qtimer, new_qemu); -} - -static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - int tm = 0; - - switch (offset) { - case OSMR3: tm ++; - /* fall through */ - case OSMR2: tm ++; - /* fall through */ - case OSMR1: tm ++; - /* fall through */ - case OSMR0: - return s->timer[tm].value; - case OSMR11: tm ++; - /* fall through */ - case OSMR10: tm ++; - /* fall through */ - case OSMR9: tm ++; - /* fall through */ - case OSMR8: tm ++; - /* fall through */ - case OSMR7: tm ++; - /* fall through */ - case OSMR6: tm ++; - /* fall through */ - case OSMR5: tm ++; - /* fall through */ - case OSMR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - return s->tm4[tm].tm.value; - case OSCR: - return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) - - s->lastload, s->freq, get_ticks_per_sec()); - case OSCR11: tm ++; - /* fall through */ - case OSCR10: tm ++; - /* fall through */ - case OSCR9: tm ++; - /* fall through */ - case OSCR8: tm ++; - /* fall through */ - case OSCR7: tm ++; - /* fall through */ - case OSCR6: tm ++; - /* fall through */ - case OSCR5: tm ++; - /* fall through */ - case OSCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - - if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) { - if (s->tm4[tm - 1].freq) - s->snapshot = s->tm4[tm - 1].clock + muldiv64( - qemu_get_clock_ns(vm_clock) - - s->tm4[tm - 1].lastload, - s->tm4[tm - 1].freq, get_ticks_per_sec()); - else - s->snapshot = s->tm4[tm - 1].clock; - } - - if (!s->tm4[tm].freq) - return s->tm4[tm].clock; - return s->tm4[tm].clock + muldiv64(qemu_get_clock_ns(vm_clock) - - s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec()); - case OIER: - return s->irq_enabled; - case OSSR: /* Status register */ - return s->events; - case OWER: - return s->reset3; - case OMCR11: tm ++; - /* fall through */ - case OMCR10: tm ++; - /* fall through */ - case OMCR9: tm ++; - /* fall through */ - case OMCR8: tm ++; - /* fall through */ - case OMCR7: tm ++; - /* fall through */ - case OMCR6: tm ++; - /* fall through */ - case OMCR5: tm ++; - /* fall through */ - case OMCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - return s->tm4[tm].control; - case OSNR: - return s->snapshot; - default: - badreg: - hw_error("pxa2xx_timer_read: Bad offset " REG_FMT "\n", offset); - } - - return 0; -} - -static void pxa2xx_timer_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - int i, tm = 0; - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - - switch (offset) { - case OSMR3: tm ++; - /* fall through */ - case OSMR2: tm ++; - /* fall through */ - case OSMR1: tm ++; - /* fall through */ - case OSMR0: - s->timer[tm].value = value; - pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock)); - break; - case OSMR11: tm ++; - /* fall through */ - case OSMR10: tm ++; - /* fall through */ - case OSMR9: tm ++; - /* fall through */ - case OSMR8: tm ++; - /* fall through */ - case OSMR7: tm ++; - /* fall through */ - case OSMR6: tm ++; - /* fall through */ - case OSMR5: tm ++; - /* fall through */ - case OSMR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].tm.value = value; - pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); - break; - case OSCR: - s->oldclock = s->clock; - s->lastload = qemu_get_clock_ns(vm_clock); - s->clock = value; - pxa2xx_timer_update(s, s->lastload); - break; - case OSCR11: tm ++; - /* fall through */ - case OSCR10: tm ++; - /* fall through */ - case OSCR9: tm ++; - /* fall through */ - case OSCR8: tm ++; - /* fall through */ - case OSCR7: tm ++; - /* fall through */ - case OSCR6: tm ++; - /* fall through */ - case OSCR5: tm ++; - /* fall through */ - case OSCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].oldclock = s->tm4[tm].clock; - s->tm4[tm].lastload = qemu_get_clock_ns(vm_clock); - s->tm4[tm].clock = value; - pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm); - break; - case OIER: - s->irq_enabled = value & 0xfff; - break; - case OSSR: /* Status register */ - value &= s->events; - s->events &= ~value; - for (i = 0; i < 4; i ++, value >>= 1) - if (value & 1) - qemu_irq_lower(s->timer[i].irq); - if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value) - qemu_irq_lower(s->irq4); - break; - case OWER: /* XXX: Reset on OSMR3 match? */ - s->reset3 = value; - break; - case OMCR7: tm ++; - /* fall through */ - case OMCR6: tm ++; - /* fall through */ - case OMCR5: tm ++; - /* fall through */ - case OMCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].control = value & 0x0ff; - /* XXX Stop if running (shouldn't happen) */ - if ((value & (1 << 7)) || tm == 0) - s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7]; - else { - s->tm4[tm].freq = 0; - pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); - } - break; - case OMCR11: tm ++; - /* fall through */ - case OMCR10: tm ++; - /* fall through */ - case OMCR9: tm ++; - /* fall through */ - case OMCR8: tm += 4; - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].control = value & 0x3ff; - /* XXX Stop if running (shouldn't happen) */ - if ((value & (1 << 7)) || !(tm & 1)) - s->tm4[tm].freq = - pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)]; - else { - s->tm4[tm].freq = 0; - pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); - } - break; - default: - badreg: - hw_error("pxa2xx_timer_write: Bad offset " REG_FMT "\n", offset); - } -} - -static const MemoryRegionOps pxa2xx_timer_ops = { - .read = pxa2xx_timer_read, - .write = pxa2xx_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_timer_tick(void *opaque) -{ - PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque; - PXA2xxTimerInfo *i = t->info; - - if (i->irq_enabled & (1 << t->num)) { - i->events |= 1 << t->num; - qemu_irq_raise(t->irq); - } - - if (t->num == 3) - if (i->reset3 & 1) { - i->reset3 = 0; - qemu_system_reset_request(); - } -} - -static void pxa2xx_timer_tick4(void *opaque) -{ - PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque; - PXA2xxTimerInfo *i = (PXA2xxTimerInfo *) t->tm.info; - - pxa2xx_timer_tick(&t->tm); - if (t->control & (1 << 3)) - t->clock = 0; - if (t->control & (1 << 6)) - pxa2xx_timer_update4(i, qemu_get_clock_ns(vm_clock), t->tm.num - 4); - if (i->events & 0xff0) - qemu_irq_raise(i->irq4); -} - -static int pxa25x_timer_post_load(void *opaque, int version_id) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - int64_t now; - int i; - - now = qemu_get_clock_ns(vm_clock); - pxa2xx_timer_update(s, now); - - if (pxa2xx_timer_has_tm4(s)) - for (i = 0; i < 8; i ++) - pxa2xx_timer_update4(s, now, i); - - return 0; -} - -static int pxa2xx_timer_init(SysBusDevice *dev) -{ - int i; - PXA2xxTimerInfo *s; - - s = FROM_SYSBUS(PXA2xxTimerInfo, dev); - s->irq_enabled = 0; - s->oldclock = 0; - s->clock = 0; - s->lastload = qemu_get_clock_ns(vm_clock); - s->reset3 = 0; - - for (i = 0; i < 4; i ++) { - s->timer[i].value = 0; - sysbus_init_irq(dev, &s->timer[i].irq); - s->timer[i].info = s; - s->timer[i].num = i; - s->timer[i].qtimer = qemu_new_timer_ns(vm_clock, - pxa2xx_timer_tick, &s->timer[i]); - } - if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) { - sysbus_init_irq(dev, &s->irq4); - - for (i = 0; i < 8; i ++) { - s->tm4[i].tm.value = 0; - s->tm4[i].tm.info = s; - s->tm4[i].tm.num = i + 4; - s->tm4[i].freq = 0; - s->tm4[i].control = 0x0; - s->tm4[i].tm.qtimer = qemu_new_timer_ns(vm_clock, - pxa2xx_timer_tick4, &s->tm4[i]); - } - } - - memory_region_init_io(&s->iomem, &pxa2xx_timer_ops, s, - "pxa2xx-timer", 0x00001000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static const VMStateDescription vmstate_pxa2xx_timer0_regs = { - .name = "pxa2xx_timer0", - .version_id = 2, - .minimum_version_id = 2, - .minimum_version_id_old = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(value, PXA2xxTimer0), - VMSTATE_END_OF_LIST(), - }, -}; - -static const VMStateDescription vmstate_pxa2xx_timer4_regs = { - .name = "pxa2xx_timer4", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(tm, PXA2xxTimer4, 1, - vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), - VMSTATE_INT32(oldclock, PXA2xxTimer4), - VMSTATE_INT32(clock, PXA2xxTimer4), - VMSTATE_UINT64(lastload, PXA2xxTimer4), - VMSTATE_UINT32(freq, PXA2xxTimer4), - VMSTATE_UINT32(control, PXA2xxTimer4), - VMSTATE_END_OF_LIST(), - }, -}; - -static bool pxa2xx_timer_has_tm4_test(void *opaque, int version_id) -{ - return pxa2xx_timer_has_tm4(opaque); -} - -static const VMStateDescription vmstate_pxa2xx_timer_regs = { - .name = "pxa2xx_timer", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = pxa25x_timer_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(clock, PXA2xxTimerInfo), - VMSTATE_INT32(oldclock, PXA2xxTimerInfo), - VMSTATE_UINT64(lastload, PXA2xxTimerInfo), - VMSTATE_STRUCT_ARRAY(timer, PXA2xxTimerInfo, 4, 1, - vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), - VMSTATE_UINT32(events, PXA2xxTimerInfo), - VMSTATE_UINT32(irq_enabled, PXA2xxTimerInfo), - VMSTATE_UINT32(reset3, PXA2xxTimerInfo), - VMSTATE_UINT32(snapshot, PXA2xxTimerInfo), - VMSTATE_STRUCT_ARRAY_TEST(tm4, PXA2xxTimerInfo, 8, - pxa2xx_timer_has_tm4_test, 0, - vmstate_pxa2xx_timer4_regs, PXA2xxTimer4), - VMSTATE_END_OF_LIST(), - } -}; - -static Property pxa25x_timer_dev_properties[] = { - DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), - DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, - PXA2XX_TIMER_HAVE_TM4, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_timer_init; - dc->desc = "PXA25x timer"; - dc->vmsd = &vmstate_pxa2xx_timer_regs; - dc->props = pxa25x_timer_dev_properties; -} - -static const TypeInfo pxa25x_timer_dev_info = { - .name = "pxa25x-timer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxTimerInfo), - .class_init = pxa25x_timer_dev_class_init, -}; - -static Property pxa27x_timer_dev_properties[] = { - DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ), - DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, - PXA2XX_TIMER_HAVE_TM4, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_timer_init; - dc->desc = "PXA27x timer"; - dc->vmsd = &vmstate_pxa2xx_timer_regs; - dc->props = pxa27x_timer_dev_properties; -} - -static const TypeInfo pxa27x_timer_dev_info = { - .name = "pxa27x-timer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxTimerInfo), - .class_init = pxa27x_timer_dev_class_init, -}; - -static void pxa2xx_timer_register_types(void) -{ - type_register_static(&pxa25x_timer_dev_info); - type_register_static(&pxa27x_timer_dev_info); -} - -type_init(pxa2xx_timer_register_types) diff --git a/hw/sh4/Makefile.objs b/hw/sh4/Makefile.objs index 10c971a300..76b37bbf6b 100644 --- a/hw/sh4/Makefile.objs +++ b/hw/sh4/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += sh_timer.o sh_intc.o sh_pci.o +obj-y += sh_intc.o sh_pci.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/sh_timer.c b/hw/sh_timer.c deleted file mode 100644 index f92ff4f73f..0000000000 --- a/hw/sh_timer.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * SuperH Timer modules. - * - * Copyright (c) 2007 Magnus Damm - * Based on arm_timer.c by Paul Brook - * Copyright (c) 2005-2006 CodeSourcery. - * - * This code is licensed under the GPL. - */ - -#include "hw/hw.h" -#include "hw/sh4/sh.h" -#include "qemu/timer.h" -#include "exec/address-spaces.h" -#include "hw/ptimer.h" - -//#define DEBUG_TIMER - -#define TIMER_TCR_TPSC (7 << 0) -#define TIMER_TCR_CKEG (3 << 3) -#define TIMER_TCR_UNIE (1 << 5) -#define TIMER_TCR_ICPE (3 << 6) -#define TIMER_TCR_UNF (1 << 8) -#define TIMER_TCR_ICPF (1 << 9) -#define TIMER_TCR_RESERVED (0x3f << 10) - -#define TIMER_FEAT_CAPT (1 << 0) -#define TIMER_FEAT_EXTCLK (1 << 1) - -#define OFFSET_TCOR 0 -#define OFFSET_TCNT 1 -#define OFFSET_TCR 2 -#define OFFSET_TCPR 3 - -typedef struct { - ptimer_state *timer; - uint32_t tcnt; - uint32_t tcor; - uint32_t tcr; - uint32_t tcpr; - int freq; - int int_level; - int old_level; - int feat; - int enabled; - qemu_irq irq; -} sh_timer_state; - -/* Check all active timers, and schedule the next timer interrupt. */ - -static void sh_timer_update(sh_timer_state *s) -{ - int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); - - if (new_level != s->old_level) - qemu_set_irq (s->irq, new_level); - - s->old_level = s->int_level; - s->int_level = new_level; -} - -static uint32_t sh_timer_read(void *opaque, hwaddr offset) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - - switch (offset >> 2) { - case OFFSET_TCOR: - return s->tcor; - case OFFSET_TCNT: - return ptimer_get_count(s->timer); - case OFFSET_TCR: - return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); - case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) - return s->tcpr; - default: - hw_error("sh_timer_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void sh_timer_write(void *opaque, hwaddr offset, - uint32_t value) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - int freq; - - switch (offset >> 2) { - case OFFSET_TCOR: - s->tcor = value; - ptimer_set_limit(s->timer, s->tcor, 0); - break; - case OFFSET_TCNT: - s->tcnt = value; - ptimer_set_count(s->timer, s->tcnt); - break; - case OFFSET_TCR: - if (s->enabled) { - /* Pause the timer if it is running. This may cause some - inaccuracy dure to rounding, but avoids a whole lot of other - messyness. */ - ptimer_stop(s->timer); - } - freq = s->freq; - /* ??? Need to recalculate expiry time after changing divisor. */ - switch (value & TIMER_TCR_TPSC) { - case 0: freq >>= 2; break; - case 1: freq >>= 4; break; - case 2: freq >>= 6; break; - case 3: freq >>= 8; break; - case 4: freq >>= 10; break; - case 6: - case 7: if (s->feat & TIMER_FEAT_EXTCLK) break; - default: hw_error("sh_timer_write: Reserved TPSC value\n"); break; - } - switch ((value & TIMER_TCR_CKEG) >> 3) { - case 0: break; - case 1: - case 2: - case 3: if (s->feat & TIMER_FEAT_EXTCLK) break; - default: hw_error("sh_timer_write: Reserved CKEG value\n"); break; - } - switch ((value & TIMER_TCR_ICPE) >> 6) { - case 0: break; - case 2: - case 3: if (s->feat & TIMER_FEAT_CAPT) break; - default: hw_error("sh_timer_write: Reserved ICPE value\n"); break; - } - if ((value & TIMER_TCR_UNF) == 0) - s->int_level = 0; - - value &= ~TIMER_TCR_UNF; - - if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) - hw_error("sh_timer_write: Reserved ICPF value\n"); - - value &= ~TIMER_TCR_ICPF; /* capture not supported */ - - if (value & TIMER_TCR_RESERVED) - hw_error("sh_timer_write: Reserved TCR bits set\n"); - s->tcr = value; - ptimer_set_limit(s->timer, s->tcor, 0); - ptimer_set_freq(s->timer, freq); - if (s->enabled) { - /* Restart the timer if still enabled. */ - ptimer_run(s->timer, 0); - } - break; - case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) { - s->tcpr = value; - break; - } - default: - hw_error("sh_timer_write: Bad offset %x\n", (int)offset); - } - sh_timer_update(s); -} - -static void sh_timer_start_stop(void *opaque, int enable) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled); -#endif - - if (s->enabled && !enable) { - ptimer_stop(s->timer); - } - if (!s->enabled && enable) { - ptimer_run(s->timer, 0); - } - s->enabled = !!enable; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop done %d\n", s->enabled); -#endif -} - -static void sh_timer_tick(void *opaque) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - s->int_level = s->enabled; - sh_timer_update(s); -} - -static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) -{ - sh_timer_state *s; - QEMUBH *bh; - - s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state)); - s->freq = freq; - s->feat = feat; - s->tcor = 0xffffffff; - s->tcnt = 0xffffffff; - s->tcpr = 0xdeadbeef; - s->tcr = 0; - s->enabled = 0; - s->irq = irq; - - bh = qemu_bh_new(sh_timer_tick, s); - s->timer = ptimer_init(bh); - - sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor); - sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt); - sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr); - sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr); - /* ??? Save/restore. */ - return s; -} - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; - void *timer[3]; - int level[3]; - uint32_t tocr; - uint32_t tstr; - int feat; -} tmu012_state; - -static uint64_t tmu012_read(void *opaque, hwaddr offset, - unsigned size) -{ - tmu012_state *s = (tmu012_state *)opaque; - -#ifdef DEBUG_TIMER - printf("tmu012_read 0x%lx\n", (unsigned long) offset); -#endif - - if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); - return sh_timer_read(s->timer[2], offset - 0x20); - } - - if (offset >= 0x14) - return sh_timer_read(s->timer[1], offset - 0x14); - - if (offset >= 0x08) - return sh_timer_read(s->timer[0], offset - 0x08); - - if (offset == 4) - return s->tstr; - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) - return s->tocr; - - hw_error("tmu012_write: Bad offset %x\n", (int)offset); - return 0; -} - -static void tmu012_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - tmu012_state *s = (tmu012_state *)opaque; - -#ifdef DEBUG_TIMER - printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value); -#endif - - if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); - sh_timer_write(s->timer[2], offset - 0x20, value); - return; - } - - if (offset >= 0x14) { - sh_timer_write(s->timer[1], offset - 0x14, value); - return; - } - - if (offset >= 0x08) { - sh_timer_write(s->timer[0], offset - 0x08, value); - return; - } - - if (offset == 4) { - sh_timer_start_stop(s->timer[0], value & (1 << 0)); - sh_timer_start_stop(s->timer[1], value & (1 << 1)); - if (s->feat & TMU012_FEAT_3CHAN) - sh_timer_start_stop(s->timer[2], value & (1 << 2)); - else - if (value & (1 << 2)) - hw_error("tmu012_write: Bad channel\n"); - - s->tstr = value; - return; - } - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { - s->tocr = value & (1 << 0); - } -} - -static const MemoryRegionOps tmu012_ops = { - .read = tmu012_read, - .write = tmu012_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void tmu012_init(MemoryRegion *sysmem, hwaddr base, - int feat, uint32_t freq, - qemu_irq ch0_irq, qemu_irq ch1_irq, - qemu_irq ch2_irq0, qemu_irq ch2_irq1) -{ - tmu012_state *s; - int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; - - s = (tmu012_state *)g_malloc0(sizeof(tmu012_state)); - s->feat = feat; - s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); - s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); - if (feat & TMU012_FEAT_3CHAN) - s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT, - ch2_irq0); /* ch2_irq1 not supported */ - - memory_region_init_io(&s->iomem, &tmu012_ops, s, - "timer", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, "timer-p4", - &s->iomem, 0, 0x1000); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, "timer-a7", - &s->iomem, 0, 0x1000); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - /* ??? Save/restore. */ -} diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c deleted file mode 100644 index 1145a87603..0000000000 --- a/hw/slavio_timer.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * QEMU Sparc SLAVIO timer controller emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/sparc/sun4m.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "hw/sysbus.h" -#include "trace.h" - -/* - * Registers of hardware timer in sun4m. - * - * This is the timer/counter part of chip STP2001 (Slave I/O), also - * produced as NCR89C105. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt - * - * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0 - * are zero. Bit 31 is 1 when count has been reached. - * - * Per-CPU timers interrupt local CPU, system timer uses normal - * interrupt routing. - * - */ - -#define MAX_CPUS 16 - -typedef struct CPUTimerState { - qemu_irq irq; - ptimer_state *timer; - uint32_t count, counthigh, reached; - /* processor only */ - uint32_t running; - uint64_t limit; -} CPUTimerState; - -typedef struct SLAVIO_TIMERState { - SysBusDevice busdev; - uint32_t num_cpus; - uint32_t cputimer_mode; - CPUTimerState cputimer[MAX_CPUS + 1]; -} SLAVIO_TIMERState; - -typedef struct TimerContext { - MemoryRegion iomem; - SLAVIO_TIMERState *s; - unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */ -} TimerContext; - -#define SYS_TIMER_SIZE 0x14 -#define CPU_TIMER_SIZE 0x10 - -#define TIMER_LIMIT 0 -#define TIMER_COUNTER 1 -#define TIMER_COUNTER_NORST 2 -#define TIMER_STATUS 3 -#define TIMER_MODE 4 - -#define TIMER_COUNT_MASK32 0xfffffe00 -#define TIMER_LIMIT_MASK32 0x7fffffff -#define TIMER_MAX_COUNT64 0x7ffffffffffffe00ULL -#define TIMER_MAX_COUNT32 0x7ffffe00ULL -#define TIMER_REACHED 0x80000000 -#define TIMER_PERIOD 500ULL // 500ns -#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1) -#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9) - -static int slavio_timer_is_user(TimerContext *tc) -{ - SLAVIO_TIMERState *s = tc->s; - unsigned int timer_index = tc->timer_index; - - return timer_index != 0 && (s->cputimer_mode & (1 << (timer_index - 1))); -} - -// Update count, set irq, update expire_time -// Convert from ptimer countdown units -static void slavio_timer_get_out(CPUTimerState *t) -{ - uint64_t count, limit; - - if (t->limit == 0) { /* free-run system or processor counter */ - limit = TIMER_MAX_COUNT32; - } else { - limit = t->limit; - } - count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer)); - - trace_slavio_timer_get_out(t->limit, t->counthigh, t->count); - t->count = count & TIMER_COUNT_MASK32; - t->counthigh = count >> 32; -} - -// timer callback -static void slavio_timer_irq(void *opaque) -{ - TimerContext *tc = opaque; - SLAVIO_TIMERState *s = tc->s; - CPUTimerState *t = &s->cputimer[tc->timer_index]; - - slavio_timer_get_out(t); - trace_slavio_timer_irq(t->counthigh, t->count); - /* if limit is 0 (free-run), there will be no match */ - if (t->limit != 0) { - t->reached = TIMER_REACHED; - } - /* there is no interrupt if user timer or free-run */ - if (!slavio_timer_is_user(tc) && t->limit != 0) { - qemu_irq_raise(t->irq); - } -} - -static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr, - unsigned size) -{ - TimerContext *tc = opaque; - SLAVIO_TIMERState *s = tc->s; - uint32_t saddr, ret; - unsigned int timer_index = tc->timer_index; - CPUTimerState *t = &s->cputimer[timer_index]; - - saddr = addr >> 2; - switch (saddr) { - case TIMER_LIMIT: - // read limit (system counter mode) or read most signifying - // part of counter (user mode) - if (slavio_timer_is_user(tc)) { - // read user timer MSW - slavio_timer_get_out(t); - ret = t->counthigh | t->reached; - } else { - // read limit - // clear irq - qemu_irq_lower(t->irq); - t->reached = 0; - ret = t->limit & TIMER_LIMIT_MASK32; - } - break; - case TIMER_COUNTER: - // read counter and reached bit (system mode) or read lsbits - // of counter (user mode) - slavio_timer_get_out(t); - if (slavio_timer_is_user(tc)) { // read user timer LSW - ret = t->count & TIMER_MAX_COUNT64; - } else { // read limit - ret = (t->count & TIMER_MAX_COUNT32) | - t->reached; - } - break; - case TIMER_STATUS: - // only available in processor counter/timer - // read start/stop status - if (timer_index > 0) { - ret = t->running; - } else { - ret = 0; - } - break; - case TIMER_MODE: - // only available in system counter - // read user/system mode - ret = s->cputimer_mode; - break; - default: - trace_slavio_timer_mem_readl_invalid(addr); - ret = 0; - break; - } - trace_slavio_timer_mem_readl(addr, ret); - return ret; -} - -static void slavio_timer_mem_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TimerContext *tc = opaque; - SLAVIO_TIMERState *s = tc->s; - uint32_t saddr; - unsigned int timer_index = tc->timer_index; - CPUTimerState *t = &s->cputimer[timer_index]; - - trace_slavio_timer_mem_writel(addr, val); - saddr = addr >> 2; - switch (saddr) { - case TIMER_LIMIT: - if (slavio_timer_is_user(tc)) { - uint64_t count; - - // set user counter MSW, reset counter - t->limit = TIMER_MAX_COUNT64; - t->counthigh = val & (TIMER_MAX_COUNT64 >> 32); - t->reached = 0; - count = ((uint64_t)t->counthigh << 32) | t->count; - trace_slavio_timer_mem_writel_limit(timer_index, count); - ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); - } else { - // set limit, reset counter - qemu_irq_lower(t->irq); - t->limit = val & TIMER_MAX_COUNT32; - if (t->timer) { - if (t->limit == 0) { /* free-run */ - ptimer_set_limit(t->timer, - LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); - } else { - ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1); - } - } - } - break; - case TIMER_COUNTER: - if (slavio_timer_is_user(tc)) { - uint64_t count; - - // set user counter LSW, reset counter - t->limit = TIMER_MAX_COUNT64; - t->count = val & TIMER_MAX_COUNT64; - t->reached = 0; - count = ((uint64_t)t->counthigh) << 32 | t->count; - trace_slavio_timer_mem_writel_limit(timer_index, count); - ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); - } else { - trace_slavio_timer_mem_writel_counter_invalid(); - } - break; - case TIMER_COUNTER_NORST: - // set limit without resetting counter - t->limit = val & TIMER_MAX_COUNT32; - if (t->limit == 0) { /* free-run */ - ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0); - } else { - ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0); - } - break; - case TIMER_STATUS: - if (slavio_timer_is_user(tc)) { - // start/stop user counter - if ((val & 1) && !t->running) { - trace_slavio_timer_mem_writel_status_start(timer_index); - ptimer_run(t->timer, 0); - t->running = 1; - } else if (!(val & 1) && t->running) { - trace_slavio_timer_mem_writel_status_stop(timer_index); - ptimer_stop(t->timer); - t->running = 0; - } - } - break; - case TIMER_MODE: - if (timer_index == 0) { - unsigned int i; - - for (i = 0; i < s->num_cpus; i++) { - unsigned int processor = 1 << i; - CPUTimerState *curr_timer = &s->cputimer[i + 1]; - - // check for a change in timer mode for this processor - if ((val & processor) != (s->cputimer_mode & processor)) { - if (val & processor) { // counter -> user timer - qemu_irq_lower(curr_timer->irq); - // counters are always running - ptimer_stop(curr_timer->timer); - curr_timer->running = 0; - // user timer limit is always the same - curr_timer->limit = TIMER_MAX_COUNT64; - ptimer_set_limit(curr_timer->timer, - LIMIT_TO_PERIODS(curr_timer->limit), - 1); - // set this processors user timer bit in config - // register - s->cputimer_mode |= processor; - trace_slavio_timer_mem_writel_mode_user(timer_index); - } else { // user timer -> counter - // stop the user timer if it is running - if (curr_timer->running) { - ptimer_stop(curr_timer->timer); - } - // start the counter - ptimer_run(curr_timer->timer, 0); - curr_timer->running = 1; - // clear this processors user timer bit in config - // register - s->cputimer_mode &= ~processor; - trace_slavio_timer_mem_writel_mode_counter(timer_index); - } - } - } - } else { - trace_slavio_timer_mem_writel_mode_invalid(); - } - break; - default: - trace_slavio_timer_mem_writel_invalid(addr); - break; - } -} - -static const MemoryRegionOps slavio_timer_mem_ops = { - .read = slavio_timer_mem_readl, - .write = slavio_timer_mem_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const VMStateDescription vmstate_timer = { - .name ="timer", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 3, - .fields = (VMStateField []) { - VMSTATE_UINT64(limit, CPUTimerState), - VMSTATE_UINT32(count, CPUTimerState), - VMSTATE_UINT32(counthigh, CPUTimerState), - VMSTATE_UINT32(reached, CPUTimerState), - VMSTATE_UINT32(running, CPUTimerState), - VMSTATE_PTIMER(timer, CPUTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slavio_timer = { - .name ="slavio_timer", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 3, - .fields = (VMStateField []) { - VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3, - vmstate_timer, CPUTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static void slavio_timer_reset(DeviceState *d) -{ - SLAVIO_TIMERState *s = container_of(d, SLAVIO_TIMERState, busdev.qdev); - unsigned int i; - CPUTimerState *curr_timer; - - for (i = 0; i <= MAX_CPUS; i++) { - curr_timer = &s->cputimer[i]; - curr_timer->limit = 0; - curr_timer->count = 0; - curr_timer->reached = 0; - if (i <= s->num_cpus) { - ptimer_set_limit(curr_timer->timer, - LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); - ptimer_run(curr_timer->timer, 0); - curr_timer->running = 1; - } - } - s->cputimer_mode = 0; -} - -static int slavio_timer_init1(SysBusDevice *dev) -{ - SLAVIO_TIMERState *s = FROM_SYSBUS(SLAVIO_TIMERState, dev); - QEMUBH *bh; - unsigned int i; - TimerContext *tc; - - for (i = 0; i <= MAX_CPUS; i++) { - uint64_t size; - char timer_name[20]; - - tc = g_malloc0(sizeof(TimerContext)); - tc->s = s; - tc->timer_index = i; - - bh = qemu_bh_new(slavio_timer_irq, tc); - s->cputimer[i].timer = ptimer_init(bh); - ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD); - - size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE; - snprintf(timer_name, sizeof(timer_name), "timer-%i", i); - memory_region_init_io(&tc->iomem, &slavio_timer_mem_ops, tc, - timer_name, size); - sysbus_init_mmio(dev, &tc->iomem); - - sysbus_init_irq(dev, &s->cputimer[i].irq); - } - - return 0; -} - -static Property slavio_timer_properties[] = { - DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void slavio_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = slavio_timer_init1; - dc->reset = slavio_timer_reset; - dc->vmsd = &vmstate_slavio_timer; - dc->props = slavio_timer_properties; -} - -static const TypeInfo slavio_timer_info = { - .name = "slavio_timer", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SLAVIO_TIMERState), - .class_init = slavio_timer_class_init, -}; - -static void slavio_timer_register_types(void) -{ - type_register_static(&slavio_timer_info); -} - -type_init(slavio_timer_register_types) diff --git a/hw/sparc/Makefile.objs b/hw/sparc/Makefile.objs index e18bc67fc7..ab1d91c2b9 100644 --- a/hw/sparc/Makefile.objs +++ b/hw/sparc/Makefile.objs @@ -1,9 +1,9 @@ obj-y = sun4m_iommu.o slavio_intctl.o -obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o +obj-y += slavio_misc.o sparc32_dma.o obj-y += eccmemctl.o sbi.o sun4c_intctl.o # GRLIB -obj-y += grlib_gptimer.o grlib_irqmp.o +obj-y += grlib_irqmp.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index a1ef26cf34..e4bd17fbb7 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -8,5 +8,21 @@ common-obj-$(CONFIG_PL031) += pl031.o common-obj-$(CONFIG_PUV3) += puv3_ost.o common-obj-$(CONFIG_TWL92230) += twl92230.o common-obj-$(CONFIG_XILINX) += xilinx_timer.o +common-obj-$(CONFIG_SLAVIO) += slavio_timer.o +common-obj-$(CONFIG_ETRAXFS) += etraxfs_timer.o +common-obj-$(CONFIG_GRLIB) += grlib_gptimer.o +common-obj-$(CONFIG_IMX) += imx_timer.o +common-obj-$(CONFIG_LM32) += lm32_timer.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o +obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o +obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o +obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o +obj-$(CONFIG_OMAP) += omap_gptimer.o +obj-$(CONFIG_OMAP) += omap_synctimer.o +obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o +obj-$(CONFIG_SH4) += sh_timer.o +obj-$(CONFIG_TUSB6010) += tusb6010.o + +obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c new file mode 100644 index 0000000000..317f5e43ed --- /dev/null +++ b/hw/timer/arm_mptimer.c @@ -0,0 +1,309 @@ +/* + * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2011 Linaro Limited + * Written by Paul Brook, Peter Maydell + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/sysbus.h" +#include "qemu/timer.h" + +/* This device implements the per-cpu private timer and watchdog block + * which is used in both the ARM11MPCore and Cortex-A9MP. + */ + +#define MAX_CPUS 4 + +/* State of a single timer or watchdog block */ +typedef struct { + uint32_t count; + uint32_t load; + uint32_t control; + uint32_t status; + int64_t tick; + QEMUTimer *timer; + qemu_irq irq; + MemoryRegion iomem; +} TimerBlock; + +typedef struct { + SysBusDevice busdev; + uint32_t num_cpu; + TimerBlock timerblock[MAX_CPUS]; + MemoryRegion iomem; +} ARMMPTimerState; + +static inline int get_current_cpu(ARMMPTimerState *s) +{ + CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env); + + if (cpu_single_cpu->cpu_index >= s->num_cpu) { + hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", + s->num_cpu, cpu_single_cpu->cpu_index); + } + return cpu_single_cpu->cpu_index; +} + +static inline void timerblock_update_irq(TimerBlock *tb) +{ + qemu_set_irq(tb->irq, tb->status); +} + +/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ +static inline uint32_t timerblock_scale(TimerBlock *tb) +{ + return (((tb->control >> 8) & 0xff) + 1) * 10; +} + +static void timerblock_reload(TimerBlock *tb, int restart) +{ + if (tb->count == 0) { + return; + } + if (restart) { + tb->tick = qemu_get_clock_ns(vm_clock); + } + tb->tick += (int64_t)tb->count * timerblock_scale(tb); + qemu_mod_timer(tb->timer, tb->tick); +} + +static void timerblock_tick(void *opaque) +{ + TimerBlock *tb = (TimerBlock *)opaque; + tb->status = 1; + if (tb->control & 2) { + tb->count = tb->load; + timerblock_reload(tb, 0); + } else { + tb->count = 0; + } + timerblock_update_irq(tb); +} + +static uint64_t timerblock_read(void *opaque, hwaddr addr, + unsigned size) +{ + TimerBlock *tb = (TimerBlock *)opaque; + int64_t val; + switch (addr) { + case 0: /* Load */ + return tb->load; + case 4: /* Counter. */ + if (((tb->control & 1) == 0) || (tb->count == 0)) { + return 0; + } + /* Slow and ugly, but hopefully won't happen too often. */ + val = tb->tick - qemu_get_clock_ns(vm_clock); + val /= timerblock_scale(tb); + if (val < 0) { + val = 0; + } + return val; + case 8: /* Control. */ + return tb->control; + case 12: /* Interrupt status. */ + return tb->status; + default: + return 0; + } +} + +static void timerblock_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + TimerBlock *tb = (TimerBlock *)opaque; + int64_t old; + switch (addr) { + case 0: /* Load */ + tb->load = value; + /* Fall through. */ + case 4: /* Counter. */ + if ((tb->control & 1) && tb->count) { + /* Cancel the previous timer. */ + qemu_del_timer(tb->timer); + } + tb->count = value; + if (tb->control & 1) { + timerblock_reload(tb, 1); + } + break; + case 8: /* Control. */ + old = tb->control; + tb->control = value; + if (((old & 1) == 0) && (value & 1)) { + if (tb->count == 0 && (tb->control & 2)) { + tb->count = tb->load; + } + timerblock_reload(tb, 1); + } + break; + case 12: /* Interrupt status. */ + tb->status &= ~value; + timerblock_update_irq(tb); + break; + } +} + +/* Wrapper functions to implement the "read timer/watchdog for + * the current CPU" memory regions. + */ +static uint64_t arm_thistimer_read(void *opaque, hwaddr addr, + unsigned size) +{ + ARMMPTimerState *s = (ARMMPTimerState *)opaque; + int id = get_current_cpu(s); + return timerblock_read(&s->timerblock[id], addr, size); +} + +static void arm_thistimer_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + ARMMPTimerState *s = (ARMMPTimerState *)opaque; + int id = get_current_cpu(s); + timerblock_write(&s->timerblock[id], addr, value, size); +} + +static const MemoryRegionOps arm_thistimer_ops = { + .read = arm_thistimer_read, + .write = arm_thistimer_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps timerblock_ops = { + .read = timerblock_read, + .write = timerblock_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void timerblock_reset(TimerBlock *tb) +{ + tb->count = 0; + tb->load = 0; + tb->control = 0; + tb->status = 0; + tb->tick = 0; + if (tb->timer) { + qemu_del_timer(tb->timer); + } +} + +static void arm_mptimer_reset(DeviceState *dev) +{ + ARMMPTimerState *s = + FROM_SYSBUS(ARMMPTimerState, SYS_BUS_DEVICE(dev)); + int i; + for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { + timerblock_reset(&s->timerblock[i]); + } +} + +static int arm_mptimer_init(SysBusDevice *dev) +{ + ARMMPTimerState *s = FROM_SYSBUS(ARMMPTimerState, dev); + int i; + if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) { + hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS); + } + /* We implement one timer block per CPU, and expose multiple MMIO regions: + * * region 0 is "timer for this core" + * * region 1 is "timer for core 0" + * * region 2 is "timer for core 1" + * and so on. + * The outgoing interrupt lines are + * * timer for core 0 + * * timer for core 1 + * and so on. + */ + memory_region_init_io(&s->iomem, &arm_thistimer_ops, s, + "arm_mptimer_timer", 0x20); + sysbus_init_mmio(dev, &s->iomem); + for (i = 0; i < s->num_cpu; i++) { + TimerBlock *tb = &s->timerblock[i]; + tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb); + sysbus_init_irq(dev, &tb->irq); + memory_region_init_io(&tb->iomem, &timerblock_ops, tb, + "arm_mptimer_timerblock", 0x20); + sysbus_init_mmio(dev, &tb->iomem); + } + + return 0; +} + +static const VMStateDescription vmstate_timerblock = { + .name = "arm_mptimer_timerblock", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(count, TimerBlock), + VMSTATE_UINT32(load, TimerBlock), + VMSTATE_UINT32(control, TimerBlock), + VMSTATE_UINT32(status, TimerBlock), + VMSTATE_INT64(tick, TimerBlock), + VMSTATE_TIMER(timer, TimerBlock), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_arm_mptimer = { + .name = "arm_mptimer", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, + 2, vmstate_timerblock, TimerBlock), + VMSTATE_END_OF_LIST() + } +}; + +static Property arm_mptimer_properties[] = { + DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void arm_mptimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + + sbc->init = arm_mptimer_init; + dc->vmsd = &vmstate_arm_mptimer; + dc->reset = arm_mptimer_reset; + dc->no_user = 1; + dc->props = arm_mptimer_properties; +} + +static const TypeInfo arm_mptimer_info = { + .name = "arm_mptimer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMMPTimerState), + .class_init = arm_mptimer_class_init, +}; + +static void arm_mptimer_register_types(void) +{ + type_register_static(&arm_mptimer_info); +} + +type_init(arm_mptimer_register_types) diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c new file mode 100644 index 0000000000..3cd9476bb1 --- /dev/null +++ b/hw/timer/etraxfs_timer.c @@ -0,0 +1,351 @@ +/* + * QEMU ETRAX Timers + * + * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" + +#define D(x) + +#define RW_TMR0_DIV 0x00 +#define R_TMR0_DATA 0x04 +#define RW_TMR0_CTRL 0x08 +#define RW_TMR1_DIV 0x10 +#define R_TMR1_DATA 0x14 +#define RW_TMR1_CTRL 0x18 +#define R_TIME 0x38 +#define RW_WD_CTRL 0x40 +#define R_WD_STAT 0x44 +#define RW_INTR_MASK 0x48 +#define RW_ACK_INTR 0x4c +#define R_INTR 0x50 +#define R_MASKED_INTR 0x54 + +struct etrax_timer { + SysBusDevice busdev; + MemoryRegion mmio; + qemu_irq irq; + qemu_irq nmi; + + QEMUBH *bh_t0; + QEMUBH *bh_t1; + QEMUBH *bh_wd; + ptimer_state *ptimer_t0; + ptimer_state *ptimer_t1; + ptimer_state *ptimer_wd; + + int wd_hits; + + /* Control registers. */ + uint32_t rw_tmr0_div; + uint32_t r_tmr0_data; + uint32_t rw_tmr0_ctrl; + + uint32_t rw_tmr1_div; + uint32_t r_tmr1_data; + uint32_t rw_tmr1_ctrl; + + uint32_t rw_wd_ctrl; + + uint32_t rw_intr_mask; + uint32_t rw_ack_intr; + uint32_t r_intr; + uint32_t r_masked_intr; +}; + +static uint64_t +timer_read(void *opaque, hwaddr addr, unsigned int size) +{ + struct etrax_timer *t = opaque; + uint32_t r = 0; + + switch (addr) { + case R_TMR0_DATA: + r = ptimer_get_count(t->ptimer_t0); + break; + case R_TMR1_DATA: + r = ptimer_get_count(t->ptimer_t1); + break; + case R_TIME: + r = qemu_get_clock_ns(vm_clock) / 10; + break; + case RW_INTR_MASK: + r = t->rw_intr_mask; + break; + case R_MASKED_INTR: + r = t->r_intr & t->rw_intr_mask; + break; + default: + D(printf ("%s %x\n", __func__, addr)); + break; + } + return r; +} + +static void update_ctrl(struct etrax_timer *t, int tnum) +{ + unsigned int op; + unsigned int freq; + unsigned int freq_hz; + unsigned int div; + uint32_t ctrl; + + ptimer_state *timer; + + if (tnum == 0) { + ctrl = t->rw_tmr0_ctrl; + div = t->rw_tmr0_div; + timer = t->ptimer_t0; + } else { + ctrl = t->rw_tmr1_ctrl; + div = t->rw_tmr1_div; + timer = t->ptimer_t1; + } + + + op = ctrl & 3; + freq = ctrl >> 2; + freq_hz = 32000000; + + switch (freq) + { + case 0: + case 1: + D(printf ("extern or disabled timer clock?\n")); + break; + case 4: freq_hz = 29493000; break; + case 5: freq_hz = 32000000; break; + case 6: freq_hz = 32768000; break; + case 7: freq_hz = 100000000; break; + default: + abort(); + break; + } + + D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); + ptimer_set_freq(timer, freq_hz); + ptimer_set_limit(timer, div, 0); + + switch (op) + { + case 0: + /* Load. */ + ptimer_set_limit(timer, div, 1); + break; + case 1: + /* Hold. */ + ptimer_stop(timer); + break; + case 2: + /* Run. */ + ptimer_run(timer, 0); + break; + default: + abort(); + break; + } +} + +static void timer_update_irq(struct etrax_timer *t) +{ + t->r_intr &= ~(t->rw_ack_intr); + t->r_masked_intr = t->r_intr & t->rw_intr_mask; + + D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr)); + qemu_set_irq(t->irq, !!t->r_masked_intr); +} + +static void timer0_hit(void *opaque) +{ + struct etrax_timer *t = opaque; + t->r_intr |= 1; + timer_update_irq(t); +} + +static void timer1_hit(void *opaque) +{ + struct etrax_timer *t = opaque; + t->r_intr |= 2; + timer_update_irq(t); +} + +static void watchdog_hit(void *opaque) +{ + struct etrax_timer *t = opaque; + if (t->wd_hits == 0) { + /* real hw gives a single tick before reseting but we are + a bit friendlier to compensate for our slower execution. */ + ptimer_set_count(t->ptimer_wd, 10); + ptimer_run(t->ptimer_wd, 1); + qemu_irq_raise(t->nmi); + } + else + qemu_system_reset_request(); + + t->wd_hits++; +} + +static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value) +{ + unsigned int wd_en = t->rw_wd_ctrl & (1 << 8); + unsigned int wd_key = t->rw_wd_ctrl >> 9; + unsigned int wd_cnt = t->rw_wd_ctrl & 511; + unsigned int new_key = value >> 9 & ((1 << 7) - 1); + unsigned int new_cmd = (value >> 8) & 1; + + /* If the watchdog is enabled, they written key must match the + complement of the previous. */ + wd_key = ~wd_key & ((1 << 7) - 1); + + if (wd_en && wd_key != new_key) + return; + + D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", + wd_en, new_key, wd_key, new_cmd, wd_cnt)); + + if (t->wd_hits) + qemu_irq_lower(t->nmi); + + t->wd_hits = 0; + + ptimer_set_freq(t->ptimer_wd, 760); + if (wd_cnt == 0) + wd_cnt = 256; + ptimer_set_count(t->ptimer_wd, wd_cnt); + if (new_cmd) + ptimer_run(t->ptimer_wd, 1); + else + ptimer_stop(t->ptimer_wd); + + t->rw_wd_ctrl = value; +} + +static void +timer_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + struct etrax_timer *t = opaque; + uint32_t value = val64; + + switch (addr) + { + case RW_TMR0_DIV: + t->rw_tmr0_div = value; + break; + case RW_TMR0_CTRL: + D(printf ("RW_TMR0_CTRL=%x\n", value)); + t->rw_tmr0_ctrl = value; + update_ctrl(t, 0); + break; + case RW_TMR1_DIV: + t->rw_tmr1_div = value; + break; + case RW_TMR1_CTRL: + D(printf ("RW_TMR1_CTRL=%x\n", value)); + t->rw_tmr1_ctrl = value; + update_ctrl(t, 1); + break; + case RW_INTR_MASK: + D(printf ("RW_INTR_MASK=%x\n", value)); + t->rw_intr_mask = value; + timer_update_irq(t); + break; + case RW_WD_CTRL: + timer_watchdog_update(t, value); + break; + case RW_ACK_INTR: + t->rw_ack_intr = value; + timer_update_irq(t); + t->rw_ack_intr = 0; + break; + default: + printf ("%s " TARGET_FMT_plx " %x\n", + __func__, addr, value); + break; + } +} + +static const MemoryRegionOps timer_ops = { + .read = timer_read, + .write = timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void etraxfs_timer_reset(void *opaque) +{ + struct etrax_timer *t = opaque; + + ptimer_stop(t->ptimer_t0); + ptimer_stop(t->ptimer_t1); + ptimer_stop(t->ptimer_wd); + t->rw_wd_ctrl = 0; + t->r_intr = 0; + t->rw_intr_mask = 0; + qemu_irq_lower(t->irq); +} + +static int etraxfs_timer_init(SysBusDevice *dev) +{ + struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev); + + t->bh_t0 = qemu_bh_new(timer0_hit, t); + t->bh_t1 = qemu_bh_new(timer1_hit, t); + t->bh_wd = qemu_bh_new(watchdog_hit, t); + t->ptimer_t0 = ptimer_init(t->bh_t0); + t->ptimer_t1 = ptimer_init(t->bh_t1); + t->ptimer_wd = ptimer_init(t->bh_wd); + + sysbus_init_irq(dev, &t->irq); + sysbus_init_irq(dev, &t->nmi); + + memory_region_init_io(&t->mmio, &timer_ops, t, "etraxfs-timer", 0x5c); + sysbus_init_mmio(dev, &t->mmio); + qemu_register_reset(etraxfs_timer_reset, t); + return 0; +} + +static void etraxfs_timer_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = etraxfs_timer_init; +} + +static const TypeInfo etraxfs_timer_info = { + .name = "etraxfs,timer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof (struct etrax_timer), + .class_init = etraxfs_timer_class_init, +}; + +static void etraxfs_timer_register_types(void) +{ + type_register_static(&etraxfs_timer_info); +} + +type_init(etraxfs_timer_register_types) diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c new file mode 100644 index 0000000000..87ce75b643 --- /dev/null +++ b/hw/timer/exynos4210_mct.c @@ -0,0 +1,1482 @@ +/* + * Samsung exynos4210 Multi Core timer + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +/* + * Global Timer: + * + * Consists of two timers. First represents Free Running Counter and second + * is used to measure interval from FRC to nearest comparator. + * + * 0 UINT64_MAX + * | timer0 | + * | <-------------------------------------------------------------- | + * | --------------------------------------------frc---------------> | + * |______________________________________________|__________________| + * CMP0 CMP1 CMP2 | CMP3 + * __| |_ + * | timer1 | + * | -------------> | + * frc CMPx + * + * Problem: when implementing global timer as is, overflow arises. + * next_time = cur_time + period * count; + * period and count are 64 bits width. + * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT + * register during each event. + * + * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because + * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--. + * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0 + * generates IRQs suffers from too frequently events. Better to have one + * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT, + * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values, + * there is no way to avoid frequently events). + */ + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "qemu-common.h" +#include "hw/ptimer.h" + +#include "hw/arm/exynos4210.h" + +//#define DEBUG_MCT + +#ifdef DEBUG_MCT +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define MCT_CFG 0x000 +#define G_CNT_L 0x100 +#define G_CNT_U 0x104 +#define G_CNT_WSTAT 0x110 +#define G_COMP0_L 0x200 +#define G_COMP0_U 0x204 +#define G_COMP0_ADD_INCR 0x208 +#define G_COMP1_L 0x210 +#define G_COMP1_U 0x214 +#define G_COMP1_ADD_INCR 0x218 +#define G_COMP2_L 0x220 +#define G_COMP2_U 0x224 +#define G_COMP2_ADD_INCR 0x228 +#define G_COMP3_L 0x230 +#define G_COMP3_U 0x234 +#define G_COMP3_ADD_INCR 0x238 +#define G_TCON 0x240 +#define G_INT_CSTAT 0x244 +#define G_INT_ENB 0x248 +#define G_WSTAT 0x24C +#define L0_TCNTB 0x300 +#define L0_TCNTO 0x304 +#define L0_ICNTB 0x308 +#define L0_ICNTO 0x30C +#define L0_FRCNTB 0x310 +#define L0_FRCNTO 0x314 +#define L0_TCON 0x320 +#define L0_INT_CSTAT 0x330 +#define L0_INT_ENB 0x334 +#define L0_WSTAT 0x340 +#define L1_TCNTB 0x400 +#define L1_TCNTO 0x404 +#define L1_ICNTB 0x408 +#define L1_ICNTO 0x40C +#define L1_FRCNTB 0x410 +#define L1_FRCNTO 0x414 +#define L1_TCON 0x420 +#define L1_INT_CSTAT 0x430 +#define L1_INT_ENB 0x434 +#define L1_WSTAT 0x440 + +#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF) +#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7)) + +#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10) +#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10) + +#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10) +#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10) + +#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10) + +/* MCT bits */ +#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x)) +#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1)) +#define G_TCON_TIMER_ENABLE (1 << 8) + +#define G_INT_ENABLE(x) (1 << (x)) +#define G_INT_CSTAT_COMP(x) (1 << (x)) + +#define G_CNT_WSTAT_L 1 +#define G_CNT_WSTAT_U 2 + +#define G_WSTAT_COMP_L(x) (1 << 4 * (x)) +#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1)) +#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2)) +#define G_WSTAT_TCON_WRITE (1 << 16) + +#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100) +#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \ + (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2) + +#define L_ICNTB_MANUAL_UPDATE (1 << 31) + +#define L_TCON_TICK_START (1) +#define L_TCON_INT_START (1 << 1) +#define L_TCON_INTERVAL_MODE (1 << 2) +#define L_TCON_FRC_START (1 << 3) + +#define L_INT_CSTAT_INTCNT (1 << 0) +#define L_INT_CSTAT_FRCCNT (1 << 1) + +#define L_INT_INTENB_ICNTEIE (1 << 0) +#define L_INT_INTENB_FRCEIE (1 << 1) + +#define L_WSTAT_TCNTB_WRITE (1 << 0) +#define L_WSTAT_ICNTB_WRITE (1 << 1) +#define L_WSTAT_FRCCNTB_WRITE (1 << 2) +#define L_WSTAT_TCON_WRITE (1 << 3) + +enum LocalTimerRegCntIndexes { + L_REG_CNT_TCNTB, + L_REG_CNT_TCNTO, + L_REG_CNT_ICNTB, + L_REG_CNT_ICNTO, + L_REG_CNT_FRCCNTB, + L_REG_CNT_FRCCNTO, + + L_REG_CNT_AMOUNT +}; + +#define MCT_NIRQ 6 +#define MCT_SFR_SIZE 0x444 + +#define MCT_GT_CMP_NUM 4 + +#define MCT_GT_MAX_VAL UINT64_MAX + +#define MCT_GT_COUNTER_STEP 0x100000000ULL +#define MCT_LT_COUNTER_STEP 0x100000000ULL +#define MCT_LT_CNT_LOW_LIMIT 0x100 + +/* global timer */ +typedef struct { + qemu_irq irq[MCT_GT_CMP_NUM]; + + struct gregs { + uint64_t cnt; + uint32_t cnt_wstat; + uint32_t tcon; + uint32_t int_cstat; + uint32_t int_enb; + uint32_t wstat; + uint64_t comp[MCT_GT_CMP_NUM]; + uint32_t comp_add_incr[MCT_GT_CMP_NUM]; + } reg; + + uint64_t count; /* Value FRC was armed with */ + int32_t curr_comp; /* Current comparator FRC is running to */ + + ptimer_state *ptimer_frc; /* FRC timer */ + +} Exynos4210MCTGT; + +/* local timer */ +typedef struct { + int id; /* timer id */ + qemu_irq irq; /* local timer irq */ + + struct tick_timer { + uint32_t cnt_run; /* cnt timer is running */ + uint32_t int_run; /* int timer is running */ + + uint32_t last_icnto; + uint32_t last_tcnto; + uint32_t tcntb; /* initial value for TCNTB */ + uint32_t icntb; /* initial value for ICNTB */ + + /* for step mode */ + uint64_t distance; /* distance to count to the next event */ + uint64_t progress; /* progress when counting by steps */ + uint64_t count; /* count to arm timer with */ + + ptimer_state *ptimer_tick; /* timer for tick counter */ + } tick_timer; + + /* use ptimer.c to represent count down timer */ + + ptimer_state *ptimer_frc; /* timer for free running counter */ + + /* registers */ + struct lregs { + uint32_t cnt[L_REG_CNT_AMOUNT]; + uint32_t tcon; + uint32_t int_cstat; + uint32_t int_enb; + uint32_t wstat; + } reg; + +} Exynos4210MCTLT; + +typedef struct Exynos4210MCTState { + SysBusDevice busdev; + MemoryRegion iomem; + + /* Registers */ + uint32_t reg_mct_cfg; + + Exynos4210MCTLT l_timer[2]; + Exynos4210MCTGT g_timer; + + uint32_t freq; /* all timers tick frequency, TCLK */ +} Exynos4210MCTState; + +/*** VMState ***/ +static const VMStateDescription vmstate_tick_timer = { + .name = "exynos4210.mct.tick_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cnt_run, struct tick_timer), + VMSTATE_UINT32(int_run, struct tick_timer), + VMSTATE_UINT32(last_icnto, struct tick_timer), + VMSTATE_UINT32(last_tcnto, struct tick_timer), + VMSTATE_UINT32(tcntb, struct tick_timer), + VMSTATE_UINT32(icntb, struct tick_timer), + VMSTATE_UINT64(distance, struct tick_timer), + VMSTATE_UINT64(progress, struct tick_timer), + VMSTATE_UINT64(count, struct tick_timer), + VMSTATE_PTIMER(ptimer_tick, struct tick_timer), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_lregs = { + .name = "exynos4210.mct.lregs", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT), + VMSTATE_UINT32(tcon, struct lregs), + VMSTATE_UINT32(int_cstat, struct lregs), + VMSTATE_UINT32(int_enb, struct lregs), + VMSTATE_UINT32(wstat, struct lregs), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_mct_lt = { + .name = "exynos4210.mct.lt", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(id, Exynos4210MCTLT), + VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0, + vmstate_tick_timer, + struct tick_timer), + VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT), + VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0, + vmstate_lregs, + struct lregs), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_gregs = { + .name = "exynos4210.mct.lregs", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(cnt, struct gregs), + VMSTATE_UINT32(cnt_wstat, struct gregs), + VMSTATE_UINT32(tcon, struct gregs), + VMSTATE_UINT32(int_cstat, struct gregs), + VMSTATE_UINT32(int_enb, struct gregs), + VMSTATE_UINT32(wstat, struct gregs), + VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM), + VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs, + MCT_GT_CMP_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_mct_gt = { + .name = "exynos4210.mct.lt", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs, + struct gregs), + VMSTATE_UINT64(count, Exynos4210MCTGT), + VMSTATE_INT32(curr_comp, Exynos4210MCTGT), + VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_mct_state = { + .name = "exynos4210.mct", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState), + VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0, + vmstate_exynos4210_mct_lt, Exynos4210MCTLT), + VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0, + vmstate_exynos4210_mct_gt, Exynos4210MCTGT), + VMSTATE_UINT32(freq, Exynos4210MCTState), + VMSTATE_END_OF_LIST() + } +}; + +static void exynos4210_mct_update_freq(Exynos4210MCTState *s); + +/* + * Set counter of FRC global timer. + */ +static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count) +{ + s->count = count; + DPRINTF("global timer frc set count 0x%llx\n", count); + ptimer_set_count(s->ptimer_frc, count); +} + +/* + * Get counter of FRC global timer. + */ +static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s) +{ + uint64_t count = 0; + count = ptimer_get_count(s->ptimer_frc); + count = s->count - count; + return s->reg.cnt + count; +} + +/* + * Stop global FRC timer + */ +static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) +{ + DPRINTF("global timer frc stop\n"); + + ptimer_stop(s->ptimer_frc); +} + +/* + * Start global FRC timer + */ +static void exynos4210_gfrc_start(Exynos4210MCTGT *s) +{ + DPRINTF("global timer frc start\n"); + + ptimer_run(s->ptimer_frc, 1); +} + +/* + * Find next nearest Comparator. If current Comparator value equals to other + * Comparator value, skip them both + */ +static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s) +{ + int res; + int i; + int enabled; + uint64_t min; + int min_comp_i; + uint64_t gfrc; + uint64_t distance; + uint64_t distance_min; + int comp_i; + + /* get gfrc count */ + gfrc = exynos4210_gfrc_get_count(&s->g_timer); + + min = UINT64_MAX; + distance_min = UINT64_MAX; + comp_i = MCT_GT_CMP_NUM; + min_comp_i = MCT_GT_CMP_NUM; + enabled = 0; + + /* lookup for nearest comparator */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + + if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) { + + enabled = 1; + + if (s->g_timer.reg.comp[i] > gfrc) { + /* Comparator is upper then FRC */ + distance = s->g_timer.reg.comp[i] - gfrc; + + if (distance <= distance_min) { + distance_min = distance; + comp_i = i; + } + } else { + /* Comparator is below FRC, find the smallest */ + + if (s->g_timer.reg.comp[i] <= min) { + min = s->g_timer.reg.comp[i]; + min_comp_i = i; + } + } + } + } + + if (!enabled) { + /* All Comparators disabled */ + res = -1; + } else if (comp_i < MCT_GT_CMP_NUM) { + /* Found upper Comparator */ + res = comp_i; + } else { + /* All Comparators are below or equal to FRC */ + res = min_comp_i; + } + + DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", + res, + s->g_timer.reg.comp[res], + distance_min, + gfrc); + + return res; +} + +/* + * Get distance to nearest Comparator + */ +static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id) +{ + if (id == -1) { + /* no enabled Comparators, choose max distance */ + return MCT_GT_COUNTER_STEP; + } + if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) { + return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt; + } else { + return MCT_GT_COUNTER_STEP; + } +} + +/* + * Restart global FRC timer + */ +static void exynos4210_gfrc_restart(Exynos4210MCTState *s) +{ + uint64_t distance; + + exynos4210_gfrc_stop(&s->g_timer); + + s->g_timer.curr_comp = exynos4210_gcomp_find(s); + + distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); + + if (distance > MCT_GT_COUNTER_STEP || !distance) { + distance = MCT_GT_COUNTER_STEP; + } + + exynos4210_gfrc_set_count(&s->g_timer, distance); + exynos4210_gfrc_start(&s->g_timer); +} + +/* + * Raise global timer CMP IRQ + */ +static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id) +{ + Exynos4210MCTGT *s = opaque; + + /* If CSTAT is pending and IRQ is enabled */ + if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) && + (s->reg.int_enb & G_INT_ENABLE(id))) { + DPRINTF("gcmp timer[%d] IRQ\n", id); + qemu_irq_raise(s->irq[id]); + } +} + +/* + * Lower global timer CMP IRQ + */ +static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id) +{ + Exynos4210MCTGT *s = opaque; + qemu_irq_lower(s->irq[id]); +} + +/* + * Global timer FRC event handler. + * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP + * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value + */ +static void exynos4210_gfrc_event(void *opaque) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; + int i; + uint64_t distance; + + DPRINTF("\n"); + + s->g_timer.reg.cnt += s->g_timer.count; + + /* Process all comparators */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + + if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) { + /* reached nearest comparator */ + + s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i); + + /* Auto increment */ + if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) { + s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i]; + } + + /* IRQ */ + exynos4210_gcomp_raise_irq(&s->g_timer, i); + } + } + + /* Reload FRC to reach nearest comparator */ + s->g_timer.curr_comp = exynos4210_gcomp_find(s); + distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); + if (distance > MCT_GT_COUNTER_STEP || !distance) { + distance = MCT_GT_COUNTER_STEP; + } + exynos4210_gfrc_set_count(&s->g_timer, distance); + + exynos4210_gfrc_start(&s->g_timer); +} + +/* + * Get counter of FRC local timer. + */ +static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s) +{ + return ptimer_get_count(s->ptimer_frc); +} + +/* + * Set counter of FRC local timer. + */ +static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) +{ + if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) { + ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP); + } else { + ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]); + } +} + +/* + * Start local FRC timer + */ +static void exynos4210_lfrc_start(Exynos4210MCTLT *s) +{ + ptimer_run(s->ptimer_frc, 1); +} + +/* + * Stop local FRC timer + */ +static void exynos4210_lfrc_stop(Exynos4210MCTLT *s) +{ + ptimer_stop(s->ptimer_frc); +} + +/* + * Local timer free running counter tick handler + */ +static void exynos4210_lfrc_event(void *opaque) +{ + Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; + + /* local frc expired */ + + DPRINTF("\n"); + + s->reg.int_cstat |= L_INT_CSTAT_FRCCNT; + + /* update frc counter */ + exynos4210_lfrc_update_count(s); + + /* raise irq */ + if (s->reg.int_enb & L_INT_INTENB_FRCEIE) { + qemu_irq_raise(s->irq); + } + + /* we reached here, this means that timer is enabled */ + exynos4210_lfrc_start(s); +} + +static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s); +static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s); +static void exynos4210_ltick_recalc_count(struct tick_timer *s); + +/* + * Action on enabling local tick int timer + */ +static void exynos4210_ltick_int_start(struct tick_timer *s) +{ + if (!s->int_run) { + s->int_run = 1; + } +} + +/* + * Action on disabling local tick int timer + */ +static void exynos4210_ltick_int_stop(struct tick_timer *s) +{ + if (s->int_run) { + s->last_icnto = exynos4210_ltick_int_get_cnto(s); + s->int_run = 0; + } +} + +/* + * Get count for INT timer + */ +static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s) +{ + uint32_t icnto; + uint64_t remain; + uint64_t count; + uint64_t counted; + uint64_t cur_progress; + + count = ptimer_get_count(s->ptimer_tick); + if (count) { + /* timer is still counting, called not from event */ + counted = s->count - ptimer_get_count(s->ptimer_tick); + cur_progress = s->progress + counted; + } else { + /* timer expired earlier */ + cur_progress = s->progress; + } + + remain = s->distance - cur_progress; + + if (!s->int_run) { + /* INT is stopped. */ + icnto = s->last_icnto; + } else { + /* Both are counting */ + icnto = remain / s->tcntb; + } + + return icnto; +} + +/* + * Start local tick cnt timer. + */ +static void exynos4210_ltick_cnt_start(struct tick_timer *s) +{ + if (!s->cnt_run) { + + exynos4210_ltick_recalc_count(s); + ptimer_set_count(s->ptimer_tick, s->count); + ptimer_run(s->ptimer_tick, 1); + + s->cnt_run = 1; + } +} + +/* + * Stop local tick cnt timer. + */ +static void exynos4210_ltick_cnt_stop(struct tick_timer *s) +{ + if (s->cnt_run) { + + s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s); + + if (s->int_run) { + exynos4210_ltick_int_stop(s); + } + + ptimer_stop(s->ptimer_tick); + + s->cnt_run = 0; + } +} + +/* + * Get counter for CNT timer + */ +static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) +{ + uint32_t tcnto; + uint32_t icnto; + uint64_t remain; + uint64_t counted; + uint64_t count; + uint64_t cur_progress; + + count = ptimer_get_count(s->ptimer_tick); + if (count) { + /* timer is still counting, called not from event */ + counted = s->count - ptimer_get_count(s->ptimer_tick); + cur_progress = s->progress + counted; + } else { + /* timer expired earlier */ + cur_progress = s->progress; + } + + remain = s->distance - cur_progress; + + if (!s->cnt_run) { + /* Both are stopped. */ + tcnto = s->last_tcnto; + } else if (!s->int_run) { + /* INT counter is stopped, progress is by CNT timer */ + tcnto = remain % s->tcntb; + } else { + /* Both are counting */ + icnto = remain / s->tcntb; + if (icnto) { + tcnto = remain % (icnto * s->tcntb); + } else { + tcnto = remain % s->tcntb; + } + } + + return tcnto; +} + +/* + * Set new values of counters for CNT and INT timers + */ +static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt, + uint32_t new_int) +{ + uint32_t cnt_stopped = 0; + uint32_t int_stopped = 0; + + if (s->cnt_run) { + exynos4210_ltick_cnt_stop(s); + cnt_stopped = 1; + } + + if (s->int_run) { + exynos4210_ltick_int_stop(s); + int_stopped = 1; + } + + s->tcntb = new_cnt + 1; + s->icntb = new_int + 1; + + if (cnt_stopped) { + exynos4210_ltick_cnt_start(s); + } + if (int_stopped) { + exynos4210_ltick_int_start(s); + } + +} + +/* + * Calculate new counter value for tick timer + */ +static void exynos4210_ltick_recalc_count(struct tick_timer *s) +{ + uint64_t to_count; + + if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) { + /* + * one or both timers run and not counted to the end; + * distance is not passed, recalculate with last_tcnto * last_icnto + */ + + if (s->last_tcnto) { + to_count = s->last_tcnto * s->last_icnto; + } else { + to_count = s->last_icnto; + } + } else { + /* distance is passed, recalculate with tcnto * icnto */ + if (s->icntb) { + s->distance = s->tcntb * s->icntb; + } else { + s->distance = s->tcntb; + } + + to_count = s->distance; + s->progress = 0; + } + + if (to_count > MCT_LT_COUNTER_STEP) { + /* count by step */ + s->count = MCT_LT_COUNTER_STEP; + } else { + s->count = to_count; + } +} + +/* + * Initialize tick_timer + */ +static void exynos4210_ltick_timer_init(struct tick_timer *s) +{ + exynos4210_ltick_int_stop(s); + exynos4210_ltick_cnt_stop(s); + + s->count = 0; + s->distance = 0; + s->progress = 0; + s->icntb = 0; + s->tcntb = 0; +} + +/* + * tick_timer event. + * Raises when abstract tick_timer expires. + */ +static void exynos4210_ltick_timer_event(struct tick_timer *s) +{ + s->progress += s->count; +} + +/* + * Local timer tick counter handler. + * Don't use reloaded timers. If timer counter = zero + * then handler called but after handler finished no + * timer reload occurs. + */ +static void exynos4210_ltick_event(void *opaque) +{ + Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; + uint32_t tcnto; + uint32_t icnto; +#ifdef DEBUG_MCT + static uint64_t time1[2] = {0}; + static uint64_t time2[2] = {0}; +#endif + + /* Call tick_timer event handler, it will update its tcntb and icntb. */ + exynos4210_ltick_timer_event(&s->tick_timer); + + /* get tick_timer cnt */ + tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer); + + /* get tick_timer int */ + icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer); + + /* raise IRQ if needed */ + if (!icnto && s->reg.tcon & L_TCON_INT_START) { + /* INT counter enabled and expired */ + + s->reg.int_cstat |= L_INT_CSTAT_INTCNT; + + /* raise interrupt if enabled */ + if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) { +#ifdef DEBUG_MCT + time2[s->id] = qemu_get_clock_ns(vm_clock); + DPRINTF("local timer[%d] IRQ: %llx\n", s->id, + time2[s->id] - time1[s->id]); + time1[s->id] = time2[s->id]; +#endif + qemu_irq_raise(s->irq); + } + + /* reload ICNTB */ + if (s->reg.tcon & L_TCON_INTERVAL_MODE) { + exynos4210_ltick_set_cntb(&s->tick_timer, + s->reg.cnt[L_REG_CNT_TCNTB], + s->reg.cnt[L_REG_CNT_ICNTB]); + } + } else { + /* reload TCNTB */ + if (!tcnto) { + exynos4210_ltick_set_cntb(&s->tick_timer, + s->reg.cnt[L_REG_CNT_TCNTB], + icnto); + } + } + + /* start tick_timer cnt */ + exynos4210_ltick_cnt_start(&s->tick_timer); + + /* start tick_timer int */ + exynos4210_ltick_int_start(&s->tick_timer); +} + +/* update timer frequency */ +static void exynos4210_mct_update_freq(Exynos4210MCTState *s) +{ + uint32_t freq = s->freq; + s->freq = 24000000 / + ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) * + MCT_CFG_GET_DIVIDER(s->reg_mct_cfg)); + + if (freq != s->freq) { + DPRINTF("freq=%dHz\n", s->freq); + + /* global timer */ + ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); + + /* local timer */ + ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); + ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); + ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); + ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); + } +} + +/* set defaul_timer values for all fields */ +static void exynos4210_mct_reset(DeviceState *d) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)d; + uint32_t i; + + s->reg_mct_cfg = 0; + + /* global timer */ + memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg)); + exynos4210_gfrc_stop(&s->g_timer); + + /* local timer */ + memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt)); + memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt)); + for (i = 0; i < 2; i++) { + s->l_timer[i].reg.int_cstat = 0; + s->l_timer[i].reg.int_enb = 0; + s->l_timer[i].reg.tcon = 0; + s->l_timer[i].reg.wstat = 0; + s->l_timer[i].tick_timer.count = 0; + s->l_timer[i].tick_timer.distance = 0; + s->l_timer[i].tick_timer.progress = 0; + ptimer_stop(s->l_timer[i].ptimer_frc); + + exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer); + } + + exynos4210_mct_update_freq(s); + +} + +/* Multi Core Timer read */ +static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset, + unsigned size) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; + int index; + int shift; + uint64_t count; + uint32_t value; + int lt_i; + + switch (offset) { + + case MCT_CFG: + value = s->reg_mct_cfg; + break; + + case G_CNT_L: case G_CNT_U: + shift = 8 * (offset & 0x4); + count = exynos4210_gfrc_get_count(&s->g_timer); + value = UINT32_MAX & (count >> shift); + DPRINTF("read FRC=0x%llx\n", count); + break; + + case G_CNT_WSTAT: + value = s->g_timer.reg.cnt_wstat; + break; + + case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): + case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): + index = GET_G_COMP_IDX(offset); + shift = 8 * (offset & 0x4); + value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); + break; + + case G_TCON: + value = s->g_timer.reg.tcon; + break; + + case G_INT_CSTAT: + value = s->g_timer.reg.int_cstat; + break; + + case G_INT_ENB: + value = s->g_timer.reg.int_enb; + break; + break; + case G_WSTAT: + value = s->g_timer.reg.wstat; + break; + + case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: + case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: + value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)]; + break; + + /* Local timers */ + case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB: + case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB: + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + value = s->l_timer[lt_i].reg.cnt[index]; + break; + + case L0_TCNTO: case L1_TCNTO: + lt_i = GET_L_TIMER_IDX(offset); + + value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer); + DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value); + break; + + case L0_ICNTO: case L1_ICNTO: + lt_i = GET_L_TIMER_IDX(offset); + + value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer); + DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value); + break; + + case L0_FRCNTO: case L1_FRCNTO: + lt_i = GET_L_TIMER_IDX(offset); + + value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]); + + break; + + case L0_TCON: case L1_TCON: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.tcon; + break; + + case L0_INT_CSTAT: case L1_INT_CSTAT: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.int_cstat; + break; + + case L0_INT_ENB: case L1_INT_ENB: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.int_enb; + break; + + case L0_WSTAT: case L1_WSTAT: + lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; + value = s->l_timer[lt_i].reg.wstat; + break; + + default: + hw_error("exynos4210.mct: bad read offset " + TARGET_FMT_plx "\n", offset); + break; + } + return value; +} + +/* MCT write */ +static void exynos4210_mct_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; + int index; /* index in buffer which represents register set */ + int shift; + int lt_i; + uint64_t new_frc; + uint32_t i; + uint32_t old_val; +#ifdef DEBUG_MCT + static uint32_t icntb_max[2] = {0}; + static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX}; + static uint32_t tcntb_max[2] = {0}; + static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX}; +#endif + + new_frc = s->g_timer.reg.cnt; + + switch (offset) { + + case MCT_CFG: + s->reg_mct_cfg = value; + exynos4210_mct_update_freq(s); + break; + + case G_CNT_L: + case G_CNT_U: + if (offset == G_CNT_L) { + + DPRINTF("global timer write to reg.cntl %llx\n", value); + + new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value; + s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L; + } + if (offset == G_CNT_U) { + + DPRINTF("global timer write to reg.cntu %llx\n", value); + + new_frc = (s->g_timer.reg.cnt & UINT32_MAX) + + ((uint64_t)value << 32); + s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U; + } + + s->g_timer.reg.cnt = new_frc; + exynos4210_gfrc_restart(s); + break; + + case G_CNT_WSTAT: + s->g_timer.reg.cnt_wstat &= ~(value); + break; + + case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): + case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): + index = GET_G_COMP_IDX(offset); + shift = 8 * (offset & 0x4); + s->g_timer.reg.comp[index] = + (s->g_timer.reg.comp[index] & + (((uint64_t)UINT32_MAX << 32) >> shift)) + + (value << shift); + + DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); + + if (offset&0x4) { + s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); + } else { + s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); + } + + exynos4210_gfrc_restart(s); + break; + + case G_TCON: + old_val = s->g_timer.reg.tcon; + s->g_timer.reg.tcon = value; + s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE; + + DPRINTF("global timer write to reg.g_tcon %llx\n", value); + + /* Start FRC if transition from disabled to enabled */ + if ((value & G_TCON_TIMER_ENABLE) > (old_val & + G_TCON_TIMER_ENABLE)) { + exynos4210_gfrc_start(&s->g_timer); + } + if ((value & G_TCON_TIMER_ENABLE) < (old_val & + G_TCON_TIMER_ENABLE)) { + exynos4210_gfrc_stop(&s->g_timer); + } + + /* Start CMP if transition from disabled to enabled */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + if ((value & G_TCON_COMP_ENABLE(i)) != (old_val & + G_TCON_COMP_ENABLE(i))) { + exynos4210_gfrc_restart(s); + } + } + break; + + case G_INT_CSTAT: + s->g_timer.reg.int_cstat &= ~(value); + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + if (value & G_INT_CSTAT_COMP(i)) { + exynos4210_gcomp_lower_irq(&s->g_timer, i); + } + } + break; + + case G_INT_ENB: + + /* Raise IRQ if transition from disabled to enabled and CSTAT pending */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon & + G_INT_ENABLE(i))) { + if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) { + exynos4210_gcomp_raise_irq(&s->g_timer, i); + } + } + + if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon & + G_INT_ENABLE(i))) { + exynos4210_gcomp_lower_irq(&s->g_timer, i); + } + } + + DPRINTF("global timer INT enable %llx\n", value); + s->g_timer.reg.int_enb = value; + break; + + case G_WSTAT: + s->g_timer.reg.wstat &= ~(value); + break; + + case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: + case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: + index = GET_G_COMP_ADD_INCR_IDX(offset); + s->g_timer.reg.comp_add_incr[index] = value; + s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index); + break; + + /* Local timers */ + case L0_TCON: case L1_TCON: + lt_i = GET_L_TIMER_IDX(offset); + old_val = s->l_timer[lt_i].reg.tcon; + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE; + s->l_timer[lt_i].reg.tcon = value; + + /* Stop local CNT */ + if ((value & L_TCON_TICK_START) < + (old_val & L_TCON_TICK_START)) { + DPRINTF("local timer[%d] stop cnt\n", lt_i); + exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer); + } + + /* Stop local INT */ + if ((value & L_TCON_INT_START) < + (old_val & L_TCON_INT_START)) { + DPRINTF("local timer[%d] stop int\n", lt_i); + exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer); + } + + /* Start local CNT */ + if ((value & L_TCON_TICK_START) > + (old_val & L_TCON_TICK_START)) { + DPRINTF("local timer[%d] start cnt\n", lt_i); + exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer); + } + + /* Start local INT */ + if ((value & L_TCON_INT_START) > + (old_val & L_TCON_INT_START)) { + DPRINTF("local timer[%d] start int\n", lt_i); + exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer); + } + + /* Start or Stop local FRC if TCON changed */ + if ((value & L_TCON_FRC_START) > + (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { + DPRINTF("local timer[%d] start frc\n", lt_i); + exynos4210_lfrc_start(&s->l_timer[lt_i]); + } + if ((value & L_TCON_FRC_START) < + (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { + DPRINTF("local timer[%d] stop frc\n", lt_i); + exynos4210_lfrc_stop(&s->l_timer[lt_i]); + } + break; + + case L0_TCNTB: case L1_TCNTB: + + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + + /* + * TCNTB is updated to internal register only after CNT expired. + * Due to this we should reload timer to nearest moment when CNT is + * expired and then in event handler update tcntb to new TCNTB value. + */ + exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value, + s->l_timer[lt_i].tick_timer.icntb); + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE; + s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value; + +#ifdef DEBUG_MCT + if (tcntb_min[lt_i] > value) { + tcntb_min[lt_i] = value; + } + if (tcntb_max[lt_i] < value) { + tcntb_max[lt_i] = value; + } + DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n", + lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]); +#endif + break; + + case L0_ICNTB: case L1_ICNTB: + + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE; + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value & + ~L_ICNTB_MANUAL_UPDATE; + + /* + * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event + * could raise too fast disallowing QEMU to execute target code. + */ + if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] * + s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) { + if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) { + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = + MCT_LT_CNT_LOW_LIMIT; + } else { + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = + MCT_LT_CNT_LOW_LIMIT / + s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]; + } + } + + if (value & L_ICNTB_MANUAL_UPDATE) { + exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, + s->l_timer[lt_i].tick_timer.tcntb, + s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]); + } + +#ifdef DEBUG_MCT + if (icntb_min[lt_i] > value) { + icntb_min[lt_i] = value; + } + if (icntb_max[lt_i] < value) { + icntb_max[lt_i] = value; + } +DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", + lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); +#endif +break; + + case L0_FRCNTB: case L1_FRCNTB: + + lt_i = GET_L_TIMER_IDX(offset); + index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); + + DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value); + + s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE; + s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value; + + break; + + case L0_TCNTO: case L1_TCNTO: + case L0_ICNTO: case L1_ICNTO: + case L0_FRCNTO: case L1_FRCNTO: + fprintf(stderr, "\n[exynos4210.mct: write to RO register " + TARGET_FMT_plx "]\n\n", offset); + break; + + case L0_INT_CSTAT: case L1_INT_CSTAT: + lt_i = GET_L_TIMER_IDX(offset); + + DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value); + + s->l_timer[lt_i].reg.int_cstat &= ~value; + if (!s->l_timer[lt_i].reg.int_cstat) { + qemu_irq_lower(s->l_timer[lt_i].irq); + } + break; + + case L0_INT_ENB: case L1_INT_ENB: + lt_i = GET_L_TIMER_IDX(offset); + old_val = s->l_timer[lt_i].reg.int_enb; + + /* Raise Local timer IRQ if cstat is pending */ + if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) { + if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) { + qemu_irq_raise(s->l_timer[lt_i].irq); + } + } + + s->l_timer[lt_i].reg.int_enb = value; + + break; + + case L0_WSTAT: case L1_WSTAT: + lt_i = GET_L_TIMER_IDX(offset); + + s->l_timer[lt_i].reg.wstat &= ~value; + break; + + default: + hw_error("exynos4210.mct: bad write offset " + TARGET_FMT_plx "\n", offset); + break; + } +} + +static const MemoryRegionOps exynos4210_mct_ops = { + .read = exynos4210_mct_read, + .write = exynos4210_mct_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* MCT init */ +static int exynos4210_mct_init(SysBusDevice *dev) +{ + int i; + Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev); + QEMUBH *bh[2]; + + /* Global timer */ + bh[0] = qemu_bh_new(exynos4210_gfrc_event, s); + s->g_timer.ptimer_frc = ptimer_init(bh[0]); + memset(&s->g_timer.reg, 0, sizeof(struct gregs)); + + /* Local timers */ + for (i = 0; i < 2; i++) { + bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]); + bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]); + s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]); + s->l_timer[i].ptimer_frc = ptimer_init(bh[1]); + s->l_timer[i].id = i; + } + + /* IRQs */ + for (i = 0; i < MCT_GT_CMP_NUM; i++) { + sysbus_init_irq(dev, &s->g_timer.irq[i]); + } + for (i = 0; i < 2; i++) { + sysbus_init_irq(dev, &s->l_timer[i].irq); + } + + memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct", + MCT_SFR_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void exynos4210_mct_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_mct_init; + dc->reset = exynos4210_mct_reset; + dc->vmsd = &vmstate_exynos4210_mct_state; +} + +static const TypeInfo exynos4210_mct_info = { + .name = "exynos4210.mct", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210MCTState), + .class_init = exynos4210_mct_class_init, +}; + +static void exynos4210_mct_register_types(void) +{ + type_register_static(&exynos4210_mct_info); +} + +type_init(exynos4210_mct_register_types) diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c new file mode 100644 index 0000000000..185ccb9a74 --- /dev/null +++ b/hw/timer/exynos4210_pwm.c @@ -0,0 +1,422 @@ +/* + * Samsung exynos4210 Pulse Width Modulation Timer + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. + * All rights reserved. + * + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "qemu-common.h" +#include "hw/ptimer.h" + +#include "hw/arm/exynos4210.h" + +//#define DEBUG_PWM + +#ifdef DEBUG_PWM +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define EXYNOS4210_PWM_TIMERS_NUM 5 +#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50 + +#define TCFG0 0x0000 +#define TCFG1 0x0004 +#define TCON 0x0008 +#define TCNTB0 0x000C +#define TCMPB0 0x0010 +#define TCNTO0 0x0014 +#define TCNTB1 0x0018 +#define TCMPB1 0x001C +#define TCNTO1 0x0020 +#define TCNTB2 0x0024 +#define TCMPB2 0x0028 +#define TCNTO2 0x002C +#define TCNTB3 0x0030 +#define TCMPB3 0x0034 +#define TCNTO3 0x0038 +#define TCNTB4 0x003C +#define TCNTO4 0x0040 +#define TINT_CSTAT 0x0044 + +#define TCNTB(x) (0xC * (x)) +#define TCMPB(x) (0xC * (x) + 1) +#define TCNTO(x) (0xC * (x) + 2) + +#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x)) +#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x)))) + +/* + * Attention! Timer4 doesn't have OUTPUT_INVERTER, + * so Auto Reload bit is not accessible by macros! + */ +#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x)) +#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0)) +#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1)) +#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2)) +#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3)) +#define TCON_TIMER4_AUTO_RELOAD (1 << 22) + +#define TINT_CSTAT_STATUS(x) (1 << (5 + (x))) +#define TINT_CSTAT_ENABLE(x) (1 << (x)) + +/* timer struct */ +typedef struct { + uint32_t id; /* timer id */ + qemu_irq irq; /* local timer irq */ + uint32_t freq; /* timer frequency */ + + /* use ptimer.c to represent count down timer */ + ptimer_state *ptimer; /* timer */ + + /* registers */ + uint32_t reg_tcntb; /* counter register buffer */ + uint32_t reg_tcmpb; /* compare register buffer */ + + struct Exynos4210PWMState *parent; + +} Exynos4210PWM; + + +typedef struct Exynos4210PWMState { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t reg_tcfg[2]; + uint32_t reg_tcon; + uint32_t reg_tint_cstat; + + Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM]; + +} Exynos4210PWMState; + +/*** VMState ***/ +static const VMStateDescription vmstate_exynos4210_pwm = { + .name = "exynos4210.pwm.pwm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, Exynos4210PWM), + VMSTATE_UINT32(freq, Exynos4210PWM), + VMSTATE_PTIMER(ptimer, Exynos4210PWM), + VMSTATE_UINT32(reg_tcntb, Exynos4210PWM), + VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_pwm_state = { + .name = "exynos4210.pwm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2), + VMSTATE_UINT32(reg_tcon, Exynos4210PWMState), + VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState), + VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState, + EXYNOS4210_PWM_TIMERS_NUM, 0, + vmstate_exynos4210_pwm, Exynos4210PWM), + VMSTATE_END_OF_LIST() + } +}; + +/* + * PWM update frequency + */ +static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id) +{ + uint32_t freq; + freq = s->timer[id].freq; + if (id > 1) { + s->timer[id].freq = 24000000 / + ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) * + (GET_DIVIDER(s->reg_tcfg[1], id))); + } else { + s->timer[id].freq = 24000000 / + ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) * + (GET_DIVIDER(s->reg_tcfg[1], id))); + } + + if (freq != s->timer[id].freq) { + ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq); + DPRINTF("freq=%dHz\n", s->timer[id].freq); + } +} + +/* + * Counter tick handler + */ +static void exynos4210_pwm_tick(void *opaque) +{ + Exynos4210PWM *s = (Exynos4210PWM *)opaque; + Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent; + uint32_t id = s->id; + bool cmp; + + DPRINTF("timer %d tick\n", id); + + /* set irq status */ + p->reg_tint_cstat |= TINT_CSTAT_STATUS(id); + + /* raise IRQ */ + if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) { + DPRINTF("timer %d IRQ\n", id); + qemu_irq_raise(p->timer[id].irq); + } + + /* reload timer */ + if (id != 4) { + cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id); + } else { + cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD; + } + + if (cmp) { + DPRINTF("auto reload timer %d count to %x\n", id, + p->timer[id].reg_tcntb); + ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb); + ptimer_run(p->timer[id].ptimer, 1); + } else { + /* stop timer, set status to STOP, see Basic Timer Operation */ + p->reg_tcon &= ~TCON_TIMER_START(id); + ptimer_stop(p->timer[id].ptimer); + } +} + +/* + * PWM Read + */ +static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset, + unsigned size) +{ + Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; + uint32_t value = 0; + int index; + + switch (offset) { + case TCFG0: case TCFG1: + index = (offset - TCFG0) >> 2; + value = s->reg_tcfg[index]; + break; + + case TCON: + value = s->reg_tcon; + break; + + case TCNTB0: case TCNTB1: + case TCNTB2: case TCNTB3: case TCNTB4: + index = (offset - TCNTB0) / 0xC; + value = s->timer[index].reg_tcntb; + break; + + case TCMPB0: case TCMPB1: + case TCMPB2: case TCMPB3: + index = (offset - TCMPB0) / 0xC; + value = s->timer[index].reg_tcmpb; + break; + + case TCNTO0: case TCNTO1: + case TCNTO2: case TCNTO3: case TCNTO4: + index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC; + value = ptimer_get_count(s->timer[index].ptimer); + break; + + case TINT_CSTAT: + value = s->reg_tint_cstat; + break; + + default: + fprintf(stderr, + "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n", + offset); + break; + } + return value; +} + +/* + * PWM Write + */ +static void exynos4210_pwm_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; + int index; + uint32_t new_val; + int i; + + switch (offset) { + case TCFG0: case TCFG1: + index = (offset - TCFG0) >> 2; + s->reg_tcfg[index] = value; + + /* update timers frequencies */ + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + exynos4210_pwm_update_freq(s, s->timer[i].id); + } + break; + + case TCON: + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + if ((value & TCON_TIMER_MANUAL_UPD(i)) > + (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) { + /* + * TCNTB and TCMPB are loaded into TCNT and TCMP. + * Update timers. + */ + + /* this will start timer to run, this ok, because + * during processing start bit timer will be stopped + * if needed */ + ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb); + DPRINTF("set timer %d count to %x\n", i, + s->timer[i].reg_tcntb); + } + + if ((value & TCON_TIMER_START(i)) > + (s->reg_tcon & TCON_TIMER_START(i))) { + /* changed to start */ + ptimer_run(s->timer[i].ptimer, 1); + DPRINTF("run timer %d\n", i); + } + + if ((value & TCON_TIMER_START(i)) < + (s->reg_tcon & TCON_TIMER_START(i))) { + /* changed to stop */ + ptimer_stop(s->timer[i].ptimer); + DPRINTF("stop timer %d\n", i); + } + } + s->reg_tcon = value; + break; + + case TCNTB0: case TCNTB1: + case TCNTB2: case TCNTB3: case TCNTB4: + index = (offset - TCNTB0) / 0xC; + s->timer[index].reg_tcntb = value; + break; + + case TCMPB0: case TCMPB1: + case TCMPB2: case TCMPB3: + index = (offset - TCMPB0) / 0xC; + s->timer[index].reg_tcmpb = value; + break; + + case TINT_CSTAT: + new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value); + new_val &= ~(0x3E0 & value); + + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + if ((new_val & TINT_CSTAT_STATUS(i)) < + (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) { + qemu_irq_lower(s->timer[i].irq); + } + } + + s->reg_tint_cstat = new_val; + break; + + default: + fprintf(stderr, + "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n", + offset); + break; + + } +} + +/* + * Set default values to timer fields and registers + */ +static void exynos4210_pwm_reset(DeviceState *d) +{ + Exynos4210PWMState *s = (Exynos4210PWMState *)d; + int i; + s->reg_tcfg[0] = 0x0101; + s->reg_tcfg[1] = 0x0; + s->reg_tcon = 0; + s->reg_tint_cstat = 0; + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + s->timer[i].reg_tcmpb = 0; + s->timer[i].reg_tcntb = 0; + + exynos4210_pwm_update_freq(s, s->timer[i].id); + ptimer_stop(s->timer[i].ptimer); + } +} + +static const MemoryRegionOps exynos4210_pwm_ops = { + .read = exynos4210_pwm_read, + .write = exynos4210_pwm_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* + * PWM timer initialization + */ +static int exynos4210_pwm_init(SysBusDevice *dev) +{ + Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev); + int i; + QEMUBH *bh; + + for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { + bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]); + sysbus_init_irq(dev, &s->timer[i].irq); + s->timer[i].ptimer = ptimer_init(bh); + s->timer[i].id = i; + s->timer[i].parent = s; + } + + memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm", + EXYNOS4210_PWM_REG_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_pwm_init; + dc->reset = exynos4210_pwm_reset; + dc->vmsd = &vmstate_exynos4210_pwm_state; +} + +static const TypeInfo exynos4210_pwm_info = { + .name = "exynos4210.pwm", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210PWMState), + .class_init = exynos4210_pwm_class_init, +}; + +static void exynos4210_pwm_register_types(void) +{ + type_register_static(&exynos4210_pwm_info); +} + +type_init(exynos4210_pwm_register_types) diff --git a/hw/timer/exynos4210_rtc.c b/hw/timer/exynos4210_rtc.c new file mode 100644 index 0000000000..bceee44cb2 --- /dev/null +++ b/hw/timer/exynos4210_rtc.c @@ -0,0 +1,592 @@ +/* + * Samsung exynos4210 Real Time Clock + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * Ogurtsov Oleg + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + */ + +/* Description: + * Register RTCCON: + * CLKSEL Bit[1] not used + * CLKOUTEN Bit[9] not used + */ + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "qemu-common.h" +#include "hw/ptimer.h" + +#include "hw/hw.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" + +#include "hw/arm/exynos4210.h" + +#define DEBUG_RTC 0 + +#if DEBUG_RTC +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \ + ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100 + +#define INTP 0x0030 +#define RTCCON 0x0040 +#define TICCNT 0x0044 +#define RTCALM 0x0050 +#define ALMSEC 0x0054 +#define ALMMIN 0x0058 +#define ALMHOUR 0x005C +#define ALMDAY 0x0060 +#define ALMMON 0x0064 +#define ALMYEAR 0x0068 +#define BCDSEC 0x0070 +#define BCDMIN 0x0074 +#define BCDHOUR 0x0078 +#define BCDDAY 0x007C +#define BCDDAYWEEK 0x0080 +#define BCDMON 0x0084 +#define BCDYEAR 0x0088 +#define CURTICNT 0x0090 + +#define TICK_TIMER_ENABLE 0x0100 +#define TICNT_THRESHHOLD 2 + + +#define RTC_ENABLE 0x0001 + +#define INTP_TICK_ENABLE 0x0001 +#define INTP_ALM_ENABLE 0x0002 + +#define ALARM_INT_ENABLE 0x0040 + +#define RTC_BASE_FREQ 32768 + +typedef struct Exynos4210RTCState { + SysBusDevice busdev; + MemoryRegion iomem; + + /* registers */ + uint32_t reg_intp; + uint32_t reg_rtccon; + uint32_t reg_ticcnt; + uint32_t reg_rtcalm; + uint32_t reg_almsec; + uint32_t reg_almmin; + uint32_t reg_almhour; + uint32_t reg_almday; + uint32_t reg_almmon; + uint32_t reg_almyear; + uint32_t reg_curticcnt; + + ptimer_state *ptimer; /* tick timer */ + ptimer_state *ptimer_1Hz; /* clock timer */ + uint32_t freq; + + qemu_irq tick_irq; /* Time Tick Generator irq */ + qemu_irq alm_irq; /* alarm irq */ + + struct tm current_tm; /* current time */ +} Exynos4210RTCState; + +#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4) + +/*** VMState ***/ +static const VMStateDescription vmstate_exynos4210_rtc_state = { + .name = "exynos4210.rtc", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(reg_intp, Exynos4210RTCState), + VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState), + VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState), + VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState), + VMSTATE_UINT32(reg_almsec, Exynos4210RTCState), + VMSTATE_UINT32(reg_almmin, Exynos4210RTCState), + VMSTATE_UINT32(reg_almhour, Exynos4210RTCState), + VMSTATE_UINT32(reg_almday, Exynos4210RTCState), + VMSTATE_UINT32(reg_almmon, Exynos4210RTCState), + VMSTATE_UINT32(reg_almyear, Exynos4210RTCState), + VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState), + VMSTATE_PTIMER(ptimer, Exynos4210RTCState), + VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState), + VMSTATE_UINT32(freq, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState), + VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState), + VMSTATE_END_OF_LIST() + } +}; + +#define BCD3DIGITS(x) \ + ((uint32_t)to_bcd((uint8_t)(x % 100)) + \ + ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8)) + +static void check_alarm_raise(Exynos4210RTCState *s) +{ + unsigned int alarm_raise = 0; + struct tm stm = s->current_tm; + + if ((s->reg_rtcalm & 0x01) && + (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) { + alarm_raise = 1; + } + if ((s->reg_rtcalm & 0x02) && + (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) { + alarm_raise = 1; + } + if ((s->reg_rtcalm & 0x04) && + (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) { + alarm_raise = 1; + } + if ((s->reg_rtcalm & 0x08) && + (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) { + alarm_raise = 1; + } + if ((s->reg_rtcalm & 0x10) && + (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) { + alarm_raise = 1; + } + if ((s->reg_rtcalm & 0x20) && + (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) { + alarm_raise = 1; + } + + if (alarm_raise) { + DPRINTF("ALARM IRQ\n"); + /* set irq status */ + s->reg_intp |= INTP_ALM_ENABLE; + qemu_irq_raise(s->alm_irq); + } +} + +/* + * RTC update frequency + * Parameters: + * reg_value - current RTCCON register or his new value + */ +static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, + uint32_t reg_value) +{ + uint32_t freq; + + freq = s->freq; + /* set frequncy for time generator */ + s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value)); + + if (freq != s->freq) { + ptimer_set_freq(s->ptimer, s->freq); + DPRINTF("freq=%dHz\n", s->freq); + } +} + +/* month is between 0 and 11. */ +static int get_days_in_month(int month, int year) +{ + static const int days_tab[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + int d; + if ((unsigned)month >= 12) { + return 31; + } + d = days_tab[month]; + if (month == 1) { + if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) { + d++; + } + } + return d; +} + +/* update 'tm' to the next second */ +static void rtc_next_second(struct tm *tm) +{ + int days_in_month; + + tm->tm_sec++; + if ((unsigned)tm->tm_sec >= 60) { + tm->tm_sec = 0; + tm->tm_min++; + if ((unsigned)tm->tm_min >= 60) { + tm->tm_min = 0; + tm->tm_hour++; + if ((unsigned)tm->tm_hour >= 24) { + tm->tm_hour = 0; + /* next day */ + tm->tm_wday++; + if ((unsigned)tm->tm_wday >= 7) { + tm->tm_wday = 0; + } + days_in_month = get_days_in_month(tm->tm_mon, + tm->tm_year + 1900); + tm->tm_mday++; + if (tm->tm_mday < 1) { + tm->tm_mday = 1; + } else if (tm->tm_mday > days_in_month) { + tm->tm_mday = 1; + tm->tm_mon++; + if (tm->tm_mon >= 12) { + tm->tm_mon = 0; + tm->tm_year++; + } + } + } + } + } +} + +/* + * tick handler + */ +static void exynos4210_rtc_tick(void *opaque) +{ + Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; + + DPRINTF("TICK IRQ\n"); + /* set irq status */ + s->reg_intp |= INTP_TICK_ENABLE; + /* raise IRQ */ + qemu_irq_raise(s->tick_irq); + + /* restart timer */ + ptimer_set_count(s->ptimer, s->reg_ticcnt); + ptimer_run(s->ptimer, 1); +} + +/* + * 1Hz clock handler + */ +static void exynos4210_rtc_1Hz_tick(void *opaque) +{ + Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; + + rtc_next_second(&s->current_tm); + /* DPRINTF("1Hz tick\n"); */ + + /* raise IRQ */ + if (s->reg_rtcalm & ALARM_INT_ENABLE) { + check_alarm_raise(s); + } + + ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); + ptimer_run(s->ptimer_1Hz, 1); +} + +/* + * RTC Read + */ +static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t value = 0; + Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; + + switch (offset) { + case INTP: + value = s->reg_intp; + break; + case RTCCON: + value = s->reg_rtccon; + break; + case TICCNT: + value = s->reg_ticcnt; + break; + case RTCALM: + value = s->reg_rtcalm; + break; + case ALMSEC: + value = s->reg_almsec; + break; + case ALMMIN: + value = s->reg_almmin; + break; + case ALMHOUR: + value = s->reg_almhour; + break; + case ALMDAY: + value = s->reg_almday; + break; + case ALMMON: + value = s->reg_almmon; + break; + case ALMYEAR: + value = s->reg_almyear; + break; + + case BCDSEC: + value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec); + break; + case BCDMIN: + value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min); + break; + case BCDHOUR: + value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour); + break; + case BCDDAYWEEK: + value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday); + break; + case BCDDAY: + value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday); + break; + case BCDMON: + value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1); + break; + case BCDYEAR: + value = BCD3DIGITS(s->current_tm.tm_year); + break; + + case CURTICNT: + s->reg_curticcnt = ptimer_get_count(s->ptimer); + value = s->reg_curticcnt; + break; + + default: + fprintf(stderr, + "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n", + offset); + break; + } + return value; +} + +/* + * RTC Write + */ +static void exynos4210_rtc_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; + + switch (offset) { + case INTP: + if (value & INTP_ALM_ENABLE) { + qemu_irq_lower(s->alm_irq); + s->reg_intp &= (~INTP_ALM_ENABLE); + } + if (value & INTP_TICK_ENABLE) { + qemu_irq_lower(s->tick_irq); + s->reg_intp &= (~INTP_TICK_ENABLE); + } + break; + case RTCCON: + if (value & RTC_ENABLE) { + exynos4210_rtc_update_freq(s, value); + } + if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) { + /* clock timer */ + ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); + ptimer_run(s->ptimer_1Hz, 1); + DPRINTF("run clock timer\n"); + } + if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) { + /* tick timer */ + ptimer_stop(s->ptimer); + /* clock timer */ + ptimer_stop(s->ptimer_1Hz); + DPRINTF("stop all timers\n"); + } + if (value & RTC_ENABLE) { + if ((value & TICK_TIMER_ENABLE) > + (s->reg_rtccon & TICK_TIMER_ENABLE) && + (s->reg_ticcnt)) { + ptimer_set_count(s->ptimer, s->reg_ticcnt); + ptimer_run(s->ptimer, 1); + DPRINTF("run tick timer\n"); + } + if ((value & TICK_TIMER_ENABLE) < + (s->reg_rtccon & TICK_TIMER_ENABLE)) { + ptimer_stop(s->ptimer); + } + } + s->reg_rtccon = value; + break; + case TICCNT: + if (value > TICNT_THRESHHOLD) { + s->reg_ticcnt = value; + } else { + fprintf(stderr, + "[exynos4210.rtc: bad TICNT value %u ]\n", + (uint32_t)value); + } + break; + + case RTCALM: + s->reg_rtcalm = value; + break; + case ALMSEC: + s->reg_almsec = (value & 0x7f); + break; + case ALMMIN: + s->reg_almmin = (value & 0x7f); + break; + case ALMHOUR: + s->reg_almhour = (value & 0x3f); + break; + case ALMDAY: + s->reg_almday = (value & 0x3f); + break; + case ALMMON: + s->reg_almmon = (value & 0x1f); + break; + case ALMYEAR: + s->reg_almyear = (value & 0x0fff); + break; + + case BCDSEC: + if (s->reg_rtccon & RTC_ENABLE) { + s->current_tm.tm_sec = (int)from_bcd((uint8_t)value); + } + break; + case BCDMIN: + if (s->reg_rtccon & RTC_ENABLE) { + s->current_tm.tm_min = (int)from_bcd((uint8_t)value); + } + break; + case BCDHOUR: + if (s->reg_rtccon & RTC_ENABLE) { + s->current_tm.tm_hour = (int)from_bcd((uint8_t)value); + } + break; + case BCDDAYWEEK: + if (s->reg_rtccon & RTC_ENABLE) { + s->current_tm.tm_wday = (int)from_bcd((uint8_t)value); + } + break; + case BCDDAY: + if (s->reg_rtccon & RTC_ENABLE) { + s->current_tm.tm_mday = (int)from_bcd((uint8_t)value); + } + break; + case BCDMON: + if (s->reg_rtccon & RTC_ENABLE) { + s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1; + } + break; + case BCDYEAR: + if (s->reg_rtccon & RTC_ENABLE) { + /* 3 digits */ + s->current_tm.tm_year = (int)from_bcd((uint8_t)value) + + (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100; + } + break; + + default: + fprintf(stderr, + "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n", + offset); + break; + + } +} + +/* + * Set default values to timer fields and registers + */ +static void exynos4210_rtc_reset(DeviceState *d) +{ + Exynos4210RTCState *s = (Exynos4210RTCState *)d; + + qemu_get_timedate(&s->current_tm, 0); + + DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n", + s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday, + s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec); + + s->reg_intp = 0; + s->reg_rtccon = 0; + s->reg_ticcnt = 0; + s->reg_rtcalm = 0; + s->reg_almsec = 0; + s->reg_almmin = 0; + s->reg_almhour = 0; + s->reg_almday = 0; + s->reg_almmon = 0; + s->reg_almyear = 0; + + s->reg_curticcnt = 0; + + exynos4210_rtc_update_freq(s, s->reg_rtccon); + ptimer_stop(s->ptimer); + ptimer_stop(s->ptimer_1Hz); +} + +static const MemoryRegionOps exynos4210_rtc_ops = { + .read = exynos4210_rtc_read, + .write = exynos4210_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* + * RTC timer initialization + */ +static int exynos4210_rtc_init(SysBusDevice *dev) +{ + Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev); + QEMUBH *bh; + + bh = qemu_bh_new(exynos4210_rtc_tick, s); + s->ptimer = ptimer_init(bh); + ptimer_set_freq(s->ptimer, RTC_BASE_FREQ); + exynos4210_rtc_update_freq(s, 0); + + bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s); + s->ptimer_1Hz = ptimer_init(bh); + ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ); + + sysbus_init_irq(dev, &s->alm_irq); + sysbus_init_irq(dev, &s->tick_irq); + + memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc", + EXYNOS4210_RTC_REG_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_rtc_init; + dc->reset = exynos4210_rtc_reset; + dc->vmsd = &vmstate_exynos4210_rtc_state; +} + +static const TypeInfo exynos4210_rtc_info = { + .name = "exynos4210.rtc", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210RTCState), + .class_init = exynos4210_rtc_class_init, +}; + +static void exynos4210_rtc_register_types(void) +{ + type_register_static(&exynos4210_rtc_info); +} + +type_init(exynos4210_rtc_register_types) diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c new file mode 100644 index 0000000000..7043a34684 --- /dev/null +++ b/hw/timer/grlib_gptimer.c @@ -0,0 +1,404 @@ +/* + * QEMU GRLIB GPTimer Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" + +#include "trace.h" + +#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ +#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ + +#define GPTIMER_MAX_TIMERS 8 + +/* GPTimer Config register fields */ +#define GPTIMER_ENABLE (1 << 0) +#define GPTIMER_RESTART (1 << 1) +#define GPTIMER_LOAD (1 << 2) +#define GPTIMER_INT_ENABLE (1 << 3) +#define GPTIMER_INT_PENDING (1 << 4) +#define GPTIMER_CHAIN (1 << 5) /* Not supported */ +#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ + +/* Memory mapped register offsets */ +#define SCALER_OFFSET 0x00 +#define SCALER_RELOAD_OFFSET 0x04 +#define CONFIG_OFFSET 0x08 +#define COUNTER_OFFSET 0x00 +#define COUNTER_RELOAD_OFFSET 0x04 +#define TIMER_BASE 0x10 + +typedef struct GPTimer GPTimer; +typedef struct GPTimerUnit GPTimerUnit; + +struct GPTimer { + QEMUBH *bh; + struct ptimer_state *ptimer; + + qemu_irq irq; + int id; + GPTimerUnit *unit; + + /* registers */ + uint32_t counter; + uint32_t reload; + uint32_t config; +}; + +struct GPTimerUnit { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t nr_timers; /* Number of timers available */ + uint32_t freq_hz; /* System frequency */ + uint32_t irq_line; /* Base irq line */ + + GPTimer *timers; + + /* registers */ + uint32_t scaler; + uint32_t reload; + uint32_t config; +}; + +static void grlib_gptimer_enable(GPTimer *timer) +{ + assert(timer != NULL); + + + ptimer_stop(timer->ptimer); + + if (!(timer->config & GPTIMER_ENABLE)) { + /* Timer disabled */ + trace_grlib_gptimer_disabled(timer->id, timer->config); + return; + } + + /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at + underflow. Set count + 1 to simulate the GPTimer behavior. */ + + trace_grlib_gptimer_enable(timer->id, timer->counter + 1); + + ptimer_set_count(timer->ptimer, timer->counter + 1); + ptimer_run(timer->ptimer, 1); +} + +static void grlib_gptimer_restart(GPTimer *timer) +{ + assert(timer != NULL); + + trace_grlib_gptimer_restart(timer->id, timer->reload); + + timer->counter = timer->reload; + grlib_gptimer_enable(timer); +} + +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) +{ + int i = 0; + uint32_t value = 0; + + assert(unit != NULL); + + if (scaler > 0) { + value = unit->freq_hz / (scaler + 1); + } else { + value = unit->freq_hz; + } + + trace_grlib_gptimer_set_scaler(scaler, value); + + for (i = 0; i < unit->nr_timers; i++) { + ptimer_set_freq(unit->timers[i].ptimer, value); + } +} + +static void grlib_gptimer_hit(void *opaque) +{ + GPTimer *timer = opaque; + assert(timer != NULL); + + trace_grlib_gptimer_hit(timer->id); + + /* Timer expired */ + + if (timer->config & GPTIMER_INT_ENABLE) { + /* Set the pending bit (only unset by write in the config register) */ + timer->config |= GPTIMER_INT_PENDING; + qemu_irq_pulse(timer->irq); + } + + if (timer->config & GPTIMER_RESTART) { + grlib_gptimer_restart(timer); + } +} + +static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, + unsigned size) +{ + GPTimerUnit *unit = opaque; + hwaddr timer_addr; + int id; + uint32_t value = 0; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case SCALER_OFFSET: + trace_grlib_gptimer_readl(-1, addr, unit->scaler); + return unit->scaler; + + case SCALER_RELOAD_OFFSET: + trace_grlib_gptimer_readl(-1, addr, unit->reload); + return unit->reload; + + case CONFIG_OFFSET: + trace_grlib_gptimer_readl(-1, addr, unit->config); + return unit->config; + + default: + break; + } + + timer_addr = (addr % TIMER_BASE); + id = (addr - TIMER_BASE) / TIMER_BASE; + + if (id >= 0 && id < unit->nr_timers) { + + /* GPTimer registers */ + switch (timer_addr) { + case COUNTER_OFFSET: + value = ptimer_get_count(unit->timers[id].ptimer); + trace_grlib_gptimer_readl(id, addr, value); + return value; + + case COUNTER_RELOAD_OFFSET: + value = unit->timers[id].reload; + trace_grlib_gptimer_readl(id, addr, value); + return value; + + case CONFIG_OFFSET: + trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); + return unit->timers[id].config; + + default: + break; + } + + } + + trace_grlib_gptimer_readl(-1, addr, 0); + return 0; +} + +static void grlib_gptimer_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + GPTimerUnit *unit = opaque; + hwaddr timer_addr; + int id; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case SCALER_OFFSET: + value &= 0xFFFF; /* clean up the value */ + unit->scaler = value; + trace_grlib_gptimer_writel(-1, addr, unit->scaler); + return; + + case SCALER_RELOAD_OFFSET: + value &= 0xFFFF; /* clean up the value */ + unit->reload = value; + trace_grlib_gptimer_writel(-1, addr, unit->reload); + grlib_gptimer_set_scaler(unit, value); + return; + + case CONFIG_OFFSET: + /* Read Only (disable timer freeze not supported) */ + trace_grlib_gptimer_writel(-1, addr, 0); + return; + + default: + break; + } + + timer_addr = (addr % TIMER_BASE); + id = (addr - TIMER_BASE) / TIMER_BASE; + + if (id >= 0 && id < unit->nr_timers) { + + /* GPTimer registers */ + switch (timer_addr) { + case COUNTER_OFFSET: + trace_grlib_gptimer_writel(id, addr, value); + unit->timers[id].counter = value; + grlib_gptimer_enable(&unit->timers[id]); + return; + + case COUNTER_RELOAD_OFFSET: + trace_grlib_gptimer_writel(id, addr, value); + unit->timers[id].reload = value; + return; + + case CONFIG_OFFSET: + trace_grlib_gptimer_writel(id, addr, value); + + if (value & GPTIMER_INT_PENDING) { + /* clear pending bit */ + value &= ~GPTIMER_INT_PENDING; + } else { + /* keep pending bit */ + value |= unit->timers[id].config & GPTIMER_INT_PENDING; + } + + unit->timers[id].config = value; + + /* gptimer_restart calls gptimer_enable, so if "enable" and "load" + bits are present, we just have to call restart. */ + + if (value & GPTIMER_LOAD) { + grlib_gptimer_restart(&unit->timers[id]); + } else if (value & GPTIMER_ENABLE) { + grlib_gptimer_enable(&unit->timers[id]); + } + + /* These fields must always be read as 0 */ + value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); + + unit->timers[id].config = value; + return; + + default: + break; + } + + } + + trace_grlib_gptimer_writel(-1, addr, value); +} + +static const MemoryRegionOps grlib_gptimer_ops = { + .read = grlib_gptimer_read, + .write = grlib_gptimer_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void grlib_gptimer_reset(DeviceState *d) +{ + GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev); + int i = 0; + + assert(unit != NULL); + + unit->scaler = 0; + unit->reload = 0; + unit->config = 0; + + unit->config = unit->nr_timers; + unit->config |= unit->irq_line << 3; + unit->config |= 1 << 8; /* separate interrupt */ + unit->config |= 1 << 9; /* Disable timer freeze */ + + + for (i = 0; i < unit->nr_timers; i++) { + GPTimer *timer = &unit->timers[i]; + + timer->counter = 0; + timer->reload = 0; + timer->config = 0; + ptimer_stop(timer->ptimer); + ptimer_set_count(timer->ptimer, 0); + ptimer_set_freq(timer->ptimer, unit->freq_hz); + } +} + +static int grlib_gptimer_init(SysBusDevice *dev) +{ + GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev); + unsigned int i; + + assert(unit->nr_timers > 0); + assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); + + unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); + + for (i = 0; i < unit->nr_timers; i++) { + GPTimer *timer = &unit->timers[i]; + + timer->unit = unit; + timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); + timer->ptimer = ptimer_init(timer->bh); + timer->id = i; + + /* One IRQ line for each timer */ + sysbus_init_irq(dev, &timer->irq); + + ptimer_set_freq(timer->ptimer, unit->freq_hz); + } + + memory_region_init_io(&unit->iomem, &grlib_gptimer_ops, unit, "gptimer", + UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); + + sysbus_init_mmio(dev, &unit->iomem); + return 0; +} + +static Property grlib_gptimer_properties[] = { + DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), + DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), + DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), + DEFINE_PROP_END_OF_LIST(), +}; + +static void grlib_gptimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = grlib_gptimer_init; + dc->reset = grlib_gptimer_reset; + dc->props = grlib_gptimer_properties; +} + +static const TypeInfo grlib_gptimer_info = { + .name = "grlib,gptimer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GPTimerUnit), + .class_init = grlib_gptimer_class_init, +}; + +static void grlib_gptimer_register_types(void) +{ + type_register_static(&grlib_gptimer_info); +} + +type_init(grlib_gptimer_register_types) diff --git a/hw/timer/imx_timer.c b/hw/timer/imx_timer.c new file mode 100644 index 0000000000..03197e3f54 --- /dev/null +++ b/hw/timer/imx_timer.c @@ -0,0 +1,689 @@ +/* + * IMX31 Timer + * + * Copyright (c) 2008 OK Labs + * Copyright (c) 2011 NICTA Pty Ltd + * Originally written by Hans Jiang + * Updated by Peter Chubb + * + * This code is licensed under GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ + +#include "hw/hw.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" +#include "hw/arm/imx.h" + +//#define DEBUG_TIMER 1 +#ifdef DEBUG_TIMER +# define DPRINTF(fmt, args...) \ + do { printf("imx_timer: " fmt , ##args); } while (0) +#else +# define DPRINTF(fmt, args...) do {} while (0) +#endif + +/* + * Define to 1 for messages about attempts to + * access unimplemented registers or similar. + */ +#define DEBUG_IMPLEMENTATION 1 +#if DEBUG_IMPLEMENTATION +# define IPRINTF(fmt, args...) \ + do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0) +#else +# define IPRINTF(fmt, args...) do {} while (0) +#endif + +/* + * GPT : General purpose timer + * + * This timer counts up continuously while it is enabled, resetting itself + * to 0 when it reaches TIMER_MAX (in freerun mode) or when it + * reaches the value of ocr1 (in periodic mode). WE simulate this using a + * QEMU ptimer counting down from ocr1 and reloading from ocr1 in + * periodic mode, or counting from ocr1 to zero, then TIMER_MAX - ocr1. + * waiting_rov is set when counting from TIMER_MAX. + * + * In the real hardware, there are three comparison registers that can + * trigger interrupts, and compare channel 1 can be used to + * force-reset the timer. However, this is a `bare-bones' + * implementation: only what Linux 3.x uses has been implemented + * (free-running timer from 0 to OCR1 or TIMER_MAX) . + */ + + +#define TIMER_MAX 0XFFFFFFFFUL + +/* Control register. Not all of these bits have any effect (yet) */ +#define GPT_CR_EN (1 << 0) /* GPT Enable */ +#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */ +#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */ +#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */ +#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */ +#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */ +#define GPT_CR_CLKSRC_SHIFT (6) +#define GPT_CR_CLKSRC_MASK (0x7) + +#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */ +#define GPT_CR_SWR (1 << 15) /* Software Reset */ +#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */ +#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */ +#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */ +#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */ +#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */ +#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */ +#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */ +#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */ + +#define GPT_SR_OF1 (1 << 0) +#define GPT_SR_ROV (1 << 5) + +#define GPT_IR_OF1IE (1 << 0) +#define GPT_IR_ROVIE (1 << 5) + +typedef struct { + SysBusDevice busdev; + ptimer_state *timer; + MemoryRegion iomem; + DeviceState *ccm; + + uint32_t cr; + uint32_t pr; + uint32_t sr; + uint32_t ir; + uint32_t ocr1; + uint32_t cnt; + + uint32_t waiting_rov; + qemu_irq irq; +} IMXTimerGState; + +static const VMStateDescription vmstate_imx_timerg = { + .name = "imx-timerg", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, IMXTimerGState), + VMSTATE_UINT32(pr, IMXTimerGState), + VMSTATE_UINT32(sr, IMXTimerGState), + VMSTATE_UINT32(ir, IMXTimerGState), + VMSTATE_UINT32(ocr1, IMXTimerGState), + VMSTATE_UINT32(cnt, IMXTimerGState), + VMSTATE_UINT32(waiting_rov, IMXTimerGState), + VMSTATE_PTIMER(timer, IMXTimerGState), + VMSTATE_END_OF_LIST() + } +}; + +static const IMXClk imx_timerg_clocks[] = { + NOCLK, /* 000 No clock source */ + IPG, /* 001 ipg_clk, 532MHz*/ + IPG, /* 010 ipg_clk_highfreq */ + NOCLK, /* 011 not defined */ + CLK_32k, /* 100 ipg_clk_32k */ + NOCLK, /* 101 not defined */ + NOCLK, /* 110 not defined */ + NOCLK, /* 111 not defined */ +}; + + +static void imx_timerg_set_freq(IMXTimerGState *s) +{ + int clksrc; + uint32_t freq; + + clksrc = (s->cr >> GPT_CR_CLKSRC_SHIFT) & GPT_CR_CLKSRC_MASK; + freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc]) / (1 + s->pr); + + DPRINTF("Setting gtimer clksrc %d to frequency %d\n", clksrc, freq); + if (freq) { + ptimer_set_freq(s->timer, freq); + } +} + +static void imx_timerg_update(IMXTimerGState *s) +{ + uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV); + + DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n", + s->sr & GPT_SR_OF1 ? "OF1" : "", + s->sr & GPT_SR_ROV ? "ROV" : "", + s->ir & GPT_SR_OF1 ? "OF1" : "", + s->ir & GPT_SR_ROV ? "ROV" : "", + s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled"); + + + qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags); +} + +static uint32_t imx_timerg_update_counts(IMXTimerGState *s) +{ + uint64_t target = s->waiting_rov ? TIMER_MAX : s->ocr1; + uint64_t cnt = ptimer_get_count(s->timer); + s->cnt = target - cnt; + return s->cnt; +} + +static void imx_timerg_reload(IMXTimerGState *s, uint32_t timeout) +{ + uint64_t diff_cnt; + + if (!(s->cr & GPT_CR_FRR)) { + IPRINTF("IMX_timerg_reload --- called in reset-mode\n"); + return; + } + + /* + * For small timeouts, qemu sometimes runs too slow. + * Better deliver a late interrupt than none. + * + * In Reset mode (FRR bit clear) + * the ptimer reloads itself from OCR1; + * in free-running mode we need to fake + * running from 0 to ocr1 to TIMER_MAX + */ + if (timeout > s->cnt) { + diff_cnt = timeout - s->cnt; + } else { + diff_cnt = 0; + } + ptimer_set_count(s->timer, diff_cnt); +} + +static uint64_t imx_timerg_read(void *opaque, hwaddr offset, + unsigned size) +{ + IMXTimerGState *s = (IMXTimerGState *)opaque; + + DPRINTF("g-read(offset=%x)", offset >> 2); + switch (offset >> 2) { + case 0: /* Control Register */ + DPRINTF(" cr = %x\n", s->cr); + return s->cr; + + case 1: /* prescaler */ + DPRINTF(" pr = %x\n", s->pr); + return s->pr; + + case 2: /* Status Register */ + DPRINTF(" sr = %x\n", s->sr); + return s->sr; + + case 3: /* Interrupt Register */ + DPRINTF(" ir = %x\n", s->ir); + return s->ir; + + case 4: /* Output Compare Register 1 */ + DPRINTF(" ocr1 = %x\n", s->ocr1); + return s->ocr1; + + + case 9: /* cnt */ + imx_timerg_update_counts(s); + DPRINTF(" cnt = %x\n", s->cnt); + return s->cnt; + } + + IPRINTF("imx_timerg_read: Bad offset %x\n", + (int)offset >> 2); + return 0; +} + +static void imx_timerg_reset(DeviceState *dev) +{ + IMXTimerGState *s = container_of(dev, IMXTimerGState, busdev.qdev); + + /* + * Soft reset doesn't touch some bits; hard reset clears them + */ + s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); + s->sr = 0; + s->pr = 0; + s->ir = 0; + s->cnt = 0; + s->ocr1 = TIMER_MAX; + ptimer_stop(s->timer); + ptimer_set_limit(s->timer, TIMER_MAX, 1); + imx_timerg_set_freq(s); +} + +static void imx_timerg_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMXTimerGState *s = (IMXTimerGState *)opaque; + DPRINTF("g-write(offset=%x, value = 0x%x)\n", (unsigned int)offset >> 2, + (unsigned int)value); + + switch (offset >> 2) { + case 0: { + uint32_t oldcr = s->cr; + /* CR */ + if (value & GPT_CR_SWR) { /* force reset */ + value &= ~GPT_CR_SWR; + imx_timerg_reset(&s->busdev.qdev); + imx_timerg_update(s); + } + + s->cr = value & ~0x7c00; + imx_timerg_set_freq(s); + if ((oldcr ^ value) & GPT_CR_EN) { + if (value & GPT_CR_EN) { + if (value & GPT_CR_ENMOD) { + ptimer_set_count(s->timer, s->ocr1); + s->cnt = 0; + } + ptimer_run(s->timer, + (value & GPT_CR_FRR) && (s->ocr1 != TIMER_MAX)); + } else { + ptimer_stop(s->timer); + }; + } + return; + } + + case 1: /* Prescaler */ + s->pr = value & 0xfff; + imx_timerg_set_freq(s); + return; + + case 2: /* SR */ + /* + * No point in implementing the status register bits to do with + * external interrupt sources. + */ + value &= GPT_SR_OF1 | GPT_SR_ROV; + s->sr &= ~value; + imx_timerg_update(s); + return; + + case 3: /* IR -- interrupt register */ + s->ir = value & 0x3f; + imx_timerg_update(s); + return; + + case 4: /* OCR1 -- output compare register */ + /* In non-freerun mode, reset count when this register is written */ + if (!(s->cr & GPT_CR_FRR)) { + s->waiting_rov = 0; + ptimer_set_limit(s->timer, value, 1); + } else { + imx_timerg_update_counts(s); + if (value > s->cnt) { + s->waiting_rov = 0; + imx_timerg_reload(s, value); + } else { + s->waiting_rov = 1; + imx_timerg_reload(s, TIMER_MAX - s->cnt); + } + } + s->ocr1 = value; + return; + + default: + IPRINTF("imx_timerg_write: Bad offset %x\n", + (int)offset >> 2); + } +} + +static void imx_timerg_timeout(void *opaque) +{ + IMXTimerGState *s = (IMXTimerGState *)opaque; + + DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov); + if (s->cr & GPT_CR_FRR) { + /* + * Free running timer from 0 -> TIMERMAX + * Generates interrupt at TIMER_MAX and at cnt==ocr1 + * If ocr1 == TIMER_MAX, then no need to reload timer. + */ + if (s->ocr1 == TIMER_MAX) { + DPRINTF("s->ocr1 == TIMER_MAX, FRR\n"); + s->sr |= GPT_SR_OF1 | GPT_SR_ROV; + imx_timerg_update(s); + return; + } + + if (s->waiting_rov) { + /* + * We were waiting for cnt==TIMER_MAX + */ + s->sr |= GPT_SR_ROV; + s->waiting_rov = 0; + s->cnt = 0; + imx_timerg_reload(s, s->ocr1); + } else { + /* Must have got a cnt==ocr1 timeout. */ + s->sr |= GPT_SR_OF1; + s->cnt = s->ocr1; + s->waiting_rov = 1; + imx_timerg_reload(s, TIMER_MAX); + } + imx_timerg_update(s); + return; + } + + s->sr |= GPT_SR_OF1; + imx_timerg_update(s); +} + +static const MemoryRegionOps imx_timerg_ops = { + .read = imx_timerg_read, + .write = imx_timerg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + + +static int imx_timerg_init(SysBusDevice *dev) +{ + IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev); + QEMUBH *bh; + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->iomem, &imx_timerg_ops, + s, "imxg-timer", + 0x00001000); + sysbus_init_mmio(dev, &s->iomem); + + bh = qemu_bh_new(imx_timerg_timeout, s); + s->timer = ptimer_init(bh); + + /* Hard reset resets extra bits in CR */ + s->cr = 0; + return 0; +} + + + +/* + * EPIT: Enhanced periodic interrupt timer + */ + +#define CR_EN (1 << 0) +#define CR_ENMOD (1 << 1) +#define CR_OCIEN (1 << 2) +#define CR_RLD (1 << 3) +#define CR_PRESCALE_SHIFT (4) +#define CR_PRESCALE_MASK (0xfff) +#define CR_SWR (1 << 16) +#define CR_IOVW (1 << 17) +#define CR_DBGEN (1 << 18) +#define CR_EPIT (1 << 19) +#define CR_DOZEN (1 << 20) +#define CR_STOPEN (1 << 21) +#define CR_CLKSRC_SHIFT (24) +#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT) + + +/* + * Exact clock frequencies vary from board to board. + * These are typical. + */ +static const IMXClk imx_timerp_clocks[] = { + 0, /* disabled */ + IPG, /* ipg_clk, ~532MHz */ + IPG, /* ipg_clk_highfreq */ + CLK_32k, /* ipg_clk_32k -- ~32kHz */ +}; + +typedef struct { + SysBusDevice busdev; + ptimer_state *timer; + MemoryRegion iomem; + DeviceState *ccm; + + uint32_t cr; + uint32_t lr; + uint32_t cmp; + + uint32_t freq; + int int_level; + qemu_irq irq; +} IMXTimerPState; + +/* + * Update interrupt status + */ +static void imx_timerp_update(IMXTimerPState *s) +{ + if (s->int_level && (s->cr & CR_OCIEN)) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void imx_timerp_reset(DeviceState *dev) +{ + IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev); + + s->cr = 0; + s->lr = TIMER_MAX; + s->int_level = 0; + s->cmp = 0; + ptimer_stop(s->timer); + ptimer_set_count(s->timer, TIMER_MAX); +} + +static uint64_t imx_timerp_read(void *opaque, hwaddr offset, + unsigned size) +{ + IMXTimerPState *s = (IMXTimerPState *)opaque; + + DPRINTF("p-read(offset=%x)", offset >> 2); + switch (offset >> 2) { + case 0: /* Control Register */ + DPRINTF("cr %x\n", s->cr); + return s->cr; + + case 1: /* Status Register */ + DPRINTF("int_level %x\n", s->int_level); + return s->int_level; + + case 2: /* LR - ticks*/ + DPRINTF("lr %x\n", s->lr); + return s->lr; + + case 3: /* CMP */ + DPRINTF("cmp %x\n", s->cmp); + return s->cmp; + + case 4: /* CNT */ + return ptimer_get_count(s->timer); + } + IPRINTF("imx_timerp_read: Bad offset %x\n", + (int)offset >> 2); + return 0; +} + +static void set_timerp_freq(IMXTimerPState *s) +{ + int clksrc; + unsigned prescaler; + uint32_t freq; + + clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT; + prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK); + freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler; + + s->freq = freq; + DPRINTF("Setting ptimer frequency to %u\n", freq); + + if (freq) { + ptimer_set_freq(s->timer, freq); + } +} + +static void imx_timerp_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMXTimerPState *s = (IMXTimerPState *)opaque; + DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2, + (unsigned int)value); + + switch (offset >> 2) { + case 0: /* CR */ + if (value & CR_SWR) { + imx_timerp_reset(&s->busdev.qdev); + value &= ~CR_SWR; + } + s->cr = value & 0x03ffffff; + set_timerp_freq(s); + + if (s->freq && (s->cr & CR_EN)) { + if (!(s->cr & CR_ENMOD)) { + ptimer_set_count(s->timer, s->lr); + } + ptimer_run(s->timer, 0); + } else { + ptimer_stop(s->timer); + } + break; + + case 1: /* SR - ACK*/ + s->int_level = 0; + imx_timerp_update(s); + break; + + case 2: /* LR - set ticks */ + s->lr = value; + ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW)); + break; + + case 3: /* CMP */ + s->cmp = value; + if (value) { + IPRINTF( + "Values for EPIT comparison other than zero not supported\n" + ); + } + break; + + default: + IPRINTF("imx_timerp_write: Bad offset %x\n", + (int)offset >> 2); + } +} + +static void imx_timerp_tick(void *opaque) +{ + IMXTimerPState *s = (IMXTimerPState *)opaque; + + DPRINTF("imxp tick\n"); + if (!(s->cr & CR_RLD)) { + ptimer_set_count(s->timer, TIMER_MAX); + } + s->int_level = 1; + imx_timerp_update(s); +} + +void imx_timerp_create(const hwaddr addr, + qemu_irq irq, + DeviceState *ccm) +{ + IMXTimerPState *pp; + DeviceState *dev; + + dev = sysbus_create_simple("imx_timerp", addr, irq); + pp = container_of(dev, IMXTimerPState, busdev.qdev); + pp->ccm = ccm; +} + +static const MemoryRegionOps imx_timerp_ops = { + .read = imx_timerp_read, + .write = imx_timerp_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_imx_timerp = { + .name = "imx-timerp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, IMXTimerPState), + VMSTATE_UINT32(lr, IMXTimerPState), + VMSTATE_UINT32(cmp, IMXTimerPState), + VMSTATE_UINT32(freq, IMXTimerPState), + VMSTATE_INT32(int_level, IMXTimerPState), + VMSTATE_PTIMER(timer, IMXTimerPState), + VMSTATE_END_OF_LIST() + } +}; + +static int imx_timerp_init(SysBusDevice *dev) +{ + IMXTimerPState *s = FROM_SYSBUS(IMXTimerPState, dev); + QEMUBH *bh; + + DPRINTF("imx_timerp_init\n"); + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->iomem, &imx_timerp_ops, + s, "imxp-timer", + 0x00001000); + sysbus_init_mmio(dev, &s->iomem); + + bh = qemu_bh_new(imx_timerp_tick, s); + s->timer = ptimer_init(bh); + + return 0; +} + + +void imx_timerg_create(const hwaddr addr, + qemu_irq irq, + DeviceState *ccm) +{ + IMXTimerGState *pp; + DeviceState *dev; + + dev = sysbus_create_simple("imx_timerg", addr, irq); + pp = container_of(dev, IMXTimerGState, busdev.qdev); + pp->ccm = ccm; +} + +static void imx_timerg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = imx_timerg_init; + dc->vmsd = &vmstate_imx_timerg; + dc->reset = imx_timerg_reset; + dc->desc = "i.MX general timer"; +} + +static void imx_timerp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = imx_timerp_init; + dc->vmsd = &vmstate_imx_timerp; + dc->reset = imx_timerp_reset; + dc->desc = "i.MX periodic timer"; +} + +static const TypeInfo imx_timerp_info = { + .name = "imx_timerp", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXTimerPState), + .class_init = imx_timerp_class_init, +}; + +static const TypeInfo imx_timerg_info = { + .name = "imx_timerg", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXTimerGState), + .class_init = imx_timerg_class_init, +}; + +static void imx_timer_register_types(void) +{ + type_register_static(&imx_timerp_info); + type_register_static(&imx_timerg_info); +} + +type_init(imx_timer_register_types) diff --git a/hw/timer/lm32_timer.c b/hw/timer/lm32_timer.c new file mode 100644 index 0000000000..e06fac7082 --- /dev/null +++ b/hw/timer/lm32_timer.c @@ -0,0 +1,230 @@ +/* + * QEMU model of the LatticeMico32 timer block. + * + * Copyright (c) 2010 Michael Walle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * Specification available at: + * http://www.latticesemi.com/documents/mico32timer.pdf + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" +#include "qemu/error-report.h" + +#define DEFAULT_FREQUENCY (50*1000000) + +enum { + R_SR = 0, + R_CR, + R_PERIOD, + R_SNAPSHOT, + R_MAX +}; + +enum { + SR_TO = (1 << 0), + SR_RUN = (1 << 1), +}; + +enum { + CR_ITO = (1 << 0), + CR_CONT = (1 << 1), + CR_START = (1 << 2), + CR_STOP = (1 << 3), +}; + +struct LM32TimerState { + SysBusDevice busdev; + MemoryRegion iomem; + + QEMUBH *bh; + ptimer_state *ptimer; + + qemu_irq irq; + uint32_t freq_hz; + + uint32_t regs[R_MAX]; +}; +typedef struct LM32TimerState LM32TimerState; + +static void timer_update_irq(LM32TimerState *s) +{ + int state = (s->regs[R_SR] & SR_TO) && (s->regs[R_CR] & CR_ITO); + + trace_lm32_timer_irq_state(state); + qemu_set_irq(s->irq, state); +} + +static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) +{ + LM32TimerState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_SR: + case R_CR: + case R_PERIOD: + r = s->regs[addr]; + break; + case R_SNAPSHOT: + r = (uint32_t)ptimer_get_count(s->ptimer); + break; + default: + error_report("lm32_timer: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_lm32_timer_memory_read(addr << 2, r); + return r; +} + +static void timer_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + LM32TimerState *s = opaque; + + trace_lm32_timer_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_SR: + s->regs[R_SR] &= ~SR_TO; + break; + case R_CR: + s->regs[R_CR] = value; + if (s->regs[R_CR] & CR_START) { + ptimer_run(s->ptimer, 1); + } + if (s->regs[R_CR] & CR_STOP) { + ptimer_stop(s->ptimer); + } + break; + case R_PERIOD: + s->regs[R_PERIOD] = value; + ptimer_set_count(s->ptimer, value); + break; + case R_SNAPSHOT: + error_report("lm32_timer: write access to read only register 0x" + TARGET_FMT_plx, addr << 2); + break; + default: + error_report("lm32_timer: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + timer_update_irq(s); +} + +static const MemoryRegionOps timer_ops = { + .read = timer_read, + .write = timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void timer_hit(void *opaque) +{ + LM32TimerState *s = opaque; + + trace_lm32_timer_hit(); + + s->regs[R_SR] |= SR_TO; + + if (s->regs[R_CR] & CR_CONT) { + ptimer_set_count(s->ptimer, s->regs[R_PERIOD]); + ptimer_run(s->ptimer, 1); + } + timer_update_irq(s); +} + +static void timer_reset(DeviceState *d) +{ + LM32TimerState *s = container_of(d, LM32TimerState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + ptimer_stop(s->ptimer); +} + +static int lm32_timer_init(SysBusDevice *dev) +{ + LM32TimerState *s = FROM_SYSBUS(typeof(*s), dev); + + sysbus_init_irq(dev, &s->irq); + + s->bh = qemu_bh_new(timer_hit, s); + s->ptimer = ptimer_init(s->bh); + ptimer_set_freq(s->ptimer, s->freq_hz); + + memory_region_init_io(&s->iomem, &timer_ops, s, "timer", R_MAX * 4); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static const VMStateDescription vmstate_lm32_timer = { + .name = "lm32-timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(ptimer, LM32TimerState), + VMSTATE_UINT32(freq_hz, LM32TimerState), + VMSTATE_UINT32_ARRAY(regs, LM32TimerState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static Property lm32_timer_properties[] = { + DEFINE_PROP_UINT32("frequency", LM32TimerState, freq_hz, DEFAULT_FREQUENCY), + DEFINE_PROP_END_OF_LIST(), +}; + +static void lm32_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = lm32_timer_init; + dc->reset = timer_reset; + dc->vmsd = &vmstate_lm32_timer; + dc->props = lm32_timer_properties; +} + +static const TypeInfo lm32_timer_info = { + .name = "lm32-timer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LM32TimerState), + .class_init = lm32_timer_class_init, +}; + +static void lm32_timer_register_types(void) +{ + type_register_static(&lm32_timer_info); +} + +type_init(lm32_timer_register_types) diff --git a/hw/timer/milkymist-sysctl.c b/hw/timer/milkymist-sysctl.c new file mode 100644 index 0000000000..e083a280c4 --- /dev/null +++ b/hw/timer/milkymist-sysctl.c @@ -0,0 +1,338 @@ +/* + * QEMU model of the Milkymist System Controller. + * + * Copyright (c) 2010-2012 Michael Walle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/sysctl.pdf + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "trace.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" +#include "qemu/error-report.h" + +enum { + CTRL_ENABLE = (1<<0), + CTRL_AUTORESTART = (1<<1), +}; + +enum { + ICAP_READY = (1<<0), +}; + +enum { + R_GPIO_IN = 0, + R_GPIO_OUT, + R_GPIO_INTEN, + R_TIMER0_CONTROL = 4, + R_TIMER0_COMPARE, + R_TIMER0_COUNTER, + R_TIMER1_CONTROL = 8, + R_TIMER1_COMPARE, + R_TIMER1_COUNTER, + R_ICAP = 16, + R_DBG_SCRATCHPAD = 20, + R_DBG_WRITE_LOCK, + R_CLK_FREQUENCY = 29, + R_CAPABILITIES, + R_SYSTEM_ID, + R_MAX +}; + +struct MilkymistSysctlState { + SysBusDevice busdev; + MemoryRegion regs_region; + + QEMUBH *bh0; + QEMUBH *bh1; + ptimer_state *ptimer0; + ptimer_state *ptimer1; + + uint32_t freq_hz; + uint32_t capabilities; + uint32_t systemid; + uint32_t strappings; + + uint32_t regs[R_MAX]; + + qemu_irq gpio_irq; + qemu_irq timer0_irq; + qemu_irq timer1_irq; +}; +typedef struct MilkymistSysctlState MilkymistSysctlState; + +static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value) +{ + trace_milkymist_sysctl_icap_write(value); + switch (value & 0xffff) { + case 0x000e: + qemu_system_shutdown_request(); + break; + } +} + +static uint64_t sysctl_read(void *opaque, hwaddr addr, + unsigned size) +{ + MilkymistSysctlState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_TIMER0_COUNTER: + r = (uint32_t)ptimer_get_count(s->ptimer0); + /* milkymist timer counts up */ + r = s->regs[R_TIMER0_COMPARE] - r; + break; + case R_TIMER1_COUNTER: + r = (uint32_t)ptimer_get_count(s->ptimer1); + /* milkymist timer counts up */ + r = s->regs[R_TIMER1_COMPARE] - r; + break; + case R_GPIO_IN: + case R_GPIO_OUT: + case R_GPIO_INTEN: + case R_TIMER0_CONTROL: + case R_TIMER0_COMPARE: + case R_TIMER1_CONTROL: + case R_TIMER1_COMPARE: + case R_ICAP: + case R_DBG_SCRATCHPAD: + case R_DBG_WRITE_LOCK: + case R_CLK_FREQUENCY: + case R_CAPABILITIES: + case R_SYSTEM_ID: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_sysctl: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_sysctl_memory_read(addr << 2, r); + + return r; +} + +static void sysctl_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MilkymistSysctlState *s = opaque; + + trace_milkymist_sysctl_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_GPIO_OUT: + case R_GPIO_INTEN: + case R_TIMER0_COUNTER: + case R_TIMER1_COUNTER: + case R_DBG_SCRATCHPAD: + s->regs[addr] = value; + break; + case R_TIMER0_COMPARE: + ptimer_set_limit(s->ptimer0, value, 0); + s->regs[addr] = value; + break; + case R_TIMER1_COMPARE: + ptimer_set_limit(s->ptimer1, value, 0); + s->regs[addr] = value; + break; + case R_TIMER0_CONTROL: + s->regs[addr] = value; + if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) { + trace_milkymist_sysctl_start_timer0(); + ptimer_set_count(s->ptimer0, + s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]); + ptimer_run(s->ptimer0, 0); + } else { + trace_milkymist_sysctl_stop_timer0(); + ptimer_stop(s->ptimer0); + } + break; + case R_TIMER1_CONTROL: + s->regs[addr] = value; + if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) { + trace_milkymist_sysctl_start_timer1(); + ptimer_set_count(s->ptimer1, + s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]); + ptimer_run(s->ptimer1, 0); + } else { + trace_milkymist_sysctl_stop_timer1(); + ptimer_stop(s->ptimer1); + } + break; + case R_ICAP: + sysctl_icap_write(s, value); + break; + case R_DBG_WRITE_LOCK: + s->regs[addr] = 1; + break; + case R_SYSTEM_ID: + qemu_system_reset_request(); + break; + + case R_GPIO_IN: + case R_CLK_FREQUENCY: + case R_CAPABILITIES: + error_report("milkymist_sysctl: write to read-only register 0x" + TARGET_FMT_plx, addr << 2); + break; + + default: + error_report("milkymist_sysctl: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static const MemoryRegionOps sysctl_mmio_ops = { + .read = sysctl_read, + .write = sysctl_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void timer0_hit(void *opaque) +{ + MilkymistSysctlState *s = opaque; + + if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) { + s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE; + trace_milkymist_sysctl_stop_timer0(); + ptimer_stop(s->ptimer0); + } + + trace_milkymist_sysctl_pulse_irq_timer0(); + qemu_irq_pulse(s->timer0_irq); +} + +static void timer1_hit(void *opaque) +{ + MilkymistSysctlState *s = opaque; + + if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) { + s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE; + trace_milkymist_sysctl_stop_timer1(); + ptimer_stop(s->ptimer1); + } + + trace_milkymist_sysctl_pulse_irq_timer1(); + qemu_irq_pulse(s->timer1_irq); +} + +static void milkymist_sysctl_reset(DeviceState *d) +{ + MilkymistSysctlState *s = + container_of(d, MilkymistSysctlState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + ptimer_stop(s->ptimer0); + ptimer_stop(s->ptimer1); + + /* defaults */ + s->regs[R_ICAP] = ICAP_READY; + s->regs[R_SYSTEM_ID] = s->systemid; + s->regs[R_CLK_FREQUENCY] = s->freq_hz; + s->regs[R_CAPABILITIES] = s->capabilities; + s->regs[R_GPIO_IN] = s->strappings; +} + +static int milkymist_sysctl_init(SysBusDevice *dev) +{ + MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev); + + sysbus_init_irq(dev, &s->gpio_irq); + sysbus_init_irq(dev, &s->timer0_irq); + sysbus_init_irq(dev, &s->timer1_irq); + + s->bh0 = qemu_bh_new(timer0_hit, s); + s->bh1 = qemu_bh_new(timer1_hit, s); + s->ptimer0 = ptimer_init(s->bh0); + s->ptimer1 = ptimer_init(s->bh1); + ptimer_set_freq(s->ptimer0, s->freq_hz); + ptimer_set_freq(s->ptimer1, s->freq_hz); + + memory_region_init_io(&s->regs_region, &sysctl_mmio_ops, s, + "milkymist-sysctl", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_sysctl = { + .name = "milkymist-sysctl", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX), + VMSTATE_PTIMER(ptimer0, MilkymistSysctlState), + VMSTATE_PTIMER(ptimer1, MilkymistSysctlState), + VMSTATE_END_OF_LIST() + } +}; + +static Property milkymist_sysctl_properties[] = { + DEFINE_PROP_UINT32("frequency", MilkymistSysctlState, + freq_hz, 80000000), + DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState, + capabilities, 0x00000000), + DEFINE_PROP_UINT32("systemid", MilkymistSysctlState, + systemid, 0x10014d31), + DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState, + strappings, 0x00000001), + DEFINE_PROP_END_OF_LIST(), +}; + +static void milkymist_sysctl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = milkymist_sysctl_init; + dc->reset = milkymist_sysctl_reset; + dc->vmsd = &vmstate_milkymist_sysctl; + dc->props = milkymist_sysctl_properties; +} + +static const TypeInfo milkymist_sysctl_info = { + .name = "milkymist-sysctl", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MilkymistSysctlState), + .class_init = milkymist_sysctl_class_init, +}; + +static void milkymist_sysctl_register_types(void) +{ + type_register_static(&milkymist_sysctl_info); +} + +type_init(milkymist_sysctl_register_types) diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c new file mode 100644 index 0000000000..9b0e9dd567 --- /dev/null +++ b/hw/timer/omap_gptimer.c @@ -0,0 +1,488 @@ +/* + * TI OMAP2 general purpose timers emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#include "hw/hw.h" +#include "qemu/timer.h" +#include "hw/arm/omap.h" + +/* GP timers */ +struct omap_gp_timer_s { + MemoryRegion iomem; + qemu_irq irq; + qemu_irq wkup; + qemu_irq in; + qemu_irq out; + omap_clk clk; + QEMUTimer *timer; + QEMUTimer *match; + struct omap_target_agent_s *ta; + + int in_val; + int out_val; + int64_t time; + int64_t rate; + int64_t ticks_per_sec; + + int16_t config; + int status; + int it_ena; + int wu_ena; + int enable; + int inout; + int capt2; + int pt; + enum { + gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both + } trigger; + enum { + gpt_capture_none, gpt_capture_rising, + gpt_capture_falling, gpt_capture_both + } capture; + int scpwm; + int ce; + int pre; + int ptv; + int ar; + int st; + int posted; + uint32_t val; + uint32_t load_val; + uint32_t capture_val[2]; + uint32_t match_val; + int capt_num; + + uint16_t writeh; /* LSB */ + uint16_t readh; /* MSB */ +}; + +#define GPT_TCAR_IT (1 << 2) +#define GPT_OVF_IT (1 << 1) +#define GPT_MAT_IT (1 << 0) + +static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) +{ + if (timer->it_ena & it) { + if (!timer->status) + qemu_irq_raise(timer->irq); + + timer->status |= it; + /* Or are the status bits set even when masked? + * i.e. is masking applied before or after the status register? */ + } + + if (timer->wu_ena & it) + qemu_irq_pulse(timer->wkup); +} + +static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) +{ + if (!timer->inout && timer->out_val != level) { + timer->out_val = level; + qemu_set_irq(timer->out, level); + } +} + +static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) +{ + uint64_t distance; + + if (timer->st && timer->rate) { + distance = qemu_get_clock_ns(vm_clock) - timer->time; + distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); + + if (distance >= 0xffffffff - timer->val) + return 0xffffffff; + else + return timer->val + distance; + } else + return timer->val; +} + +static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) +{ + if (timer->st) { + timer->val = omap_gp_timer_read(timer); + timer->time = qemu_get_clock_ns(vm_clock); + } +} + +static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) +{ + int64_t expires, matches; + + if (timer->st && timer->rate) { + expires = muldiv64(0x100000000ll - timer->val, + timer->ticks_per_sec, timer->rate); + qemu_mod_timer(timer->timer, timer->time + expires); + + if (timer->ce && timer->match_val >= timer->val) { + matches = muldiv64(timer->match_val - timer->val, + timer->ticks_per_sec, timer->rate); + qemu_mod_timer(timer->match, timer->time + matches); + } else + qemu_del_timer(timer->match); + } else { + qemu_del_timer(timer->timer); + qemu_del_timer(timer->match); + omap_gp_timer_out(timer, timer->scpwm); + } +} + +static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) +{ + if (timer->pt) + /* TODO in overflow-and-match mode if the first event to + * occur is the match, don't toggle. */ + omap_gp_timer_out(timer, !timer->out_val); + else + /* TODO inverted pulse on timer->out_val == 1? */ + qemu_irq_pulse(timer->out); +} + +static void omap_gp_timer_tick(void *opaque) +{ + struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + + if (!timer->ar) { + timer->st = 0; + timer->val = 0; + } else { + timer->val = timer->load_val; + timer->time = qemu_get_clock_ns(vm_clock); + } + + if (timer->trigger == gpt_trigger_overflow || + timer->trigger == gpt_trigger_both) + omap_gp_timer_trigger(timer); + + omap_gp_timer_intr(timer, GPT_OVF_IT); + omap_gp_timer_update(timer); +} + +static void omap_gp_timer_match(void *opaque) +{ + struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + + if (timer->trigger == gpt_trigger_both) + omap_gp_timer_trigger(timer); + + omap_gp_timer_intr(timer, GPT_MAT_IT); +} + +static void omap_gp_timer_input(void *opaque, int line, int on) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + int trigger; + + switch (s->capture) { + default: + case gpt_capture_none: + trigger = 0; + break; + case gpt_capture_rising: + trigger = !s->in_val && on; + break; + case gpt_capture_falling: + trigger = s->in_val && !on; + break; + case gpt_capture_both: + trigger = (s->in_val == !on); + break; + } + s->in_val = on; + + if (s->inout && trigger && s->capt_num < 2) { + s->capture_val[s->capt_num] = omap_gp_timer_read(s); + + if (s->capt2 == s->capt_num ++) + omap_gp_timer_intr(s, GPT_TCAR_IT); + } +} + +static void omap_gp_timer_clk_update(void *opaque, int line, int on) +{ + struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + + omap_gp_timer_sync(timer); + timer->rate = on ? omap_clk_getrate(timer->clk) : 0; + omap_gp_timer_update(timer); +} + +static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) +{ + omap_clk_adduser(timer->clk, + qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); + timer->rate = omap_clk_getrate(timer->clk); +} + +void omap_gp_timer_reset(struct omap_gp_timer_s *s) +{ + s->config = 0x000; + s->status = 0; + s->it_ena = 0; + s->wu_ena = 0; + s->inout = 0; + s->capt2 = 0; + s->capt_num = 0; + s->pt = 0; + s->trigger = gpt_trigger_none; + s->capture = gpt_capture_none; + s->scpwm = 0; + s->ce = 0; + s->pre = 0; + s->ptv = 0; + s->ar = 0; + s->st = 0; + s->posted = 1; + s->val = 0x00000000; + s->load_val = 0x00000000; + s->capture_val[0] = 0x00000000; + s->capture_val[1] = 0x00000000; + s->match_val = 0x00000000; + omap_gp_timer_update(s); +} + +static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + + switch (addr) { + case 0x00: /* TIDR */ + return 0x21; + + case 0x10: /* TIOCP_CFG */ + return s->config; + + case 0x14: /* TISTAT */ + /* ??? When's this bit reset? */ + return 1; /* RESETDONE */ + + case 0x18: /* TISR */ + return s->status; + + case 0x1c: /* TIER */ + return s->it_ena; + + case 0x20: /* TWER */ + return s->wu_ena; + + case 0x24: /* TCLR */ + return (s->inout << 14) | + (s->capt2 << 13) | + (s->pt << 12) | + (s->trigger << 10) | + (s->capture << 8) | + (s->scpwm << 7) | + (s->ce << 6) | + (s->pre << 5) | + (s->ptv << 2) | + (s->ar << 1) | + (s->st << 0); + + case 0x28: /* TCRR */ + return omap_gp_timer_read(s); + + case 0x2c: /* TLDR */ + return s->load_val; + + case 0x30: /* TTGR */ + return 0xffffffff; + + case 0x34: /* TWPS */ + return 0x00000000; /* No posted writes pending. */ + + case 0x38: /* TMAR */ + return s->match_val; + + case 0x3c: /* TCAR1 */ + return s->capture_val[0]; + + case 0x40: /* TSICR */ + return s->posted << 2; + + case 0x44: /* TCAR2 */ + return s->capture_val[1]; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + uint32_t ret; + + if (addr & 2) + return s->readh; + else { + ret = omap_gp_timer_readw(opaque, addr); + s->readh = ret >> 16; + return ret & 0xffff; + } +} + +static void omap_gp_timer_write(void *opaque, hwaddr addr, + uint32_t value) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + + switch (addr) { + case 0x00: /* TIDR */ + case 0x14: /* TISTAT */ + case 0x34: /* TWPS */ + case 0x3c: /* TCAR1 */ + case 0x44: /* TCAR2 */ + OMAP_RO_REG(addr); + break; + + case 0x10: /* TIOCP_CFG */ + s->config = value & 0x33d; + if (((value >> 3) & 3) == 3) /* IDLEMODE */ + fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", + __FUNCTION__); + if (value & 2) /* SOFTRESET */ + omap_gp_timer_reset(s); + break; + + case 0x18: /* TISR */ + if (value & GPT_TCAR_IT) + s->capt_num = 0; + if (s->status && !(s->status &= ~value)) + qemu_irq_lower(s->irq); + break; + + case 0x1c: /* TIER */ + s->it_ena = value & 7; + break; + + case 0x20: /* TWER */ + s->wu_ena = value & 7; + break; + + case 0x24: /* TCLR */ + omap_gp_timer_sync(s); + s->inout = (value >> 14) & 1; + s->capt2 = (value >> 13) & 1; + s->pt = (value >> 12) & 1; + s->trigger = (value >> 10) & 3; + if (s->capture == gpt_capture_none && + ((value >> 8) & 3) != gpt_capture_none) + s->capt_num = 0; + s->capture = (value >> 8) & 3; + s->scpwm = (value >> 7) & 1; + s->ce = (value >> 6) & 1; + s->pre = (value >> 5) & 1; + s->ptv = (value >> 2) & 7; + s->ar = (value >> 1) & 1; + s->st = (value >> 0) & 1; + if (s->inout && s->trigger != gpt_trigger_none) + fprintf(stderr, "%s: GP timer pin must be an output " + "for this trigger mode\n", __FUNCTION__); + if (!s->inout && s->capture != gpt_capture_none) + fprintf(stderr, "%s: GP timer pin must be an input " + "for this capture mode\n", __FUNCTION__); + if (s->trigger == gpt_trigger_none) + omap_gp_timer_out(s, s->scpwm); + /* TODO: make sure this doesn't overflow 32-bits */ + s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); + omap_gp_timer_update(s); + break; + + case 0x28: /* TCRR */ + s->time = qemu_get_clock_ns(vm_clock); + s->val = value; + omap_gp_timer_update(s); + break; + + case 0x2c: /* TLDR */ + s->load_val = value; + break; + + case 0x30: /* TTGR */ + s->time = qemu_get_clock_ns(vm_clock); + s->val = s->load_val; + omap_gp_timer_update(s); + break; + + case 0x38: /* TMAR */ + omap_gp_timer_sync(s); + s->match_val = value; + omap_gp_timer_update(s); + break; + + case 0x40: /* TSICR */ + s->posted = (value >> 2) & 1; + if (value & 2) /* How much exactly are we supposed to reset? */ + omap_gp_timer_reset(s); + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static void omap_gp_timer_writeh(void *opaque, hwaddr addr, + uint32_t value) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + + if (addr & 2) + return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); + else + s->writeh = (uint16_t) value; +} + +static const MemoryRegionOps omap_gp_timer_ops = { + .old_mmio = { + .read = { + omap_badwidth_read32, + omap_gp_timer_readh, + omap_gp_timer_readw, + }, + .write = { + omap_badwidth_write32, + omap_gp_timer_writeh, + omap_gp_timer_write, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, + qemu_irq irq, omap_clk fclk, omap_clk iclk) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) + g_malloc0(sizeof(struct omap_gp_timer_s)); + + s->ta = ta; + s->irq = irq; + s->clk = fclk; + s->timer = qemu_new_timer_ns(vm_clock, omap_gp_timer_tick, s); + s->match = qemu_new_timer_ns(vm_clock, omap_gp_timer_match, s); + s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; + omap_gp_timer_reset(s); + omap_gp_timer_clk_setup(s); + + memory_region_init_io(&s->iomem, &omap_gp_timer_ops, s, "omap.gptimer", + omap_l4_region_size(ta, 0)); + omap_l4_attach(ta, 0, &s->iomem); + + return s; +} diff --git a/hw/timer/omap_synctimer.c b/hw/timer/omap_synctimer.c new file mode 100644 index 0000000000..a24f35c277 --- /dev/null +++ b/hw/timer/omap_synctimer.c @@ -0,0 +1,102 @@ +/* + * TI OMAP2 32kHz sync timer emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#include "hw/hw.h" +#include "qemu/timer.h" +#include "hw/arm/omap.h" +struct omap_synctimer_s { + MemoryRegion iomem; + uint32_t val; + uint16_t readh; +}; + +/* 32-kHz Sync Timer of the OMAP2 */ +static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { + return muldiv64(qemu_get_clock_ns(vm_clock), 0x8000, get_ticks_per_sec()); +} + +void omap_synctimer_reset(struct omap_synctimer_s *s) +{ + s->val = omap_synctimer_read(s); +} + +static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr) +{ + struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; + + switch (addr) { + case 0x00: /* 32KSYNCNT_REV */ + return 0x21; + + case 0x10: /* CR */ + return omap_synctimer_read(s) - s->val; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr) +{ + struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; + uint32_t ret; + + if (addr & 2) + return s->readh; + else { + ret = omap_synctimer_readw(opaque, addr); + s->readh = ret >> 16; + return ret & 0xffff; + } +} + +static void omap_synctimer_write(void *opaque, hwaddr addr, + uint32_t value) +{ + OMAP_BAD_REG(addr); +} + +static const MemoryRegionOps omap_synctimer_ops = { + .old_mmio = { + .read = { + omap_badwidth_read32, + omap_synctimer_readh, + omap_synctimer_readw, + }, + .write = { + omap_badwidth_write32, + omap_synctimer_write, + omap_synctimer_write, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, + struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk) +{ + struct omap_synctimer_s *s = g_malloc0(sizeof(*s)); + + omap_synctimer_reset(s); + memory_region_init_io(&s->iomem, &omap_synctimer_ops, s, "omap.synctimer", + omap_l4_region_size(ta, 0)); + omap_l4_attach(ta, 0, &s->iomem); + + return s; +} diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c new file mode 100644 index 0000000000..8ea2416dd7 --- /dev/null +++ b/hw/timer/pxa2xx_timer.c @@ -0,0 +1,583 @@ +/* + * Intel XScale PXA255/270 OS Timers. + * + * Copyright (c) 2006 Openedhand Ltd. + * Copyright (c) 2006 Thorsten Zitterell + * + * This code is licensed under the GPL. + */ + +#include "hw/hw.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "hw/arm/pxa.h" +#include "hw/sysbus.h" + +#define OSMR0 0x00 +#define OSMR1 0x04 +#define OSMR2 0x08 +#define OSMR3 0x0c +#define OSMR4 0x80 +#define OSMR5 0x84 +#define OSMR6 0x88 +#define OSMR7 0x8c +#define OSMR8 0x90 +#define OSMR9 0x94 +#define OSMR10 0x98 +#define OSMR11 0x9c +#define OSCR 0x10 /* OS Timer Count */ +#define OSCR4 0x40 +#define OSCR5 0x44 +#define OSCR6 0x48 +#define OSCR7 0x4c +#define OSCR8 0x50 +#define OSCR9 0x54 +#define OSCR10 0x58 +#define OSCR11 0x5c +#define OSSR 0x14 /* Timer status register */ +#define OWER 0x18 +#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ +#define OMCR4 0xc0 /* OS Match Control registers */ +#define OMCR5 0xc4 +#define OMCR6 0xc8 +#define OMCR7 0xcc +#define OMCR8 0xd0 +#define OMCR9 0xd4 +#define OMCR10 0xd8 +#define OMCR11 0xdc +#define OSNR 0x20 + +#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ +#define PXA27X_FREQ 3250000 /* 3.25 MHz */ + +static int pxa2xx_timer4_freq[8] = { + [0] = 0, + [1] = 32768, + [2] = 1000, + [3] = 1, + [4] = 1000000, + /* [5] is the "Externally supplied clock". Assign if necessary. */ + [5 ... 7] = 0, +}; + +typedef struct PXA2xxTimerInfo PXA2xxTimerInfo; + +typedef struct { + uint32_t value; + qemu_irq irq; + QEMUTimer *qtimer; + int num; + PXA2xxTimerInfo *info; +} PXA2xxTimer0; + +typedef struct { + PXA2xxTimer0 tm; + int32_t oldclock; + int32_t clock; + uint64_t lastload; + uint32_t freq; + uint32_t control; +} PXA2xxTimer4; + +struct PXA2xxTimerInfo { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t flags; + + int32_t clock; + int32_t oldclock; + uint64_t lastload; + uint32_t freq; + PXA2xxTimer0 timer[4]; + uint32_t events; + uint32_t irq_enabled; + uint32_t reset3; + uint32_t snapshot; + + qemu_irq irq4; + PXA2xxTimer4 tm4[8]; +}; + +#define PXA2XX_TIMER_HAVE_TM4 0 + +static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s) +{ + return s->flags & (1 << PXA2XX_TIMER_HAVE_TM4); +} + +static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu) +{ + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; + int i; + uint32_t now_vm; + uint64_t new_qemu; + + now_vm = s->clock + + muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec()); + + for (i = 0; i < 4; i ++) { + new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm), + get_ticks_per_sec(), s->freq); + qemu_mod_timer(s->timer[i].qtimer, new_qemu); + } +} + +static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n) +{ + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; + uint32_t now_vm; + uint64_t new_qemu; + static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 }; + int counter; + + if (s->tm4[n].control & (1 << 7)) + counter = n; + else + counter = counters[n]; + + if (!s->tm4[counter].freq) { + qemu_del_timer(s->tm4[n].tm.qtimer); + return; + } + + now_vm = s->tm4[counter].clock + muldiv64(now_qemu - + s->tm4[counter].lastload, + s->tm4[counter].freq, get_ticks_per_sec()); + + new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm), + get_ticks_per_sec(), s->tm4[counter].freq); + qemu_mod_timer(s->tm4[n].tm.qtimer, new_qemu); +} + +static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, + unsigned size) +{ + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; + int tm = 0; + + switch (offset) { + case OSMR3: tm ++; + /* fall through */ + case OSMR2: tm ++; + /* fall through */ + case OSMR1: tm ++; + /* fall through */ + case OSMR0: + return s->timer[tm].value; + case OSMR11: tm ++; + /* fall through */ + case OSMR10: tm ++; + /* fall through */ + case OSMR9: tm ++; + /* fall through */ + case OSMR8: tm ++; + /* fall through */ + case OSMR7: tm ++; + /* fall through */ + case OSMR6: tm ++; + /* fall through */ + case OSMR5: tm ++; + /* fall through */ + case OSMR4: + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + return s->tm4[tm].tm.value; + case OSCR: + return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) - + s->lastload, s->freq, get_ticks_per_sec()); + case OSCR11: tm ++; + /* fall through */ + case OSCR10: tm ++; + /* fall through */ + case OSCR9: tm ++; + /* fall through */ + case OSCR8: tm ++; + /* fall through */ + case OSCR7: tm ++; + /* fall through */ + case OSCR6: tm ++; + /* fall through */ + case OSCR5: tm ++; + /* fall through */ + case OSCR4: + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + + if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) { + if (s->tm4[tm - 1].freq) + s->snapshot = s->tm4[tm - 1].clock + muldiv64( + qemu_get_clock_ns(vm_clock) - + s->tm4[tm - 1].lastload, + s->tm4[tm - 1].freq, get_ticks_per_sec()); + else + s->snapshot = s->tm4[tm - 1].clock; + } + + if (!s->tm4[tm].freq) + return s->tm4[tm].clock; + return s->tm4[tm].clock + muldiv64(qemu_get_clock_ns(vm_clock) - + s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec()); + case OIER: + return s->irq_enabled; + case OSSR: /* Status register */ + return s->events; + case OWER: + return s->reset3; + case OMCR11: tm ++; + /* fall through */ + case OMCR10: tm ++; + /* fall through */ + case OMCR9: tm ++; + /* fall through */ + case OMCR8: tm ++; + /* fall through */ + case OMCR7: tm ++; + /* fall through */ + case OMCR6: tm ++; + /* fall through */ + case OMCR5: tm ++; + /* fall through */ + case OMCR4: + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + return s->tm4[tm].control; + case OSNR: + return s->snapshot; + default: + badreg: + hw_error("pxa2xx_timer_read: Bad offset " REG_FMT "\n", offset); + } + + return 0; +} + +static void pxa2xx_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + int i, tm = 0; + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; + + switch (offset) { + case OSMR3: tm ++; + /* fall through */ + case OSMR2: tm ++; + /* fall through */ + case OSMR1: tm ++; + /* fall through */ + case OSMR0: + s->timer[tm].value = value; + pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock)); + break; + case OSMR11: tm ++; + /* fall through */ + case OSMR10: tm ++; + /* fall through */ + case OSMR9: tm ++; + /* fall through */ + case OSMR8: tm ++; + /* fall through */ + case OSMR7: tm ++; + /* fall through */ + case OSMR6: tm ++; + /* fall through */ + case OSMR5: tm ++; + /* fall through */ + case OSMR4: + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + s->tm4[tm].tm.value = value; + pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); + break; + case OSCR: + s->oldclock = s->clock; + s->lastload = qemu_get_clock_ns(vm_clock); + s->clock = value; + pxa2xx_timer_update(s, s->lastload); + break; + case OSCR11: tm ++; + /* fall through */ + case OSCR10: tm ++; + /* fall through */ + case OSCR9: tm ++; + /* fall through */ + case OSCR8: tm ++; + /* fall through */ + case OSCR7: tm ++; + /* fall through */ + case OSCR6: tm ++; + /* fall through */ + case OSCR5: tm ++; + /* fall through */ + case OSCR4: + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + s->tm4[tm].oldclock = s->tm4[tm].clock; + s->tm4[tm].lastload = qemu_get_clock_ns(vm_clock); + s->tm4[tm].clock = value; + pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm); + break; + case OIER: + s->irq_enabled = value & 0xfff; + break; + case OSSR: /* Status register */ + value &= s->events; + s->events &= ~value; + for (i = 0; i < 4; i ++, value >>= 1) + if (value & 1) + qemu_irq_lower(s->timer[i].irq); + if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value) + qemu_irq_lower(s->irq4); + break; + case OWER: /* XXX: Reset on OSMR3 match? */ + s->reset3 = value; + break; + case OMCR7: tm ++; + /* fall through */ + case OMCR6: tm ++; + /* fall through */ + case OMCR5: tm ++; + /* fall through */ + case OMCR4: + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + s->tm4[tm].control = value & 0x0ff; + /* XXX Stop if running (shouldn't happen) */ + if ((value & (1 << 7)) || tm == 0) + s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7]; + else { + s->tm4[tm].freq = 0; + pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); + } + break; + case OMCR11: tm ++; + /* fall through */ + case OMCR10: tm ++; + /* fall through */ + case OMCR9: tm ++; + /* fall through */ + case OMCR8: tm += 4; + if (!pxa2xx_timer_has_tm4(s)) + goto badreg; + s->tm4[tm].control = value & 0x3ff; + /* XXX Stop if running (shouldn't happen) */ + if ((value & (1 << 7)) || !(tm & 1)) + s->tm4[tm].freq = + pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)]; + else { + s->tm4[tm].freq = 0; + pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm); + } + break; + default: + badreg: + hw_error("pxa2xx_timer_write: Bad offset " REG_FMT "\n", offset); + } +} + +static const MemoryRegionOps pxa2xx_timer_ops = { + .read = pxa2xx_timer_read, + .write = pxa2xx_timer_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void pxa2xx_timer_tick(void *opaque) +{ + PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque; + PXA2xxTimerInfo *i = t->info; + + if (i->irq_enabled & (1 << t->num)) { + i->events |= 1 << t->num; + qemu_irq_raise(t->irq); + } + + if (t->num == 3) + if (i->reset3 & 1) { + i->reset3 = 0; + qemu_system_reset_request(); + } +} + +static void pxa2xx_timer_tick4(void *opaque) +{ + PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque; + PXA2xxTimerInfo *i = (PXA2xxTimerInfo *) t->tm.info; + + pxa2xx_timer_tick(&t->tm); + if (t->control & (1 << 3)) + t->clock = 0; + if (t->control & (1 << 6)) + pxa2xx_timer_update4(i, qemu_get_clock_ns(vm_clock), t->tm.num - 4); + if (i->events & 0xff0) + qemu_irq_raise(i->irq4); +} + +static int pxa25x_timer_post_load(void *opaque, int version_id) +{ + PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; + int64_t now; + int i; + + now = qemu_get_clock_ns(vm_clock); + pxa2xx_timer_update(s, now); + + if (pxa2xx_timer_has_tm4(s)) + for (i = 0; i < 8; i ++) + pxa2xx_timer_update4(s, now, i); + + return 0; +} + +static int pxa2xx_timer_init(SysBusDevice *dev) +{ + int i; + PXA2xxTimerInfo *s; + + s = FROM_SYSBUS(PXA2xxTimerInfo, dev); + s->irq_enabled = 0; + s->oldclock = 0; + s->clock = 0; + s->lastload = qemu_get_clock_ns(vm_clock); + s->reset3 = 0; + + for (i = 0; i < 4; i ++) { + s->timer[i].value = 0; + sysbus_init_irq(dev, &s->timer[i].irq); + s->timer[i].info = s; + s->timer[i].num = i; + s->timer[i].qtimer = qemu_new_timer_ns(vm_clock, + pxa2xx_timer_tick, &s->timer[i]); + } + if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) { + sysbus_init_irq(dev, &s->irq4); + + for (i = 0; i < 8; i ++) { + s->tm4[i].tm.value = 0; + s->tm4[i].tm.info = s; + s->tm4[i].tm.num = i + 4; + s->tm4[i].freq = 0; + s->tm4[i].control = 0x0; + s->tm4[i].tm.qtimer = qemu_new_timer_ns(vm_clock, + pxa2xx_timer_tick4, &s->tm4[i]); + } + } + + memory_region_init_io(&s->iomem, &pxa2xx_timer_ops, s, + "pxa2xx-timer", 0x00001000); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static const VMStateDescription vmstate_pxa2xx_timer0_regs = { + .name = "pxa2xx_timer0", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(value, PXA2xxTimer0), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_pxa2xx_timer4_regs = { + .name = "pxa2xx_timer4", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(tm, PXA2xxTimer4, 1, + vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), + VMSTATE_INT32(oldclock, PXA2xxTimer4), + VMSTATE_INT32(clock, PXA2xxTimer4), + VMSTATE_UINT64(lastload, PXA2xxTimer4), + VMSTATE_UINT32(freq, PXA2xxTimer4), + VMSTATE_UINT32(control, PXA2xxTimer4), + VMSTATE_END_OF_LIST(), + }, +}; + +static bool pxa2xx_timer_has_tm4_test(void *opaque, int version_id) +{ + return pxa2xx_timer_has_tm4(opaque); +} + +static const VMStateDescription vmstate_pxa2xx_timer_regs = { + .name = "pxa2xx_timer", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = pxa25x_timer_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32(clock, PXA2xxTimerInfo), + VMSTATE_INT32(oldclock, PXA2xxTimerInfo), + VMSTATE_UINT64(lastload, PXA2xxTimerInfo), + VMSTATE_STRUCT_ARRAY(timer, PXA2xxTimerInfo, 4, 1, + vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), + VMSTATE_UINT32(events, PXA2xxTimerInfo), + VMSTATE_UINT32(irq_enabled, PXA2xxTimerInfo), + VMSTATE_UINT32(reset3, PXA2xxTimerInfo), + VMSTATE_UINT32(snapshot, PXA2xxTimerInfo), + VMSTATE_STRUCT_ARRAY_TEST(tm4, PXA2xxTimerInfo, 8, + pxa2xx_timer_has_tm4_test, 0, + vmstate_pxa2xx_timer4_regs, PXA2xxTimer4), + VMSTATE_END_OF_LIST(), + } +}; + +static Property pxa25x_timer_dev_properties[] = { + DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), + DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, + PXA2XX_TIMER_HAVE_TM4, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = pxa2xx_timer_init; + dc->desc = "PXA25x timer"; + dc->vmsd = &vmstate_pxa2xx_timer_regs; + dc->props = pxa25x_timer_dev_properties; +} + +static const TypeInfo pxa25x_timer_dev_info = { + .name = "pxa25x-timer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxTimerInfo), + .class_init = pxa25x_timer_dev_class_init, +}; + +static Property pxa27x_timer_dev_properties[] = { + DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ), + DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, + PXA2XX_TIMER_HAVE_TM4, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = pxa2xx_timer_init; + dc->desc = "PXA27x timer"; + dc->vmsd = &vmstate_pxa2xx_timer_regs; + dc->props = pxa27x_timer_dev_properties; +} + +static const TypeInfo pxa27x_timer_dev_info = { + .name = "pxa27x-timer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxTimerInfo), + .class_init = pxa27x_timer_dev_class_init, +}; + +static void pxa2xx_timer_register_types(void) +{ + type_register_static(&pxa25x_timer_dev_info); + type_register_static(&pxa27x_timer_dev_info); +} + +type_init(pxa2xx_timer_register_types) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c new file mode 100644 index 0000000000..f92ff4f73f --- /dev/null +++ b/hw/timer/sh_timer.c @@ -0,0 +1,333 @@ +/* + * SuperH Timer modules. + * + * Copyright (c) 2007 Magnus Damm + * Based on arm_timer.c by Paul Brook + * Copyright (c) 2005-2006 CodeSourcery. + * + * This code is licensed under the GPL. + */ + +#include "hw/hw.h" +#include "hw/sh4/sh.h" +#include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "hw/ptimer.h" + +//#define DEBUG_TIMER + +#define TIMER_TCR_TPSC (7 << 0) +#define TIMER_TCR_CKEG (3 << 3) +#define TIMER_TCR_UNIE (1 << 5) +#define TIMER_TCR_ICPE (3 << 6) +#define TIMER_TCR_UNF (1 << 8) +#define TIMER_TCR_ICPF (1 << 9) +#define TIMER_TCR_RESERVED (0x3f << 10) + +#define TIMER_FEAT_CAPT (1 << 0) +#define TIMER_FEAT_EXTCLK (1 << 1) + +#define OFFSET_TCOR 0 +#define OFFSET_TCNT 1 +#define OFFSET_TCR 2 +#define OFFSET_TCPR 3 + +typedef struct { + ptimer_state *timer; + uint32_t tcnt; + uint32_t tcor; + uint32_t tcr; + uint32_t tcpr; + int freq; + int int_level; + int old_level; + int feat; + int enabled; + qemu_irq irq; +} sh_timer_state; + +/* Check all active timers, and schedule the next timer interrupt. */ + +static void sh_timer_update(sh_timer_state *s) +{ + int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); + + if (new_level != s->old_level) + qemu_set_irq (s->irq, new_level); + + s->old_level = s->int_level; + s->int_level = new_level; +} + +static uint32_t sh_timer_read(void *opaque, hwaddr offset) +{ + sh_timer_state *s = (sh_timer_state *)opaque; + + switch (offset >> 2) { + case OFFSET_TCOR: + return s->tcor; + case OFFSET_TCNT: + return ptimer_get_count(s->timer); + case OFFSET_TCR: + return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); + case OFFSET_TCPR: + if (s->feat & TIMER_FEAT_CAPT) + return s->tcpr; + default: + hw_error("sh_timer_read: Bad offset %x\n", (int)offset); + return 0; + } +} + +static void sh_timer_write(void *opaque, hwaddr offset, + uint32_t value) +{ + sh_timer_state *s = (sh_timer_state *)opaque; + int freq; + + switch (offset >> 2) { + case OFFSET_TCOR: + s->tcor = value; + ptimer_set_limit(s->timer, s->tcor, 0); + break; + case OFFSET_TCNT: + s->tcnt = value; + ptimer_set_count(s->timer, s->tcnt); + break; + case OFFSET_TCR: + if (s->enabled) { + /* Pause the timer if it is running. This may cause some + inaccuracy dure to rounding, but avoids a whole lot of other + messyness. */ + ptimer_stop(s->timer); + } + freq = s->freq; + /* ??? Need to recalculate expiry time after changing divisor. */ + switch (value & TIMER_TCR_TPSC) { + case 0: freq >>= 2; break; + case 1: freq >>= 4; break; + case 2: freq >>= 6; break; + case 3: freq >>= 8; break; + case 4: freq >>= 10; break; + case 6: + case 7: if (s->feat & TIMER_FEAT_EXTCLK) break; + default: hw_error("sh_timer_write: Reserved TPSC value\n"); break; + } + switch ((value & TIMER_TCR_CKEG) >> 3) { + case 0: break; + case 1: + case 2: + case 3: if (s->feat & TIMER_FEAT_EXTCLK) break; + default: hw_error("sh_timer_write: Reserved CKEG value\n"); break; + } + switch ((value & TIMER_TCR_ICPE) >> 6) { + case 0: break; + case 2: + case 3: if (s->feat & TIMER_FEAT_CAPT) break; + default: hw_error("sh_timer_write: Reserved ICPE value\n"); break; + } + if ((value & TIMER_TCR_UNF) == 0) + s->int_level = 0; + + value &= ~TIMER_TCR_UNF; + + if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) + hw_error("sh_timer_write: Reserved ICPF value\n"); + + value &= ~TIMER_TCR_ICPF; /* capture not supported */ + + if (value & TIMER_TCR_RESERVED) + hw_error("sh_timer_write: Reserved TCR bits set\n"); + s->tcr = value; + ptimer_set_limit(s->timer, s->tcor, 0); + ptimer_set_freq(s->timer, freq); + if (s->enabled) { + /* Restart the timer if still enabled. */ + ptimer_run(s->timer, 0); + } + break; + case OFFSET_TCPR: + if (s->feat & TIMER_FEAT_CAPT) { + s->tcpr = value; + break; + } + default: + hw_error("sh_timer_write: Bad offset %x\n", (int)offset); + } + sh_timer_update(s); +} + +static void sh_timer_start_stop(void *opaque, int enable) +{ + sh_timer_state *s = (sh_timer_state *)opaque; + +#ifdef DEBUG_TIMER + printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled); +#endif + + if (s->enabled && !enable) { + ptimer_stop(s->timer); + } + if (!s->enabled && enable) { + ptimer_run(s->timer, 0); + } + s->enabled = !!enable; + +#ifdef DEBUG_TIMER + printf("sh_timer_start_stop done %d\n", s->enabled); +#endif +} + +static void sh_timer_tick(void *opaque) +{ + sh_timer_state *s = (sh_timer_state *)opaque; + s->int_level = s->enabled; + sh_timer_update(s); +} + +static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) +{ + sh_timer_state *s; + QEMUBH *bh; + + s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state)); + s->freq = freq; + s->feat = feat; + s->tcor = 0xffffffff; + s->tcnt = 0xffffffff; + s->tcpr = 0xdeadbeef; + s->tcr = 0; + s->enabled = 0; + s->irq = irq; + + bh = qemu_bh_new(sh_timer_tick, s); + s->timer = ptimer_init(bh); + + sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor); + sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt); + sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr); + sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr); + /* ??? Save/restore. */ + return s; +} + +typedef struct { + MemoryRegion iomem; + MemoryRegion iomem_p4; + MemoryRegion iomem_a7; + void *timer[3]; + int level[3]; + uint32_t tocr; + uint32_t tstr; + int feat; +} tmu012_state; + +static uint64_t tmu012_read(void *opaque, hwaddr offset, + unsigned size) +{ + tmu012_state *s = (tmu012_state *)opaque; + +#ifdef DEBUG_TIMER + printf("tmu012_read 0x%lx\n", (unsigned long) offset); +#endif + + if (offset >= 0x20) { + if (!(s->feat & TMU012_FEAT_3CHAN)) + hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + return sh_timer_read(s->timer[2], offset - 0x20); + } + + if (offset >= 0x14) + return sh_timer_read(s->timer[1], offset - 0x14); + + if (offset >= 0x08) + return sh_timer_read(s->timer[0], offset - 0x08); + + if (offset == 4) + return s->tstr; + + if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) + return s->tocr; + + hw_error("tmu012_write: Bad offset %x\n", (int)offset); + return 0; +} + +static void tmu012_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + tmu012_state *s = (tmu012_state *)opaque; + +#ifdef DEBUG_TIMER + printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value); +#endif + + if (offset >= 0x20) { + if (!(s->feat & TMU012_FEAT_3CHAN)) + hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + sh_timer_write(s->timer[2], offset - 0x20, value); + return; + } + + if (offset >= 0x14) { + sh_timer_write(s->timer[1], offset - 0x14, value); + return; + } + + if (offset >= 0x08) { + sh_timer_write(s->timer[0], offset - 0x08, value); + return; + } + + if (offset == 4) { + sh_timer_start_stop(s->timer[0], value & (1 << 0)); + sh_timer_start_stop(s->timer[1], value & (1 << 1)); + if (s->feat & TMU012_FEAT_3CHAN) + sh_timer_start_stop(s->timer[2], value & (1 << 2)); + else + if (value & (1 << 2)) + hw_error("tmu012_write: Bad channel\n"); + + s->tstr = value; + return; + } + + if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { + s->tocr = value & (1 << 0); + } +} + +static const MemoryRegionOps tmu012_ops = { + .read = tmu012_read, + .write = tmu012_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void tmu012_init(MemoryRegion *sysmem, hwaddr base, + int feat, uint32_t freq, + qemu_irq ch0_irq, qemu_irq ch1_irq, + qemu_irq ch2_irq0, qemu_irq ch2_irq1) +{ + tmu012_state *s; + int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; + + s = (tmu012_state *)g_malloc0(sizeof(tmu012_state)); + s->feat = feat; + s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); + s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); + if (feat & TMU012_FEAT_3CHAN) + s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT, + ch2_irq0); /* ch2_irq1 not supported */ + + memory_region_init_io(&s->iomem, &tmu012_ops, s, + "timer", 0x100000000ULL); + + memory_region_init_alias(&s->iomem_p4, "timer-p4", + &s->iomem, 0, 0x1000); + memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); + + memory_region_init_alias(&s->iomem_a7, "timer-a7", + &s->iomem, 0, 0x1000); + memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); + /* ??? Save/restore. */ +} diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c new file mode 100644 index 0000000000..1145a87603 --- /dev/null +++ b/hw/timer/slavio_timer.c @@ -0,0 +1,435 @@ +/* + * QEMU Sparc SLAVIO timer controller emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/sparc/sun4m.h" +#include "qemu/timer.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" +#include "trace.h" + +/* + * Registers of hardware timer in sun4m. + * + * This is the timer/counter part of chip STP2001 (Slave I/O), also + * produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0 + * are zero. Bit 31 is 1 when count has been reached. + * + * Per-CPU timers interrupt local CPU, system timer uses normal + * interrupt routing. + * + */ + +#define MAX_CPUS 16 + +typedef struct CPUTimerState { + qemu_irq irq; + ptimer_state *timer; + uint32_t count, counthigh, reached; + /* processor only */ + uint32_t running; + uint64_t limit; +} CPUTimerState; + +typedef struct SLAVIO_TIMERState { + SysBusDevice busdev; + uint32_t num_cpus; + uint32_t cputimer_mode; + CPUTimerState cputimer[MAX_CPUS + 1]; +} SLAVIO_TIMERState; + +typedef struct TimerContext { + MemoryRegion iomem; + SLAVIO_TIMERState *s; + unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */ +} TimerContext; + +#define SYS_TIMER_SIZE 0x14 +#define CPU_TIMER_SIZE 0x10 + +#define TIMER_LIMIT 0 +#define TIMER_COUNTER 1 +#define TIMER_COUNTER_NORST 2 +#define TIMER_STATUS 3 +#define TIMER_MODE 4 + +#define TIMER_COUNT_MASK32 0xfffffe00 +#define TIMER_LIMIT_MASK32 0x7fffffff +#define TIMER_MAX_COUNT64 0x7ffffffffffffe00ULL +#define TIMER_MAX_COUNT32 0x7ffffe00ULL +#define TIMER_REACHED 0x80000000 +#define TIMER_PERIOD 500ULL // 500ns +#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1) +#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9) + +static int slavio_timer_is_user(TimerContext *tc) +{ + SLAVIO_TIMERState *s = tc->s; + unsigned int timer_index = tc->timer_index; + + return timer_index != 0 && (s->cputimer_mode & (1 << (timer_index - 1))); +} + +// Update count, set irq, update expire_time +// Convert from ptimer countdown units +static void slavio_timer_get_out(CPUTimerState *t) +{ + uint64_t count, limit; + + if (t->limit == 0) { /* free-run system or processor counter */ + limit = TIMER_MAX_COUNT32; + } else { + limit = t->limit; + } + count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer)); + + trace_slavio_timer_get_out(t->limit, t->counthigh, t->count); + t->count = count & TIMER_COUNT_MASK32; + t->counthigh = count >> 32; +} + +// timer callback +static void slavio_timer_irq(void *opaque) +{ + TimerContext *tc = opaque; + SLAVIO_TIMERState *s = tc->s; + CPUTimerState *t = &s->cputimer[tc->timer_index]; + + slavio_timer_get_out(t); + trace_slavio_timer_irq(t->counthigh, t->count); + /* if limit is 0 (free-run), there will be no match */ + if (t->limit != 0) { + t->reached = TIMER_REACHED; + } + /* there is no interrupt if user timer or free-run */ + if (!slavio_timer_is_user(tc) && t->limit != 0) { + qemu_irq_raise(t->irq); + } +} + +static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr, + unsigned size) +{ + TimerContext *tc = opaque; + SLAVIO_TIMERState *s = tc->s; + uint32_t saddr, ret; + unsigned int timer_index = tc->timer_index; + CPUTimerState *t = &s->cputimer[timer_index]; + + saddr = addr >> 2; + switch (saddr) { + case TIMER_LIMIT: + // read limit (system counter mode) or read most signifying + // part of counter (user mode) + if (slavio_timer_is_user(tc)) { + // read user timer MSW + slavio_timer_get_out(t); + ret = t->counthigh | t->reached; + } else { + // read limit + // clear irq + qemu_irq_lower(t->irq); + t->reached = 0; + ret = t->limit & TIMER_LIMIT_MASK32; + } + break; + case TIMER_COUNTER: + // read counter and reached bit (system mode) or read lsbits + // of counter (user mode) + slavio_timer_get_out(t); + if (slavio_timer_is_user(tc)) { // read user timer LSW + ret = t->count & TIMER_MAX_COUNT64; + } else { // read limit + ret = (t->count & TIMER_MAX_COUNT32) | + t->reached; + } + break; + case TIMER_STATUS: + // only available in processor counter/timer + // read start/stop status + if (timer_index > 0) { + ret = t->running; + } else { + ret = 0; + } + break; + case TIMER_MODE: + // only available in system counter + // read user/system mode + ret = s->cputimer_mode; + break; + default: + trace_slavio_timer_mem_readl_invalid(addr); + ret = 0; + break; + } + trace_slavio_timer_mem_readl(addr, ret); + return ret; +} + +static void slavio_timer_mem_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + TimerContext *tc = opaque; + SLAVIO_TIMERState *s = tc->s; + uint32_t saddr; + unsigned int timer_index = tc->timer_index; + CPUTimerState *t = &s->cputimer[timer_index]; + + trace_slavio_timer_mem_writel(addr, val); + saddr = addr >> 2; + switch (saddr) { + case TIMER_LIMIT: + if (slavio_timer_is_user(tc)) { + uint64_t count; + + // set user counter MSW, reset counter + t->limit = TIMER_MAX_COUNT64; + t->counthigh = val & (TIMER_MAX_COUNT64 >> 32); + t->reached = 0; + count = ((uint64_t)t->counthigh << 32) | t->count; + trace_slavio_timer_mem_writel_limit(timer_index, count); + ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); + } else { + // set limit, reset counter + qemu_irq_lower(t->irq); + t->limit = val & TIMER_MAX_COUNT32; + if (t->timer) { + if (t->limit == 0) { /* free-run */ + ptimer_set_limit(t->timer, + LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); + } else { + ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1); + } + } + } + break; + case TIMER_COUNTER: + if (slavio_timer_is_user(tc)) { + uint64_t count; + + // set user counter LSW, reset counter + t->limit = TIMER_MAX_COUNT64; + t->count = val & TIMER_MAX_COUNT64; + t->reached = 0; + count = ((uint64_t)t->counthigh) << 32 | t->count; + trace_slavio_timer_mem_writel_limit(timer_index, count); + ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); + } else { + trace_slavio_timer_mem_writel_counter_invalid(); + } + break; + case TIMER_COUNTER_NORST: + // set limit without resetting counter + t->limit = val & TIMER_MAX_COUNT32; + if (t->limit == 0) { /* free-run */ + ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0); + } else { + ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0); + } + break; + case TIMER_STATUS: + if (slavio_timer_is_user(tc)) { + // start/stop user counter + if ((val & 1) && !t->running) { + trace_slavio_timer_mem_writel_status_start(timer_index); + ptimer_run(t->timer, 0); + t->running = 1; + } else if (!(val & 1) && t->running) { + trace_slavio_timer_mem_writel_status_stop(timer_index); + ptimer_stop(t->timer); + t->running = 0; + } + } + break; + case TIMER_MODE: + if (timer_index == 0) { + unsigned int i; + + for (i = 0; i < s->num_cpus; i++) { + unsigned int processor = 1 << i; + CPUTimerState *curr_timer = &s->cputimer[i + 1]; + + // check for a change in timer mode for this processor + if ((val & processor) != (s->cputimer_mode & processor)) { + if (val & processor) { // counter -> user timer + qemu_irq_lower(curr_timer->irq); + // counters are always running + ptimer_stop(curr_timer->timer); + curr_timer->running = 0; + // user timer limit is always the same + curr_timer->limit = TIMER_MAX_COUNT64; + ptimer_set_limit(curr_timer->timer, + LIMIT_TO_PERIODS(curr_timer->limit), + 1); + // set this processors user timer bit in config + // register + s->cputimer_mode |= processor; + trace_slavio_timer_mem_writel_mode_user(timer_index); + } else { // user timer -> counter + // stop the user timer if it is running + if (curr_timer->running) { + ptimer_stop(curr_timer->timer); + } + // start the counter + ptimer_run(curr_timer->timer, 0); + curr_timer->running = 1; + // clear this processors user timer bit in config + // register + s->cputimer_mode &= ~processor; + trace_slavio_timer_mem_writel_mode_counter(timer_index); + } + } + } + } else { + trace_slavio_timer_mem_writel_mode_invalid(); + } + break; + default: + trace_slavio_timer_mem_writel_invalid(addr); + break; + } +} + +static const MemoryRegionOps slavio_timer_mem_ops = { + .read = slavio_timer_mem_readl, + .write = slavio_timer_mem_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const VMStateDescription vmstate_timer = { + .name ="timer", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_UINT64(limit, CPUTimerState), + VMSTATE_UINT32(count, CPUTimerState), + VMSTATE_UINT32(counthigh, CPUTimerState), + VMSTATE_UINT32(reached, CPUTimerState), + VMSTATE_UINT32(running, CPUTimerState), + VMSTATE_PTIMER(timer, CPUTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slavio_timer = { + .name ="slavio_timer", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3, + vmstate_timer, CPUTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void slavio_timer_reset(DeviceState *d) +{ + SLAVIO_TIMERState *s = container_of(d, SLAVIO_TIMERState, busdev.qdev); + unsigned int i; + CPUTimerState *curr_timer; + + for (i = 0; i <= MAX_CPUS; i++) { + curr_timer = &s->cputimer[i]; + curr_timer->limit = 0; + curr_timer->count = 0; + curr_timer->reached = 0; + if (i <= s->num_cpus) { + ptimer_set_limit(curr_timer->timer, + LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); + ptimer_run(curr_timer->timer, 0); + curr_timer->running = 1; + } + } + s->cputimer_mode = 0; +} + +static int slavio_timer_init1(SysBusDevice *dev) +{ + SLAVIO_TIMERState *s = FROM_SYSBUS(SLAVIO_TIMERState, dev); + QEMUBH *bh; + unsigned int i; + TimerContext *tc; + + for (i = 0; i <= MAX_CPUS; i++) { + uint64_t size; + char timer_name[20]; + + tc = g_malloc0(sizeof(TimerContext)); + tc->s = s; + tc->timer_index = i; + + bh = qemu_bh_new(slavio_timer_irq, tc); + s->cputimer[i].timer = ptimer_init(bh); + ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD); + + size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE; + snprintf(timer_name, sizeof(timer_name), "timer-%i", i); + memory_region_init_io(&tc->iomem, &slavio_timer_mem_ops, tc, + timer_name, size); + sysbus_init_mmio(dev, &tc->iomem); + + sysbus_init_irq(dev, &s->cputimer[i].irq); + } + + return 0; +} + +static Property slavio_timer_properties[] = { + DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void slavio_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = slavio_timer_init1; + dc->reset = slavio_timer_reset; + dc->vmsd = &vmstate_slavio_timer; + dc->props = slavio_timer_properties; +} + +static const TypeInfo slavio_timer_info = { + .name = "slavio_timer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SLAVIO_TIMERState), + .class_init = slavio_timer_class_init, +}; + +static void slavio_timer_register_types(void) +{ + type_register_static(&slavio_timer_info); +} + +type_init(slavio_timer_register_types) diff --git a/hw/timer/tusb6010.c b/hw/timer/tusb6010.c new file mode 100644 index 0000000000..533938a9b4 --- /dev/null +++ b/hw/timer/tusb6010.c @@ -0,0 +1,813 @@ +/* + * Texas Instruments TUSB6010 emulation. + * Based on reverse-engineering of a linux driver. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#include "qemu-common.h" +#include "qemu/timer.h" +#include "hw/usb.h" +#include "hw/arm/omap.h" +#include "hw/irq.h" +#include "hw/arm/devices.h" +#include "hw/sysbus.h" + +typedef struct TUSBState { + SysBusDevice busdev; + MemoryRegion iomem[2]; + qemu_irq irq; + MUSBState *musb; + QEMUTimer *otg_timer; + QEMUTimer *pwr_timer; + + int power; + uint32_t scratch; + uint16_t test_reset; + uint32_t prcm_config; + uint32_t prcm_mngmt; + uint16_t otg_status; + uint32_t dev_config; + int host_mode; + uint32_t intr; + uint32_t intr_ok; + uint32_t mask; + uint32_t usbip_intr; + uint32_t usbip_mask; + uint32_t gpio_intr; + uint32_t gpio_mask; + uint32_t gpio_config; + uint32_t dma_intr; + uint32_t dma_mask; + uint32_t dma_map; + uint32_t dma_config; + uint32_t ep0_config; + uint32_t rx_config[15]; + uint32_t tx_config[15]; + uint32_t wkup_mask; + uint32_t pullup[2]; + uint32_t control_config; + uint32_t otg_timer_val; +} TUSBState; + +#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ + +#define TUSB_VLYNQ_CTRL 0x004 + +/* Mentor Graphics OTG core registers. */ +#define TUSB_BASE_OFFSET 0x400 + +/* FIFO registers, 32-bit. */ +#define TUSB_FIFO_BASE 0x600 + +/* Device System & Control registers, 32-bit. */ +#define TUSB_SYS_REG_BASE 0x800 + +#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) +#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) +#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) +#define TUSB_DEV_CONF_SOFT_ID (1 << 1) +#define TUSB_DEV_CONF_ID_SEL (1 << 0) + +#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) +#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) +#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) +#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) +#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) +#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) +#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) +#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) +#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) +#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) +#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) +#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) +#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) +#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) +#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) +#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) +#define TUSB_PHY_OTG_CTRL_PD (1 << 6) +#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) +#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) +#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) +#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) +#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) +#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) + +/* OTG status register */ +#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) +#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) +#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) +#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) +#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) +#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) +#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) +#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) +#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) +#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) +#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) + +#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) +#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) +#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) +#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) + +/* PRCM configuration register */ +#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) +#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) +#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) + +/* PRCM management register */ +#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) +#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) +#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) +#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) +#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) +#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) +#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) +#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) +#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) +#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) +#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) +#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) +#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) +#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) +#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) + +/* Wake-up source clear and mask registers */ +#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) +#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) +#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) +#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) +#define TUSB_PRCM_WGPIO_7 (1 << 12) +#define TUSB_PRCM_WGPIO_6 (1 << 11) +#define TUSB_PRCM_WGPIO_5 (1 << 10) +#define TUSB_PRCM_WGPIO_4 (1 << 9) +#define TUSB_PRCM_WGPIO_3 (1 << 8) +#define TUSB_PRCM_WGPIO_2 (1 << 7) +#define TUSB_PRCM_WGPIO_1 (1 << 6) +#define TUSB_PRCM_WGPIO_0 (1 << 5) +#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ +#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ +#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ +#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ +#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ + +#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) +#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) +#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) +#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) +#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) +#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) +#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) +#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) +#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) +#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) +#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) +#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) +#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) +#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) +#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) +#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) + +/* NOR flash interrupt source registers */ +#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) +#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) +#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) +#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) +#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) +#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) +#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) +#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) +#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) +#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) +#define TUSB_INT_SRC_DEV_READY (1 << 12) +#define TUSB_INT_SRC_USB_IP_TX (1 << 9) +#define TUSB_INT_SRC_USB_IP_RX (1 << 8) +#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) +#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) +#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) +#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) +#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) +#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) +#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) +#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) + +#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) +#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) +#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) +#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) +#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) +#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) +#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) +#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) +#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) +#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) +#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) +#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) + +#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) +#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) + +/* Device System & Control register bitfields */ +#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) +#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) +#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) +#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) +#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) +#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) +#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) +#define TUSB_EP0_CONFIG_SW_EN (1 << 8) +#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) +#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) +#define TUSB_EP_CONFIG_SW_EN (1 << 31) +#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) +#define TUSB_PROD_TEST_RESET_VAL 0xa596 + +static void tusb_intr_update(TUSBState *s) +{ + if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) + qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); + else + qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); +} + +static void tusb_usbip_intr_update(TUSBState *s) +{ + /* TX interrupt in the MUSB */ + if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) + s->intr |= TUSB_INT_SRC_USB_IP_TX; + else + s->intr &= ~TUSB_INT_SRC_USB_IP_TX; + + /* RX interrupt in the MUSB */ + if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) + s->intr |= TUSB_INT_SRC_USB_IP_RX; + else + s->intr &= ~TUSB_INT_SRC_USB_IP_RX; + + /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ + + tusb_intr_update(s); +} + +static void tusb_dma_intr_update(TUSBState *s) +{ + if (s->dma_intr & ~s->dma_mask) + s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; + else + s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; + + tusb_intr_update(s); +} + +static void tusb_gpio_intr_update(TUSBState *s) +{ + /* TODO: How is this signalled? */ +} + +extern CPUReadMemoryFunc * const musb_read[]; +extern CPUWriteMemoryFunc * const musb_write[]; + +static uint32_t tusb_async_readb(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[0](s->musb, addr & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + } + + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return 0; +} + +static uint32_t tusb_async_readh(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[1](s->musb, addr & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + } + + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return 0; +} + +static uint32_t tusb_async_readw(void *opaque, hwaddr addr) +{ + TUSBState *s = (TUSBState *) opaque; + int offset = addr & 0xfff; + int epnum; + uint32_t ret; + + switch (offset) { + case TUSB_DEV_CONF: + return s->dev_config; + + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + return musb_read[2](s->musb, offset & 0x1ff); + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); + + case TUSB_PHY_OTG_CTRL_ENABLE: + case TUSB_PHY_OTG_CTRL: + return 0x00; /* TODO */ + + case TUSB_DEV_OTG_STAT: + ret = s->otg_status; +#if 0 + if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) + ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; +#endif + return ret; + case TUSB_DEV_OTG_TIMER: + return s->otg_timer_val; + + case TUSB_PRCM_REV: + return 0x20; + case TUSB_PRCM_CONF: + return s->prcm_config; + case TUSB_PRCM_MNGMT: + return s->prcm_mngmt; + case TUSB_PRCM_WAKEUP_SOURCE: + case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ + return 0x00000000; + case TUSB_PRCM_WAKEUP_MASK: + return s->wkup_mask; + + case TUSB_PULLUP_1_CTRL: + return s->pullup[0]; + case TUSB_PULLUP_2_CTRL: + return s->pullup[1]; + + case TUSB_INT_CTRL_REV: + return 0x20; + case TUSB_INT_CTRL_CONF: + return s->control_config; + + case TUSB_USBIP_INT_SRC: + case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ + case TUSB_USBIP_INT_CLEAR: + return s->usbip_intr; + case TUSB_USBIP_INT_MASK: + return s->usbip_mask; + + case TUSB_DMA_INT_SRC: + case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ + case TUSB_DMA_INT_CLEAR: + return s->dma_intr; + case TUSB_DMA_INT_MASK: + return s->dma_mask; + + case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ + case TUSB_GPIO_INT_SET: + case TUSB_GPIO_INT_CLEAR: + return s->gpio_intr; + case TUSB_GPIO_INT_MASK: + return s->gpio_mask; + + case TUSB_INT_SRC: + case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ + case TUSB_INT_SRC_CLEAR: + return s->intr; + case TUSB_INT_MASK: + return s->mask; + + case TUSB_GPIO_REV: + return 0x30; + case TUSB_GPIO_CONF: + return s->gpio_config; + + case TUSB_DMA_CTRL_REV: + return 0x30; + case TUSB_DMA_REQ_CONF: + return s->dma_config; + case TUSB_EP0_CONF: + return s->ep0_config; + case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): + epnum = (offset - TUSB_EP_IN_SIZE) >> 2; + return s->tx_config[epnum]; + case TUSB_DMA_EP_MAP: + return s->dma_map; + case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): + epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; + return s->rx_config[epnum]; + case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... + (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): + return 0x00000000; /* TODO */ + case TUSB_WAIT_COUNT: + return 0x00; /* TODO */ + + case TUSB_SCRATCH_PAD: + return s->scratch; + + case TUSB_PROD_TEST_RESET: + return s->test_reset; + + /* DIE IDs */ + case TUSB_DIDR1_LO: + return 0xa9453c59; + case TUSB_DIDR1_HI: + return 0x54059adf; + } + + printf("%s: unknown register at %03x\n", __FUNCTION__, offset); + return 0; +} + +static void tusb_async_writeb(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[0](s->musb, addr & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + default: + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return; + } +} + +static void tusb_async_writeh(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + + switch (addr & 0xfff) { + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[1](s->musb, addr & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + default: + printf("%s: unknown register at %03x\n", + __FUNCTION__, (int) (addr & 0xfff)); + return; + } +} + +static void tusb_async_writew(void *opaque, hwaddr addr, + uint32_t value) +{ + TUSBState *s = (TUSBState *) opaque; + int offset = addr & 0xfff; + int epnum; + + switch (offset) { + case TUSB_VLYNQ_CTRL: + break; + + case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): + musb_write[2](s->musb, offset & 0x1ff, value); + break; + + case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): + musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); + break; + + case TUSB_DEV_CONF: + s->dev_config = value; + s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); + if (value & TUSB_DEV_CONF_PROD_TEST_MODE) + hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); + break; + + case TUSB_PHY_OTG_CTRL_ENABLE: + case TUSB_PHY_OTG_CTRL: + return; /* TODO */ + case TUSB_DEV_OTG_TIMER: + s->otg_timer_val = value; + if (value & TUSB_DEV_OTG_TIMER_ENABLE) + qemu_mod_timer(s->otg_timer, qemu_get_clock_ns(vm_clock) + + muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), + get_ticks_per_sec(), TUSB_DEVCLOCK)); + else + qemu_del_timer(s->otg_timer); + break; + + case TUSB_PRCM_CONF: + s->prcm_config = value; + break; + case TUSB_PRCM_MNGMT: + s->prcm_mngmt = value; + break; + case TUSB_PRCM_WAKEUP_CLEAR: + break; + case TUSB_PRCM_WAKEUP_MASK: + s->wkup_mask = value; + break; + + case TUSB_PULLUP_1_CTRL: + s->pullup[0] = value; + break; + case TUSB_PULLUP_2_CTRL: + s->pullup[1] = value; + break; + case TUSB_INT_CTRL_CONF: + s->control_config = value; + tusb_intr_update(s); + break; + + case TUSB_USBIP_INT_SET: + s->usbip_intr |= value; + tusb_usbip_intr_update(s); + break; + case TUSB_USBIP_INT_CLEAR: + s->usbip_intr &= ~value; + tusb_usbip_intr_update(s); + musb_core_intr_clear(s->musb, ~value); + break; + case TUSB_USBIP_INT_MASK: + s->usbip_mask = value; + tusb_usbip_intr_update(s); + break; + + case TUSB_DMA_INT_SET: + s->dma_intr |= value; + tusb_dma_intr_update(s); + break; + case TUSB_DMA_INT_CLEAR: + s->dma_intr &= ~value; + tusb_dma_intr_update(s); + break; + case TUSB_DMA_INT_MASK: + s->dma_mask = value; + tusb_dma_intr_update(s); + break; + + case TUSB_GPIO_INT_SET: + s->gpio_intr |= value; + tusb_gpio_intr_update(s); + break; + case TUSB_GPIO_INT_CLEAR: + s->gpio_intr &= ~value; + tusb_gpio_intr_update(s); + break; + case TUSB_GPIO_INT_MASK: + s->gpio_mask = value; + tusb_gpio_intr_update(s); + break; + + case TUSB_INT_SRC_SET: + s->intr |= value; + tusb_intr_update(s); + break; + case TUSB_INT_SRC_CLEAR: + s->intr &= ~value; + tusb_intr_update(s); + break; + case TUSB_INT_MASK: + s->mask = value; + tusb_intr_update(s); + break; + + case TUSB_GPIO_CONF: + s->gpio_config = value; + break; + case TUSB_DMA_REQ_CONF: + s->dma_config = value; + break; + case TUSB_EP0_CONF: + s->ep0_config = value & 0x1ff; + musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), + value & TUSB_EP0_CONFIG_DIR_TX); + break; + case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): + epnum = (offset - TUSB_EP_IN_SIZE) >> 2; + s->tx_config[epnum] = value; + musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); + break; + case TUSB_DMA_EP_MAP: + s->dma_map = value; + break; + case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): + epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; + s->rx_config[epnum] = value; + musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); + break; + case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... + (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): + return; /* TODO */ + case TUSB_WAIT_COUNT: + return; /* TODO */ + + case TUSB_SCRATCH_PAD: + s->scratch = value; + break; + + case TUSB_PROD_TEST_RESET: + s->test_reset = value; + break; + + default: + printf("%s: unknown register at %03x\n", __FUNCTION__, offset); + return; + } +} + +static const MemoryRegionOps tusb_async_ops = { + .old_mmio = { + .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, + .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void tusb_otg_tick(void *opaque) +{ + TUSBState *s = (TUSBState *) opaque; + + s->otg_timer_val = 0; + s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; + tusb_intr_update(s); +} + +static void tusb_power_tick(void *opaque) +{ + TUSBState *s = (TUSBState *) opaque; + + if (s->power) { + s->intr_ok = ~0; + tusb_intr_update(s); + } +} + +static void tusb_musb_core_intr(void *opaque, int source, int level) +{ + TUSBState *s = (TUSBState *) opaque; + uint16_t otg_status = s->otg_status; + + switch (source) { + case musb_set_vbus: + if (level) + otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; + else + otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; + + /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ + /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ + if (s->otg_status != otg_status) { + s->otg_status = otg_status; + s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; + tusb_intr_update(s); + } + break; + + case musb_set_session: + /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ + /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ + if (level) { + s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; + s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; + } else { + s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; + s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; + } + + /* XXX: some IRQ or anything? */ + break; + + case musb_irq_tx: + case musb_irq_rx: + s->usbip_intr = musb_core_intr_get(s->musb); + /* Fall through. */ + default: + if (level) + s->intr |= 1 << source; + else + s->intr &= ~(1 << source); + tusb_intr_update(s); + break; + } +} + +static void tusb6010_power(TUSBState *s, int on) +{ + if (!on) { + s->power = 0; + } else if (!s->power && on) { + s->power = 1; + /* Pull the interrupt down after TUSB6010 comes up. */ + s->intr_ok = 0; + tusb_intr_update(s); + qemu_mod_timer(s->pwr_timer, + qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2); + } +} + +static void tusb6010_irq(void *opaque, int source, int level) +{ + if (source) { + tusb_musb_core_intr(opaque, source - 1, level); + } else { + tusb6010_power(opaque, level); + } +} + +static void tusb6010_reset(DeviceState *dev) +{ + TUSBState *s = FROM_SYSBUS(TUSBState, SYS_BUS_DEVICE(dev)); + int i; + + s->test_reset = TUSB_PROD_TEST_RESET_VAL; + s->host_mode = 0; + s->dev_config = 0; + s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ + s->power = 0; + s->mask = 0xffffffff; + s->intr = 0x00000000; + s->otg_timer_val = 0; + s->scratch = 0; + s->prcm_config = 0; + s->prcm_mngmt = 0; + s->intr_ok = 0; + s->usbip_intr = 0; + s->usbip_mask = 0; + s->gpio_intr = 0; + s->gpio_mask = 0; + s->gpio_config = 0; + s->dma_intr = 0; + s->dma_mask = 0; + s->dma_map = 0; + s->dma_config = 0; + s->ep0_config = 0; + s->wkup_mask = 0; + s->pullup[0] = s->pullup[1] = 0; + s->control_config = 0; + for (i = 0; i < 15; i++) { + s->rx_config[i] = s->tx_config[i] = 0; + } + musb_reset(s->musb); +} + +static int tusb6010_init(SysBusDevice *dev) +{ + TUSBState *s = FROM_SYSBUS(TUSBState, dev); + s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s); + s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s); + memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async", + UINT32_MAX); + sysbus_init_mmio(dev, &s->iomem[0]); + sysbus_init_mmio(dev, &s->iomem[1]); + sysbus_init_irq(dev, &s->irq); + qdev_init_gpio_in(&dev->qdev, tusb6010_irq, musb_irq_max + 1); + s->musb = musb_init(&dev->qdev, 1); + return 0; +} + +static void tusb6010_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = tusb6010_init; + dc->reset = tusb6010_reset; +} + +static const TypeInfo tusb6010_info = { + .name = "tusb6010", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TUSBState), + .class_init = tusb6010_class_init, +}; + +static void tusb6010_register_types(void) +{ + type_register_static(&tusb6010_info); +} + +type_init(tusb6010_register_types) diff --git a/hw/tsc210x.c b/hw/tsc210x.c deleted file mode 100644 index e6c217c8db..0000000000 --- a/hw/tsc210x.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* - * TI TSC2102 (touchscreen/sensors/audio controller) emulator. - * TI TSC2301 (touchscreen/sensors/keypad). - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (C) 2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/hw.h" -#include "audio/audio.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "hw/arm/omap.h" /* For I2SCodec and uWireSlave */ -#include "hw/arm/devices.h" - -#define TSC_DATA_REGISTERS_PAGE 0x0 -#define TSC_CONTROL_REGISTERS_PAGE 0x1 -#define TSC_AUDIO_REGISTERS_PAGE 0x2 - -#define TSC_VERBOSE - -#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p])) - -typedef struct { - qemu_irq pint; - qemu_irq kbint; - qemu_irq davint; - QEMUTimer *timer; - QEMUSoundCard card; - uWireSlave chip; - I2SCodec codec; - uint8_t in_fifo[16384]; - uint8_t out_fifo[16384]; - uint16_t model; - - int x, y; - int pressure; - - int state, page, offset, irq; - uint16_t command, dav; - - int busy; - int enabled; - int host_mode; - int function; - int nextfunction; - int precision; - int nextprecision; - int filter; - int pin_func; - int ref; - int timing; - int noise; - - uint16_t audio_ctrl1; - uint16_t audio_ctrl2; - uint16_t audio_ctrl3; - uint16_t pll[3]; - uint16_t volume; - int64_t volume_change; - int softstep; - uint16_t dac_power; - int64_t powerdown; - uint16_t filter_data[0x14]; - - const char *name; - SWVoiceIn *adc_voice[1]; - SWVoiceOut *dac_voice[1]; - int i2s_rx_rate; - int i2s_tx_rate; - - int tr[8]; - - struct { - uint16_t down; - uint16_t mask; - int scan; - int debounce; - int mode; - int intr; - } kb; -} TSC210xState; - -static const int resolution[4] = { 12, 8, 10, 12 }; - -#define TSC_MODE_NO_SCAN 0x0 -#define TSC_MODE_XY_SCAN 0x1 -#define TSC_MODE_XYZ_SCAN 0x2 -#define TSC_MODE_X 0x3 -#define TSC_MODE_Y 0x4 -#define TSC_MODE_Z 0x5 -#define TSC_MODE_BAT1 0x6 -#define TSC_MODE_BAT2 0x7 -#define TSC_MODE_AUX 0x8 -#define TSC_MODE_AUX_SCAN 0x9 -#define TSC_MODE_TEMP1 0xa -#define TSC_MODE_PORT_SCAN 0xb -#define TSC_MODE_TEMP2 0xc -#define TSC_MODE_XX_DRV 0xd -#define TSC_MODE_YY_DRV 0xe -#define TSC_MODE_YX_DRV 0xf - -static const uint16_t mode_regs[16] = { - 0x0000, /* No scan */ - 0x0600, /* X, Y scan */ - 0x0780, /* X, Y, Z scan */ - 0x0400, /* X */ - 0x0200, /* Y */ - 0x0180, /* Z */ - 0x0040, /* BAT1 */ - 0x0030, /* BAT2 */ - 0x0010, /* AUX */ - 0x0010, /* AUX scan */ - 0x0004, /* TEMP1 */ - 0x0070, /* Port scan */ - 0x0002, /* TEMP2 */ - 0x0000, /* X+, X- drivers */ - 0x0000, /* Y+, Y- drivers */ - 0x0000, /* Y+, X- drivers */ -}; - -#define X_TRANSFORM(s) \ - ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) -#define Y_TRANSFORM(s) \ - ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) -#define Z1_TRANSFORM(s) \ - ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) -#define Z2_TRANSFORM(s) \ - ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) - -#define BAT1_VAL 0x8660 -#define BAT2_VAL 0x0000 -#define AUX1_VAL 0x35c0 -#define AUX2_VAL 0xffff -#define TEMP1_VAL 0x8c70 -#define TEMP2_VAL 0xa5b0 - -#define TSC_POWEROFF_DELAY 50 -#define TSC_SOFTSTEP_DELAY 50 - -static void tsc210x_reset(TSC210xState *s) -{ - s->state = 0; - s->pin_func = 2; - s->enabled = 0; - s->busy = 0; - s->nextfunction = 0; - s->ref = 0; - s->timing = 0; - s->irq = 0; - s->dav = 0; - - s->audio_ctrl1 = 0x0000; - s->audio_ctrl2 = 0x4410; - s->audio_ctrl3 = 0x0000; - s->pll[0] = 0x1004; - s->pll[1] = 0x0000; - s->pll[2] = 0x1fff; - s->volume = 0xffff; - s->dac_power = 0x8540; - s->softstep = 1; - s->volume_change = 0; - s->powerdown = 0; - s->filter_data[0x00] = 0x6be3; - s->filter_data[0x01] = 0x9666; - s->filter_data[0x02] = 0x675d; - s->filter_data[0x03] = 0x6be3; - s->filter_data[0x04] = 0x9666; - s->filter_data[0x05] = 0x675d; - s->filter_data[0x06] = 0x7d83; - s->filter_data[0x07] = 0x84ee; - s->filter_data[0x08] = 0x7d83; - s->filter_data[0x09] = 0x84ee; - s->filter_data[0x0a] = 0x6be3; - s->filter_data[0x0b] = 0x9666; - s->filter_data[0x0c] = 0x675d; - s->filter_data[0x0d] = 0x6be3; - s->filter_data[0x0e] = 0x9666; - s->filter_data[0x0f] = 0x675d; - s->filter_data[0x10] = 0x7d83; - s->filter_data[0x11] = 0x84ee; - s->filter_data[0x12] = 0x7d83; - s->filter_data[0x13] = 0x84ee; - - s->i2s_tx_rate = 0; - s->i2s_rx_rate = 0; - - s->kb.scan = 1; - s->kb.debounce = 0; - s->kb.mask = 0x0000; - s->kb.mode = 3; - s->kb.intr = 0; - - qemu_set_irq(s->pint, !s->irq); - qemu_set_irq(s->davint, !s->dav); - qemu_irq_raise(s->kbint); -} - -typedef struct { - int rate; - int dsor; - int fsref; -} TSC210xRateInfo; - -/* { rate, dsor, fsref } */ -static const TSC210xRateInfo tsc2101_rates[] = { - /* Fsref / 6.0 */ - { 7350, 7, 1 }, - { 8000, 7, 0 }, - /* Fsref / 5.5 */ - { 8018, 6, 1 }, - { 8727, 6, 0 }, - /* Fsref / 5.0 */ - { 8820, 5, 1 }, - { 9600, 5, 0 }, - /* Fsref / 4.0 */ - { 11025, 4, 1 }, - { 12000, 4, 0 }, - /* Fsref / 3.0 */ - { 14700, 3, 1 }, - { 16000, 3, 0 }, - /* Fsref / 2.0 */ - { 22050, 2, 1 }, - { 24000, 2, 0 }, - /* Fsref / 1.5 */ - { 29400, 1, 1 }, - { 32000, 1, 0 }, - /* Fsref */ - { 44100, 0, 1 }, - { 48000, 0, 0 }, - - { 0, 0, 0 }, -}; - -/* { rate, dsor, fsref } */ -static const TSC210xRateInfo tsc2102_rates[] = { - /* Fsref / 6.0 */ - { 7350, 63, 1 }, - { 8000, 63, 0 }, - /* Fsref / 6.0 */ - { 7350, 54, 1 }, - { 8000, 54, 0 }, - /* Fsref / 5.0 */ - { 8820, 45, 1 }, - { 9600, 45, 0 }, - /* Fsref / 4.0 */ - { 11025, 36, 1 }, - { 12000, 36, 0 }, - /* Fsref / 3.0 */ - { 14700, 27, 1 }, - { 16000, 27, 0 }, - /* Fsref / 2.0 */ - { 22050, 18, 1 }, - { 24000, 18, 0 }, - /* Fsref / 1.5 */ - { 29400, 9, 1 }, - { 32000, 9, 0 }, - /* Fsref */ - { 44100, 0, 1 }, - { 48000, 0, 0 }, - - { 0, 0, 0 }, -}; - -static inline void tsc210x_out_flush(TSC210xState *s, int len) -{ - uint8_t *data = s->codec.out.fifo + s->codec.out.start; - uint8_t *end = data + len; - - while (data < end) - data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); - - s->codec.out.len -= len; - if (s->codec.out.len) - memmove(s->codec.out.fifo, end, s->codec.out.len); - s->codec.out.start = 0; -} - -static void tsc210x_audio_out_cb(TSC210xState *s, int free_b) -{ - if (s->codec.out.len >= free_b) { - tsc210x_out_flush(s, free_b); - return; - } - - s->codec.out.size = MIN(free_b, 16384); - qemu_irq_raise(s->codec.tx_start); -} - -static void tsc2102_audio_rate_update(TSC210xState *s) -{ - const TSC210xRateInfo *rate; - - s->codec.tx_rate = 0; - s->codec.rx_rate = 0; - if (s->dac_power & (1 << 15)) /* PWDNC */ - return; - - for (rate = tsc2102_rates; rate->rate; rate ++) - if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ - rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ - break; - if (!rate->rate) { - printf("%s: unknown sampling rate configured\n", __FUNCTION__); - return; - } - - s->codec.tx_rate = rate->rate; -} - -static void tsc2102_audio_output_update(TSC210xState *s) -{ - int enable; - struct audsettings fmt; - - if (s->dac_voice[0]) { - tsc210x_out_flush(s, s->codec.out.len); - s->codec.out.size = 0; - AUD_set_active_out(s->dac_voice[0], 0); - AUD_close_out(&s->card, s->dac_voice[0]); - s->dac_voice[0] = NULL; - } - s->codec.cts = 0; - - enable = - (~s->dac_power & (1 << 15)) && /* PWDNC */ - (~s->dac_power & (1 << 10)); /* DAPWDN */ - if (!enable || !s->codec.tx_rate) - return; - - /* Force our own sampling rate even in slave DAC mode */ - fmt.endianness = 0; - fmt.nchannels = 2; - fmt.freq = s->codec.tx_rate; - fmt.fmt = AUD_FMT_S16; - - s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], - "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); - if (s->dac_voice[0]) { - s->codec.cts = 1; - AUD_set_active_out(s->dac_voice[0], 1); - } -} - -static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg) -{ - switch (reg) { - case 0x00: /* X */ - s->dav &= 0xfbff; - return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + - (s->noise & 3); - - case 0x01: /* Y */ - s->noise ++; - s->dav &= 0xfdff; - return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ - (s->noise & 3); - - case 0x02: /* Z1 */ - s->dav &= 0xfeff; - return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - - (s->noise & 3); - - case 0x03: /* Z2 */ - s->dav &= 0xff7f; - return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | - (s->noise & 3); - - case 0x04: /* KPData */ - if ((s->model & 0xff00) == 0x2300) { - if (s->kb.intr && (s->kb.mode & 2)) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } - return s->kb.down; - } - - return 0xffff; - - case 0x05: /* BAT1 */ - s->dav &= 0xffbf; - return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) + - (s->noise & 6); - - case 0x06: /* BAT2 */ - s->dav &= 0xffdf; - return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision); - - case 0x07: /* AUX1 */ - s->dav &= 0xffef; - return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision); - - case 0x08: /* AUX2 */ - s->dav &= 0xfff7; - return 0xffff; - - case 0x09: /* TEMP1 */ - s->dav &= 0xfffb; - return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - - (s->noise & 5); - - case 0x0a: /* TEMP2 */ - s->dav &= 0xfffd; - return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ - (s->noise & 3); - - case 0x0b: /* DAC */ - s->dav &= 0xfffe; - return 0xffff; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_data_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static uint16_t tsc2102_control_register_read( - TSC210xState *s, int reg) -{ - switch (reg) { - case 0x00: /* TSC ADC */ - return (s->pressure << 15) | ((!s->busy) << 14) | - (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; - - case 0x01: /* Status / Keypad Control */ - if ((s->model & 0xff00) == 0x2100) - return (s->pin_func << 14) | ((!s->enabled) << 13) | - (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav; - else - return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) | - (s->kb.debounce << 11); - - case 0x02: /* DAC Control */ - if ((s->model & 0xff00) == 0x2300) - return s->dac_power & 0x8000; - else - goto bad_reg; - - case 0x03: /* Reference */ - return s->ref; - - case 0x04: /* Reset */ - return 0xffff; - - case 0x05: /* Configuration */ - return s->timing; - - case 0x06: /* Secondary configuration */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2]; - - case 0x10: /* Keypad Mask */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - return s->kb.mask; - - default: - bad_reg: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_control_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg) -{ - int l_ch, r_ch; - uint16_t val; - - switch (reg) { - case 0x00: /* Audio Control 1 */ - return s->audio_ctrl1; - - case 0x01: - return 0xff00; - - case 0x02: /* DAC Volume Control */ - return s->volume; - - case 0x03: - return 0x8b00; - - case 0x04: /* Audio Control 2 */ - l_ch = 1; - r_ch = 1; - if (s->softstep && !(s->dac_power & (1 << 10))) { - l_ch = (qemu_get_clock_ns(vm_clock) > - s->volume_change + TSC_SOFTSTEP_DELAY); - r_ch = (qemu_get_clock_ns(vm_clock) > - s->volume_change + TSC_SOFTSTEP_DELAY); - } - - return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2); - - case 0x05: /* Stereo DAC Power Control */ - return 0x2aa0 | s->dac_power | - (((s->dac_power & (1 << 10)) && - (qemu_get_clock_ns(vm_clock) > - s->powerdown + TSC_POWEROFF_DELAY)) << 6); - - case 0x06: /* Audio Control 3 */ - val = s->audio_ctrl3 | 0x0001; - s->audio_ctrl3 &= 0xff3f; - return val; - - case 0x07: /* LCH_BASS_BOOST_N0 */ - case 0x08: /* LCH_BASS_BOOST_N1 */ - case 0x09: /* LCH_BASS_BOOST_N2 */ - case 0x0a: /* LCH_BASS_BOOST_N3 */ - case 0x0b: /* LCH_BASS_BOOST_N4 */ - case 0x0c: /* LCH_BASS_BOOST_N5 */ - case 0x0d: /* LCH_BASS_BOOST_D1 */ - case 0x0e: /* LCH_BASS_BOOST_D2 */ - case 0x0f: /* LCH_BASS_BOOST_D4 */ - case 0x10: /* LCH_BASS_BOOST_D5 */ - case 0x11: /* RCH_BASS_BOOST_N0 */ - case 0x12: /* RCH_BASS_BOOST_N1 */ - case 0x13: /* RCH_BASS_BOOST_N2 */ - case 0x14: /* RCH_BASS_BOOST_N3 */ - case 0x15: /* RCH_BASS_BOOST_N4 */ - case 0x16: /* RCH_BASS_BOOST_N5 */ - case 0x17: /* RCH_BASS_BOOST_D1 */ - case 0x18: /* RCH_BASS_BOOST_D2 */ - case 0x19: /* RCH_BASS_BOOST_D4 */ - case 0x1a: /* RCH_BASS_BOOST_D5 */ - return s->filter_data[reg - 0x07]; - - case 0x1b: /* PLL Programmability 1 */ - return s->pll[0]; - - case 0x1c: /* PLL Programmability 2 */ - return s->pll[1]; - - case 0x1d: /* Audio Control 4 */ - return (!s->softstep) << 14; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_audio_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static void tsc2102_data_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* X */ - case 0x01: /* Y */ - case 0x02: /* Z1 */ - case 0x03: /* Z2 */ - case 0x05: /* BAT1 */ - case 0x06: /* BAT2 */ - case 0x07: /* AUX1 */ - case 0x08: /* AUX2 */ - case 0x09: /* TEMP1 */ - case 0x0a: /* TEMP2 */ - return; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_data_register_write: " - "no such register: 0x%02x\n", reg); -#endif - } -} - -static void tsc2102_control_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* TSC ADC */ - s->host_mode = value >> 15; - s->enabled = !(value & 0x4000); - if (s->busy && !s->enabled) - qemu_del_timer(s->timer); - s->busy &= s->enabled; - s->nextfunction = (value >> 10) & 0xf; - s->nextprecision = (value >> 8) & 3; - s->filter = value & 0xff; - return; - - case 0x01: /* Status / Keypad Control */ - if ((s->model & 0xff00) == 0x2100) - s->pin_func = value >> 14; - else { - s->kb.scan = (value >> 14) & 1; - s->kb.debounce = (value >> 11) & 7; - if (s->kb.intr && s->kb.scan) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } - } - return; - - case 0x02: /* DAC Control */ - if ((s->model & 0xff00) == 0x2300) { - s->dac_power &= 0x7fff; - s->dac_power |= 0x8000 & value; - } else - goto bad_reg; - break; - - case 0x03: /* Reference */ - s->ref = value & 0x1f; - return; - - case 0x04: /* Reset */ - if (value == 0xbb00) { - if (s->busy) - qemu_del_timer(s->timer); - tsc210x_reset(s); -#ifdef TSC_VERBOSE - } else { - fprintf(stderr, "tsc2102_control_register_write: " - "wrong value written into RESET\n"); -#endif - } - return; - - case 0x05: /* Configuration */ - s->timing = value & 0x3f; -#ifdef TSC_VERBOSE - if (value & ~0x3f) - fprintf(stderr, "tsc2102_control_register_write: " - "wrong value written into CONFIG\n"); -#endif - return; - - case 0x06: /* Secondary configuration */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - s->kb.mode = value >> 14; - s->pll[2] = value & 0x3ffff; - return; - - case 0x10: /* Keypad Mask */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - s->kb.mask = value; - return; - - default: - bad_reg: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_control_register_write: " - "no such register: 0x%02x\n", reg); -#endif - } -} - -static void tsc2102_audio_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* Audio Control 1 */ - s->audio_ctrl1 = value & 0x0f3f; -#ifdef TSC_VERBOSE - if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7))) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 1\n"); -#endif - tsc2102_audio_rate_update(s); - tsc2102_audio_output_update(s); - return; - - case 0x01: -#ifdef TSC_VERBOSE - if (value != 0xff00) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into reg 0x01\n"); -#endif - return; - - case 0x02: /* DAC Volume Control */ - s->volume = value; - s->volume_change = qemu_get_clock_ns(vm_clock); - return; - - case 0x03: -#ifdef TSC_VERBOSE - if (value != 0x8b00) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into reg 0x03\n"); -#endif - return; - - case 0x04: /* Audio Control 2 */ - s->audio_ctrl2 = value & 0xf7f2; -#ifdef TSC_VERBOSE - if (value & ~0xf7fd) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 2\n"); -#endif - return; - - case 0x05: /* Stereo DAC Power Control */ - if ((value & ~s->dac_power) & (1 << 10)) - s->powerdown = qemu_get_clock_ns(vm_clock); - - s->dac_power = value & 0x9543; -#ifdef TSC_VERBOSE - if ((value & ~0x9543) != 0x2aa0) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Power\n"); -#endif - tsc2102_audio_rate_update(s); - tsc2102_audio_output_update(s); - return; - - case 0x06: /* Audio Control 3 */ - s->audio_ctrl3 &= 0x00c0; - s->audio_ctrl3 |= value & 0xf800; -#ifdef TSC_VERBOSE - if (value & ~0xf8c7) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 3\n"); -#endif - tsc2102_audio_output_update(s); - return; - - case 0x07: /* LCH_BASS_BOOST_N0 */ - case 0x08: /* LCH_BASS_BOOST_N1 */ - case 0x09: /* LCH_BASS_BOOST_N2 */ - case 0x0a: /* LCH_BASS_BOOST_N3 */ - case 0x0b: /* LCH_BASS_BOOST_N4 */ - case 0x0c: /* LCH_BASS_BOOST_N5 */ - case 0x0d: /* LCH_BASS_BOOST_D1 */ - case 0x0e: /* LCH_BASS_BOOST_D2 */ - case 0x0f: /* LCH_BASS_BOOST_D4 */ - case 0x10: /* LCH_BASS_BOOST_D5 */ - case 0x11: /* RCH_BASS_BOOST_N0 */ - case 0x12: /* RCH_BASS_BOOST_N1 */ - case 0x13: /* RCH_BASS_BOOST_N2 */ - case 0x14: /* RCH_BASS_BOOST_N3 */ - case 0x15: /* RCH_BASS_BOOST_N4 */ - case 0x16: /* RCH_BASS_BOOST_N5 */ - case 0x17: /* RCH_BASS_BOOST_D1 */ - case 0x18: /* RCH_BASS_BOOST_D2 */ - case 0x19: /* RCH_BASS_BOOST_D4 */ - case 0x1a: /* RCH_BASS_BOOST_D5 */ - s->filter_data[reg - 0x07] = value; - return; - - case 0x1b: /* PLL Programmability 1 */ - s->pll[0] = value & 0xfffc; -#ifdef TSC_VERBOSE - if (value & ~0xfffc) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into PLL 1\n"); -#endif - return; - - case 0x1c: /* PLL Programmability 2 */ - s->pll[1] = value & 0xfffc; -#ifdef TSC_VERBOSE - if (value & ~0xfffc) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into PLL 2\n"); -#endif - return; - - case 0x1d: /* Audio Control 4 */ - s->softstep = !(value & 0x4000); -#ifdef TSC_VERBOSE - if (value & ~0x4000) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 4\n"); -#endif - return; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_audio_register_write: " - "no such register: 0x%02x\n", reg); -#endif - } -} - -/* This handles most of the chip logic. */ -static void tsc210x_pin_update(TSC210xState *s) -{ - int64_t expires; - int pin_state; - - switch (s->pin_func) { - case 0: - pin_state = s->pressure; - break; - case 1: - pin_state = !!s->dav; - break; - case 2: - default: - pin_state = s->pressure && !s->dav; - } - - if (!s->enabled) - pin_state = 0; - - if (pin_state != s->irq) { - s->irq = pin_state; - qemu_set_irq(s->pint, !s->irq); - } - - switch (s->nextfunction) { - case TSC_MODE_XY_SCAN: - case TSC_MODE_XYZ_SCAN: - if (!s->pressure) - return; - break; - - case TSC_MODE_X: - case TSC_MODE_Y: - case TSC_MODE_Z: - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_BAT1: - case TSC_MODE_BAT2: - case TSC_MODE_AUX: - case TSC_MODE_TEMP1: - case TSC_MODE_TEMP2: - if (s->dav) - s->enabled = 0; - break; - - case TSC_MODE_AUX_SCAN: - case TSC_MODE_PORT_SCAN: - break; - - case TSC_MODE_NO_SCAN: - case TSC_MODE_XX_DRV: - case TSC_MODE_YY_DRV: - case TSC_MODE_YX_DRV: - default: - return; - } - - if (!s->enabled || s->busy || s->dav) - return; - - s->busy = 1; - s->precision = s->nextprecision; - s->function = s->nextfunction; - expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 10); - qemu_mod_timer(s->timer, expires); -} - -static uint16_t tsc210x_read(TSC210xState *s) -{ - uint16_t ret = 0x0000; - - if (!s->command) - fprintf(stderr, "tsc210x_read: SPI underrun!\n"); - - switch (s->page) { - case TSC_DATA_REGISTERS_PAGE: - ret = tsc2102_data_register_read(s, s->offset); - if (!s->dav) - qemu_irq_raise(s->davint); - break; - case TSC_CONTROL_REGISTERS_PAGE: - ret = tsc2102_control_register_read(s, s->offset); - break; - case TSC_AUDIO_REGISTERS_PAGE: - ret = tsc2102_audio_register_read(s, s->offset); - break; - default: - hw_error("tsc210x_read: wrong memory page\n"); - } - - tsc210x_pin_update(s); - - /* Allow sequential reads. */ - s->offset ++; - s->state = 0; - return ret; -} - -static void tsc210x_write(TSC210xState *s, uint16_t value) -{ - /* - * This is a two-state state machine for reading - * command and data every second time. - */ - if (!s->state) { - s->command = value >> 15; - s->page = (value >> 11) & 0x0f; - s->offset = (value >> 5) & 0x3f; - s->state = 1; - } else { - if (s->command) - fprintf(stderr, "tsc210x_write: SPI overrun!\n"); - else - switch (s->page) { - case TSC_DATA_REGISTERS_PAGE: - tsc2102_data_register_write(s, s->offset, value); - break; - case TSC_CONTROL_REGISTERS_PAGE: - tsc2102_control_register_write(s, s->offset, value); - break; - case TSC_AUDIO_REGISTERS_PAGE: - tsc2102_audio_register_write(s, s->offset, value); - break; - default: - hw_error("tsc210x_write: wrong memory page\n"); - } - - tsc210x_pin_update(s); - s->state = 0; - } -} - -uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len) -{ - TSC210xState *s = opaque; - uint32_t ret = 0; - - if (len != 16) - hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len); - - /* TODO: sequential reads etc - how do we make sure the host doesn't - * unintentionally read out a conversion result from a register while - * transmitting the command word of the next command? */ - if (!value || (s->state && s->command)) - ret = tsc210x_read(s); - if (value || (s->state && !s->command)) - tsc210x_write(s, value); - - return ret; -} - -static void tsc210x_timer_tick(void *opaque) -{ - TSC210xState *s = opaque; - - /* Timer ticked -- a set of conversions has been finished. */ - - if (!s->busy) - return; - - s->busy = 0; - s->dav |= mode_regs[s->function]; - tsc210x_pin_update(s); - qemu_irq_lower(s->davint); -} - -static void tsc210x_touchscreen_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - TSC210xState *s = opaque; - int p = s->pressure; - - if (buttons_state) { - s->x = x; - s->y = y; - } - s->pressure = !!buttons_state; - - /* - * Note: We would get better responsiveness in the guest by - * signaling TS events immediately, but for now we simulate - * the first conversion delay for sake of correctness. - */ - if (p != s->pressure) - tsc210x_pin_update(s); -} - -static void tsc210x_i2s_swallow(TSC210xState *s) -{ - if (s->dac_voice[0]) - tsc210x_out_flush(s, s->codec.out.len); - else - s->codec.out.len = 0; -} - -static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out) -{ - s->i2s_tx_rate = out; - s->i2s_rx_rate = in; -} - -static void tsc210x_save(QEMUFile *f, void *opaque) -{ - TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_get_clock_ns(vm_clock); - int i; - - qemu_put_be16(f, s->x); - qemu_put_be16(f, s->y); - qemu_put_byte(f, s->pressure); - - qemu_put_byte(f, s->state); - qemu_put_byte(f, s->page); - qemu_put_byte(f, s->offset); - qemu_put_byte(f, s->command); - - qemu_put_byte(f, s->irq); - qemu_put_be16s(f, &s->dav); - - qemu_put_timer(f, s->timer); - qemu_put_byte(f, s->enabled); - qemu_put_byte(f, s->host_mode); - qemu_put_byte(f, s->function); - qemu_put_byte(f, s->nextfunction); - qemu_put_byte(f, s->precision); - qemu_put_byte(f, s->nextprecision); - qemu_put_byte(f, s->filter); - qemu_put_byte(f, s->pin_func); - qemu_put_byte(f, s->ref); - qemu_put_byte(f, s->timing); - qemu_put_be32(f, s->noise); - - qemu_put_be16s(f, &s->audio_ctrl1); - qemu_put_be16s(f, &s->audio_ctrl2); - qemu_put_be16s(f, &s->audio_ctrl3); - qemu_put_be16s(f, &s->pll[0]); - qemu_put_be16s(f, &s->pll[1]); - qemu_put_be16s(f, &s->volume); - qemu_put_sbe64(f, (s->volume_change - now)); - qemu_put_sbe64(f, (s->powerdown - now)); - qemu_put_byte(f, s->softstep); - qemu_put_be16s(f, &s->dac_power); - - for (i = 0; i < 0x14; i ++) - qemu_put_be16s(f, &s->filter_data[i]); -} - -static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) -{ - TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_get_clock_ns(vm_clock); - int i; - - s->x = qemu_get_be16(f); - s->y = qemu_get_be16(f); - s->pressure = qemu_get_byte(f); - - s->state = qemu_get_byte(f); - s->page = qemu_get_byte(f); - s->offset = qemu_get_byte(f); - s->command = qemu_get_byte(f); - - s->irq = qemu_get_byte(f); - qemu_get_be16s(f, &s->dav); - - qemu_get_timer(f, s->timer); - s->enabled = qemu_get_byte(f); - s->host_mode = qemu_get_byte(f); - s->function = qemu_get_byte(f); - s->nextfunction = qemu_get_byte(f); - s->precision = qemu_get_byte(f); - s->nextprecision = qemu_get_byte(f); - s->filter = qemu_get_byte(f); - s->pin_func = qemu_get_byte(f); - s->ref = qemu_get_byte(f); - s->timing = qemu_get_byte(f); - s->noise = qemu_get_be32(f); - - qemu_get_be16s(f, &s->audio_ctrl1); - qemu_get_be16s(f, &s->audio_ctrl2); - qemu_get_be16s(f, &s->audio_ctrl3); - qemu_get_be16s(f, &s->pll[0]); - qemu_get_be16s(f, &s->pll[1]); - qemu_get_be16s(f, &s->volume); - s->volume_change = qemu_get_sbe64(f) + now; - s->powerdown = qemu_get_sbe64(f) + now; - s->softstep = qemu_get_byte(f); - qemu_get_be16s(f, &s->dac_power); - - for (i = 0; i < 0x14; i ++) - qemu_get_be16s(f, &s->filter_data[i]); - - s->busy = qemu_timer_pending(s->timer); - qemu_set_irq(s->pint, !s->irq); - qemu_set_irq(s->davint, !s->dav); - - return 0; -} - -uWireSlave *tsc2102_init(qemu_irq pint) -{ - TSC210xState *s; - - s = (TSC210xState *) - g_malloc0(sizeof(TSC210xState)); - memset(s, 0, sizeof(TSC210xState)); - s->x = 160; - s->y = 160; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s); - s->pint = pint; - s->model = 0x2102; - s->name = "tsc2102"; - - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - s->chip.opaque = s; - s->chip.send = (void *) tsc210x_write; - s->chip.receive = (void *) tsc210x_read; - - s->codec.opaque = s; - s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; - s->codec.set_rate = (void *) tsc210x_i2s_set_rate; - s->codec.in.fifo = s->in_fifo; - s->codec.out.fifo = s->out_fifo; - - tsc210x_reset(s); - - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2102-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - register_savevm(NULL, s->name, -1, 0, - tsc210x_save, tsc210x_load, s); - - return &s->chip; -} - -uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) -{ - TSC210xState *s; - - s = (TSC210xState *) - g_malloc0(sizeof(TSC210xState)); - memset(s, 0, sizeof(TSC210xState)); - s->x = 400; - s->y = 240; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s); - s->pint = penirq; - s->kbint = kbirq; - s->davint = dav; - s->model = 0x2301; - s->name = "tsc2301"; - - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - s->chip.opaque = s; - s->chip.send = (void *) tsc210x_write; - s->chip.receive = (void *) tsc210x_read; - - s->codec.opaque = s; - s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; - s->codec.set_rate = (void *) tsc210x_i2s_set_rate; - s->codec.in.fifo = s->in_fifo; - s->codec.out.fifo = s->out_fifo; - - tsc210x_reset(s); - - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2301-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s); - - return &s->chip; -} - -I2SCodec *tsc210x_codec(uWireSlave *chip) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; - - return &s->codec; -} - -/* - * Use tslib generated calibration data to generate ADC input values - * from the touchscreen. Assuming 12-bit precision was used during - * tslib calibration. - */ -void tsc210x_set_transform(uWireSlave *chip, - MouseTransformInfo *info) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; -#if 0 - int64_t ltr[8]; - - ltr[0] = (int64_t) info->a[1] * info->y; - ltr[1] = (int64_t) info->a[4] * info->x; - ltr[2] = (int64_t) info->a[1] * info->a[3] - - (int64_t) info->a[4] * info->a[0]; - ltr[3] = (int64_t) info->a[2] * info->a[4] - - (int64_t) info->a[5] * info->a[1]; - ltr[4] = (int64_t) info->a[0] * info->y; - ltr[5] = (int64_t) info->a[3] * info->x; - ltr[6] = (int64_t) info->a[4] * info->a[0] - - (int64_t) info->a[1] * info->a[3]; - ltr[7] = (int64_t) info->a[2] * info->a[3] - - (int64_t) info->a[5] * info->a[0]; - - /* Avoid integer overflow */ - s->tr[0] = ltr[0] >> 11; - s->tr[1] = ltr[1] >> 11; - s->tr[2] = muldiv64(ltr[2], 1, info->a[6]); - s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]); - s->tr[4] = ltr[4] >> 11; - s->tr[5] = ltr[5] >> 11; - s->tr[6] = muldiv64(ltr[6], 1, info->a[6]); - s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]); -#else - - /* This version assumes touchscreen X & Y axis are parallel or - * perpendicular to LCD's X & Y axis in some way. */ - if (abs(info->a[0]) > abs(info->a[1])) { - s->tr[0] = 0; - s->tr[1] = -info->a[6] * info->x; - s->tr[2] = info->a[0]; - s->tr[3] = -info->a[2] / info->a[0]; - s->tr[4] = info->a[6] * info->y; - s->tr[5] = 0; - s->tr[6] = info->a[4]; - s->tr[7] = -info->a[5] / info->a[4]; - } else { - s->tr[0] = info->a[6] * info->y; - s->tr[1] = 0; - s->tr[2] = info->a[1]; - s->tr[3] = -info->a[2] / info->a[1]; - s->tr[4] = 0; - s->tr[5] = -info->a[6] * info->x; - s->tr[6] = info->a[3]; - s->tr[7] = -info->a[5] / info->a[3]; - } - - s->tr[0] >>= 11; - s->tr[1] >>= 11; - s->tr[3] <<= 4; - s->tr[4] >>= 11; - s->tr[5] >>= 11; - s->tr[7] <<= 4; -#endif -} - -void tsc210x_key_event(uWireSlave *chip, int key, int down) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; - - if (down) - s->kb.down |= 1 << key; - else - s->kb.down &= ~(1 << key); - - if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) { - s->kb.intr = 1; - qemu_irq_lower(s->kbint); - } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) && - !(s->kb.mode & 1)) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } -} diff --git a/hw/tusb6010.c b/hw/tusb6010.c deleted file mode 100644 index 533938a9b4..0000000000 --- a/hw/tusb6010.c +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Texas Instruments TUSB6010 emulation. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/arm/omap.h" -#include "hw/irq.h" -#include "hw/arm/devices.h" -#include "hw/sysbus.h" - -typedef struct TUSBState { - SysBusDevice busdev; - MemoryRegion iomem[2]; - qemu_irq irq; - MUSBState *musb; - QEMUTimer *otg_timer; - QEMUTimer *pwr_timer; - - int power; - uint32_t scratch; - uint16_t test_reset; - uint32_t prcm_config; - uint32_t prcm_mngmt; - uint16_t otg_status; - uint32_t dev_config; - int host_mode; - uint32_t intr; - uint32_t intr_ok; - uint32_t mask; - uint32_t usbip_intr; - uint32_t usbip_mask; - uint32_t gpio_intr; - uint32_t gpio_mask; - uint32_t gpio_config; - uint32_t dma_intr; - uint32_t dma_mask; - uint32_t dma_map; - uint32_t dma_config; - uint32_t ep0_config; - uint32_t rx_config[15]; - uint32_t tx_config[15]; - uint32_t wkup_mask; - uint32_t pullup[2]; - uint32_t control_config; - uint32_t otg_timer_val; -} TUSBState; - -#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ - -#define TUSB_VLYNQ_CTRL 0x004 - -/* Mentor Graphics OTG core registers. */ -#define TUSB_BASE_OFFSET 0x400 - -/* FIFO registers, 32-bit. */ -#define TUSB_FIFO_BASE 0x600 - -/* Device System & Control registers, 32-bit. */ -#define TUSB_SYS_REG_BASE 0x800 - -#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) -#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) -#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) -#define TUSB_DEV_CONF_SOFT_ID (1 << 1) -#define TUSB_DEV_CONF_ID_SEL (1 << 0) - -#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) -#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) -#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) -#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) -#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) -#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) -#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) -#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) -#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) -#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) -#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) -#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) -#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) -#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) -#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) -#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) -#define TUSB_PHY_OTG_CTRL_PD (1 << 6) -#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) -#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) -#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) -#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) -#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) -#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) - -/* OTG status register */ -#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) -#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) -#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) -#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) -#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) -#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) -#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) -#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) -#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) -#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) -#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) - -#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) -#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) -#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) -#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) - -/* PRCM configuration register */ -#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) -#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) -#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) - -/* PRCM management register */ -#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) -#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) -#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) -#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) -#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) -#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) -#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) -#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) -#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) -#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) -#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) -#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) -#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) -#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) -#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) - -/* Wake-up source clear and mask registers */ -#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) -#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) -#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) -#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) -#define TUSB_PRCM_WGPIO_7 (1 << 12) -#define TUSB_PRCM_WGPIO_6 (1 << 11) -#define TUSB_PRCM_WGPIO_5 (1 << 10) -#define TUSB_PRCM_WGPIO_4 (1 << 9) -#define TUSB_PRCM_WGPIO_3 (1 << 8) -#define TUSB_PRCM_WGPIO_2 (1 << 7) -#define TUSB_PRCM_WGPIO_1 (1 << 6) -#define TUSB_PRCM_WGPIO_0 (1 << 5) -#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ -#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ -#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ -#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ -#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ - -#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) -#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) -#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) -#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) -#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) -#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) -#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) -#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) -#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) -#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) -#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) -#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) -#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) -#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) -#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) -#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) - -/* NOR flash interrupt source registers */ -#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) -#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) -#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) -#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) -#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) -#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) -#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) -#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) -#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) -#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) -#define TUSB_INT_SRC_DEV_READY (1 << 12) -#define TUSB_INT_SRC_USB_IP_TX (1 << 9) -#define TUSB_INT_SRC_USB_IP_RX (1 << 8) -#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) -#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) -#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) -#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) -#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) -#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) -#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) -#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) - -#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) -#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) -#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) -#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) -#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) -#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) -#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) -#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) -#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) -#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) -#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) -#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) - -#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) -#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) - -/* Device System & Control register bitfields */ -#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) -#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) -#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) -#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) -#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) -#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) -#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) -#define TUSB_EP0_CONFIG_SW_EN (1 << 8) -#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) -#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) -#define TUSB_EP_CONFIG_SW_EN (1 << 31) -#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) -#define TUSB_PROD_TEST_RESET_VAL 0xa596 - -static void tusb_intr_update(TUSBState *s) -{ - if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) - qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); - else - qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); -} - -static void tusb_usbip_intr_update(TUSBState *s) -{ - /* TX interrupt in the MUSB */ - if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_TX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_TX; - - /* RX interrupt in the MUSB */ - if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_RX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_RX; - - /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ - - tusb_intr_update(s); -} - -static void tusb_dma_intr_update(TUSBState *s) -{ - if (s->dma_intr & ~s->dma_mask) - s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; - else - s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; - - tusb_intr_update(s); -} - -static void tusb_gpio_intr_update(TUSBState *s) -{ - /* TODO: How is this signalled? */ -} - -extern CPUReadMemoryFunc * const musb_read[]; -extern CPUWriteMemoryFunc * const musb_write[]; - -static uint32_t tusb_async_readb(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[0](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readh(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[1](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readw(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - uint32_t ret; - - switch (offset) { - case TUSB_DEV_CONF: - return s->dev_config; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[2](s->musb, offset & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return 0x00; /* TODO */ - - case TUSB_DEV_OTG_STAT: - ret = s->otg_status; -#if 0 - if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) - ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; -#endif - return ret; - case TUSB_DEV_OTG_TIMER: - return s->otg_timer_val; - - case TUSB_PRCM_REV: - return 0x20; - case TUSB_PRCM_CONF: - return s->prcm_config; - case TUSB_PRCM_MNGMT: - return s->prcm_mngmt; - case TUSB_PRCM_WAKEUP_SOURCE: - case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ - return 0x00000000; - case TUSB_PRCM_WAKEUP_MASK: - return s->wkup_mask; - - case TUSB_PULLUP_1_CTRL: - return s->pullup[0]; - case TUSB_PULLUP_2_CTRL: - return s->pullup[1]; - - case TUSB_INT_CTRL_REV: - return 0x20; - case TUSB_INT_CTRL_CONF: - return s->control_config; - - case TUSB_USBIP_INT_SRC: - case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ - case TUSB_USBIP_INT_CLEAR: - return s->usbip_intr; - case TUSB_USBIP_INT_MASK: - return s->usbip_mask; - - case TUSB_DMA_INT_SRC: - case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ - case TUSB_DMA_INT_CLEAR: - return s->dma_intr; - case TUSB_DMA_INT_MASK: - return s->dma_mask; - - case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ - case TUSB_GPIO_INT_SET: - case TUSB_GPIO_INT_CLEAR: - return s->gpio_intr; - case TUSB_GPIO_INT_MASK: - return s->gpio_mask; - - case TUSB_INT_SRC: - case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ - case TUSB_INT_SRC_CLEAR: - return s->intr; - case TUSB_INT_MASK: - return s->mask; - - case TUSB_GPIO_REV: - return 0x30; - case TUSB_GPIO_CONF: - return s->gpio_config; - - case TUSB_DMA_CTRL_REV: - return 0x30; - case TUSB_DMA_REQ_CONF: - return s->dma_config; - case TUSB_EP0_CONF: - return s->ep0_config; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - return s->tx_config[epnum]; - case TUSB_DMA_EP_MAP: - return s->dma_map; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - return s->rx_config[epnum]; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return 0x00000000; /* TODO */ - case TUSB_WAIT_COUNT: - return 0x00; /* TODO */ - - case TUSB_SCRATCH_PAD: - return s->scratch; - - case TUSB_PROD_TEST_RESET: - return s->test_reset; - - /* DIE IDs */ - case TUSB_DIDR1_LO: - return 0xa9453c59; - case TUSB_DIDR1_HI: - return 0x54059adf; - } - - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return 0; -} - -static void tusb_async_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[0](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[1](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writew(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - - switch (offset) { - case TUSB_VLYNQ_CTRL: - break; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[2](s->musb, offset & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - case TUSB_DEV_CONF: - s->dev_config = value; - s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); - if (value & TUSB_DEV_CONF_PROD_TEST_MODE) - hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); - break; - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return; /* TODO */ - case TUSB_DEV_OTG_TIMER: - s->otg_timer_val = value; - if (value & TUSB_DEV_OTG_TIMER_ENABLE) - qemu_mod_timer(s->otg_timer, qemu_get_clock_ns(vm_clock) + - muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), - get_ticks_per_sec(), TUSB_DEVCLOCK)); - else - qemu_del_timer(s->otg_timer); - break; - - case TUSB_PRCM_CONF: - s->prcm_config = value; - break; - case TUSB_PRCM_MNGMT: - s->prcm_mngmt = value; - break; - case TUSB_PRCM_WAKEUP_CLEAR: - break; - case TUSB_PRCM_WAKEUP_MASK: - s->wkup_mask = value; - break; - - case TUSB_PULLUP_1_CTRL: - s->pullup[0] = value; - break; - case TUSB_PULLUP_2_CTRL: - s->pullup[1] = value; - break; - case TUSB_INT_CTRL_CONF: - s->control_config = value; - tusb_intr_update(s); - break; - - case TUSB_USBIP_INT_SET: - s->usbip_intr |= value; - tusb_usbip_intr_update(s); - break; - case TUSB_USBIP_INT_CLEAR: - s->usbip_intr &= ~value; - tusb_usbip_intr_update(s); - musb_core_intr_clear(s->musb, ~value); - break; - case TUSB_USBIP_INT_MASK: - s->usbip_mask = value; - tusb_usbip_intr_update(s); - break; - - case TUSB_DMA_INT_SET: - s->dma_intr |= value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_CLEAR: - s->dma_intr &= ~value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_MASK: - s->dma_mask = value; - tusb_dma_intr_update(s); - break; - - case TUSB_GPIO_INT_SET: - s->gpio_intr |= value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_CLEAR: - s->gpio_intr &= ~value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_MASK: - s->gpio_mask = value; - tusb_gpio_intr_update(s); - break; - - case TUSB_INT_SRC_SET: - s->intr |= value; - tusb_intr_update(s); - break; - case TUSB_INT_SRC_CLEAR: - s->intr &= ~value; - tusb_intr_update(s); - break; - case TUSB_INT_MASK: - s->mask = value; - tusb_intr_update(s); - break; - - case TUSB_GPIO_CONF: - s->gpio_config = value; - break; - case TUSB_DMA_REQ_CONF: - s->dma_config = value; - break; - case TUSB_EP0_CONF: - s->ep0_config = value & 0x1ff; - musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), - value & TUSB_EP0_CONFIG_DIR_TX); - break; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - s->tx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); - break; - case TUSB_DMA_EP_MAP: - s->dma_map = value; - break; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - s->rx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); - break; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return; /* TODO */ - case TUSB_WAIT_COUNT: - return; /* TODO */ - - case TUSB_SCRATCH_PAD: - s->scratch = value; - break; - - case TUSB_PROD_TEST_RESET: - s->test_reset = value; - break; - - default: - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return; - } -} - -static const MemoryRegionOps tusb_async_ops = { - .old_mmio = { - .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, - .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void tusb_otg_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - s->otg_timer_val = 0; - s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; - tusb_intr_update(s); -} - -static void tusb_power_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - if (s->power) { - s->intr_ok = ~0; - tusb_intr_update(s); - } -} - -static void tusb_musb_core_intr(void *opaque, int source, int level) -{ - TUSBState *s = (TUSBState *) opaque; - uint16_t otg_status = s->otg_status; - - switch (source) { - case musb_set_vbus: - if (level) - otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; - else - otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; - - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ - if (s->otg_status != otg_status) { - s->otg_status = otg_status; - s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; - tusb_intr_update(s); - } - break; - - case musb_set_session: - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ - if (level) { - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; - } else { - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; - } - - /* XXX: some IRQ or anything? */ - break; - - case musb_irq_tx: - case musb_irq_rx: - s->usbip_intr = musb_core_intr_get(s->musb); - /* Fall through. */ - default: - if (level) - s->intr |= 1 << source; - else - s->intr &= ~(1 << source); - tusb_intr_update(s); - break; - } -} - -static void tusb6010_power(TUSBState *s, int on) -{ - if (!on) { - s->power = 0; - } else if (!s->power && on) { - s->power = 1; - /* Pull the interrupt down after TUSB6010 comes up. */ - s->intr_ok = 0; - tusb_intr_update(s); - qemu_mod_timer(s->pwr_timer, - qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2); - } -} - -static void tusb6010_irq(void *opaque, int source, int level) -{ - if (source) { - tusb_musb_core_intr(opaque, source - 1, level); - } else { - tusb6010_power(opaque, level); - } -} - -static void tusb6010_reset(DeviceState *dev) -{ - TUSBState *s = FROM_SYSBUS(TUSBState, SYS_BUS_DEVICE(dev)); - int i; - - s->test_reset = TUSB_PROD_TEST_RESET_VAL; - s->host_mode = 0; - s->dev_config = 0; - s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ - s->power = 0; - s->mask = 0xffffffff; - s->intr = 0x00000000; - s->otg_timer_val = 0; - s->scratch = 0; - s->prcm_config = 0; - s->prcm_mngmt = 0; - s->intr_ok = 0; - s->usbip_intr = 0; - s->usbip_mask = 0; - s->gpio_intr = 0; - s->gpio_mask = 0; - s->gpio_config = 0; - s->dma_intr = 0; - s->dma_mask = 0; - s->dma_map = 0; - s->dma_config = 0; - s->ep0_config = 0; - s->wkup_mask = 0; - s->pullup[0] = s->pullup[1] = 0; - s->control_config = 0; - for (i = 0; i < 15; i++) { - s->rx_config[i] = s->tx_config[i] = 0; - } - musb_reset(s->musb); -} - -static int tusb6010_init(SysBusDevice *dev) -{ - TUSBState *s = FROM_SYSBUS(TUSBState, dev); - s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s); - s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s); - memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async", - UINT32_MAX); - sysbus_init_mmio(dev, &s->iomem[0]); - sysbus_init_mmio(dev, &s->iomem[1]); - sysbus_init_irq(dev, &s->irq); - qdev_init_gpio_in(&dev->qdev, tusb6010_irq, musb_irq_max + 1); - s->musb = musb_init(&dev->qdev, 1); - return 0; -} - -static void tusb6010_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = tusb6010_init; - dc->reset = tusb6010_reset; -} - -static const TypeInfo tusb6010_info = { - .name = "tusb6010", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TUSBState), - .class_init = tusb6010_class_init, -}; - -static void tusb6010_register_types(void) -{ - type_register_static(&tusb6010_info); -} - -type_init(tusb6010_register_types) diff --git a/include/hw/sparc/sun4m.h b/include/hw/sparc/sun4m.h index e984671129..a587700a8b 100644 --- a/include/hw/sparc/sun4m.h +++ b/include/hw/sparc/sun4m.h @@ -2,6 +2,8 @@ #define SUN4M_H #include "qemu-common.h" +#include "exec/hwaddr.h" +#include "qapi/qmp/types.h" /* Devices used by sparc32 system. */ -- cgit v1.2.3-55-g7522 From c0907c9e6417cb959dfd9ef6873221536ec91351 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 15:06:20 +0100 Subject: hw: move PCI bridges to hw/pci-* or hw/ARCH Signed-off-by: Paolo Bonzini --- default-configs/i386-softmmu.mak | 4 + default-configs/sparc64-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 4 + hw/Makefile.objs | 1 + hw/alpha/Makefile.objs | 6 +- hw/alpha/typhoon.c | 842 +++++++++++++++++++++++++ hw/alpha_typhoon.c | 842 ------------------------- hw/apb_pci.c | 542 ---------------- hw/bonito.c | 847 ------------------------- hw/gt64xxx.c | 1188 ----------------------------------- hw/i386/Makefile.objs | 3 +- hw/mips/Makefile.objs | 6 +- hw/mips/gt64xxx_pci.c | 1188 +++++++++++++++++++++++++++++++++++ hw/pci-bridge/Makefile.objs | 3 + hw/pci-bridge/i82801b11.c | 125 ++++ hw/pci-bridge/ioh3420.c | 250 ++++++++ hw/pci-bridge/pci_bridge_dev.c | 158 +++++ hw/pci-bridge/xio3130_downstream.c | 217 +++++++ hw/pci-bridge/xio3130_upstream.c | 192 ++++++ hw/pci-host/Makefile.objs | 18 + hw/pci-host/apb.c | 542 ++++++++++++++++ hw/pci-host/bonito.c | 847 +++++++++++++++++++++++++ hw/pci-host/dec.c | 156 +++++ hw/pci-host/grackle.c | 165 +++++ hw/pci-host/pam.c | 87 +++ hw/pci-host/piix.c | 657 +++++++++++++++++++ hw/pci-host/ppce500.c | 427 +++++++++++++ hw/pci-host/prep.c | 232 +++++++ hw/pci-host/q35.c | 310 +++++++++ hw/pci-host/uninorth.c | 492 +++++++++++++++ hw/pci-host/versatile.c | 164 +++++ hw/pci/Makefile.objs | 2 - hw/pci/bridge/Makefile.objs | 3 - hw/pci/bridge/i82801b11.c | 125 ---- hw/pci/bridge/ioh3420.c | 250 -------- hw/pci/bridge/pci_bridge_dev.c | 158 ----- hw/pci/bridge/xio3130_downstream.c | 217 ------- hw/pci/bridge/xio3130_upstream.c | 192 ------ hw/pci/host/Makefile.objs | 13 - hw/pci/host/dec.c | 156 ----- hw/pci/host/grackle.c | 165 ----- hw/pci/host/pam.c | 87 --- hw/pci/host/ppce500.c | 427 ------------- hw/pci/host/prep.c | 232 ------- hw/pci/host/uninorth.c | 492 --------------- hw/pci/host/versatile.c | 164 ----- hw/piix_pci.c | 657 ------------------- hw/ppc/Makefile.objs | 5 +- hw/ppc/ppc4xx_pci.c | 414 ++++++++++++ hw/ppc/spapr_pci.c | 839 +++++++++++++++++++++++++ hw/ppc4xx_pci.c | 414 ------------ hw/q35.c | 310 --------- hw/sh4/Makefile.objs | 3 +- hw/sh4/sh_pci.c | 186 ++++++ hw/sh_pci.c | 186 ------ hw/spapr_pci.c | 839 ------------------------- hw/sparc64/Makefile.objs | 4 - include/hw/pci-host/q35.h | 1 - 58 files changed, 8528 insertions(+), 8529 deletions(-) create mode 100644 hw/alpha/typhoon.c delete mode 100644 hw/alpha_typhoon.c delete mode 100644 hw/apb_pci.c delete mode 100644 hw/bonito.c delete mode 100644 hw/gt64xxx.c create mode 100644 hw/mips/gt64xxx_pci.c create mode 100644 hw/pci-bridge/Makefile.objs create mode 100644 hw/pci-bridge/i82801b11.c create mode 100644 hw/pci-bridge/ioh3420.c create mode 100644 hw/pci-bridge/pci_bridge_dev.c create mode 100644 hw/pci-bridge/xio3130_downstream.c create mode 100644 hw/pci-bridge/xio3130_upstream.c create mode 100644 hw/pci-host/Makefile.objs create mode 100644 hw/pci-host/apb.c create mode 100644 hw/pci-host/bonito.c create mode 100644 hw/pci-host/dec.c create mode 100644 hw/pci-host/grackle.c create mode 100644 hw/pci-host/pam.c create mode 100644 hw/pci-host/piix.c create mode 100644 hw/pci-host/ppce500.c create mode 100644 hw/pci-host/prep.c create mode 100644 hw/pci-host/q35.c create mode 100644 hw/pci-host/uninorth.c create mode 100644 hw/pci-host/versatile.c delete mode 100644 hw/pci/bridge/Makefile.objs delete mode 100644 hw/pci/bridge/i82801b11.c delete mode 100644 hw/pci/bridge/ioh3420.c delete mode 100644 hw/pci/bridge/pci_bridge_dev.c delete mode 100644 hw/pci/bridge/xio3130_downstream.c delete mode 100644 hw/pci/bridge/xio3130_upstream.c delete mode 100644 hw/pci/host/Makefile.objs delete mode 100644 hw/pci/host/dec.c delete mode 100644 hw/pci/host/grackle.c delete mode 100644 hw/pci/host/pam.c delete mode 100644 hw/pci/host/ppce500.c delete mode 100644 hw/pci/host/prep.c delete mode 100644 hw/pci/host/uninorth.c delete mode 100644 hw/pci/host/versatile.c delete mode 100644 hw/piix_pci.c create mode 100644 hw/ppc/ppc4xx_pci.c create mode 100644 hw/ppc/spapr_pci.c delete mode 100644 hw/ppc4xx_pci.c delete mode 100644 hw/q35.c create mode 100644 hw/sh4/sh_pci.c delete mode 100644 hw/sh_pci.c delete mode 100644 hw/spapr_pci.c (limited to 'include') diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 717c8f452f..fa070cd981 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -31,8 +31,12 @@ CONFIG_TPM_TIS=y CONFIG_TPM_PASSTHROUGH=y CONFIG_PCI_HOTPLUG=y CONFIG_MC146818RTC=y +CONFIG_PAM=y +CONFIG_PCI_PIIX=y +CONFIG_PCI_HOTPLUG=y CONFIG_WDT_IB700=y CONFIG_PC_SYSFW=y CONFIG_XEN_I386=$(CONFIG_XEN) CONFIG_ISA_DEBUG=y CONFIG_LPC_ICH9=y +CONFIG_Q35=y diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak index 3b3dc55cbd..9b08ee8a20 100644 --- a/default-configs/sparc64-softmmu.mak +++ b/default-configs/sparc64-softmmu.mak @@ -13,4 +13,5 @@ CONFIG_PCKBD=y CONFIG_FDC=y CONFIG_IDE_ISA=y CONFIG_IDE_CMD646=y +CONFIG_PCI_APB=y CONFIG_MC146818RTC=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 31de9453a9..3102c09066 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -31,8 +31,12 @@ CONFIG_TPM_TIS=y CONFIG_TPM_PASSTHROUGH=y CONFIG_PCI_HOTPLUG=y CONFIG_MC146818RTC=y +CONFIG_PAM=y +CONFIG_PCI_PIIX=y +CONFIG_PCI_HOTPLUG=y CONFIG_WDT_IB700=y CONFIG_PC_SYSFW=y CONFIG_XEN_I386=$(CONFIG_XEN) CONFIG_ISA_DEBUG=y CONFIG_LPC_ICH9=y +CONFIG_Q35=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index eef8742af3..8d0cc1fa9c 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -17,6 +17,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += misc/ devices-dirs-$(CONFIG_SOFTMMU) += net/ devices-dirs-$(CONFIG_SOFTMMU) += nvram/ devices-dirs-$(CONFIG_SOFTMMU) += pci/ +devices-dirs-$(CONFIG_PCI) += pci-bridge/ pci-host/ devices-dirs-$(CONFIG_SOFTMMU) += scsi/ devices-dirs-$(CONFIG_SOFTMMU) += sd/ devices-dirs-$(CONFIG_SOFTMMU) += ssi/ diff --git a/hw/alpha/Makefile.objs b/hw/alpha/Makefile.objs index 5dfea7aaa1..5c742756f0 100644 --- a/hw/alpha/Makefile.objs +++ b/hw/alpha/Makefile.objs @@ -1,5 +1 @@ -obj-y += alpha_typhoon.o - -obj-y := $(addprefix ../,$(obj-y)) - -obj-y += dp264.o pci.o +obj-y += dp264.o pci.o typhoon.o diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c new file mode 100644 index 0000000000..41a0ebc7a3 --- /dev/null +++ b/hw/alpha/typhoon.c @@ -0,0 +1,842 @@ +/* + * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. + * + * Written by Richard Henderson. + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "cpu.h" +#include "exec/exec-all.h" +#include "hw/hw.h" +#include "hw/arm/devices.h" +#include "sysemu/sysemu.h" +#include "hw/alpha_sys.h" +#include "exec/address-spaces.h" + + +#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" + +typedef struct TyphoonCchip { + MemoryRegion region; + uint64_t misc; + uint64_t drir; + uint64_t dim[4]; + uint32_t iic[4]; + AlphaCPU *cpu[4]; +} TyphoonCchip; + +typedef struct TyphoonWindow { + uint32_t base_addr; + uint32_t mask; + uint32_t translated_base_pfn; +} TyphoonWindow; + +typedef struct TyphoonPchip { + MemoryRegion region; + MemoryRegion reg_iack; + MemoryRegion reg_mem; + MemoryRegion reg_io; + MemoryRegion reg_conf; + uint64_t ctl; + TyphoonWindow win[4]; +} TyphoonPchip; + +#define TYPHOON_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE) + +typedef struct TyphoonState { + PCIHostState parent_obj; + + TyphoonCchip cchip; + TyphoonPchip pchip; + MemoryRegion dchip_region; + MemoryRegion ram_region; + + /* QEMU emulation state. */ + uint32_t latch_tmp; +} TyphoonState; + +/* Called when one of DRIR or DIM changes. */ +static void cpu_irq_change(AlphaCPU *cpu, uint64_t req) +{ + /* If there are any non-masked interrupts, tell the cpu. */ + if (cpu != NULL) { + CPUState *cs = CPU(cpu); + if (req) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) +{ + CPUAlphaState *env = cpu_single_env; + TyphoonState *s = opaque; + CPUState *cpu; + uint64_t ret = 0; + + if (addr & 4) { + return s->latch_tmp; + } + + switch (addr) { + case 0x0000: + /* CSC: Cchip System Configuration Register. */ + /* All sorts of data here; probably the only thing relevant is + PIP<14> Pchip 1 Present = 0. */ + break; + + case 0x0040: + /* MTR: Memory Timing Register. */ + /* All sorts of stuff related to real DRAM. */ + break; + + case 0x0080: + /* MISC: Miscellaneous Register. */ + cpu = ENV_GET_CPU(env); + ret = s->cchip.misc | (cpu->cpu_index & 3); + break; + + case 0x00c0: + /* MPD: Memory Presence Detect Register. */ + break; + + case 0x0100: /* AAR0 */ + case 0x0140: /* AAR1 */ + case 0x0180: /* AAR2 */ + case 0x01c0: /* AAR3 */ + /* AAR: Array Address Register. */ + /* All sorts of information about DRAM. */ + break; + + case 0x0200: + /* DIM0: Device Interrupt Mask Register, CPU0. */ + ret = s->cchip.dim[0]; + break; + case 0x0240: + /* DIM1: Device Interrupt Mask Register, CPU1. */ + ret = s->cchip.dim[1]; + break; + case 0x0280: + /* DIR0: Device Interrupt Request Register, CPU0. */ + ret = s->cchip.dim[0] & s->cchip.drir; + break; + case 0x02c0: + /* DIR1: Device Interrupt Request Register, CPU1. */ + ret = s->cchip.dim[1] & s->cchip.drir; + break; + case 0x0300: + /* DRIR: Device Raw Interrupt Request Register. */ + ret = s->cchip.drir; + break; + + case 0x0340: + /* PRBEN: Probe Enable Register. */ + break; + + case 0x0380: + /* IIC0: Interval Ignore Count Register, CPU0. */ + ret = s->cchip.iic[0]; + break; + case 0x03c0: + /* IIC1: Interval Ignore Count Register, CPU1. */ + ret = s->cchip.iic[1]; + break; + + case 0x0400: /* MPR0 */ + case 0x0440: /* MPR1 */ + case 0x0480: /* MPR2 */ + case 0x04c0: /* MPR3 */ + /* MPR: Memory Programming Register. */ + break; + + case 0x0580: + /* TTR: TIGbus Timing Register. */ + /* All sorts of stuff related to interrupt delivery timings. */ + break; + case 0x05c0: + /* TDR: TIGbug Device Timing Register. */ + break; + + case 0x0600: + /* DIM2: Device Interrupt Mask Register, CPU2. */ + ret = s->cchip.dim[2]; + break; + case 0x0640: + /* DIM3: Device Interrupt Mask Register, CPU3. */ + ret = s->cchip.dim[3]; + break; + case 0x0680: + /* DIR2: Device Interrupt Request Register, CPU2. */ + ret = s->cchip.dim[2] & s->cchip.drir; + break; + case 0x06c0: + /* DIR3: Device Interrupt Request Register, CPU3. */ + ret = s->cchip.dim[3] & s->cchip.drir; + break; + + case 0x0700: + /* IIC2: Interval Ignore Count Register, CPU2. */ + ret = s->cchip.iic[2]; + break; + case 0x0740: + /* IIC3: Interval Ignore Count Register, CPU3. */ + ret = s->cchip.iic[3]; + break; + + case 0x0780: + /* PWR: Power Management Control. */ + break; + + case 0x0c00: /* CMONCTLA */ + case 0x0c40: /* CMONCTLB */ + case 0x0c80: /* CMONCNT01 */ + case 0x0cc0: /* CMONCNT23 */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); + return -1; + } + + s->latch_tmp = ret >> 32; + return ret; +} + +static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size) +{ + /* Skip this. It's all related to DRAM timing and setup. */ + return 0; +} + +static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t ret = 0; + + if (addr & 4) { + return s->latch_tmp; + } + + switch (addr) { + case 0x0000: + /* WSBA0: Window Space Base Address Register. */ + ret = s->pchip.win[0].base_addr; + break; + case 0x0040: + /* WSBA1 */ + ret = s->pchip.win[1].base_addr; + break; + case 0x0080: + /* WSBA2 */ + ret = s->pchip.win[2].base_addr; + break; + case 0x00c0: + /* WSBA3 */ + ret = s->pchip.win[3].base_addr; + break; + + case 0x0100: + /* WSM0: Window Space Mask Register. */ + ret = s->pchip.win[0].mask; + break; + case 0x0140: + /* WSM1 */ + ret = s->pchip.win[1].mask; + break; + case 0x0180: + /* WSM2 */ + ret = s->pchip.win[2].mask; + break; + case 0x01c0: + /* WSM3 */ + ret = s->pchip.win[3].mask; + break; + + case 0x0200: + /* TBA0: Translated Base Address Register. */ + ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10; + break; + case 0x0240: + /* TBA1 */ + ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10; + break; + case 0x0280: + /* TBA2 */ + ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10; + break; + case 0x02c0: + /* TBA3 */ + ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10; + break; + + case 0x0300: + /* PCTL: Pchip Control Register. */ + ret = s->pchip.ctl; + break; + case 0x0340: + /* PLAT: Pchip Master Latency Register. */ + break; + case 0x03c0: + /* PERROR: Pchip Error Register. */ + break; + case 0x0400: + /* PERRMASK: Pchip Error Mask Register. */ + break; + case 0x0440: + /* PERRSET: Pchip Error Set Register. */ + break; + case 0x0480: + /* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */ + break; + case 0x04c0: + /* TLBIA: Translation Buffer Invalidate All Register (WO). */ + break; + case 0x0500: /* PMONCTL */ + case 0x0540: /* PMONCNT */ + case 0x0800: /* SPRST */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); + return -1; + } + + s->latch_tmp = ret >> 32; + return ret; +} + +static void cchip_write(void *opaque, hwaddr addr, + uint64_t v32, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t val, oldval, newval; + + if (addr & 4) { + val = v32 << 32 | s->latch_tmp; + addr ^= 4; + } else { + s->latch_tmp = v32; + return; + } + + switch (addr) { + case 0x0000: + /* CSC: Cchip System Configuration Register. */ + /* All sorts of data here; nothing relevant RW. */ + break; + + case 0x0040: + /* MTR: Memory Timing Register. */ + /* All sorts of stuff related to real DRAM. */ + break; + + case 0x0080: + /* MISC: Miscellaneous Register. */ + newval = oldval = s->cchip.misc; + newval &= ~(val & 0x10000ff0); /* W1C fields */ + if (val & 0x100000) { + newval &= ~0xff0000ull; /* ACL clears ABT and ABW */ + } else { + newval |= val & 0x00f00000; /* ABT field is W1S */ + if ((newval & 0xf0000) == 0) { + newval |= val & 0xf0000; /* ABW field is W1S iff zero */ + } + } + newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */ + + newval &= ~0xf0000000000ull; /* WO and RW fields */ + newval |= val & 0xf0000000000ull; + s->cchip.misc = newval; + + /* Pass on changes to IPI and ITI state. */ + if ((newval ^ oldval) & 0xff0) { + int i; + for (i = 0; i < 4; ++i) { + AlphaCPU *cpu = s->cchip.cpu[i]; + if (cpu != NULL) { + CPUState *cs = CPU(cpu); + /* IPI can be either cleared or set by the write. */ + if (newval & (1 << (i + 8))) { + cpu_interrupt(cs, CPU_INTERRUPT_SMP); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_SMP); + } + + /* ITI can only be cleared by the write. */ + if ((newval & (1 << (i + 4))) == 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); + } + } + } + } + break; + + case 0x00c0: + /* MPD: Memory Presence Detect Register. */ + break; + + case 0x0100: /* AAR0 */ + case 0x0140: /* AAR1 */ + case 0x0180: /* AAR2 */ + case 0x01c0: /* AAR3 */ + /* AAR: Array Address Register. */ + /* All sorts of information about DRAM. */ + break; + + case 0x0200: /* DIM0 */ + /* DIM: Device Interrupt Mask Register, CPU0. */ + s->cchip.dim[0] = val; + cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir); + break; + case 0x0240: /* DIM1 */ + /* DIM: Device Interrupt Mask Register, CPU1. */ + s->cchip.dim[0] = val; + cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir); + break; + + case 0x0280: /* DIR0 (RO) */ + case 0x02c0: /* DIR1 (RO) */ + case 0x0300: /* DRIR (RO) */ + break; + + case 0x0340: + /* PRBEN: Probe Enable Register. */ + break; + + case 0x0380: /* IIC0 */ + s->cchip.iic[0] = val & 0xffffff; + break; + case 0x03c0: /* IIC1 */ + s->cchip.iic[1] = val & 0xffffff; + break; + + case 0x0400: /* MPR0 */ + case 0x0440: /* MPR1 */ + case 0x0480: /* MPR2 */ + case 0x04c0: /* MPR3 */ + /* MPR: Memory Programming Register. */ + break; + + case 0x0580: + /* TTR: TIGbus Timing Register. */ + /* All sorts of stuff related to interrupt delivery timings. */ + break; + case 0x05c0: + /* TDR: TIGbug Device Timing Register. */ + break; + + case 0x0600: + /* DIM2: Device Interrupt Mask Register, CPU2. */ + s->cchip.dim[2] = val; + cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir); + break; + case 0x0640: + /* DIM3: Device Interrupt Mask Register, CPU3. */ + s->cchip.dim[3] = val; + cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir); + break; + + case 0x0680: /* DIR2 (RO) */ + case 0x06c0: /* DIR3 (RO) */ + break; + + case 0x0700: /* IIC2 */ + s->cchip.iic[2] = val & 0xffffff; + break; + case 0x0740: /* IIC3 */ + s->cchip.iic[3] = val & 0xffffff; + break; + + case 0x0780: + /* PWR: Power Management Control. */ + break; + + case 0x0c00: /* CMONCTLA */ + case 0x0c40: /* CMONCTLB */ + case 0x0c80: /* CMONCNT01 */ + case 0x0cc0: /* CMONCNT23 */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); + return; + } +} + +static void dchip_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + /* Skip this. It's all related to DRAM timing and setup. */ +} + +static void pchip_write(void *opaque, hwaddr addr, + uint64_t v32, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t val, oldval; + + if (addr & 4) { + val = v32 << 32 | s->latch_tmp; + addr ^= 4; + } else { + s->latch_tmp = v32; + return; + } + + switch (addr) { + case 0x0000: + /* WSBA0: Window Space Base Address Register. */ + s->pchip.win[0].base_addr = val; + break; + case 0x0040: + /* WSBA1 */ + s->pchip.win[1].base_addr = val; + break; + case 0x0080: + /* WSBA2 */ + s->pchip.win[2].base_addr = val; + break; + case 0x00c0: + /* WSBA3 */ + s->pchip.win[3].base_addr = val; + break; + + case 0x0100: + /* WSM0: Window Space Mask Register. */ + s->pchip.win[0].mask = val; + break; + case 0x0140: + /* WSM1 */ + s->pchip.win[1].mask = val; + break; + case 0x0180: + /* WSM2 */ + s->pchip.win[2].mask = val; + break; + case 0x01c0: + /* WSM3 */ + s->pchip.win[3].mask = val; + break; + + case 0x0200: + /* TBA0: Translated Base Address Register. */ + s->pchip.win[0].translated_base_pfn = val >> 10; + break; + case 0x0240: + /* TBA1 */ + s->pchip.win[1].translated_base_pfn = val >> 10; + break; + case 0x0280: + /* TBA2 */ + s->pchip.win[2].translated_base_pfn = val >> 10; + break; + case 0x02c0: + /* TBA3 */ + s->pchip.win[3].translated_base_pfn = val >> 10; + break; + + case 0x0300: + /* PCTL: Pchip Control Register. */ + oldval = s->pchip.ctl; + oldval &= ~0x00001cff0fc7ffull; /* RW fields */ + oldval |= val & 0x00001cff0fc7ffull; + + s->pchip.ctl = oldval; + break; + + case 0x0340: + /* PLAT: Pchip Master Latency Register. */ + break; + case 0x03c0: + /* PERROR: Pchip Error Register. */ + break; + case 0x0400: + /* PERRMASK: Pchip Error Mask Register. */ + break; + case 0x0440: + /* PERRSET: Pchip Error Set Register. */ + break; + + case 0x0480: + /* TLBIV: Translation Buffer Invalidate Virtual Register. */ + break; + + case 0x04c0: + /* TLBIA: Translation Buffer Invalidate All Register (WO). */ + break; + + case 0x0500: + /* PMONCTL */ + case 0x0540: + /* PMONCNT */ + case 0x0800: + /* SPRST */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); + return; + } +} + +static const MemoryRegionOps cchip_ops = { + .read = cchip_read, + .write = cchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps dchip_ops = { + .read = dchip_read, + .write = dchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static const MemoryRegionOps pchip_ops = { + .read = pchip_read, + .write = pchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void typhoon_set_irq(void *opaque, int irq, int level) +{ + TyphoonState *s = opaque; + uint64_t drir; + int i; + + /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */ + drir = s->cchip.drir; + if (level) { + drir |= 1ull << irq; + } else { + drir &= ~(1ull << irq); + } + s->cchip.drir = drir; + + for (i = 0; i < 4; ++i) { + cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir); + } +} + +static void typhoon_set_isa_irq(void *opaque, int irq, int level) +{ + typhoon_set_irq(opaque, 55, level); +} + +static void typhoon_set_timer_irq(void *opaque, int irq, int level) +{ + TyphoonState *s = opaque; + int i; + + /* Thankfully, the mc146818rtc code doesn't track the IRQ state, + and so we don't have to worry about missing interrupts just + because we never actually ACK the interrupt. Just ignore any + case of the interrupt level going low. */ + if (level == 0) { + return; + } + + /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ + for (i = 0; i < 4; ++i) { + AlphaCPU *cpu = s->cchip.cpu[i]; + if (cpu != NULL) { + uint32_t iic = s->cchip.iic[i]; + + /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. + Bit 24 is the OverFlow bit, RO, and set when the count + decrements past 0. When is OF cleared? My guess is that + OF is actually cleared when the IIC is written, and that + the ICNT field always decrements. At least, that's an + interpretation that makes sense, and "allows the CPU to + determine exactly how mant interval timer ticks were + skipped". At least within the next 4M ticks... */ + + iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000); + s->cchip.iic[i] = iic; + + if (iic & 0x1000000) { + /* Set the ITI bit for this cpu. */ + s->cchip.misc |= 1 << (i + 4); + /* And signal the interrupt. */ + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TIMER); + } + } + } +} + +static void typhoon_alarm_timer(void *opaque) +{ + TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3); + int cpu = (uintptr_t)opaque & 3; + + /* Set the ITI bit for this cpu. */ + s->cchip.misc |= 1 << (cpu + 4); + cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER); +} + +PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, + qemu_irq *p_rtc_irq, + AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) +{ + const uint64_t MB = 1024 * 1024; + const uint64_t GB = 1024 * MB; + MemoryRegion *addr_space = get_system_memory(); + MemoryRegion *addr_space_io = get_system_io(); + DeviceState *dev; + TyphoonState *s; + PCIHostState *phb; + PCIBus *b; + int i; + + dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE); + qdev_init_nofail(dev); + + s = TYPHOON_PCI_HOST_BRIDGE(dev); + phb = PCI_HOST_BRIDGE(dev); + + /* Remember the CPUs so that we can deliver interrupts to them. */ + for (i = 0; i < 4; i++) { + AlphaCPU *cpu = cpus[i]; + s->cchip.cpu[i] = cpu; + if (cpu != NULL) { + cpu->alarm_timer = qemu_new_timer_ns(rtc_clock, + typhoon_alarm_timer, + (void *)((uintptr_t)s + i)); + } + } + + *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); + + /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, + but the address space hole reserved at this point is 8TB. */ + memory_region_init_ram(&s->ram_region, "ram", ram_size); + vmstate_register_ram_global(&s->ram_region); + memory_region_add_subregion(addr_space, 0, &s->ram_region); + + /* TIGbus, 0x801.0000.0000, 1GB. */ + /* ??? The TIGbus is used for delivering interrupts, and access to + the flash ROM. I'm not sure that we need to implement it at all. */ + + /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ + memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x80180000000ULL, + &s->pchip.region); + + /* Cchip CSRs, 0x801.A000.0000, 256MB. */ + memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x801a0000000ULL, + &s->cchip.region); + + /* Dchip CSRs, 0x801.B000.0000, 256MB. */ + memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x801b0000000ULL, + &s->dchip_region); + + /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ + memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB); + memory_region_add_subregion(addr_space, 0x80000000000ULL, + &s->pchip.reg_mem); + + /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ + /* ??? Ideally we drop the "system" i/o space on the floor and give the + PCI subsystem the full address space reserved by the chipset. + We can't do that until the MEM and IO paths in memory.c are unified. */ + memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL, + "pci0-io", 32*MB); + memory_region_add_subregion(addr_space, 0x801fc000000ULL, + &s->pchip.reg_io); + + b = pci_register_bus(dev, "pci", + typhoon_set_irq, sys_map_irq, s, + &s->pchip.reg_mem, addr_space_io, 0, 64, TYPE_PCI_BUS); + phb->bus = b; + + /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ + memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, + "pci0-iack", 64*MB); + memory_region_add_subregion(addr_space, 0x801f8000000ULL, + &s->pchip.reg_iack); + + /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ + memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b, + "pci0-conf", 16*MB); + memory_region_add_subregion(addr_space, 0x801fe000000ULL, + &s->pchip.reg_conf); + + /* For the record, these are the mappings for the second PCI bus. + We can get away with not implementing them because we indicate + via the Cchip.CSC bit that Pchip1 is not present. */ + /* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */ + /* Pchip1 CSRs, 0x802.8000.0000, 256MB. */ + /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */ + /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */ + /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */ + + /* Init the ISA bus. */ + /* ??? Technically there should be a cy82c693ub pci-isa bridge. */ + { + qemu_irq isa_pci_irq, *isa_irqs; + + *isa_bus = isa_bus_new(NULL, addr_space_io); + isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); + isa_irqs = i8259_init(*isa_bus, isa_pci_irq); + isa_bus_irqs(*isa_bus, isa_irqs); + } + + return b; +} + +static int typhoon_pcihost_init(SysBusDevice *dev) +{ + return 0; +} + +static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = typhoon_pcihost_init; + dc->no_user = 1; +} + +static const TypeInfo typhoon_pcihost_info = { + .name = TYPE_TYPHOON_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(TyphoonState), + .class_init = typhoon_pcihost_class_init, +}; + +static void typhoon_register_types(void) +{ + type_register_static(&typhoon_pcihost_info); +} + +type_init(typhoon_register_types) diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c deleted file mode 100644 index 41a0ebc7a3..0000000000 --- a/hw/alpha_typhoon.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. - * - * Written by Richard Henderson. - * - * This work is licensed under the GNU GPL license version 2 or later. - */ - -#include "cpu.h" -#include "exec/exec-all.h" -#include "hw/hw.h" -#include "hw/arm/devices.h" -#include "sysemu/sysemu.h" -#include "hw/alpha_sys.h" -#include "exec/address-spaces.h" - - -#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" - -typedef struct TyphoonCchip { - MemoryRegion region; - uint64_t misc; - uint64_t drir; - uint64_t dim[4]; - uint32_t iic[4]; - AlphaCPU *cpu[4]; -} TyphoonCchip; - -typedef struct TyphoonWindow { - uint32_t base_addr; - uint32_t mask; - uint32_t translated_base_pfn; -} TyphoonWindow; - -typedef struct TyphoonPchip { - MemoryRegion region; - MemoryRegion reg_iack; - MemoryRegion reg_mem; - MemoryRegion reg_io; - MemoryRegion reg_conf; - uint64_t ctl; - TyphoonWindow win[4]; -} TyphoonPchip; - -#define TYPHOON_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE) - -typedef struct TyphoonState { - PCIHostState parent_obj; - - TyphoonCchip cchip; - TyphoonPchip pchip; - MemoryRegion dchip_region; - MemoryRegion ram_region; - - /* QEMU emulation state. */ - uint32_t latch_tmp; -} TyphoonState; - -/* Called when one of DRIR or DIM changes. */ -static void cpu_irq_change(AlphaCPU *cpu, uint64_t req) -{ - /* If there are any non-masked interrupts, tell the cpu. */ - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - if (req) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - -static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) -{ - CPUAlphaState *env = cpu_single_env; - TyphoonState *s = opaque; - CPUState *cpu; - uint64_t ret = 0; - - if (addr & 4) { - return s->latch_tmp; - } - - switch (addr) { - case 0x0000: - /* CSC: Cchip System Configuration Register. */ - /* All sorts of data here; probably the only thing relevant is - PIP<14> Pchip 1 Present = 0. */ - break; - - case 0x0040: - /* MTR: Memory Timing Register. */ - /* All sorts of stuff related to real DRAM. */ - break; - - case 0x0080: - /* MISC: Miscellaneous Register. */ - cpu = ENV_GET_CPU(env); - ret = s->cchip.misc | (cpu->cpu_index & 3); - break; - - case 0x00c0: - /* MPD: Memory Presence Detect Register. */ - break; - - case 0x0100: /* AAR0 */ - case 0x0140: /* AAR1 */ - case 0x0180: /* AAR2 */ - case 0x01c0: /* AAR3 */ - /* AAR: Array Address Register. */ - /* All sorts of information about DRAM. */ - break; - - case 0x0200: - /* DIM0: Device Interrupt Mask Register, CPU0. */ - ret = s->cchip.dim[0]; - break; - case 0x0240: - /* DIM1: Device Interrupt Mask Register, CPU1. */ - ret = s->cchip.dim[1]; - break; - case 0x0280: - /* DIR0: Device Interrupt Request Register, CPU0. */ - ret = s->cchip.dim[0] & s->cchip.drir; - break; - case 0x02c0: - /* DIR1: Device Interrupt Request Register, CPU1. */ - ret = s->cchip.dim[1] & s->cchip.drir; - break; - case 0x0300: - /* DRIR: Device Raw Interrupt Request Register. */ - ret = s->cchip.drir; - break; - - case 0x0340: - /* PRBEN: Probe Enable Register. */ - break; - - case 0x0380: - /* IIC0: Interval Ignore Count Register, CPU0. */ - ret = s->cchip.iic[0]; - break; - case 0x03c0: - /* IIC1: Interval Ignore Count Register, CPU1. */ - ret = s->cchip.iic[1]; - break; - - case 0x0400: /* MPR0 */ - case 0x0440: /* MPR1 */ - case 0x0480: /* MPR2 */ - case 0x04c0: /* MPR3 */ - /* MPR: Memory Programming Register. */ - break; - - case 0x0580: - /* TTR: TIGbus Timing Register. */ - /* All sorts of stuff related to interrupt delivery timings. */ - break; - case 0x05c0: - /* TDR: TIGbug Device Timing Register. */ - break; - - case 0x0600: - /* DIM2: Device Interrupt Mask Register, CPU2. */ - ret = s->cchip.dim[2]; - break; - case 0x0640: - /* DIM3: Device Interrupt Mask Register, CPU3. */ - ret = s->cchip.dim[3]; - break; - case 0x0680: - /* DIR2: Device Interrupt Request Register, CPU2. */ - ret = s->cchip.dim[2] & s->cchip.drir; - break; - case 0x06c0: - /* DIR3: Device Interrupt Request Register, CPU3. */ - ret = s->cchip.dim[3] & s->cchip.drir; - break; - - case 0x0700: - /* IIC2: Interval Ignore Count Register, CPU2. */ - ret = s->cchip.iic[2]; - break; - case 0x0740: - /* IIC3: Interval Ignore Count Register, CPU3. */ - ret = s->cchip.iic[3]; - break; - - case 0x0780: - /* PWR: Power Management Control. */ - break; - - case 0x0c00: /* CMONCTLA */ - case 0x0c40: /* CMONCTLB */ - case 0x0c80: /* CMONCNT01 */ - case 0x0cc0: /* CMONCNT23 */ - break; - - default: - cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); - return -1; - } - - s->latch_tmp = ret >> 32; - return ret; -} - -static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size) -{ - /* Skip this. It's all related to DRAM timing and setup. */ - return 0; -} - -static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) -{ - TyphoonState *s = opaque; - uint64_t ret = 0; - - if (addr & 4) { - return s->latch_tmp; - } - - switch (addr) { - case 0x0000: - /* WSBA0: Window Space Base Address Register. */ - ret = s->pchip.win[0].base_addr; - break; - case 0x0040: - /* WSBA1 */ - ret = s->pchip.win[1].base_addr; - break; - case 0x0080: - /* WSBA2 */ - ret = s->pchip.win[2].base_addr; - break; - case 0x00c0: - /* WSBA3 */ - ret = s->pchip.win[3].base_addr; - break; - - case 0x0100: - /* WSM0: Window Space Mask Register. */ - ret = s->pchip.win[0].mask; - break; - case 0x0140: - /* WSM1 */ - ret = s->pchip.win[1].mask; - break; - case 0x0180: - /* WSM2 */ - ret = s->pchip.win[2].mask; - break; - case 0x01c0: - /* WSM3 */ - ret = s->pchip.win[3].mask; - break; - - case 0x0200: - /* TBA0: Translated Base Address Register. */ - ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10; - break; - case 0x0240: - /* TBA1 */ - ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10; - break; - case 0x0280: - /* TBA2 */ - ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10; - break; - case 0x02c0: - /* TBA3 */ - ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10; - break; - - case 0x0300: - /* PCTL: Pchip Control Register. */ - ret = s->pchip.ctl; - break; - case 0x0340: - /* PLAT: Pchip Master Latency Register. */ - break; - case 0x03c0: - /* PERROR: Pchip Error Register. */ - break; - case 0x0400: - /* PERRMASK: Pchip Error Mask Register. */ - break; - case 0x0440: - /* PERRSET: Pchip Error Set Register. */ - break; - case 0x0480: - /* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */ - break; - case 0x04c0: - /* TLBIA: Translation Buffer Invalidate All Register (WO). */ - break; - case 0x0500: /* PMONCTL */ - case 0x0540: /* PMONCNT */ - case 0x0800: /* SPRST */ - break; - - default: - cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); - return -1; - } - - s->latch_tmp = ret >> 32; - return ret; -} - -static void cchip_write(void *opaque, hwaddr addr, - uint64_t v32, unsigned size) -{ - TyphoonState *s = opaque; - uint64_t val, oldval, newval; - - if (addr & 4) { - val = v32 << 32 | s->latch_tmp; - addr ^= 4; - } else { - s->latch_tmp = v32; - return; - } - - switch (addr) { - case 0x0000: - /* CSC: Cchip System Configuration Register. */ - /* All sorts of data here; nothing relevant RW. */ - break; - - case 0x0040: - /* MTR: Memory Timing Register. */ - /* All sorts of stuff related to real DRAM. */ - break; - - case 0x0080: - /* MISC: Miscellaneous Register. */ - newval = oldval = s->cchip.misc; - newval &= ~(val & 0x10000ff0); /* W1C fields */ - if (val & 0x100000) { - newval &= ~0xff0000ull; /* ACL clears ABT and ABW */ - } else { - newval |= val & 0x00f00000; /* ABT field is W1S */ - if ((newval & 0xf0000) == 0) { - newval |= val & 0xf0000; /* ABW field is W1S iff zero */ - } - } - newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */ - - newval &= ~0xf0000000000ull; /* WO and RW fields */ - newval |= val & 0xf0000000000ull; - s->cchip.misc = newval; - - /* Pass on changes to IPI and ITI state. */ - if ((newval ^ oldval) & 0xff0) { - int i; - for (i = 0; i < 4; ++i) { - AlphaCPU *cpu = s->cchip.cpu[i]; - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - /* IPI can be either cleared or set by the write. */ - if (newval & (1 << (i + 8))) { - cpu_interrupt(cs, CPU_INTERRUPT_SMP); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_SMP); - } - - /* ITI can only be cleared by the write. */ - if ((newval & (1 << (i + 4))) == 0) { - cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); - } - } - } - } - break; - - case 0x00c0: - /* MPD: Memory Presence Detect Register. */ - break; - - case 0x0100: /* AAR0 */ - case 0x0140: /* AAR1 */ - case 0x0180: /* AAR2 */ - case 0x01c0: /* AAR3 */ - /* AAR: Array Address Register. */ - /* All sorts of information about DRAM. */ - break; - - case 0x0200: /* DIM0 */ - /* DIM: Device Interrupt Mask Register, CPU0. */ - s->cchip.dim[0] = val; - cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir); - break; - case 0x0240: /* DIM1 */ - /* DIM: Device Interrupt Mask Register, CPU1. */ - s->cchip.dim[0] = val; - cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir); - break; - - case 0x0280: /* DIR0 (RO) */ - case 0x02c0: /* DIR1 (RO) */ - case 0x0300: /* DRIR (RO) */ - break; - - case 0x0340: - /* PRBEN: Probe Enable Register. */ - break; - - case 0x0380: /* IIC0 */ - s->cchip.iic[0] = val & 0xffffff; - break; - case 0x03c0: /* IIC1 */ - s->cchip.iic[1] = val & 0xffffff; - break; - - case 0x0400: /* MPR0 */ - case 0x0440: /* MPR1 */ - case 0x0480: /* MPR2 */ - case 0x04c0: /* MPR3 */ - /* MPR: Memory Programming Register. */ - break; - - case 0x0580: - /* TTR: TIGbus Timing Register. */ - /* All sorts of stuff related to interrupt delivery timings. */ - break; - case 0x05c0: - /* TDR: TIGbug Device Timing Register. */ - break; - - case 0x0600: - /* DIM2: Device Interrupt Mask Register, CPU2. */ - s->cchip.dim[2] = val; - cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir); - break; - case 0x0640: - /* DIM3: Device Interrupt Mask Register, CPU3. */ - s->cchip.dim[3] = val; - cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir); - break; - - case 0x0680: /* DIR2 (RO) */ - case 0x06c0: /* DIR3 (RO) */ - break; - - case 0x0700: /* IIC2 */ - s->cchip.iic[2] = val & 0xffffff; - break; - case 0x0740: /* IIC3 */ - s->cchip.iic[3] = val & 0xffffff; - break; - - case 0x0780: - /* PWR: Power Management Control. */ - break; - - case 0x0c00: /* CMONCTLA */ - case 0x0c40: /* CMONCTLB */ - case 0x0c80: /* CMONCNT01 */ - case 0x0cc0: /* CMONCNT23 */ - break; - - default: - cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); - return; - } -} - -static void dchip_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* Skip this. It's all related to DRAM timing and setup. */ -} - -static void pchip_write(void *opaque, hwaddr addr, - uint64_t v32, unsigned size) -{ - TyphoonState *s = opaque; - uint64_t val, oldval; - - if (addr & 4) { - val = v32 << 32 | s->latch_tmp; - addr ^= 4; - } else { - s->latch_tmp = v32; - return; - } - - switch (addr) { - case 0x0000: - /* WSBA0: Window Space Base Address Register. */ - s->pchip.win[0].base_addr = val; - break; - case 0x0040: - /* WSBA1 */ - s->pchip.win[1].base_addr = val; - break; - case 0x0080: - /* WSBA2 */ - s->pchip.win[2].base_addr = val; - break; - case 0x00c0: - /* WSBA3 */ - s->pchip.win[3].base_addr = val; - break; - - case 0x0100: - /* WSM0: Window Space Mask Register. */ - s->pchip.win[0].mask = val; - break; - case 0x0140: - /* WSM1 */ - s->pchip.win[1].mask = val; - break; - case 0x0180: - /* WSM2 */ - s->pchip.win[2].mask = val; - break; - case 0x01c0: - /* WSM3 */ - s->pchip.win[3].mask = val; - break; - - case 0x0200: - /* TBA0: Translated Base Address Register. */ - s->pchip.win[0].translated_base_pfn = val >> 10; - break; - case 0x0240: - /* TBA1 */ - s->pchip.win[1].translated_base_pfn = val >> 10; - break; - case 0x0280: - /* TBA2 */ - s->pchip.win[2].translated_base_pfn = val >> 10; - break; - case 0x02c0: - /* TBA3 */ - s->pchip.win[3].translated_base_pfn = val >> 10; - break; - - case 0x0300: - /* PCTL: Pchip Control Register. */ - oldval = s->pchip.ctl; - oldval &= ~0x00001cff0fc7ffull; /* RW fields */ - oldval |= val & 0x00001cff0fc7ffull; - - s->pchip.ctl = oldval; - break; - - case 0x0340: - /* PLAT: Pchip Master Latency Register. */ - break; - case 0x03c0: - /* PERROR: Pchip Error Register. */ - break; - case 0x0400: - /* PERRMASK: Pchip Error Mask Register. */ - break; - case 0x0440: - /* PERRSET: Pchip Error Set Register. */ - break; - - case 0x0480: - /* TLBIV: Translation Buffer Invalidate Virtual Register. */ - break; - - case 0x04c0: - /* TLBIA: Translation Buffer Invalidate All Register (WO). */ - break; - - case 0x0500: - /* PMONCTL */ - case 0x0540: - /* PMONCNT */ - case 0x0800: - /* SPRST */ - break; - - default: - cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); - return; - } -} - -static const MemoryRegionOps cchip_ops = { - .read = cchip_read, - .write = cchip_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, /* ??? Should be 8. */ - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps dchip_ops = { - .read = dchip_read, - .write = dchip_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, /* ??? Should be 8. */ - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 8, - }, -}; - -static const MemoryRegionOps pchip_ops = { - .read = pchip_read, - .write = pchip_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, /* ??? Should be 8. */ - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void typhoon_set_irq(void *opaque, int irq, int level) -{ - TyphoonState *s = opaque; - uint64_t drir; - int i; - - /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */ - drir = s->cchip.drir; - if (level) { - drir |= 1ull << irq; - } else { - drir &= ~(1ull << irq); - } - s->cchip.drir = drir; - - for (i = 0; i < 4; ++i) { - cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir); - } -} - -static void typhoon_set_isa_irq(void *opaque, int irq, int level) -{ - typhoon_set_irq(opaque, 55, level); -} - -static void typhoon_set_timer_irq(void *opaque, int irq, int level) -{ - TyphoonState *s = opaque; - int i; - - /* Thankfully, the mc146818rtc code doesn't track the IRQ state, - and so we don't have to worry about missing interrupts just - because we never actually ACK the interrupt. Just ignore any - case of the interrupt level going low. */ - if (level == 0) { - return; - } - - /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ - for (i = 0; i < 4; ++i) { - AlphaCPU *cpu = s->cchip.cpu[i]; - if (cpu != NULL) { - uint32_t iic = s->cchip.iic[i]; - - /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. - Bit 24 is the OverFlow bit, RO, and set when the count - decrements past 0. When is OF cleared? My guess is that - OF is actually cleared when the IIC is written, and that - the ICNT field always decrements. At least, that's an - interpretation that makes sense, and "allows the CPU to - determine exactly how mant interval timer ticks were - skipped". At least within the next 4M ticks... */ - - iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000); - s->cchip.iic[i] = iic; - - if (iic & 0x1000000) { - /* Set the ITI bit for this cpu. */ - s->cchip.misc |= 1 << (i + 4); - /* And signal the interrupt. */ - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TIMER); - } - } - } -} - -static void typhoon_alarm_timer(void *opaque) -{ - TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3); - int cpu = (uintptr_t)opaque & 3; - - /* Set the ITI bit for this cpu. */ - s->cchip.misc |= 1 << (cpu + 4); - cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER); -} - -PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, - qemu_irq *p_rtc_irq, - AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) -{ - const uint64_t MB = 1024 * 1024; - const uint64_t GB = 1024 * MB; - MemoryRegion *addr_space = get_system_memory(); - MemoryRegion *addr_space_io = get_system_io(); - DeviceState *dev; - TyphoonState *s; - PCIHostState *phb; - PCIBus *b; - int i; - - dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - - s = TYPHOON_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(dev); - - /* Remember the CPUs so that we can deliver interrupts to them. */ - for (i = 0; i < 4; i++) { - AlphaCPU *cpu = cpus[i]; - s->cchip.cpu[i] = cpu; - if (cpu != NULL) { - cpu->alarm_timer = qemu_new_timer_ns(rtc_clock, - typhoon_alarm_timer, - (void *)((uintptr_t)s + i)); - } - } - - *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); - - /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, - but the address space hole reserved at this point is 8TB. */ - memory_region_init_ram(&s->ram_region, "ram", ram_size); - vmstate_register_ram_global(&s->ram_region); - memory_region_add_subregion(addr_space, 0, &s->ram_region); - - /* TIGbus, 0x801.0000.0000, 1GB. */ - /* ??? The TIGbus is used for delivering interrupts, and access to - the flash ROM. I'm not sure that we need to implement it at all. */ - - /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ - memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB); - memory_region_add_subregion(addr_space, 0x80180000000ULL, - &s->pchip.region); - - /* Cchip CSRs, 0x801.A000.0000, 256MB. */ - memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB); - memory_region_add_subregion(addr_space, 0x801a0000000ULL, - &s->cchip.region); - - /* Dchip CSRs, 0x801.B000.0000, 256MB. */ - memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB); - memory_region_add_subregion(addr_space, 0x801b0000000ULL, - &s->dchip_region); - - /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ - memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB); - memory_region_add_subregion(addr_space, 0x80000000000ULL, - &s->pchip.reg_mem); - - /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ - /* ??? Ideally we drop the "system" i/o space on the floor and give the - PCI subsystem the full address space reserved by the chipset. - We can't do that until the MEM and IO paths in memory.c are unified. */ - memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL, - "pci0-io", 32*MB); - memory_region_add_subregion(addr_space, 0x801fc000000ULL, - &s->pchip.reg_io); - - b = pci_register_bus(dev, "pci", - typhoon_set_irq, sys_map_irq, s, - &s->pchip.reg_mem, addr_space_io, 0, 64, TYPE_PCI_BUS); - phb->bus = b; - - /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ - memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, - "pci0-iack", 64*MB); - memory_region_add_subregion(addr_space, 0x801f8000000ULL, - &s->pchip.reg_iack); - - /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ - memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b, - "pci0-conf", 16*MB); - memory_region_add_subregion(addr_space, 0x801fe000000ULL, - &s->pchip.reg_conf); - - /* For the record, these are the mappings for the second PCI bus. - We can get away with not implementing them because we indicate - via the Cchip.CSC bit that Pchip1 is not present. */ - /* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */ - /* Pchip1 CSRs, 0x802.8000.0000, 256MB. */ - /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */ - /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */ - /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */ - - /* Init the ISA bus. */ - /* ??? Technically there should be a cy82c693ub pci-isa bridge. */ - { - qemu_irq isa_pci_irq, *isa_irqs; - - *isa_bus = isa_bus_new(NULL, addr_space_io); - isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); - isa_irqs = i8259_init(*isa_bus, isa_pci_irq); - isa_bus_irqs(*isa_bus, isa_irqs); - } - - return b; -} - -static int typhoon_pcihost_init(SysBusDevice *dev) -{ - return 0; -} - -static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = typhoon_pcihost_init; - dc->no_user = 1; -} - -static const TypeInfo typhoon_pcihost_info = { - .name = TYPE_TYPHOON_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(TyphoonState), - .class_init = typhoon_pcihost_class_init, -}; - -static void typhoon_register_types(void) -{ - type_register_static(&typhoon_pcihost_info); -} - -type_init(typhoon_register_types) diff --git a/hw/apb_pci.c b/hw/apb_pci.c deleted file mode 100644 index fe15ae8f8a..0000000000 --- a/hw/apb_pci.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * QEMU Ultrasparc APB PCI host - * - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* XXX This file and most of its contents are somewhat misnamed. The - Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is - the secondary PCI bridge. */ - -#include "hw/sysbus.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci-host/apb.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" - -/* debug APB */ -//#define DEBUG_APB - -#ifdef DEBUG_APB -#define APB_DPRINTF(fmt, ...) \ -do { printf("APB: " fmt , ## __VA_ARGS__); } while (0) -#else -#define APB_DPRINTF(fmt, ...) -#endif - -/* - * Chipset docs: - * PBM: "UltraSPARC IIi User's Manual", - * http://www.sun.com/processors/manuals/805-0087.pdf - * - * APB: "Advanced PCI Bridge (APB) User's Manual", - * http://www.sun.com/processors/manuals/805-1251.pdf - */ - -#define PBM_PCI_IMR_MASK 0x7fffffff -#define PBM_PCI_IMR_ENABLED 0x80000000 - -#define POR (1 << 31) -#define SOFT_POR (1 << 30) -#define SOFT_XIR (1 << 29) -#define BTN_POR (1 << 28) -#define BTN_XIR (1 << 27) -#define RESET_MASK 0xf8000000 -#define RESET_WCMASK 0x98000000 -#define RESET_WMASK 0x60000000 - -#define MAX_IVEC 0x30 - -typedef struct APBState { - SysBusDevice busdev; - PCIBus *bus; - MemoryRegion apb_config; - MemoryRegion pci_config; - MemoryRegion pci_mmio; - MemoryRegion pci_ioport; - uint32_t iommu[4]; - uint32_t pci_control[16]; - uint32_t pci_irq_map[8]; - uint32_t obio_irq_map[32]; - qemu_irq *pbm_irqs; - qemu_irq *ivec_irqs; - uint32_t reset_control; - unsigned int nr_resets; -} APBState; - -static void pci_apb_set_irq(void *opaque, int irq_num, int level); - -static void apb_config_writel (void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - APBState *s = opaque; - - APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val); - - switch (addr & 0xffff) { - case 0x30 ... 0x4f: /* DMA error registers */ - /* XXX: not implemented yet */ - break; - case 0x200 ... 0x20b: /* IOMMU */ - s->iommu[(addr & 0xf) >> 2] = val; - break; - case 0x20c ... 0x3ff: /* IOMMU flush */ - break; - case 0xc00 ... 0xc3f: /* PCI interrupt control */ - if (addr & 4) { - s->pci_irq_map[(addr & 0x3f) >> 3] &= PBM_PCI_IMR_MASK; - s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK; - } - break; - case 0x1000 ... 0x1080: /* OBIO interrupt control */ - if (addr & 4) { - s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK; - s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK; - } - break; - case 0x1400 ... 0x143f: /* PCI interrupt clear */ - if (addr & 4) { - pci_apb_set_irq(s, (addr & 0x3f) >> 3, 0); - } - break; - case 0x1800 ... 0x1860: /* OBIO interrupt clear */ - if (addr & 4) { - pci_apb_set_irq(s, 0x20 | ((addr & 0xff) >> 3), 0); - } - break; - case 0x2000 ... 0x202f: /* PCI control */ - s->pci_control[(addr & 0x3f) >> 2] = val; - break; - case 0xf020 ... 0xf027: /* Reset control */ - if (addr & 4) { - val &= RESET_MASK; - s->reset_control &= ~(val & RESET_WCMASK); - s->reset_control |= val & RESET_WMASK; - if (val & SOFT_POR) { - s->nr_resets = 0; - qemu_system_reset_request(); - } else if (val & SOFT_XIR) { - qemu_system_reset_request(); - } - } - break; - case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ - case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ - case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ - case 0xf000 ... 0xf01f: /* FFB config, memory control */ - /* we don't care */ - default: - break; - } -} - -static uint64_t apb_config_readl (void *opaque, - hwaddr addr, unsigned size) -{ - APBState *s = opaque; - uint32_t val; - - switch (addr & 0xffff) { - case 0x30 ... 0x4f: /* DMA error registers */ - val = 0; - /* XXX: not implemented yet */ - break; - case 0x200 ... 0x20b: /* IOMMU */ - val = s->iommu[(addr & 0xf) >> 2]; - break; - case 0x20c ... 0x3ff: /* IOMMU flush */ - val = 0; - break; - case 0xc00 ... 0xc3f: /* PCI interrupt control */ - if (addr & 4) { - val = s->pci_irq_map[(addr & 0x3f) >> 3]; - } else { - val = 0; - } - break; - case 0x1000 ... 0x1080: /* OBIO interrupt control */ - if (addr & 4) { - val = s->obio_irq_map[(addr & 0xff) >> 3]; - } else { - val = 0; - } - break; - case 0x2000 ... 0x202f: /* PCI control */ - val = s->pci_control[(addr & 0x3f) >> 2]; - break; - case 0xf020 ... 0xf027: /* Reset control */ - if (addr & 4) { - val = s->reset_control; - } else { - val = 0; - } - break; - case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ - case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ - case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ - case 0xf000 ... 0xf01f: /* FFB config, memory control */ - /* we don't care */ - default: - val = 0; - break; - } - APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, val); - - return val; -} - -static const MemoryRegionOps apb_config_ops = { - .read = apb_config_readl, - .write = apb_config_writel, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void apb_pci_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - APBState *s = opaque; - - val = qemu_bswap_len(val, size); - APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val); - pci_data_write(s->bus, addr, val, size); -} - -static uint64_t apb_pci_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t ret; - APBState *s = opaque; - - ret = pci_data_read(s->bus, addr, size); - ret = qemu_bswap_len(ret, size); - APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, ret); - return ret; -} - -static void pci_apb_iowriteb (void *opaque, hwaddr addr, - uint32_t val) -{ - cpu_outb(addr & IOPORTS_MASK, val); -} - -static void pci_apb_iowritew (void *opaque, hwaddr addr, - uint32_t val) -{ - cpu_outw(addr & IOPORTS_MASK, bswap16(val)); -} - -static void pci_apb_iowritel (void *opaque, hwaddr addr, - uint32_t val) -{ - cpu_outl(addr & IOPORTS_MASK, bswap32(val)); -} - -static uint32_t pci_apb_ioreadb (void *opaque, hwaddr addr) -{ - uint32_t val; - - val = cpu_inb(addr & IOPORTS_MASK); - return val; -} - -static uint32_t pci_apb_ioreadw (void *opaque, hwaddr addr) -{ - uint32_t val; - - val = bswap16(cpu_inw(addr & IOPORTS_MASK)); - return val; -} - -static uint32_t pci_apb_ioreadl (void *opaque, hwaddr addr) -{ - uint32_t val; - - val = bswap32(cpu_inl(addr & IOPORTS_MASK)); - return val; -} - -static const MemoryRegionOps pci_ioport_ops = { - .old_mmio = { - .read = { pci_apb_ioreadb, pci_apb_ioreadw, pci_apb_ioreadl }, - .write = { pci_apb_iowriteb, pci_apb_iowritew, pci_apb_iowritel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* The APB host has an IRQ line for each IRQ line of each slot. */ -static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return ((pci_dev->devfn & 0x18) >> 1) + irq_num; -} - -static int pci_pbm_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int bus_offset; - if (pci_dev->devfn & 1) - bus_offset = 16; - else - bus_offset = 0; - return bus_offset + irq_num; -} - -static void pci_apb_set_irq(void *opaque, int irq_num, int level) -{ - APBState *s = opaque; - - /* PCI IRQ map onto the first 32 INO. */ - if (irq_num < 32) { - if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) { - APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); - qemu_set_irq(s->ivec_irqs[irq_num], level); - } else { - APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); - qemu_irq_lower(s->ivec_irqs[irq_num]); - } - } else { - /* OBIO IRQ map onto the next 16 INO. */ - if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) { - APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); - qemu_set_irq(s->ivec_irqs[irq_num], level); - } else { - APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); - qemu_irq_lower(s->ivec_irqs[irq_num]); - } - } -} - -static int apb_pci_bridge_initfn(PCIDevice *dev) -{ - int rc; - - rc = pci_bridge_initfn(dev, TYPE_PCI_BUS); - if (rc < 0) { - return rc; - } - - /* - * command register: - * According to PCI bridge spec, after reset - * bus master bit is off - * memory space enable bit is off - * According to manual (805-1251.pdf). - * the reset value should be zero unless the boot pin is tied high - * (which is true) and thus it should be PCI_COMMAND_MEMORY. - */ - pci_set_word(dev->config + PCI_COMMAND, - PCI_COMMAND_MEMORY); - pci_set_word(dev->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | - PCI_STATUS_DEVSEL_MEDIUM); - return 0; -} - -PCIBus *pci_apb_init(hwaddr special_base, - hwaddr mem_base, - qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3, - qemu_irq **pbm_irqs) -{ - DeviceState *dev; - SysBusDevice *s; - APBState *d; - PCIDevice *pci_dev; - PCIBridge *br; - - /* Ultrasparc PBM main bus */ - dev = qdev_create(NULL, "pbm"); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - /* apb_config */ - sysbus_mmio_map(s, 0, special_base); - /* PCI configuration space */ - sysbus_mmio_map(s, 1, special_base + 0x1000000ULL); - /* pci_ioport */ - sysbus_mmio_map(s, 2, special_base + 0x2000000ULL); - d = FROM_SYSBUS(APBState, s); - - memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); - memory_region_add_subregion(get_system_memory(), mem_base, &d->pci_mmio); - - d->bus = pci_register_bus(&d->busdev.qdev, "pci", - pci_apb_set_irq, pci_pbm_map_irq, d, - &d->pci_mmio, - get_system_io(), - 0, 32, TYPE_PCI_BUS); - - *pbm_irqs = d->pbm_irqs; - d->ivec_irqs = ivec_irqs; - - pci_create_simple(d->bus, 0, "pbm-pci"); - - /* APB secondary busses */ - pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true, - "pbm-bridge"); - br = DO_UPCAST(PCIBridge, dev, pci_dev); - pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1", - pci_apb_map_irq); - qdev_init_nofail(&pci_dev->qdev); - *bus2 = pci_bridge_get_sec_bus(br); - - pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true, - "pbm-bridge"); - br = DO_UPCAST(PCIBridge, dev, pci_dev); - pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2", - pci_apb_map_irq); - qdev_init_nofail(&pci_dev->qdev); - *bus3 = pci_bridge_get_sec_bus(br); - - return d->bus; -} - -static void pci_pbm_reset(DeviceState *d) -{ - unsigned int i; - APBState *s = container_of(d, APBState, busdev.qdev); - - for (i = 0; i < 8; i++) { - s->pci_irq_map[i] &= PBM_PCI_IMR_MASK; - } - for (i = 0; i < 32; i++) { - s->obio_irq_map[i] &= PBM_PCI_IMR_MASK; - } - - if (s->nr_resets++ == 0) { - /* Power on reset */ - s->reset_control = POR; - } -} - -static const MemoryRegionOps pci_config_ops = { - .read = apb_pci_config_read, - .write = apb_pci_config_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pci_pbm_init_device(SysBusDevice *dev) -{ - APBState *s; - unsigned int i; - - s = FROM_SYSBUS(APBState, dev); - for (i = 0; i < 8; i++) { - s->pci_irq_map[i] = (0x1f << 6) | (i << 2); - } - for (i = 0; i < 32; i++) { - s->obio_irq_map[i] = ((0x1f << 6) | 0x20) + i; - } - s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC); - - /* apb_config */ - memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config", - 0x10000); - /* at region 0 */ - sysbus_init_mmio(dev, &s->apb_config); - - memory_region_init_io(&s->pci_config, &pci_config_ops, s, "apb-pci-config", - 0x1000000); - /* at region 1 */ - sysbus_init_mmio(dev, &s->pci_config); - - /* pci_ioport */ - memory_region_init_io(&s->pci_ioport, &pci_ioport_ops, s, - "apb-pci-ioport", 0x10000); - /* at region 2 */ - sysbus_init_mmio(dev, &s->pci_ioport); - - return 0; -} - -static int pbm_pci_host_init(PCIDevice *d) -{ - pci_set_word(d->config + PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | - PCI_STATUS_DEVSEL_MEDIUM); - return 0; -} - -static void pbm_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = pbm_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_SUN; - k->device_id = PCI_DEVICE_ID_SUN_SABRE; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo pbm_pci_host_info = { - .name = "pbm-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = pbm_pci_host_class_init, -}; - -static void pbm_host_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pci_pbm_init_device; - dc->reset = pci_pbm_reset; -} - -static const TypeInfo pbm_host_info = { - .name = "pbm", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(APBState), - .class_init = pbm_host_class_init, -}; - -static void pbm_pci_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = apb_pci_bridge_initfn; - k->exit = pci_bridge_exitfn; - k->vendor_id = PCI_VENDOR_ID_SUN; - k->device_id = PCI_DEVICE_ID_SUN_SIMBA; - k->revision = 0x11; - k->config_write = pci_bridge_write_config; - k->is_bridge = 1; - dc->reset = pci_bridge_reset; - dc->vmsd = &vmstate_pci_device; -} - -static const TypeInfo pbm_pci_bridge_info = { - .name = "pbm-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIBridge), - .class_init = pbm_pci_bridge_class_init, -}; - -static void pbm_register_types(void) -{ - type_register_static(&pbm_host_info); - type_register_static(&pbm_pci_host_info); - type_register_static(&pbm_pci_bridge_info); -} - -type_init(pbm_register_types) diff --git a/hw/bonito.c b/hw/bonito.c deleted file mode 100644 index 974150bfc1..0000000000 --- a/hw/bonito.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - * bonito north bridge support - * - * Copyright (c) 2008 yajin (yajin@vm-kernel.org) - * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* - * fulong 2e mini pc has a bonito north bridge. - */ - -/* what is the meaning of devfn in qemu and IDSEL in bonito northbridge? - * - * devfn pci_slot<<3 + funno - * one pci bus can have 32 devices and each device can have 8 functions. - * - * In bonito north bridge, pci slot = IDSEL bit - 12. - * For example, PCI_IDSEL_VIA686B = 17, - * pci slot = 17-12=5 - * - * so - * VT686B_FUN0's devfn = (5<<3)+0 - * VT686B_FUN1's devfn = (5<<3)+1 - * - * qemu also uses pci address for north bridge to access pci config register. - * bus_no [23:16] - * dev_no [15:11] - * fun_no [10:8] - * reg_no [7:2] - * - * so function bonito_sbridge_pciaddr for the translation from - * north bridge address to pci address. - */ - -#include - -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/i386/pc.h" -#include "hw/mips/mips.h" -#include "hw/pci/pci_host.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" - -//#define DEBUG_BONITO - -#ifdef DEBUG_BONITO -#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif - -/* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/ -#define BONITO_BOOT_BASE 0x1fc00000 -#define BONITO_BOOT_SIZE 0x00100000 -#define BONITO_BOOT_TOP (BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1) -#define BONITO_FLASH_BASE 0x1c000000 -#define BONITO_FLASH_SIZE 0x03000000 -#define BONITO_FLASH_TOP (BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1) -#define BONITO_SOCKET_BASE 0x1f800000 -#define BONITO_SOCKET_SIZE 0x00400000 -#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1) -#define BONITO_REG_BASE 0x1fe00000 -#define BONITO_REG_SIZE 0x00040000 -#define BONITO_REG_TOP (BONITO_REG_BASE+BONITO_REG_SIZE-1) -#define BONITO_DEV_BASE 0x1ff00000 -#define BONITO_DEV_SIZE 0x00100000 -#define BONITO_DEV_TOP (BONITO_DEV_BASE+BONITO_DEV_SIZE-1) -#define BONITO_PCILO_BASE 0x10000000 -#define BONITO_PCILO_BASE_VA 0xb0000000 -#define BONITO_PCILO_SIZE 0x0c000000 -#define BONITO_PCILO_TOP (BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1) -#define BONITO_PCILO0_BASE 0x10000000 -#define BONITO_PCILO1_BASE 0x14000000 -#define BONITO_PCILO2_BASE 0x18000000 -#define BONITO_PCIHI_BASE 0x20000000 -#define BONITO_PCIHI_SIZE 0x20000000 -#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1) -#define BONITO_PCIIO_BASE 0x1fd00000 -#define BONITO_PCIIO_BASE_VA 0xbfd00000 -#define BONITO_PCIIO_SIZE 0x00010000 -#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1) -#define BONITO_PCICFG_BASE 0x1fe80000 -#define BONITO_PCICFG_SIZE 0x00080000 -#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1) - - -#define BONITO_PCICONFIGBASE 0x00 -#define BONITO_REGBASE 0x100 - -#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE+BONITO_REG_BASE) -#define BONITO_PCICONFIG_SIZE (0x100) - -#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE+BONITO_REG_BASE) -#define BONITO_INTERNAL_REG_SIZE (0x70) - -#define BONITO_SPCICONFIG_BASE (BONITO_PCICFG_BASE) -#define BONITO_SPCICONFIG_SIZE (BONITO_PCICFG_SIZE) - - - -/* 1. Bonito h/w Configuration */ -/* Power on register */ - -#define BONITO_BONPONCFG (0x00 >> 2) /* 0x100 */ -#define BONITO_BONGENCFG_OFFSET 0x4 -#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET>>2) /*0x104 */ - -/* 2. IO & IDE configuration */ -#define BONITO_IODEVCFG (0x08 >> 2) /* 0x108 */ - -/* 3. IO & IDE configuration */ -#define BONITO_SDCFG (0x0c >> 2) /* 0x10c */ - -/* 4. PCI address map control */ -#define BONITO_PCIMAP (0x10 >> 2) /* 0x110 */ -#define BONITO_PCIMEMBASECFG (0x14 >> 2) /* 0x114 */ -#define BONITO_PCIMAP_CFG (0x18 >> 2) /* 0x118 */ - -/* 5. ICU & GPIO regs */ -/* GPIO Regs - r/w */ -#define BONITO_GPIODATA_OFFSET 0x1c -#define BONITO_GPIODATA (BONITO_GPIODATA_OFFSET >> 2) /* 0x11c */ -#define BONITO_GPIOIE (0x20 >> 2) /* 0x120 */ - -/* ICU Configuration Regs - r/w */ -#define BONITO_INTEDGE (0x24 >> 2) /* 0x124 */ -#define BONITO_INTSTEER (0x28 >> 2) /* 0x128 */ -#define BONITO_INTPOL (0x2c >> 2) /* 0x12c */ - -/* ICU Enable Regs - IntEn & IntISR are r/o. */ -#define BONITO_INTENSET (0x30 >> 2) /* 0x130 */ -#define BONITO_INTENCLR (0x34 >> 2) /* 0x134 */ -#define BONITO_INTEN (0x38 >> 2) /* 0x138 */ -#define BONITO_INTISR (0x3c >> 2) /* 0x13c */ - -/* PCI mail boxes */ -#define BONITO_PCIMAIL0_OFFSET 0x40 -#define BONITO_PCIMAIL1_OFFSET 0x44 -#define BONITO_PCIMAIL2_OFFSET 0x48 -#define BONITO_PCIMAIL3_OFFSET 0x4c -#define BONITO_PCIMAIL0 (0x40 >> 2) /* 0x140 */ -#define BONITO_PCIMAIL1 (0x44 >> 2) /* 0x144 */ -#define BONITO_PCIMAIL2 (0x48 >> 2) /* 0x148 */ -#define BONITO_PCIMAIL3 (0x4c >> 2) /* 0x14c */ - -/* 6. PCI cache */ -#define BONITO_PCICACHECTRL (0x50 >> 2) /* 0x150 */ -#define BONITO_PCICACHETAG (0x54 >> 2) /* 0x154 */ -#define BONITO_PCIBADADDR (0x58 >> 2) /* 0x158 */ -#define BONITO_PCIMSTAT (0x5c >> 2) /* 0x15c */ - -/* 7. other*/ -#define BONITO_TIMECFG (0x60 >> 2) /* 0x160 */ -#define BONITO_CPUCFG (0x64 >> 2) /* 0x164 */ -#define BONITO_DQCFG (0x68 >> 2) /* 0x168 */ -#define BONITO_MEMSIZE (0x6C >> 2) /* 0x16c */ - -#define BONITO_REGS (0x70 >> 2) - -/* PCI config for south bridge. type 0 */ -#define BONITO_PCICONF_IDSEL_MASK 0xfffff800 /* [31:11] */ -#define BONITO_PCICONF_IDSEL_OFFSET 11 -#define BONITO_PCICONF_FUN_MASK 0x700 /* [10:8] */ -#define BONITO_PCICONF_FUN_OFFSET 8 -#define BONITO_PCICONF_REG_MASK 0xFC -#define BONITO_PCICONF_REG_OFFSET 0 - - -/* idsel BIT = pci slot number +12 */ -#define PCI_SLOT_BASE 12 -#define PCI_IDSEL_VIA686B_BIT (17) -#define PCI_IDSEL_VIA686B (1<> 2; - - DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x\n", addr, val, saddr); - switch (saddr) { - case BONITO_BONPONCFG: - case BONITO_IODEVCFG: - case BONITO_SDCFG: - case BONITO_PCIMAP: - case BONITO_PCIMEMBASECFG: - case BONITO_PCIMAP_CFG: - case BONITO_GPIODATA: - case BONITO_GPIOIE: - case BONITO_INTEDGE: - case BONITO_INTSTEER: - case BONITO_INTPOL: - case BONITO_PCIMAIL0: - case BONITO_PCIMAIL1: - case BONITO_PCIMAIL2: - case BONITO_PCIMAIL3: - case BONITO_PCICACHECTRL: - case BONITO_PCICACHETAG: - case BONITO_PCIBADADDR: - case BONITO_PCIMSTAT: - case BONITO_TIMECFG: - case BONITO_CPUCFG: - case BONITO_DQCFG: - case BONITO_MEMSIZE: - s->regs[saddr] = val; - break; - case BONITO_BONGENCFG: - if (!(s->regs[saddr] & 0x04) && (val & 0x04)) { - reset = 1; /* bit 2 jump from 0 to 1 cause reset */ - } - s->regs[saddr] = val; - if (reset) { - qemu_system_reset_request(); - } - break; - case BONITO_INTENSET: - s->regs[BONITO_INTENSET] = val; - s->regs[BONITO_INTEN] |= val; - break; - case BONITO_INTENCLR: - s->regs[BONITO_INTENCLR] = val; - s->regs[BONITO_INTEN] &= ~val; - break; - case BONITO_INTEN: - case BONITO_INTISR: - DPRINTF("write to readonly bonito register %x\n", saddr); - break; - default: - DPRINTF("write to unknown bonito register %x\n", saddr); - break; - } -} - -static uint64_t bonito_readl(void *opaque, hwaddr addr, - unsigned size) -{ - PCIBonitoState *s = opaque; - uint32_t saddr; - - saddr = (addr - BONITO_REGBASE) >> 2; - - DPRINTF("bonito_readl "TARGET_FMT_plx"\n", addr); - switch (saddr) { - case BONITO_INTISR: - return s->regs[saddr]; - default: - return s->regs[saddr]; - } -} - -static const MemoryRegionOps bonito_ops = { - .read = bonito_readl, - .write = bonito_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void bonito_pciconf_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - - DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - d->config_write(d, addr, val, 4); -} - -static uint64_t bonito_pciconf_readl(void *opaque, hwaddr addr, - unsigned size) -{ - - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - - DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr); - return d->config_read(d, addr, 4); -} - -/* north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */ - -static const MemoryRegionOps bonito_pciconf_ops = { - .read = bonito_pciconf_readl, - .write = bonito_pciconf_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t val; - PCIBonitoState *s = opaque; - - val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)]; - - return val; -} - -static void bonito_ldma_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBonitoState *s = opaque; - - ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff; -} - -static const MemoryRegionOps bonito_ldma_ops = { - .read = bonito_ldma_readl, - .write = bonito_ldma_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t bonito_cop_readl(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t val; - PCIBonitoState *s = opaque; - - val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)]; - - return val; -} - -static void bonito_cop_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBonitoState *s = opaque; - - ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff; -} - -static const MemoryRegionOps bonito_cop_ops = { - .read = bonito_cop_readl, - .write = bonito_cop_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t cfgaddr; - uint32_t idsel; - uint32_t devno; - uint32_t funno; - uint32_t regno; - uint32_t pciaddr; - - /* support type0 pci config */ - if ((s->regs[BONITO_PCIMAP_CFG] & 0x10000) != 0x0) { - return 0xffffffff; - } - - cfgaddr = addr & 0xffff; - cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16; - - idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET; - devno = ffs(idsel) - 1; - funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET; - regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET; - - if (idsel == 0) { - fprintf(stderr, "error in bonito pci config address " TARGET_FMT_plx - ",pcimap_cfg=%x\n", addr, s->regs[BONITO_PCIMAP_CFG]); - exit(1); - } - pciaddr = PCI_ADDR(pci_bus_num(phb->bus), devno, funno, regno); - DPRINTF("cfgaddr %x pciaddr %x busno %x devno %d funno %d regno %d\n", - cfgaddr, pciaddr, pci_bus_num(phb->bus), devno, funno, regno); - - return pciaddr; -} - -static void bonito_spciconf_writeb(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writeb "TARGET_FMT_plx" val %x\n", addr, val); - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val & 0xff, 1); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static void bonito_spciconf_writew(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr & 0x1) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val, 2); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static void bonito_spciconf_writel(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr & 0x3) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val, 4); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static uint32_t bonito_spciconf_readb(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readb "TARGET_FMT_plx"\n", addr); - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 1); -} - -static uint32_t bonito_spciconf_readw(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx"\n", addr); - assert((addr & 0x1) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xffff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 2); -} - -static uint32_t bonito_spciconf_readl(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx"\n", addr); - assert((addr & 0x3) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xffffffff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 4); -} - -/* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */ -static const MemoryRegionOps bonito_spciconf_ops = { - .old_mmio = { - .read = { - bonito_spciconf_readb, - bonito_spciconf_readw, - bonito_spciconf_readl, - }, - .write = { - bonito_spciconf_writeb, - bonito_spciconf_writew, - bonito_spciconf_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define BONITO_IRQ_BASE 32 - -static void pci_bonito_set_irq(void *opaque, int irq_num, int level) -{ - BonitoState *s = opaque; - qemu_irq *pic = s->pic; - PCIBonitoState *bonito_state = s->pci_dev; - int internal_irq = irq_num - BONITO_IRQ_BASE; - - if (bonito_state->regs[BONITO_INTEDGE] & (1 << internal_irq)) { - qemu_irq_pulse(*pic); - } else { /* level triggered */ - if (bonito_state->regs[BONITO_INTPOL] & (1 << internal_irq)) { - qemu_irq_raise(*pic); - } else { - qemu_irq_lower(*pic); - } - } -} - -/* map the original irq (0~3) to bonito irq (16~47, but 16~31 are unused) */ -static int pci_bonito_map_irq(PCIDevice * pci_dev, int irq_num) -{ - int slot; - - slot = (pci_dev->devfn >> 3); - - switch (slot) { - case 5: /* FULONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */ - return irq_num % 4 + BONITO_IRQ_BASE; - case 6: /* FULONG2E_ATI_SLOT, VGA */ - return 4 + BONITO_IRQ_BASE; - case 7: /* FULONG2E_RTL_SLOT, RTL8139 */ - return 5 + BONITO_IRQ_BASE; - case 8 ... 12: /* PCI slot 1 to 4 */ - return (slot - 8 + irq_num) + 6 + BONITO_IRQ_BASE; - default: /* Unknown device, don't do any translation */ - return irq_num; - } -} - -static void bonito_reset(void *opaque) -{ - PCIBonitoState *s = opaque; - - /* set the default value of north bridge registers */ - - s->regs[BONITO_BONPONCFG] = 0xc40; - s->regs[BONITO_BONGENCFG] = 0x1384; - s->regs[BONITO_IODEVCFG] = 0x2bff8010; - s->regs[BONITO_SDCFG] = 0x255e0091; - - s->regs[BONITO_GPIODATA] = 0x1ff; - s->regs[BONITO_GPIOIE] = 0x1ff; - s->regs[BONITO_DQCFG] = 0x8; - s->regs[BONITO_MEMSIZE] = 0x10000000; - s->regs[BONITO_PCIMAP] = 0x6140; -} - -static const VMStateDescription vmstate_bonito = { - .name = "Bonito", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_PCI_DEVICE(dev, PCIBonitoState), - VMSTATE_END_OF_LIST() - } -}; - -static int bonito_pcihost_initfn(SysBusDevice *dev) -{ - PCIHostState *phb = PCI_HOST_BRIDGE(dev); - - phb->bus = pci_register_bus(DEVICE(dev), "pci", - pci_bonito_set_irq, pci_bonito_map_irq, dev, - get_system_memory(), get_system_io(), - 0x28, 32, TYPE_PCI_BUS); - - return 0; -} - -static int bonito_initfn(PCIDevice *dev) -{ - PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev); - SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - - /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */ - pci_config_set_prog_interface(dev->config, 0x00); - - /* set the north bridge register mapping */ - memory_region_init_io(&s->iomem, &bonito_ops, s, - "north-bridge-register", BONITO_INTERNAL_REG_SIZE); - sysbus_init_mmio(sysbus, &s->iomem); - sysbus_mmio_map(sysbus, 0, BONITO_INTERNAL_REG_BASE); - - /* set the north bridge pci configure mapping */ - memory_region_init_io(&phb->conf_mem, &bonito_pciconf_ops, s, - "north-bridge-pci-config", BONITO_PCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &phb->conf_mem); - sysbus_mmio_map(sysbus, 1, BONITO_PCICONFIG_BASE); - - /* set the south bridge pci configure mapping */ - memory_region_init_io(&phb->data_mem, &bonito_spciconf_ops, s, - "south-bridge-pci-config", BONITO_SPCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &phb->data_mem); - sysbus_mmio_map(sysbus, 2, BONITO_SPCICONFIG_BASE); - - memory_region_init_io(&s->iomem_ldma, &bonito_ldma_ops, s, - "ldma", 0x100); - sysbus_init_mmio(sysbus, &s->iomem_ldma); - sysbus_mmio_map(sysbus, 3, 0xbfe00200); - - memory_region_init_io(&s->iomem_cop, &bonito_cop_ops, s, - "cop", 0x100); - sysbus_init_mmio(sysbus, &s->iomem_cop); - sysbus_mmio_map(sysbus, 4, 0xbfe00300); - - /* Map PCI IO Space 0x1fd0 0000 - 0x1fd1 0000 */ - s->bonito_pciio_start = BONITO_PCIIO_BASE; - s->bonito_pciio_length = BONITO_PCIIO_SIZE; - isa_mem_base = s->bonito_pciio_start; - isa_mmio_init(s->bonito_pciio_start, s->bonito_pciio_length); - - /* add pci local io mapping */ - s->bonito_localio_start = BONITO_DEV_BASE; - s->bonito_localio_length = BONITO_DEV_SIZE; - isa_mmio_init(s->bonito_localio_start, s->bonito_localio_length); - - /* set the default value of north bridge pci config */ - pci_set_word(dev->config + PCI_COMMAND, 0x0000); - pci_set_word(dev->config + PCI_STATUS, 0x0000); - pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0000); - pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000); - - pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00); - pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01); - pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c); - pci_set_byte(dev->config + PCI_MAX_LAT, 0x00); - - qemu_register_reset(bonito_reset, s); - - return 0; -} - -PCIBus *bonito_init(qemu_irq *pic) -{ - DeviceState *dev; - BonitoState *pcihost; - PCIHostState *phb; - PCIBonitoState *s; - PCIDevice *d; - - dev = qdev_create(NULL, TYPE_BONITO_PCI_HOST_BRIDGE); - phb = PCI_HOST_BRIDGE(dev); - pcihost = BONITO_PCI_HOST_BRIDGE(dev); - pcihost->pic = pic; - qdev_init_nofail(dev); - - /* set the pcihost pointer before bonito_initfn is called */ - d = pci_create(phb->bus, PCI_DEVFN(0, 0), "Bonito"); - s = DO_UPCAST(PCIBonitoState, dev, d); - s->pcihost = pcihost; - pcihost->pci_dev = s; - qdev_init_nofail(DEVICE(d)); - - return phb->bus; -} - -static void bonito_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = bonito_initfn; - k->vendor_id = 0xdf53; - k->device_id = 0x00d5; - k->revision = 0x01; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->desc = "Host bridge"; - dc->no_user = 1; - dc->vmsd = &vmstate_bonito; -} - -static const TypeInfo bonito_info = { - .name = "Bonito", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIBonitoState), - .class_init = bonito_class_init, -}; - -static void bonito_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = bonito_pcihost_initfn; - dc->no_user = 1; -} - -static const TypeInfo bonito_pcihost_info = { - .name = TYPE_BONITO_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(BonitoState), - .class_init = bonito_pcihost_class_init, -}; - -static void bonito_register_types(void) -{ - type_register_static(&bonito_pcihost_info); - type_register_static(&bonito_info); -} - -type_init(bonito_register_types) diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c deleted file mode 100644 index 189e865a65..0000000000 --- a/hw/gt64xxx.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* - * QEMU GT64120 PCI host - * - * Copyright (c) 2006,2007 Aurelien Jarno - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/hw.h" -#include "hw/mips/mips.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/i386/pc.h" -#include "exec/address-spaces.h" - -//#define DEBUG - -#ifdef DEBUG -#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif - -#define GT_REGS (0x1000 >> 2) - -/* CPU Configuration */ -#define GT_CPU (0x000 >> 2) -#define GT_MULTI (0x120 >> 2) - -/* CPU Address Decode */ -#define GT_SCS10LD (0x008 >> 2) -#define GT_SCS10HD (0x010 >> 2) -#define GT_SCS32LD (0x018 >> 2) -#define GT_SCS32HD (0x020 >> 2) -#define GT_CS20LD (0x028 >> 2) -#define GT_CS20HD (0x030 >> 2) -#define GT_CS3BOOTLD (0x038 >> 2) -#define GT_CS3BOOTHD (0x040 >> 2) -#define GT_PCI0IOLD (0x048 >> 2) -#define GT_PCI0IOHD (0x050 >> 2) -#define GT_PCI0M0LD (0x058 >> 2) -#define GT_PCI0M0HD (0x060 >> 2) -#define GT_PCI0M1LD (0x080 >> 2) -#define GT_PCI0M1HD (0x088 >> 2) -#define GT_PCI1IOLD (0x090 >> 2) -#define GT_PCI1IOHD (0x098 >> 2) -#define GT_PCI1M0LD (0x0a0 >> 2) -#define GT_PCI1M0HD (0x0a8 >> 2) -#define GT_PCI1M1LD (0x0b0 >> 2) -#define GT_PCI1M1HD (0x0b8 >> 2) -#define GT_ISD (0x068 >> 2) - -#define GT_SCS10AR (0x0d0 >> 2) -#define GT_SCS32AR (0x0d8 >> 2) -#define GT_CS20R (0x0e0 >> 2) -#define GT_CS3BOOTR (0x0e8 >> 2) - -#define GT_PCI0IOREMAP (0x0f0 >> 2) -#define GT_PCI0M0REMAP (0x0f8 >> 2) -#define GT_PCI0M1REMAP (0x100 >> 2) -#define GT_PCI1IOREMAP (0x108 >> 2) -#define GT_PCI1M0REMAP (0x110 >> 2) -#define GT_PCI1M1REMAP (0x118 >> 2) - -/* CPU Error Report */ -#define GT_CPUERR_ADDRLO (0x070 >> 2) -#define GT_CPUERR_ADDRHI (0x078 >> 2) -#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */ -#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */ -#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */ - -/* CPU Sync Barrier */ -#define GT_PCI0SYNC (0x0c0 >> 2) -#define GT_PCI1SYNC (0x0c8 >> 2) - -/* SDRAM and Device Address Decode */ -#define GT_SCS0LD (0x400 >> 2) -#define GT_SCS0HD (0x404 >> 2) -#define GT_SCS1LD (0x408 >> 2) -#define GT_SCS1HD (0x40c >> 2) -#define GT_SCS2LD (0x410 >> 2) -#define GT_SCS2HD (0x414 >> 2) -#define GT_SCS3LD (0x418 >> 2) -#define GT_SCS3HD (0x41c >> 2) -#define GT_CS0LD (0x420 >> 2) -#define GT_CS0HD (0x424 >> 2) -#define GT_CS1LD (0x428 >> 2) -#define GT_CS1HD (0x42c >> 2) -#define GT_CS2LD (0x430 >> 2) -#define GT_CS2HD (0x434 >> 2) -#define GT_CS3LD (0x438 >> 2) -#define GT_CS3HD (0x43c >> 2) -#define GT_BOOTLD (0x440 >> 2) -#define GT_BOOTHD (0x444 >> 2) -#define GT_ADERR (0x470 >> 2) - -/* SDRAM Configuration */ -#define GT_SDRAM_CFG (0x448 >> 2) -#define GT_SDRAM_OPMODE (0x474 >> 2) -#define GT_SDRAM_BM (0x478 >> 2) -#define GT_SDRAM_ADDRDECODE (0x47c >> 2) - -/* SDRAM Parameters */ -#define GT_SDRAM_B0 (0x44c >> 2) -#define GT_SDRAM_B1 (0x450 >> 2) -#define GT_SDRAM_B2 (0x454 >> 2) -#define GT_SDRAM_B3 (0x458 >> 2) - -/* Device Parameters */ -#define GT_DEV_B0 (0x45c >> 2) -#define GT_DEV_B1 (0x460 >> 2) -#define GT_DEV_B2 (0x464 >> 2) -#define GT_DEV_B3 (0x468 >> 2) -#define GT_DEV_BOOT (0x46c >> 2) - -/* ECC */ -#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */ -#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */ -#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */ -#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */ -#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */ - -/* DMA Record */ -#define GT_DMA0_CNT (0x800 >> 2) -#define GT_DMA1_CNT (0x804 >> 2) -#define GT_DMA2_CNT (0x808 >> 2) -#define GT_DMA3_CNT (0x80c >> 2) -#define GT_DMA0_SA (0x810 >> 2) -#define GT_DMA1_SA (0x814 >> 2) -#define GT_DMA2_SA (0x818 >> 2) -#define GT_DMA3_SA (0x81c >> 2) -#define GT_DMA0_DA (0x820 >> 2) -#define GT_DMA1_DA (0x824 >> 2) -#define GT_DMA2_DA (0x828 >> 2) -#define GT_DMA3_DA (0x82c >> 2) -#define GT_DMA0_NEXT (0x830 >> 2) -#define GT_DMA1_NEXT (0x834 >> 2) -#define GT_DMA2_NEXT (0x838 >> 2) -#define GT_DMA3_NEXT (0x83c >> 2) -#define GT_DMA0_CUR (0x870 >> 2) -#define GT_DMA1_CUR (0x874 >> 2) -#define GT_DMA2_CUR (0x878 >> 2) -#define GT_DMA3_CUR (0x87c >> 2) - -/* DMA Channel Control */ -#define GT_DMA0_CTRL (0x840 >> 2) -#define GT_DMA1_CTRL (0x844 >> 2) -#define GT_DMA2_CTRL (0x848 >> 2) -#define GT_DMA3_CTRL (0x84c >> 2) - -/* DMA Arbiter */ -#define GT_DMA_ARB (0x860 >> 2) - -/* Timer/Counter */ -#define GT_TC0 (0x850 >> 2) -#define GT_TC1 (0x854 >> 2) -#define GT_TC2 (0x858 >> 2) -#define GT_TC3 (0x85c >> 2) -#define GT_TC_CONTROL (0x864 >> 2) - -/* PCI Internal */ -#define GT_PCI0_CMD (0xc00 >> 2) -#define GT_PCI0_TOR (0xc04 >> 2) -#define GT_PCI0_BS_SCS10 (0xc08 >> 2) -#define GT_PCI0_BS_SCS32 (0xc0c >> 2) -#define GT_PCI0_BS_CS20 (0xc10 >> 2) -#define GT_PCI0_BS_CS3BT (0xc14 >> 2) -#define GT_PCI1_IACK (0xc30 >> 2) -#define GT_PCI0_IACK (0xc34 >> 2) -#define GT_PCI0_BARE (0xc3c >> 2) -#define GT_PCI0_PREFMBR (0xc40 >> 2) -#define GT_PCI0_SCS10_BAR (0xc48 >> 2) -#define GT_PCI0_SCS32_BAR (0xc4c >> 2) -#define GT_PCI0_CS20_BAR (0xc50 >> 2) -#define GT_PCI0_CS3BT_BAR (0xc54 >> 2) -#define GT_PCI0_SSCS10_BAR (0xc58 >> 2) -#define GT_PCI0_SSCS32_BAR (0xc5c >> 2) -#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2) -#define GT_PCI1_CMD (0xc80 >> 2) -#define GT_PCI1_TOR (0xc84 >> 2) -#define GT_PCI1_BS_SCS10 (0xc88 >> 2) -#define GT_PCI1_BS_SCS32 (0xc8c >> 2) -#define GT_PCI1_BS_CS20 (0xc90 >> 2) -#define GT_PCI1_BS_CS3BT (0xc94 >> 2) -#define GT_PCI1_BARE (0xcbc >> 2) -#define GT_PCI1_PREFMBR (0xcc0 >> 2) -#define GT_PCI1_SCS10_BAR (0xcc8 >> 2) -#define GT_PCI1_SCS32_BAR (0xccc >> 2) -#define GT_PCI1_CS20_BAR (0xcd0 >> 2) -#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2) -#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2) -#define GT_PCI1_SSCS32_BAR (0xcdc >> 2) -#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2) -#define GT_PCI1_CFGADDR (0xcf0 >> 2) -#define GT_PCI1_CFGDATA (0xcf4 >> 2) -#define GT_PCI0_CFGADDR (0xcf8 >> 2) -#define GT_PCI0_CFGDATA (0xcfc >> 2) - -/* Interrupts */ -#define GT_INTRCAUSE (0xc18 >> 2) -#define GT_INTRMASK (0xc1c >> 2) -#define GT_PCI0_ICMASK (0xc24 >> 2) -#define GT_PCI0_SERR0MASK (0xc28 >> 2) -#define GT_CPU_INTSEL (0xc70 >> 2) -#define GT_PCI0_INTSEL (0xc74 >> 2) -#define GT_HINTRCAUSE (0xc98 >> 2) -#define GT_HINTRMASK (0xc9c >> 2) -#define GT_PCI0_HICMASK (0xca4 >> 2) -#define GT_PCI1_SERR1MASK (0xca8 >> 2) - -#define PCI_MAPPING_ENTRY(regname) \ - hwaddr regname ##_start; \ - hwaddr regname ##_length; \ - MemoryRegion regname ##_mem - -#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120" - -#define GT64120_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(GT64120State, (obj), TYPE_GT64120_PCI_HOST_BRIDGE) - -typedef struct GT64120State { - PCIHostState parent_obj; - - uint32_t regs[GT_REGS]; - PCI_MAPPING_ENTRY(PCI0IO); - PCI_MAPPING_ENTRY(ISD); -} GT64120State; - -/* Adjust range to avoid touching space which isn't mappable via PCI */ -/* XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000 - 0x1fc00000 - 0x1fd00000 */ -static void check_reserved_space (hwaddr *start, - hwaddr *length) -{ - hwaddr begin = *start; - hwaddr end = *start + *length; - - if (end >= 0x1e000000LL && end < 0x1f100000LL) - end = 0x1e000000LL; - if (begin >= 0x1e000000LL && begin < 0x1f100000LL) - begin = 0x1f100000LL; - if (end >= 0x1fc00000LL && end < 0x1fd00000LL) - end = 0x1fc00000LL; - if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL) - begin = 0x1fd00000LL; - /* XXX: This is broken when a reserved range splits the requested range */ - if (end >= 0x1f100000LL && begin < 0x1e000000LL) - end = 0x1e000000LL; - if (end >= 0x1fd00000LL && begin < 0x1fc00000LL) - end = 0x1fc00000LL; - - *start = begin; - *length = end - begin; -} - -static void gt64120_isd_mapping(GT64120State *s) -{ - hwaddr start = s->regs[GT_ISD] << 21; - hwaddr length = 0x1000; - - if (s->ISD_length) { - memory_region_del_subregion(get_system_memory(), &s->ISD_mem); - } - check_reserved_space(&start, &length); - length = 0x1000; - /* Map new address */ - DPRINTF("ISD: "TARGET_FMT_plx"@"TARGET_FMT_plx - " -> "TARGET_FMT_plx"@"TARGET_FMT_plx"\n", - s->ISD_length, s->ISD_start, length, start); - s->ISD_start = start; - s->ISD_length = length; - memory_region_add_subregion(get_system_memory(), s->ISD_start, &s->ISD_mem); -} - -static void gt64120_pci_mapping(GT64120State *s) -{ - /* Update IO mapping */ - if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD]) - { - /* Unmap old IO address */ - if (s->PCI0IO_length) - { - memory_region_del_subregion(get_system_memory(), &s->PCI0IO_mem); - memory_region_destroy(&s->PCI0IO_mem); - } - /* Map new IO address */ - s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21; - s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) - (s->regs[GT_PCI0IOLD] & 0x7f)) << 21; - isa_mem_base = s->PCI0IO_start; - if (s->PCI0IO_length) { - isa_mmio_setup(&s->PCI0IO_mem, s->PCI0IO_length); - memory_region_add_subregion(get_system_memory(), s->PCI0IO_start, - &s->PCI0IO_mem); - } - } -} - -static void gt64120_writel (void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - GT64120State *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - uint32_t saddr; - - if (!(s->regs[GT_CPU] & 0x00001000)) - val = bswap32(val); - - saddr = (addr & 0xfff) >> 2; - switch (saddr) { - - /* CPU Configuration */ - case GT_CPU: - s->regs[GT_CPU] = val; - break; - case GT_MULTI: - /* Read-only register as only one GT64xxx is present on the CPU bus */ - break; - - /* CPU Address Decode */ - case GT_PCI0IOLD: - s->regs[GT_PCI0IOLD] = val & 0x00007fff; - s->regs[GT_PCI0IOREMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI0M0LD: - s->regs[GT_PCI0M0LD] = val & 0x00007fff; - s->regs[GT_PCI0M0REMAP] = val & 0x000007ff; - break; - case GT_PCI0M1LD: - s->regs[GT_PCI0M1LD] = val & 0x00007fff; - s->regs[GT_PCI0M1REMAP] = val & 0x000007ff; - break; - case GT_PCI1IOLD: - s->regs[GT_PCI1IOLD] = val & 0x00007fff; - s->regs[GT_PCI1IOREMAP] = val & 0x000007ff; - break; - case GT_PCI1M0LD: - s->regs[GT_PCI1M0LD] = val & 0x00007fff; - s->regs[GT_PCI1M0REMAP] = val & 0x000007ff; - break; - case GT_PCI1M1LD: - s->regs[GT_PCI1M1LD] = val & 0x00007fff; - s->regs[GT_PCI1M1REMAP] = val & 0x000007ff; - break; - case GT_PCI0IOHD: - s->regs[saddr] = val & 0x0000007f; - gt64120_pci_mapping(s); - break; - case GT_PCI0M0HD: - case GT_PCI0M1HD: - case GT_PCI1IOHD: - case GT_PCI1M0HD: - case GT_PCI1M1HD: - s->regs[saddr] = val & 0x0000007f; - break; - case GT_ISD: - s->regs[saddr] = val & 0x00007fff; - gt64120_isd_mapping(s); - break; - - case GT_PCI0IOREMAP: - case GT_PCI0M0REMAP: - case GT_PCI0M1REMAP: - case GT_PCI1IOREMAP: - case GT_PCI1M0REMAP: - case GT_PCI1M1REMAP: - s->regs[saddr] = val & 0x000007ff; - break; - - /* CPU Error Report */ - case GT_CPUERR_ADDRLO: - case GT_CPUERR_ADDRHI: - case GT_CPUERR_DATALO: - case GT_CPUERR_DATAHI: - case GT_CPUERR_PARITY: - /* Read-only registers, do nothing */ - break; - - /* CPU Sync Barrier */ - case GT_PCI0SYNC: - case GT_PCI1SYNC: - /* Read-only registers, do nothing */ - break; - - /* SDRAM and Device Address Decode */ - case GT_SCS0LD: - case GT_SCS0HD: - case GT_SCS1LD: - case GT_SCS1HD: - case GT_SCS2LD: - case GT_SCS2HD: - case GT_SCS3LD: - case GT_SCS3HD: - case GT_CS0LD: - case GT_CS0HD: - case GT_CS1LD: - case GT_CS1HD: - case GT_CS2LD: - case GT_CS2HD: - case GT_CS3LD: - case GT_CS3HD: - case GT_BOOTLD: - case GT_BOOTHD: - case GT_ADERR: - /* SDRAM Configuration */ - case GT_SDRAM_CFG: - case GT_SDRAM_OPMODE: - case GT_SDRAM_BM: - case GT_SDRAM_ADDRDECODE: - /* Accept and ignore SDRAM interleave configuration */ - s->regs[saddr] = val; - break; - - /* Device Parameters */ - case GT_DEV_B0: - case GT_DEV_B1: - case GT_DEV_B2: - case GT_DEV_B3: - case GT_DEV_BOOT: - /* Not implemented */ - DPRINTF ("Unimplemented device register offset 0x%x\n", saddr << 2); - break; - - /* ECC */ - case GT_ECC_ERRDATALO: - case GT_ECC_ERRDATAHI: - case GT_ECC_MEM: - case GT_ECC_CALC: - case GT_ECC_ERRADDR: - /* Read-only registers, do nothing */ - break; - - /* DMA Record */ - case GT_DMA0_CNT: - case GT_DMA1_CNT: - case GT_DMA2_CNT: - case GT_DMA3_CNT: - case GT_DMA0_SA: - case GT_DMA1_SA: - case GT_DMA2_SA: - case GT_DMA3_SA: - case GT_DMA0_DA: - case GT_DMA1_DA: - case GT_DMA2_DA: - case GT_DMA3_DA: - case GT_DMA0_NEXT: - case GT_DMA1_NEXT: - case GT_DMA2_NEXT: - case GT_DMA3_NEXT: - case GT_DMA0_CUR: - case GT_DMA1_CUR: - case GT_DMA2_CUR: - case GT_DMA3_CUR: - /* Not implemented */ - DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); - break; - - /* DMA Channel Control */ - case GT_DMA0_CTRL: - case GT_DMA1_CTRL: - case GT_DMA2_CTRL: - case GT_DMA3_CTRL: - /* Not implemented */ - DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); - break; - - /* DMA Arbiter */ - case GT_DMA_ARB: - /* Not implemented */ - DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); - break; - - /* Timer/Counter */ - case GT_TC0: - case GT_TC1: - case GT_TC2: - case GT_TC3: - case GT_TC_CONTROL: - /* Not implemented */ - DPRINTF ("Unimplemented timer register offset 0x%x\n", saddr << 2); - break; - - /* PCI Internal */ - case GT_PCI0_CMD: - case GT_PCI1_CMD: - s->regs[saddr] = val & 0x0401fc0f; - break; - case GT_PCI0_TOR: - case GT_PCI0_BS_SCS10: - case GT_PCI0_BS_SCS32: - case GT_PCI0_BS_CS20: - case GT_PCI0_BS_CS3BT: - case GT_PCI1_IACK: - case GT_PCI0_IACK: - case GT_PCI0_BARE: - case GT_PCI0_PREFMBR: - case GT_PCI0_SCS10_BAR: - case GT_PCI0_SCS32_BAR: - case GT_PCI0_CS20_BAR: - case GT_PCI0_CS3BT_BAR: - case GT_PCI0_SSCS10_BAR: - case GT_PCI0_SSCS32_BAR: - case GT_PCI0_SCS3BT_BAR: - case GT_PCI1_TOR: - case GT_PCI1_BS_SCS10: - case GT_PCI1_BS_SCS32: - case GT_PCI1_BS_CS20: - case GT_PCI1_BS_CS3BT: - case GT_PCI1_BARE: - case GT_PCI1_PREFMBR: - case GT_PCI1_SCS10_BAR: - case GT_PCI1_SCS32_BAR: - case GT_PCI1_CS20_BAR: - case GT_PCI1_CS3BT_BAR: - case GT_PCI1_SSCS10_BAR: - case GT_PCI1_SSCS32_BAR: - case GT_PCI1_SCS3BT_BAR: - case GT_PCI1_CFGADDR: - case GT_PCI1_CFGDATA: - /* not implemented */ - break; - case GT_PCI0_CFGADDR: - phb->config_reg = val & 0x80fffffc; - break; - case GT_PCI0_CFGDATA: - if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { - val = bswap32(val); - } - if (phb->config_reg & (1u << 31)) { - pci_data_write(phb->bus, phb->config_reg, val, 4); - } - break; - - /* Interrupts */ - case GT_INTRCAUSE: - /* not really implemented */ - s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe)); - s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe); - DPRINTF("INTRCAUSE %" PRIx64 "\n", val); - break; - case GT_INTRMASK: - s->regs[saddr] = val & 0x3c3ffffe; - DPRINTF("INTRMASK %" PRIx64 "\n", val); - break; - case GT_PCI0_ICMASK: - s->regs[saddr] = val & 0x03fffffe; - DPRINTF("ICMASK %" PRIx64 "\n", val); - break; - case GT_PCI0_SERR0MASK: - s->regs[saddr] = val & 0x0000003f; - DPRINTF("SERR0MASK %" PRIx64 "\n", val); - break; - - /* Reserved when only PCI_0 is configured. */ - case GT_HINTRCAUSE: - case GT_CPU_INTSEL: - case GT_PCI0_INTSEL: - case GT_HINTRMASK: - case GT_PCI0_HICMASK: - case GT_PCI1_SERR1MASK: - /* not implemented */ - break; - - /* SDRAM Parameters */ - case GT_SDRAM_B0: - case GT_SDRAM_B1: - case GT_SDRAM_B2: - case GT_SDRAM_B3: - /* We don't simulate electrical parameters of the SDRAM. - Accept, but ignore the values. */ - s->regs[saddr] = val; - break; - - default: - DPRINTF ("Bad register offset 0x%x\n", (int)addr); - break; - } -} - -static uint64_t gt64120_readl (void *opaque, - hwaddr addr, unsigned size) -{ - GT64120State *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - uint32_t val; - uint32_t saddr; - - saddr = (addr & 0xfff) >> 2; - switch (saddr) { - - /* CPU Configuration */ - case GT_MULTI: - /* Only one GT64xxx is present on the CPU bus, return - the initial value */ - val = s->regs[saddr]; - break; - - /* CPU Error Report */ - case GT_CPUERR_ADDRLO: - case GT_CPUERR_ADDRHI: - case GT_CPUERR_DATALO: - case GT_CPUERR_DATAHI: - case GT_CPUERR_PARITY: - /* Emulated memory has no error, always return the initial - values */ - val = s->regs[saddr]; - break; - - /* CPU Sync Barrier */ - case GT_PCI0SYNC: - case GT_PCI1SYNC: - /* Reading those register should empty all FIFO on the PCI - bus, which are not emulated. The return value should be - a random value that should be ignored. */ - val = 0xc000ffee; - break; - - /* ECC */ - case GT_ECC_ERRDATALO: - case GT_ECC_ERRDATAHI: - case GT_ECC_MEM: - case GT_ECC_CALC: - case GT_ECC_ERRADDR: - /* Emulated memory has no error, always return the initial - values */ - val = s->regs[saddr]; - break; - - case GT_CPU: - case GT_SCS10LD: - case GT_SCS10HD: - case GT_SCS32LD: - case GT_SCS32HD: - case GT_CS20LD: - case GT_CS20HD: - case GT_CS3BOOTLD: - case GT_CS3BOOTHD: - case GT_SCS10AR: - case GT_SCS32AR: - case GT_CS20R: - case GT_CS3BOOTR: - case GT_PCI0IOLD: - case GT_PCI0M0LD: - case GT_PCI0M1LD: - case GT_PCI1IOLD: - case GT_PCI1M0LD: - case GT_PCI1M1LD: - case GT_PCI0IOHD: - case GT_PCI0M0HD: - case GT_PCI0M1HD: - case GT_PCI1IOHD: - case GT_PCI1M0HD: - case GT_PCI1M1HD: - case GT_PCI0IOREMAP: - case GT_PCI0M0REMAP: - case GT_PCI0M1REMAP: - case GT_PCI1IOREMAP: - case GT_PCI1M0REMAP: - case GT_PCI1M1REMAP: - case GT_ISD: - val = s->regs[saddr]; - break; - case GT_PCI0_IACK: - /* Read the IRQ number */ - val = pic_read_irq(isa_pic); - break; - - /* SDRAM and Device Address Decode */ - case GT_SCS0LD: - case GT_SCS0HD: - case GT_SCS1LD: - case GT_SCS1HD: - case GT_SCS2LD: - case GT_SCS2HD: - case GT_SCS3LD: - case GT_SCS3HD: - case GT_CS0LD: - case GT_CS0HD: - case GT_CS1LD: - case GT_CS1HD: - case GT_CS2LD: - case GT_CS2HD: - case GT_CS3LD: - case GT_CS3HD: - case GT_BOOTLD: - case GT_BOOTHD: - case GT_ADERR: - val = s->regs[saddr]; - break; - - /* SDRAM Configuration */ - case GT_SDRAM_CFG: - case GT_SDRAM_OPMODE: - case GT_SDRAM_BM: - case GT_SDRAM_ADDRDECODE: - val = s->regs[saddr]; - break; - - /* SDRAM Parameters */ - case GT_SDRAM_B0: - case GT_SDRAM_B1: - case GT_SDRAM_B2: - case GT_SDRAM_B3: - /* We don't simulate electrical parameters of the SDRAM. - Just return the last written value. */ - val = s->regs[saddr]; - break; - - /* Device Parameters */ - case GT_DEV_B0: - case GT_DEV_B1: - case GT_DEV_B2: - case GT_DEV_B3: - case GT_DEV_BOOT: - val = s->regs[saddr]; - break; - - /* DMA Record */ - case GT_DMA0_CNT: - case GT_DMA1_CNT: - case GT_DMA2_CNT: - case GT_DMA3_CNT: - case GT_DMA0_SA: - case GT_DMA1_SA: - case GT_DMA2_SA: - case GT_DMA3_SA: - case GT_DMA0_DA: - case GT_DMA1_DA: - case GT_DMA2_DA: - case GT_DMA3_DA: - case GT_DMA0_NEXT: - case GT_DMA1_NEXT: - case GT_DMA2_NEXT: - case GT_DMA3_NEXT: - case GT_DMA0_CUR: - case GT_DMA1_CUR: - case GT_DMA2_CUR: - case GT_DMA3_CUR: - val = s->regs[saddr]; - break; - - /* DMA Channel Control */ - case GT_DMA0_CTRL: - case GT_DMA1_CTRL: - case GT_DMA2_CTRL: - case GT_DMA3_CTRL: - val = s->regs[saddr]; - break; - - /* DMA Arbiter */ - case GT_DMA_ARB: - val = s->regs[saddr]; - break; - - /* Timer/Counter */ - case GT_TC0: - case GT_TC1: - case GT_TC2: - case GT_TC3: - case GT_TC_CONTROL: - val = s->regs[saddr]; - break; - - /* PCI Internal */ - case GT_PCI0_CFGADDR: - val = phb->config_reg; - break; - case GT_PCI0_CFGDATA: - if (!(phb->config_reg & (1 << 31))) { - val = 0xffffffff; - } else { - val = pci_data_read(phb->bus, phb->config_reg, 4); - } - if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { - val = bswap32(val); - } - break; - - case GT_PCI0_CMD: - case GT_PCI0_TOR: - case GT_PCI0_BS_SCS10: - case GT_PCI0_BS_SCS32: - case GT_PCI0_BS_CS20: - case GT_PCI0_BS_CS3BT: - case GT_PCI1_IACK: - case GT_PCI0_BARE: - case GT_PCI0_PREFMBR: - case GT_PCI0_SCS10_BAR: - case GT_PCI0_SCS32_BAR: - case GT_PCI0_CS20_BAR: - case GT_PCI0_CS3BT_BAR: - case GT_PCI0_SSCS10_BAR: - case GT_PCI0_SSCS32_BAR: - case GT_PCI0_SCS3BT_BAR: - case GT_PCI1_CMD: - case GT_PCI1_TOR: - case GT_PCI1_BS_SCS10: - case GT_PCI1_BS_SCS32: - case GT_PCI1_BS_CS20: - case GT_PCI1_BS_CS3BT: - case GT_PCI1_BARE: - case GT_PCI1_PREFMBR: - case GT_PCI1_SCS10_BAR: - case GT_PCI1_SCS32_BAR: - case GT_PCI1_CS20_BAR: - case GT_PCI1_CS3BT_BAR: - case GT_PCI1_SSCS10_BAR: - case GT_PCI1_SSCS32_BAR: - case GT_PCI1_SCS3BT_BAR: - case GT_PCI1_CFGADDR: - case GT_PCI1_CFGDATA: - val = s->regs[saddr]; - break; - - /* Interrupts */ - case GT_INTRCAUSE: - val = s->regs[saddr]; - DPRINTF("INTRCAUSE %x\n", val); - break; - case GT_INTRMASK: - val = s->regs[saddr]; - DPRINTF("INTRMASK %x\n", val); - break; - case GT_PCI0_ICMASK: - val = s->regs[saddr]; - DPRINTF("ICMASK %x\n", val); - break; - case GT_PCI0_SERR0MASK: - val = s->regs[saddr]; - DPRINTF("SERR0MASK %x\n", val); - break; - - /* Reserved when only PCI_0 is configured. */ - case GT_HINTRCAUSE: - case GT_CPU_INTSEL: - case GT_PCI0_INTSEL: - case GT_HINTRMASK: - case GT_PCI0_HICMASK: - case GT_PCI1_SERR1MASK: - val = s->regs[saddr]; - break; - - default: - val = s->regs[saddr]; - DPRINTF ("Bad register offset 0x%x\n", (int)addr); - break; - } - - if (!(s->regs[GT_CPU] & 0x00001000)) - val = bswap32(val); - - return val; -} - -static const MemoryRegionOps isd_mem_ops = { - .read = gt64120_readl, - .write = gt64120_writel, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int slot; - - slot = (pci_dev->devfn >> 3); - - switch (slot) { - /* PIIX4 USB */ - case 10: - return 3; - /* AMD 79C973 Ethernet */ - case 11: - return 1; - /* Crystal 4281 Sound */ - case 12: - return 2; - /* PCI slot 1 to 4 */ - case 18 ... 21: - return ((slot - 18) + irq_num) & 0x03; - /* Unknown device, don't do any translation */ - default: - return irq_num; - } -} - -static int pci_irq_levels[4]; - -static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) -{ - int i, pic_irq, pic_level; - qemu_irq *pic = opaque; - - pci_irq_levels[irq_num] = level; - - /* now we change the pic irq level according to the piix irq mappings */ - /* XXX: optimize */ - pic_irq = piix4_dev->config[0x60 + irq_num]; - if (pic_irq < 16) { - /* The pic level is the logical OR of all the PCI irqs mapped - to it */ - pic_level = 0; - for (i = 0; i < 4; i++) { - if (pic_irq == piix4_dev->config[0x60 + i]) - pic_level |= pci_irq_levels[i]; - } - qemu_set_irq(pic[pic_irq], pic_level); - } -} - - -static void gt64120_reset(void *opaque) -{ - GT64120State *s = opaque; - - /* FIXME: Malta specific hw assumptions ahead */ - - /* CPU Configuration */ -#ifdef TARGET_WORDS_BIGENDIAN - s->regs[GT_CPU] = 0x00000000; -#else - s->regs[GT_CPU] = 0x00001000; -#endif - s->regs[GT_MULTI] = 0x00000003; - - /* CPU Address decode */ - s->regs[GT_SCS10LD] = 0x00000000; - s->regs[GT_SCS10HD] = 0x00000007; - s->regs[GT_SCS32LD] = 0x00000008; - s->regs[GT_SCS32HD] = 0x0000000f; - s->regs[GT_CS20LD] = 0x000000e0; - s->regs[GT_CS20HD] = 0x00000070; - s->regs[GT_CS3BOOTLD] = 0x000000f8; - s->regs[GT_CS3BOOTHD] = 0x0000007f; - - s->regs[GT_PCI0IOLD] = 0x00000080; - s->regs[GT_PCI0IOHD] = 0x0000000f; - s->regs[GT_PCI0M0LD] = 0x00000090; - s->regs[GT_PCI0M0HD] = 0x0000001f; - s->regs[GT_ISD] = 0x000000a0; - s->regs[GT_PCI0M1LD] = 0x00000790; - s->regs[GT_PCI0M1HD] = 0x0000001f; - s->regs[GT_PCI1IOLD] = 0x00000100; - s->regs[GT_PCI1IOHD] = 0x0000000f; - s->regs[GT_PCI1M0LD] = 0x00000110; - s->regs[GT_PCI1M0HD] = 0x0000001f; - s->regs[GT_PCI1M1LD] = 0x00000120; - s->regs[GT_PCI1M1HD] = 0x0000002f; - - s->regs[GT_SCS10AR] = 0x00000000; - s->regs[GT_SCS32AR] = 0x00000008; - s->regs[GT_CS20R] = 0x000000e0; - s->regs[GT_CS3BOOTR] = 0x000000f8; - - s->regs[GT_PCI0IOREMAP] = 0x00000080; - s->regs[GT_PCI0M0REMAP] = 0x00000090; - s->regs[GT_PCI0M1REMAP] = 0x00000790; - s->regs[GT_PCI1IOREMAP] = 0x00000100; - s->regs[GT_PCI1M0REMAP] = 0x00000110; - s->regs[GT_PCI1M1REMAP] = 0x00000120; - - /* CPU Error Report */ - s->regs[GT_CPUERR_ADDRLO] = 0x00000000; - s->regs[GT_CPUERR_ADDRHI] = 0x00000000; - s->regs[GT_CPUERR_DATALO] = 0xffffffff; - s->regs[GT_CPUERR_DATAHI] = 0xffffffff; - s->regs[GT_CPUERR_PARITY] = 0x000000ff; - - /* CPU Sync Barrier */ - s->regs[GT_PCI0SYNC] = 0x00000000; - s->regs[GT_PCI1SYNC] = 0x00000000; - - /* SDRAM and Device Address Decode */ - s->regs[GT_SCS0LD] = 0x00000000; - s->regs[GT_SCS0HD] = 0x00000007; - s->regs[GT_SCS1LD] = 0x00000008; - s->regs[GT_SCS1HD] = 0x0000000f; - s->regs[GT_SCS2LD] = 0x00000010; - s->regs[GT_SCS2HD] = 0x00000017; - s->regs[GT_SCS3LD] = 0x00000018; - s->regs[GT_SCS3HD] = 0x0000001f; - s->regs[GT_CS0LD] = 0x000000c0; - s->regs[GT_CS0HD] = 0x000000c7; - s->regs[GT_CS1LD] = 0x000000c8; - s->regs[GT_CS1HD] = 0x000000cf; - s->regs[GT_CS2LD] = 0x000000d0; - s->regs[GT_CS2HD] = 0x000000df; - s->regs[GT_CS3LD] = 0x000000f0; - s->regs[GT_CS3HD] = 0x000000fb; - s->regs[GT_BOOTLD] = 0x000000fc; - s->regs[GT_BOOTHD] = 0x000000ff; - s->regs[GT_ADERR] = 0xffffffff; - - /* SDRAM Configuration */ - s->regs[GT_SDRAM_CFG] = 0x00000200; - s->regs[GT_SDRAM_OPMODE] = 0x00000000; - s->regs[GT_SDRAM_BM] = 0x00000007; - s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002; - - /* SDRAM Parameters */ - s->regs[GT_SDRAM_B0] = 0x00000005; - s->regs[GT_SDRAM_B1] = 0x00000005; - s->regs[GT_SDRAM_B2] = 0x00000005; - s->regs[GT_SDRAM_B3] = 0x00000005; - - /* ECC */ - s->regs[GT_ECC_ERRDATALO] = 0x00000000; - s->regs[GT_ECC_ERRDATAHI] = 0x00000000; - s->regs[GT_ECC_MEM] = 0x00000000; - s->regs[GT_ECC_CALC] = 0x00000000; - s->regs[GT_ECC_ERRADDR] = 0x00000000; - - /* Device Parameters */ - s->regs[GT_DEV_B0] = 0x386fffff; - s->regs[GT_DEV_B1] = 0x386fffff; - s->regs[GT_DEV_B2] = 0x386fffff; - s->regs[GT_DEV_B3] = 0x386fffff; - s->regs[GT_DEV_BOOT] = 0x146fffff; - - /* DMA registers are all zeroed at reset */ - - /* Timer/Counter */ - s->regs[GT_TC0] = 0xffffffff; - s->regs[GT_TC1] = 0x00ffffff; - s->regs[GT_TC2] = 0x00ffffff; - s->regs[GT_TC3] = 0x00ffffff; - s->regs[GT_TC_CONTROL] = 0x00000000; - - /* PCI Internal */ -#ifdef TARGET_WORDS_BIGENDIAN - s->regs[GT_PCI0_CMD] = 0x00000000; -#else - s->regs[GT_PCI0_CMD] = 0x00010001; -#endif - s->regs[GT_PCI0_TOR] = 0x0000070f; - s->regs[GT_PCI0_BS_SCS10] = 0x00fff000; - s->regs[GT_PCI0_BS_SCS32] = 0x00fff000; - s->regs[GT_PCI0_BS_CS20] = 0x01fff000; - s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000; - s->regs[GT_PCI1_IACK] = 0x00000000; - s->regs[GT_PCI0_IACK] = 0x00000000; - s->regs[GT_PCI0_BARE] = 0x0000000f; - s->regs[GT_PCI0_PREFMBR] = 0x00000040; - s->regs[GT_PCI0_SCS10_BAR] = 0x00000000; - s->regs[GT_PCI0_SCS32_BAR] = 0x01000000; - s->regs[GT_PCI0_CS20_BAR] = 0x1c000000; - s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000; - s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000; - s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000; -#ifdef TARGET_WORDS_BIGENDIAN - s->regs[GT_PCI1_CMD] = 0x00000000; -#else - s->regs[GT_PCI1_CMD] = 0x00010001; -#endif - s->regs[GT_PCI1_TOR] = 0x0000070f; - s->regs[GT_PCI1_BS_SCS10] = 0x00fff000; - s->regs[GT_PCI1_BS_SCS32] = 0x00fff000; - s->regs[GT_PCI1_BS_CS20] = 0x01fff000; - s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000; - s->regs[GT_PCI1_BARE] = 0x0000000f; - s->regs[GT_PCI1_PREFMBR] = 0x00000040; - s->regs[GT_PCI1_SCS10_BAR] = 0x00000000; - s->regs[GT_PCI1_SCS32_BAR] = 0x01000000; - s->regs[GT_PCI1_CS20_BAR] = 0x1c000000; - s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000; - s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000; - s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI1_CFGADDR] = 0x00000000; - s->regs[GT_PCI1_CFGDATA] = 0x00000000; - s->regs[GT_PCI0_CFGADDR] = 0x00000000; - - /* Interrupt registers are all zeroed at reset */ - - gt64120_isd_mapping(s); - gt64120_pci_mapping(s); -} - -PCIBus *gt64120_register(qemu_irq *pic) -{ - GT64120State *d; - PCIHostState *phb; - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_GT64120_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - d = GT64120_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(dev); - phb->bus = pci_register_bus(dev, "pci", - gt64120_pci_set_irq, gt64120_pci_map_irq, - pic, - get_system_memory(), - get_system_io(), - PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS); - memory_region_init_io(&d->ISD_mem, &isd_mem_ops, d, "isd-mem", 0x1000); - - pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); - return phb->bus; -} - -static int gt64120_init(SysBusDevice *dev) -{ - GT64120State *s; - - s = GT64120_PCI_HOST_BRIDGE(dev); - - /* FIXME: This value is computed from registers during reset, but some - devices (e.g. VGA card) need to know it when they are registered. - This also mean that changing the register to change the mapping - does not fully work. */ - isa_mem_base = 0x10000000; - qemu_register_reset(gt64120_reset, s); - return 0; -} - -static int gt64120_pci_init(PCIDevice *d) -{ - /* FIXME: Malta specific hw assumptions ahead */ - pci_set_word(d->config + PCI_COMMAND, 0); - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - pci_config_set_prog_interface(d->config, 0); - pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); - pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); - pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); - pci_set_byte(d->config + 0x3d, 0x01); - - return 0; -} - -static void gt64120_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = gt64120_pci_init; - k->vendor_id = PCI_VENDOR_ID_MARVELL; - k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; - k->revision = 0x10; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo gt64120_pci_info = { - .name = "gt64120_pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = gt64120_pci_class_init, -}; - -static void gt64120_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = gt64120_init; -} - -static const TypeInfo gt64120_info = { - .name = TYPE_GT64120_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(GT64120State), - .class_init = gt64120_class_init, -}; - -static void gt64120_pci_register_types(void) -{ - type_register_static(>64120_info); - type_register_static(>64120_pci_info); -} - -type_init(gt64120_pci_register_types) diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index af7f4b1fe5..a531d3aec6 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,8 +1,7 @@ obj-y += apic_common.o apic.o -obj-y += sga.o ioapic_common.o ioapic.o piix_pci.o +obj-y += sga.o ioapic_common.o ioapic.o obj-y += vmport.o obj-y += debugexit.o -obj-y += q35.o obj-y += kvm/ obj-y += pc-testdev.o diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs index af4d1f947b..0a652f8521 100644 --- a/hw/mips/Makefile.objs +++ b/hw/mips/Makefile.objs @@ -1,8 +1,4 @@ -obj-y += gt64xxx.o -obj-$(CONFIG_FULONG) += bonito.o - -obj-y := $(addprefix ../,$(obj-y)) - obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o obj-y += addr.o cputimer.o mips_int.o obj-$(CONFIG_FULONG) += mips_fulong2e.o +obj-y += gt64xxx_pci.o diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c new file mode 100644 index 0000000000..189e865a65 --- /dev/null +++ b/hw/mips/gt64xxx_pci.c @@ -0,0 +1,1188 @@ +/* + * QEMU GT64120 PCI host + * + * Copyright (c) 2006,2007 Aurelien Jarno + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "hw/mips/mips.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/i386/pc.h" +#include "exec/address-spaces.h" + +//#define DEBUG + +#ifdef DEBUG +#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +#define GT_REGS (0x1000 >> 2) + +/* CPU Configuration */ +#define GT_CPU (0x000 >> 2) +#define GT_MULTI (0x120 >> 2) + +/* CPU Address Decode */ +#define GT_SCS10LD (0x008 >> 2) +#define GT_SCS10HD (0x010 >> 2) +#define GT_SCS32LD (0x018 >> 2) +#define GT_SCS32HD (0x020 >> 2) +#define GT_CS20LD (0x028 >> 2) +#define GT_CS20HD (0x030 >> 2) +#define GT_CS3BOOTLD (0x038 >> 2) +#define GT_CS3BOOTHD (0x040 >> 2) +#define GT_PCI0IOLD (0x048 >> 2) +#define GT_PCI0IOHD (0x050 >> 2) +#define GT_PCI0M0LD (0x058 >> 2) +#define GT_PCI0M0HD (0x060 >> 2) +#define GT_PCI0M1LD (0x080 >> 2) +#define GT_PCI0M1HD (0x088 >> 2) +#define GT_PCI1IOLD (0x090 >> 2) +#define GT_PCI1IOHD (0x098 >> 2) +#define GT_PCI1M0LD (0x0a0 >> 2) +#define GT_PCI1M0HD (0x0a8 >> 2) +#define GT_PCI1M1LD (0x0b0 >> 2) +#define GT_PCI1M1HD (0x0b8 >> 2) +#define GT_ISD (0x068 >> 2) + +#define GT_SCS10AR (0x0d0 >> 2) +#define GT_SCS32AR (0x0d8 >> 2) +#define GT_CS20R (0x0e0 >> 2) +#define GT_CS3BOOTR (0x0e8 >> 2) + +#define GT_PCI0IOREMAP (0x0f0 >> 2) +#define GT_PCI0M0REMAP (0x0f8 >> 2) +#define GT_PCI0M1REMAP (0x100 >> 2) +#define GT_PCI1IOREMAP (0x108 >> 2) +#define GT_PCI1M0REMAP (0x110 >> 2) +#define GT_PCI1M1REMAP (0x118 >> 2) + +/* CPU Error Report */ +#define GT_CPUERR_ADDRLO (0x070 >> 2) +#define GT_CPUERR_ADDRHI (0x078 >> 2) +#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */ +#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */ +#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */ + +/* CPU Sync Barrier */ +#define GT_PCI0SYNC (0x0c0 >> 2) +#define GT_PCI1SYNC (0x0c8 >> 2) + +/* SDRAM and Device Address Decode */ +#define GT_SCS0LD (0x400 >> 2) +#define GT_SCS0HD (0x404 >> 2) +#define GT_SCS1LD (0x408 >> 2) +#define GT_SCS1HD (0x40c >> 2) +#define GT_SCS2LD (0x410 >> 2) +#define GT_SCS2HD (0x414 >> 2) +#define GT_SCS3LD (0x418 >> 2) +#define GT_SCS3HD (0x41c >> 2) +#define GT_CS0LD (0x420 >> 2) +#define GT_CS0HD (0x424 >> 2) +#define GT_CS1LD (0x428 >> 2) +#define GT_CS1HD (0x42c >> 2) +#define GT_CS2LD (0x430 >> 2) +#define GT_CS2HD (0x434 >> 2) +#define GT_CS3LD (0x438 >> 2) +#define GT_CS3HD (0x43c >> 2) +#define GT_BOOTLD (0x440 >> 2) +#define GT_BOOTHD (0x444 >> 2) +#define GT_ADERR (0x470 >> 2) + +/* SDRAM Configuration */ +#define GT_SDRAM_CFG (0x448 >> 2) +#define GT_SDRAM_OPMODE (0x474 >> 2) +#define GT_SDRAM_BM (0x478 >> 2) +#define GT_SDRAM_ADDRDECODE (0x47c >> 2) + +/* SDRAM Parameters */ +#define GT_SDRAM_B0 (0x44c >> 2) +#define GT_SDRAM_B1 (0x450 >> 2) +#define GT_SDRAM_B2 (0x454 >> 2) +#define GT_SDRAM_B3 (0x458 >> 2) + +/* Device Parameters */ +#define GT_DEV_B0 (0x45c >> 2) +#define GT_DEV_B1 (0x460 >> 2) +#define GT_DEV_B2 (0x464 >> 2) +#define GT_DEV_B3 (0x468 >> 2) +#define GT_DEV_BOOT (0x46c >> 2) + +/* ECC */ +#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */ +#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */ +#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */ +#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */ +#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */ + +/* DMA Record */ +#define GT_DMA0_CNT (0x800 >> 2) +#define GT_DMA1_CNT (0x804 >> 2) +#define GT_DMA2_CNT (0x808 >> 2) +#define GT_DMA3_CNT (0x80c >> 2) +#define GT_DMA0_SA (0x810 >> 2) +#define GT_DMA1_SA (0x814 >> 2) +#define GT_DMA2_SA (0x818 >> 2) +#define GT_DMA3_SA (0x81c >> 2) +#define GT_DMA0_DA (0x820 >> 2) +#define GT_DMA1_DA (0x824 >> 2) +#define GT_DMA2_DA (0x828 >> 2) +#define GT_DMA3_DA (0x82c >> 2) +#define GT_DMA0_NEXT (0x830 >> 2) +#define GT_DMA1_NEXT (0x834 >> 2) +#define GT_DMA2_NEXT (0x838 >> 2) +#define GT_DMA3_NEXT (0x83c >> 2) +#define GT_DMA0_CUR (0x870 >> 2) +#define GT_DMA1_CUR (0x874 >> 2) +#define GT_DMA2_CUR (0x878 >> 2) +#define GT_DMA3_CUR (0x87c >> 2) + +/* DMA Channel Control */ +#define GT_DMA0_CTRL (0x840 >> 2) +#define GT_DMA1_CTRL (0x844 >> 2) +#define GT_DMA2_CTRL (0x848 >> 2) +#define GT_DMA3_CTRL (0x84c >> 2) + +/* DMA Arbiter */ +#define GT_DMA_ARB (0x860 >> 2) + +/* Timer/Counter */ +#define GT_TC0 (0x850 >> 2) +#define GT_TC1 (0x854 >> 2) +#define GT_TC2 (0x858 >> 2) +#define GT_TC3 (0x85c >> 2) +#define GT_TC_CONTROL (0x864 >> 2) + +/* PCI Internal */ +#define GT_PCI0_CMD (0xc00 >> 2) +#define GT_PCI0_TOR (0xc04 >> 2) +#define GT_PCI0_BS_SCS10 (0xc08 >> 2) +#define GT_PCI0_BS_SCS32 (0xc0c >> 2) +#define GT_PCI0_BS_CS20 (0xc10 >> 2) +#define GT_PCI0_BS_CS3BT (0xc14 >> 2) +#define GT_PCI1_IACK (0xc30 >> 2) +#define GT_PCI0_IACK (0xc34 >> 2) +#define GT_PCI0_BARE (0xc3c >> 2) +#define GT_PCI0_PREFMBR (0xc40 >> 2) +#define GT_PCI0_SCS10_BAR (0xc48 >> 2) +#define GT_PCI0_SCS32_BAR (0xc4c >> 2) +#define GT_PCI0_CS20_BAR (0xc50 >> 2) +#define GT_PCI0_CS3BT_BAR (0xc54 >> 2) +#define GT_PCI0_SSCS10_BAR (0xc58 >> 2) +#define GT_PCI0_SSCS32_BAR (0xc5c >> 2) +#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2) +#define GT_PCI1_CMD (0xc80 >> 2) +#define GT_PCI1_TOR (0xc84 >> 2) +#define GT_PCI1_BS_SCS10 (0xc88 >> 2) +#define GT_PCI1_BS_SCS32 (0xc8c >> 2) +#define GT_PCI1_BS_CS20 (0xc90 >> 2) +#define GT_PCI1_BS_CS3BT (0xc94 >> 2) +#define GT_PCI1_BARE (0xcbc >> 2) +#define GT_PCI1_PREFMBR (0xcc0 >> 2) +#define GT_PCI1_SCS10_BAR (0xcc8 >> 2) +#define GT_PCI1_SCS32_BAR (0xccc >> 2) +#define GT_PCI1_CS20_BAR (0xcd0 >> 2) +#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2) +#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2) +#define GT_PCI1_SSCS32_BAR (0xcdc >> 2) +#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2) +#define GT_PCI1_CFGADDR (0xcf0 >> 2) +#define GT_PCI1_CFGDATA (0xcf4 >> 2) +#define GT_PCI0_CFGADDR (0xcf8 >> 2) +#define GT_PCI0_CFGDATA (0xcfc >> 2) + +/* Interrupts */ +#define GT_INTRCAUSE (0xc18 >> 2) +#define GT_INTRMASK (0xc1c >> 2) +#define GT_PCI0_ICMASK (0xc24 >> 2) +#define GT_PCI0_SERR0MASK (0xc28 >> 2) +#define GT_CPU_INTSEL (0xc70 >> 2) +#define GT_PCI0_INTSEL (0xc74 >> 2) +#define GT_HINTRCAUSE (0xc98 >> 2) +#define GT_HINTRMASK (0xc9c >> 2) +#define GT_PCI0_HICMASK (0xca4 >> 2) +#define GT_PCI1_SERR1MASK (0xca8 >> 2) + +#define PCI_MAPPING_ENTRY(regname) \ + hwaddr regname ##_start; \ + hwaddr regname ##_length; \ + MemoryRegion regname ##_mem + +#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120" + +#define GT64120_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(GT64120State, (obj), TYPE_GT64120_PCI_HOST_BRIDGE) + +typedef struct GT64120State { + PCIHostState parent_obj; + + uint32_t regs[GT_REGS]; + PCI_MAPPING_ENTRY(PCI0IO); + PCI_MAPPING_ENTRY(ISD); +} GT64120State; + +/* Adjust range to avoid touching space which isn't mappable via PCI */ +/* XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000 + 0x1fc00000 - 0x1fd00000 */ +static void check_reserved_space (hwaddr *start, + hwaddr *length) +{ + hwaddr begin = *start; + hwaddr end = *start + *length; + + if (end >= 0x1e000000LL && end < 0x1f100000LL) + end = 0x1e000000LL; + if (begin >= 0x1e000000LL && begin < 0x1f100000LL) + begin = 0x1f100000LL; + if (end >= 0x1fc00000LL && end < 0x1fd00000LL) + end = 0x1fc00000LL; + if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL) + begin = 0x1fd00000LL; + /* XXX: This is broken when a reserved range splits the requested range */ + if (end >= 0x1f100000LL && begin < 0x1e000000LL) + end = 0x1e000000LL; + if (end >= 0x1fd00000LL && begin < 0x1fc00000LL) + end = 0x1fc00000LL; + + *start = begin; + *length = end - begin; +} + +static void gt64120_isd_mapping(GT64120State *s) +{ + hwaddr start = s->regs[GT_ISD] << 21; + hwaddr length = 0x1000; + + if (s->ISD_length) { + memory_region_del_subregion(get_system_memory(), &s->ISD_mem); + } + check_reserved_space(&start, &length); + length = 0x1000; + /* Map new address */ + DPRINTF("ISD: "TARGET_FMT_plx"@"TARGET_FMT_plx + " -> "TARGET_FMT_plx"@"TARGET_FMT_plx"\n", + s->ISD_length, s->ISD_start, length, start); + s->ISD_start = start; + s->ISD_length = length; + memory_region_add_subregion(get_system_memory(), s->ISD_start, &s->ISD_mem); +} + +static void gt64120_pci_mapping(GT64120State *s) +{ + /* Update IO mapping */ + if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD]) + { + /* Unmap old IO address */ + if (s->PCI0IO_length) + { + memory_region_del_subregion(get_system_memory(), &s->PCI0IO_mem); + memory_region_destroy(&s->PCI0IO_mem); + } + /* Map new IO address */ + s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21; + s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) - (s->regs[GT_PCI0IOLD] & 0x7f)) << 21; + isa_mem_base = s->PCI0IO_start; + if (s->PCI0IO_length) { + isa_mmio_setup(&s->PCI0IO_mem, s->PCI0IO_length); + memory_region_add_subregion(get_system_memory(), s->PCI0IO_start, + &s->PCI0IO_mem); + } + } +} + +static void gt64120_writel (void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + GT64120State *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + uint32_t saddr; + + if (!(s->regs[GT_CPU] & 0x00001000)) + val = bswap32(val); + + saddr = (addr & 0xfff) >> 2; + switch (saddr) { + + /* CPU Configuration */ + case GT_CPU: + s->regs[GT_CPU] = val; + break; + case GT_MULTI: + /* Read-only register as only one GT64xxx is present on the CPU bus */ + break; + + /* CPU Address Decode */ + case GT_PCI0IOLD: + s->regs[GT_PCI0IOLD] = val & 0x00007fff; + s->regs[GT_PCI0IOREMAP] = val & 0x000007ff; + gt64120_pci_mapping(s); + break; + case GT_PCI0M0LD: + s->regs[GT_PCI0M0LD] = val & 0x00007fff; + s->regs[GT_PCI0M0REMAP] = val & 0x000007ff; + break; + case GT_PCI0M1LD: + s->regs[GT_PCI0M1LD] = val & 0x00007fff; + s->regs[GT_PCI0M1REMAP] = val & 0x000007ff; + break; + case GT_PCI1IOLD: + s->regs[GT_PCI1IOLD] = val & 0x00007fff; + s->regs[GT_PCI1IOREMAP] = val & 0x000007ff; + break; + case GT_PCI1M0LD: + s->regs[GT_PCI1M0LD] = val & 0x00007fff; + s->regs[GT_PCI1M0REMAP] = val & 0x000007ff; + break; + case GT_PCI1M1LD: + s->regs[GT_PCI1M1LD] = val & 0x00007fff; + s->regs[GT_PCI1M1REMAP] = val & 0x000007ff; + break; + case GT_PCI0IOHD: + s->regs[saddr] = val & 0x0000007f; + gt64120_pci_mapping(s); + break; + case GT_PCI0M0HD: + case GT_PCI0M1HD: + case GT_PCI1IOHD: + case GT_PCI1M0HD: + case GT_PCI1M1HD: + s->regs[saddr] = val & 0x0000007f; + break; + case GT_ISD: + s->regs[saddr] = val & 0x00007fff; + gt64120_isd_mapping(s); + break; + + case GT_PCI0IOREMAP: + case GT_PCI0M0REMAP: + case GT_PCI0M1REMAP: + case GT_PCI1IOREMAP: + case GT_PCI1M0REMAP: + case GT_PCI1M1REMAP: + s->regs[saddr] = val & 0x000007ff; + break; + + /* CPU Error Report */ + case GT_CPUERR_ADDRLO: + case GT_CPUERR_ADDRHI: + case GT_CPUERR_DATALO: + case GT_CPUERR_DATAHI: + case GT_CPUERR_PARITY: + /* Read-only registers, do nothing */ + break; + + /* CPU Sync Barrier */ + case GT_PCI0SYNC: + case GT_PCI1SYNC: + /* Read-only registers, do nothing */ + break; + + /* SDRAM and Device Address Decode */ + case GT_SCS0LD: + case GT_SCS0HD: + case GT_SCS1LD: + case GT_SCS1HD: + case GT_SCS2LD: + case GT_SCS2HD: + case GT_SCS3LD: + case GT_SCS3HD: + case GT_CS0LD: + case GT_CS0HD: + case GT_CS1LD: + case GT_CS1HD: + case GT_CS2LD: + case GT_CS2HD: + case GT_CS3LD: + case GT_CS3HD: + case GT_BOOTLD: + case GT_BOOTHD: + case GT_ADERR: + /* SDRAM Configuration */ + case GT_SDRAM_CFG: + case GT_SDRAM_OPMODE: + case GT_SDRAM_BM: + case GT_SDRAM_ADDRDECODE: + /* Accept and ignore SDRAM interleave configuration */ + s->regs[saddr] = val; + break; + + /* Device Parameters */ + case GT_DEV_B0: + case GT_DEV_B1: + case GT_DEV_B2: + case GT_DEV_B3: + case GT_DEV_BOOT: + /* Not implemented */ + DPRINTF ("Unimplemented device register offset 0x%x\n", saddr << 2); + break; + + /* ECC */ + case GT_ECC_ERRDATALO: + case GT_ECC_ERRDATAHI: + case GT_ECC_MEM: + case GT_ECC_CALC: + case GT_ECC_ERRADDR: + /* Read-only registers, do nothing */ + break; + + /* DMA Record */ + case GT_DMA0_CNT: + case GT_DMA1_CNT: + case GT_DMA2_CNT: + case GT_DMA3_CNT: + case GT_DMA0_SA: + case GT_DMA1_SA: + case GT_DMA2_SA: + case GT_DMA3_SA: + case GT_DMA0_DA: + case GT_DMA1_DA: + case GT_DMA2_DA: + case GT_DMA3_DA: + case GT_DMA0_NEXT: + case GT_DMA1_NEXT: + case GT_DMA2_NEXT: + case GT_DMA3_NEXT: + case GT_DMA0_CUR: + case GT_DMA1_CUR: + case GT_DMA2_CUR: + case GT_DMA3_CUR: + /* Not implemented */ + DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); + break; + + /* DMA Channel Control */ + case GT_DMA0_CTRL: + case GT_DMA1_CTRL: + case GT_DMA2_CTRL: + case GT_DMA3_CTRL: + /* Not implemented */ + DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); + break; + + /* DMA Arbiter */ + case GT_DMA_ARB: + /* Not implemented */ + DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); + break; + + /* Timer/Counter */ + case GT_TC0: + case GT_TC1: + case GT_TC2: + case GT_TC3: + case GT_TC_CONTROL: + /* Not implemented */ + DPRINTF ("Unimplemented timer register offset 0x%x\n", saddr << 2); + break; + + /* PCI Internal */ + case GT_PCI0_CMD: + case GT_PCI1_CMD: + s->regs[saddr] = val & 0x0401fc0f; + break; + case GT_PCI0_TOR: + case GT_PCI0_BS_SCS10: + case GT_PCI0_BS_SCS32: + case GT_PCI0_BS_CS20: + case GT_PCI0_BS_CS3BT: + case GT_PCI1_IACK: + case GT_PCI0_IACK: + case GT_PCI0_BARE: + case GT_PCI0_PREFMBR: + case GT_PCI0_SCS10_BAR: + case GT_PCI0_SCS32_BAR: + case GT_PCI0_CS20_BAR: + case GT_PCI0_CS3BT_BAR: + case GT_PCI0_SSCS10_BAR: + case GT_PCI0_SSCS32_BAR: + case GT_PCI0_SCS3BT_BAR: + case GT_PCI1_TOR: + case GT_PCI1_BS_SCS10: + case GT_PCI1_BS_SCS32: + case GT_PCI1_BS_CS20: + case GT_PCI1_BS_CS3BT: + case GT_PCI1_BARE: + case GT_PCI1_PREFMBR: + case GT_PCI1_SCS10_BAR: + case GT_PCI1_SCS32_BAR: + case GT_PCI1_CS20_BAR: + case GT_PCI1_CS3BT_BAR: + case GT_PCI1_SSCS10_BAR: + case GT_PCI1_SSCS32_BAR: + case GT_PCI1_SCS3BT_BAR: + case GT_PCI1_CFGADDR: + case GT_PCI1_CFGDATA: + /* not implemented */ + break; + case GT_PCI0_CFGADDR: + phb->config_reg = val & 0x80fffffc; + break; + case GT_PCI0_CFGDATA: + if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { + val = bswap32(val); + } + if (phb->config_reg & (1u << 31)) { + pci_data_write(phb->bus, phb->config_reg, val, 4); + } + break; + + /* Interrupts */ + case GT_INTRCAUSE: + /* not really implemented */ + s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe)); + s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe); + DPRINTF("INTRCAUSE %" PRIx64 "\n", val); + break; + case GT_INTRMASK: + s->regs[saddr] = val & 0x3c3ffffe; + DPRINTF("INTRMASK %" PRIx64 "\n", val); + break; + case GT_PCI0_ICMASK: + s->regs[saddr] = val & 0x03fffffe; + DPRINTF("ICMASK %" PRIx64 "\n", val); + break; + case GT_PCI0_SERR0MASK: + s->regs[saddr] = val & 0x0000003f; + DPRINTF("SERR0MASK %" PRIx64 "\n", val); + break; + + /* Reserved when only PCI_0 is configured. */ + case GT_HINTRCAUSE: + case GT_CPU_INTSEL: + case GT_PCI0_INTSEL: + case GT_HINTRMASK: + case GT_PCI0_HICMASK: + case GT_PCI1_SERR1MASK: + /* not implemented */ + break; + + /* SDRAM Parameters */ + case GT_SDRAM_B0: + case GT_SDRAM_B1: + case GT_SDRAM_B2: + case GT_SDRAM_B3: + /* We don't simulate electrical parameters of the SDRAM. + Accept, but ignore the values. */ + s->regs[saddr] = val; + break; + + default: + DPRINTF ("Bad register offset 0x%x\n", (int)addr); + break; + } +} + +static uint64_t gt64120_readl (void *opaque, + hwaddr addr, unsigned size) +{ + GT64120State *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + uint32_t val; + uint32_t saddr; + + saddr = (addr & 0xfff) >> 2; + switch (saddr) { + + /* CPU Configuration */ + case GT_MULTI: + /* Only one GT64xxx is present on the CPU bus, return + the initial value */ + val = s->regs[saddr]; + break; + + /* CPU Error Report */ + case GT_CPUERR_ADDRLO: + case GT_CPUERR_ADDRHI: + case GT_CPUERR_DATALO: + case GT_CPUERR_DATAHI: + case GT_CPUERR_PARITY: + /* Emulated memory has no error, always return the initial + values */ + val = s->regs[saddr]; + break; + + /* CPU Sync Barrier */ + case GT_PCI0SYNC: + case GT_PCI1SYNC: + /* Reading those register should empty all FIFO on the PCI + bus, which are not emulated. The return value should be + a random value that should be ignored. */ + val = 0xc000ffee; + break; + + /* ECC */ + case GT_ECC_ERRDATALO: + case GT_ECC_ERRDATAHI: + case GT_ECC_MEM: + case GT_ECC_CALC: + case GT_ECC_ERRADDR: + /* Emulated memory has no error, always return the initial + values */ + val = s->regs[saddr]; + break; + + case GT_CPU: + case GT_SCS10LD: + case GT_SCS10HD: + case GT_SCS32LD: + case GT_SCS32HD: + case GT_CS20LD: + case GT_CS20HD: + case GT_CS3BOOTLD: + case GT_CS3BOOTHD: + case GT_SCS10AR: + case GT_SCS32AR: + case GT_CS20R: + case GT_CS3BOOTR: + case GT_PCI0IOLD: + case GT_PCI0M0LD: + case GT_PCI0M1LD: + case GT_PCI1IOLD: + case GT_PCI1M0LD: + case GT_PCI1M1LD: + case GT_PCI0IOHD: + case GT_PCI0M0HD: + case GT_PCI0M1HD: + case GT_PCI1IOHD: + case GT_PCI1M0HD: + case GT_PCI1M1HD: + case GT_PCI0IOREMAP: + case GT_PCI0M0REMAP: + case GT_PCI0M1REMAP: + case GT_PCI1IOREMAP: + case GT_PCI1M0REMAP: + case GT_PCI1M1REMAP: + case GT_ISD: + val = s->regs[saddr]; + break; + case GT_PCI0_IACK: + /* Read the IRQ number */ + val = pic_read_irq(isa_pic); + break; + + /* SDRAM and Device Address Decode */ + case GT_SCS0LD: + case GT_SCS0HD: + case GT_SCS1LD: + case GT_SCS1HD: + case GT_SCS2LD: + case GT_SCS2HD: + case GT_SCS3LD: + case GT_SCS3HD: + case GT_CS0LD: + case GT_CS0HD: + case GT_CS1LD: + case GT_CS1HD: + case GT_CS2LD: + case GT_CS2HD: + case GT_CS3LD: + case GT_CS3HD: + case GT_BOOTLD: + case GT_BOOTHD: + case GT_ADERR: + val = s->regs[saddr]; + break; + + /* SDRAM Configuration */ + case GT_SDRAM_CFG: + case GT_SDRAM_OPMODE: + case GT_SDRAM_BM: + case GT_SDRAM_ADDRDECODE: + val = s->regs[saddr]; + break; + + /* SDRAM Parameters */ + case GT_SDRAM_B0: + case GT_SDRAM_B1: + case GT_SDRAM_B2: + case GT_SDRAM_B3: + /* We don't simulate electrical parameters of the SDRAM. + Just return the last written value. */ + val = s->regs[saddr]; + break; + + /* Device Parameters */ + case GT_DEV_B0: + case GT_DEV_B1: + case GT_DEV_B2: + case GT_DEV_B3: + case GT_DEV_BOOT: + val = s->regs[saddr]; + break; + + /* DMA Record */ + case GT_DMA0_CNT: + case GT_DMA1_CNT: + case GT_DMA2_CNT: + case GT_DMA3_CNT: + case GT_DMA0_SA: + case GT_DMA1_SA: + case GT_DMA2_SA: + case GT_DMA3_SA: + case GT_DMA0_DA: + case GT_DMA1_DA: + case GT_DMA2_DA: + case GT_DMA3_DA: + case GT_DMA0_NEXT: + case GT_DMA1_NEXT: + case GT_DMA2_NEXT: + case GT_DMA3_NEXT: + case GT_DMA0_CUR: + case GT_DMA1_CUR: + case GT_DMA2_CUR: + case GT_DMA3_CUR: + val = s->regs[saddr]; + break; + + /* DMA Channel Control */ + case GT_DMA0_CTRL: + case GT_DMA1_CTRL: + case GT_DMA2_CTRL: + case GT_DMA3_CTRL: + val = s->regs[saddr]; + break; + + /* DMA Arbiter */ + case GT_DMA_ARB: + val = s->regs[saddr]; + break; + + /* Timer/Counter */ + case GT_TC0: + case GT_TC1: + case GT_TC2: + case GT_TC3: + case GT_TC_CONTROL: + val = s->regs[saddr]; + break; + + /* PCI Internal */ + case GT_PCI0_CFGADDR: + val = phb->config_reg; + break; + case GT_PCI0_CFGDATA: + if (!(phb->config_reg & (1 << 31))) { + val = 0xffffffff; + } else { + val = pci_data_read(phb->bus, phb->config_reg, 4); + } + if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { + val = bswap32(val); + } + break; + + case GT_PCI0_CMD: + case GT_PCI0_TOR: + case GT_PCI0_BS_SCS10: + case GT_PCI0_BS_SCS32: + case GT_PCI0_BS_CS20: + case GT_PCI0_BS_CS3BT: + case GT_PCI1_IACK: + case GT_PCI0_BARE: + case GT_PCI0_PREFMBR: + case GT_PCI0_SCS10_BAR: + case GT_PCI0_SCS32_BAR: + case GT_PCI0_CS20_BAR: + case GT_PCI0_CS3BT_BAR: + case GT_PCI0_SSCS10_BAR: + case GT_PCI0_SSCS32_BAR: + case GT_PCI0_SCS3BT_BAR: + case GT_PCI1_CMD: + case GT_PCI1_TOR: + case GT_PCI1_BS_SCS10: + case GT_PCI1_BS_SCS32: + case GT_PCI1_BS_CS20: + case GT_PCI1_BS_CS3BT: + case GT_PCI1_BARE: + case GT_PCI1_PREFMBR: + case GT_PCI1_SCS10_BAR: + case GT_PCI1_SCS32_BAR: + case GT_PCI1_CS20_BAR: + case GT_PCI1_CS3BT_BAR: + case GT_PCI1_SSCS10_BAR: + case GT_PCI1_SSCS32_BAR: + case GT_PCI1_SCS3BT_BAR: + case GT_PCI1_CFGADDR: + case GT_PCI1_CFGDATA: + val = s->regs[saddr]; + break; + + /* Interrupts */ + case GT_INTRCAUSE: + val = s->regs[saddr]; + DPRINTF("INTRCAUSE %x\n", val); + break; + case GT_INTRMASK: + val = s->regs[saddr]; + DPRINTF("INTRMASK %x\n", val); + break; + case GT_PCI0_ICMASK: + val = s->regs[saddr]; + DPRINTF("ICMASK %x\n", val); + break; + case GT_PCI0_SERR0MASK: + val = s->regs[saddr]; + DPRINTF("SERR0MASK %x\n", val); + break; + + /* Reserved when only PCI_0 is configured. */ + case GT_HINTRCAUSE: + case GT_CPU_INTSEL: + case GT_PCI0_INTSEL: + case GT_HINTRMASK: + case GT_PCI0_HICMASK: + case GT_PCI1_SERR1MASK: + val = s->regs[saddr]; + break; + + default: + val = s->regs[saddr]; + DPRINTF ("Bad register offset 0x%x\n", (int)addr); + break; + } + + if (!(s->regs[GT_CPU] & 0x00001000)) + val = bswap32(val); + + return val; +} + +static const MemoryRegionOps isd_mem_ops = { + .read = gt64120_readl, + .write = gt64120_writel, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int slot; + + slot = (pci_dev->devfn >> 3); + + switch (slot) { + /* PIIX4 USB */ + case 10: + return 3; + /* AMD 79C973 Ethernet */ + case 11: + return 1; + /* Crystal 4281 Sound */ + case 12: + return 2; + /* PCI slot 1 to 4 */ + case 18 ... 21: + return ((slot - 18) + irq_num) & 0x03; + /* Unknown device, don't do any translation */ + default: + return irq_num; + } +} + +static int pci_irq_levels[4]; + +static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) +{ + int i, pic_irq, pic_level; + qemu_irq *pic = opaque; + + pci_irq_levels[irq_num] = level; + + /* now we change the pic irq level according to the piix irq mappings */ + /* XXX: optimize */ + pic_irq = piix4_dev->config[0x60 + irq_num]; + if (pic_irq < 16) { + /* The pic level is the logical OR of all the PCI irqs mapped + to it */ + pic_level = 0; + for (i = 0; i < 4; i++) { + if (pic_irq == piix4_dev->config[0x60 + i]) + pic_level |= pci_irq_levels[i]; + } + qemu_set_irq(pic[pic_irq], pic_level); + } +} + + +static void gt64120_reset(void *opaque) +{ + GT64120State *s = opaque; + + /* FIXME: Malta specific hw assumptions ahead */ + + /* CPU Configuration */ +#ifdef TARGET_WORDS_BIGENDIAN + s->regs[GT_CPU] = 0x00000000; +#else + s->regs[GT_CPU] = 0x00001000; +#endif + s->regs[GT_MULTI] = 0x00000003; + + /* CPU Address decode */ + s->regs[GT_SCS10LD] = 0x00000000; + s->regs[GT_SCS10HD] = 0x00000007; + s->regs[GT_SCS32LD] = 0x00000008; + s->regs[GT_SCS32HD] = 0x0000000f; + s->regs[GT_CS20LD] = 0x000000e0; + s->regs[GT_CS20HD] = 0x00000070; + s->regs[GT_CS3BOOTLD] = 0x000000f8; + s->regs[GT_CS3BOOTHD] = 0x0000007f; + + s->regs[GT_PCI0IOLD] = 0x00000080; + s->regs[GT_PCI0IOHD] = 0x0000000f; + s->regs[GT_PCI0M0LD] = 0x00000090; + s->regs[GT_PCI0M0HD] = 0x0000001f; + s->regs[GT_ISD] = 0x000000a0; + s->regs[GT_PCI0M1LD] = 0x00000790; + s->regs[GT_PCI0M1HD] = 0x0000001f; + s->regs[GT_PCI1IOLD] = 0x00000100; + s->regs[GT_PCI1IOHD] = 0x0000000f; + s->regs[GT_PCI1M0LD] = 0x00000110; + s->regs[GT_PCI1M0HD] = 0x0000001f; + s->regs[GT_PCI1M1LD] = 0x00000120; + s->regs[GT_PCI1M1HD] = 0x0000002f; + + s->regs[GT_SCS10AR] = 0x00000000; + s->regs[GT_SCS32AR] = 0x00000008; + s->regs[GT_CS20R] = 0x000000e0; + s->regs[GT_CS3BOOTR] = 0x000000f8; + + s->regs[GT_PCI0IOREMAP] = 0x00000080; + s->regs[GT_PCI0M0REMAP] = 0x00000090; + s->regs[GT_PCI0M1REMAP] = 0x00000790; + s->regs[GT_PCI1IOREMAP] = 0x00000100; + s->regs[GT_PCI1M0REMAP] = 0x00000110; + s->regs[GT_PCI1M1REMAP] = 0x00000120; + + /* CPU Error Report */ + s->regs[GT_CPUERR_ADDRLO] = 0x00000000; + s->regs[GT_CPUERR_ADDRHI] = 0x00000000; + s->regs[GT_CPUERR_DATALO] = 0xffffffff; + s->regs[GT_CPUERR_DATAHI] = 0xffffffff; + s->regs[GT_CPUERR_PARITY] = 0x000000ff; + + /* CPU Sync Barrier */ + s->regs[GT_PCI0SYNC] = 0x00000000; + s->regs[GT_PCI1SYNC] = 0x00000000; + + /* SDRAM and Device Address Decode */ + s->regs[GT_SCS0LD] = 0x00000000; + s->regs[GT_SCS0HD] = 0x00000007; + s->regs[GT_SCS1LD] = 0x00000008; + s->regs[GT_SCS1HD] = 0x0000000f; + s->regs[GT_SCS2LD] = 0x00000010; + s->regs[GT_SCS2HD] = 0x00000017; + s->regs[GT_SCS3LD] = 0x00000018; + s->regs[GT_SCS3HD] = 0x0000001f; + s->regs[GT_CS0LD] = 0x000000c0; + s->regs[GT_CS0HD] = 0x000000c7; + s->regs[GT_CS1LD] = 0x000000c8; + s->regs[GT_CS1HD] = 0x000000cf; + s->regs[GT_CS2LD] = 0x000000d0; + s->regs[GT_CS2HD] = 0x000000df; + s->regs[GT_CS3LD] = 0x000000f0; + s->regs[GT_CS3HD] = 0x000000fb; + s->regs[GT_BOOTLD] = 0x000000fc; + s->regs[GT_BOOTHD] = 0x000000ff; + s->regs[GT_ADERR] = 0xffffffff; + + /* SDRAM Configuration */ + s->regs[GT_SDRAM_CFG] = 0x00000200; + s->regs[GT_SDRAM_OPMODE] = 0x00000000; + s->regs[GT_SDRAM_BM] = 0x00000007; + s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002; + + /* SDRAM Parameters */ + s->regs[GT_SDRAM_B0] = 0x00000005; + s->regs[GT_SDRAM_B1] = 0x00000005; + s->regs[GT_SDRAM_B2] = 0x00000005; + s->regs[GT_SDRAM_B3] = 0x00000005; + + /* ECC */ + s->regs[GT_ECC_ERRDATALO] = 0x00000000; + s->regs[GT_ECC_ERRDATAHI] = 0x00000000; + s->regs[GT_ECC_MEM] = 0x00000000; + s->regs[GT_ECC_CALC] = 0x00000000; + s->regs[GT_ECC_ERRADDR] = 0x00000000; + + /* Device Parameters */ + s->regs[GT_DEV_B0] = 0x386fffff; + s->regs[GT_DEV_B1] = 0x386fffff; + s->regs[GT_DEV_B2] = 0x386fffff; + s->regs[GT_DEV_B3] = 0x386fffff; + s->regs[GT_DEV_BOOT] = 0x146fffff; + + /* DMA registers are all zeroed at reset */ + + /* Timer/Counter */ + s->regs[GT_TC0] = 0xffffffff; + s->regs[GT_TC1] = 0x00ffffff; + s->regs[GT_TC2] = 0x00ffffff; + s->regs[GT_TC3] = 0x00ffffff; + s->regs[GT_TC_CONTROL] = 0x00000000; + + /* PCI Internal */ +#ifdef TARGET_WORDS_BIGENDIAN + s->regs[GT_PCI0_CMD] = 0x00000000; +#else + s->regs[GT_PCI0_CMD] = 0x00010001; +#endif + s->regs[GT_PCI0_TOR] = 0x0000070f; + s->regs[GT_PCI0_BS_SCS10] = 0x00fff000; + s->regs[GT_PCI0_BS_SCS32] = 0x00fff000; + s->regs[GT_PCI0_BS_CS20] = 0x01fff000; + s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000; + s->regs[GT_PCI1_IACK] = 0x00000000; + s->regs[GT_PCI0_IACK] = 0x00000000; + s->regs[GT_PCI0_BARE] = 0x0000000f; + s->regs[GT_PCI0_PREFMBR] = 0x00000040; + s->regs[GT_PCI0_SCS10_BAR] = 0x00000000; + s->regs[GT_PCI0_SCS32_BAR] = 0x01000000; + s->regs[GT_PCI0_CS20_BAR] = 0x1c000000; + s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000; + s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000; + s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000; + s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000; +#ifdef TARGET_WORDS_BIGENDIAN + s->regs[GT_PCI1_CMD] = 0x00000000; +#else + s->regs[GT_PCI1_CMD] = 0x00010001; +#endif + s->regs[GT_PCI1_TOR] = 0x0000070f; + s->regs[GT_PCI1_BS_SCS10] = 0x00fff000; + s->regs[GT_PCI1_BS_SCS32] = 0x00fff000; + s->regs[GT_PCI1_BS_CS20] = 0x01fff000; + s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000; + s->regs[GT_PCI1_BARE] = 0x0000000f; + s->regs[GT_PCI1_PREFMBR] = 0x00000040; + s->regs[GT_PCI1_SCS10_BAR] = 0x00000000; + s->regs[GT_PCI1_SCS32_BAR] = 0x01000000; + s->regs[GT_PCI1_CS20_BAR] = 0x1c000000; + s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000; + s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000; + s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000; + s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000; + s->regs[GT_PCI1_CFGADDR] = 0x00000000; + s->regs[GT_PCI1_CFGDATA] = 0x00000000; + s->regs[GT_PCI0_CFGADDR] = 0x00000000; + + /* Interrupt registers are all zeroed at reset */ + + gt64120_isd_mapping(s); + gt64120_pci_mapping(s); +} + +PCIBus *gt64120_register(qemu_irq *pic) +{ + GT64120State *d; + PCIHostState *phb; + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_GT64120_PCI_HOST_BRIDGE); + qdev_init_nofail(dev); + d = GT64120_PCI_HOST_BRIDGE(dev); + phb = PCI_HOST_BRIDGE(dev); + phb->bus = pci_register_bus(dev, "pci", + gt64120_pci_set_irq, gt64120_pci_map_irq, + pic, + get_system_memory(), + get_system_io(), + PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS); + memory_region_init_io(&d->ISD_mem, &isd_mem_ops, d, "isd-mem", 0x1000); + + pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); + return phb->bus; +} + +static int gt64120_init(SysBusDevice *dev) +{ + GT64120State *s; + + s = GT64120_PCI_HOST_BRIDGE(dev); + + /* FIXME: This value is computed from registers during reset, but some + devices (e.g. VGA card) need to know it when they are registered. + This also mean that changing the register to change the mapping + does not fully work. */ + isa_mem_base = 0x10000000; + qemu_register_reset(gt64120_reset, s); + return 0; +} + +static int gt64120_pci_init(PCIDevice *d) +{ + /* FIXME: Malta specific hw assumptions ahead */ + pci_set_word(d->config + PCI_COMMAND, 0); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + pci_config_set_prog_interface(d->config, 0); + pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); + pci_set_byte(d->config + 0x3d, 0x01); + + return 0; +} + +static void gt64120_pci_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = gt64120_pci_init; + k->vendor_id = PCI_VENDOR_ID_MARVELL; + k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; + k->revision = 0x10; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo gt64120_pci_info = { + .name = "gt64120_pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = gt64120_pci_class_init, +}; + +static void gt64120_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = gt64120_init; +} + +static const TypeInfo gt64120_info = { + .name = TYPE_GT64120_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(GT64120State), + .class_init = gt64120_class_init, +}; + +static void gt64120_pci_register_types(void) +{ + type_register_static(>64120_info); + type_register_static(>64120_pci_info); +} + +type_init(gt64120_pci_register_types) diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs new file mode 100644 index 0000000000..5dd92d28a0 --- /dev/null +++ b/hw/pci-bridge/Makefile.objs @@ -0,0 +1,3 @@ +common-obj-y += pci_bridge_dev.o +common-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o +common-obj-y += i82801b11.o diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c new file mode 100644 index 0000000000..5807a92d7f --- /dev/null +++ b/hw/pci-bridge/i82801b11.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * QEMU i82801b11 dmi-to-pci Bridge Emulation + * + * Copyright (c) 2009, 2010, 2011 + * Isaku Yamahata + * VA Linux Systems Japan K.K. + * Copyright (C) 2012 Jason Baron + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "hw/pci/pci.h" +#include "hw/i386/ich9.h" + + +/*****************************************************************************/ +/* ICH9 DMI-to-PCI bridge */ +#define I82801ba_SSVID_OFFSET 0x50 +#define I82801ba_SSVID_SVID 0 +#define I82801ba_SSVID_SSID 0 + +typedef struct I82801b11Bridge { + PCIBridge br; +} I82801b11Bridge; + +static int i82801b11_bridge_initfn(PCIDevice *d) +{ + int rc; + + rc = pci_bridge_initfn(d, TYPE_PCI_BUS); + if (rc < 0) { + return rc; + } + + rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET, + I82801ba_SSVID_SVID, I82801ba_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB); + return 0; + +err_bridge: + pci_bridge_exitfn(d); + + return rc; +} + +static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->is_bridge = 1; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11; + k->revision = ICH9_D2P_A2_REVISION; + k->init = i82801b11_bridge_initfn; +} + +static const TypeInfo i82801b11_bridge_info = { + .name = "i82801b11-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(I82801b11Bridge), + .class_init = i82801b11_bridge_class_init, +}; + +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus) +{ + PCIDevice *d; + PCIBridge *br; + char buf[16]; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, true, "i82801b11-bridge"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + qdev = &br->dev.qdev; + + snprintf(buf, sizeof(buf), "pci.%d", sec_bus); + pci_bridge_map_irq(br, buf, pci_swizzle_map_irq_fn); + qdev_init_nofail(qdev); + + return pci_bridge_get_sec_bus(br); +} + +static void d2pbr_register(void) +{ + type_register_static(&i82801b11_bridge_info); +} + +type_init(d2pbr_register); diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c new file mode 100644 index 0000000000..5cff61e095 --- /dev/null +++ b/hw/pci-bridge/ioh3420.c @@ -0,0 +1,250 @@ +/* + * ioh3420.c + * Intel X58 north bridge IOH + * PCI Express root port device id 3420 + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/ioh3420.h" + +#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ +#define PCI_DEVICE_ID_IOH_REV 0x2 +#define IOH_EP_SSVID_OFFSET 0x40 +#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL +#define IOH_EP_SSVID_SSID 0 +#define IOH_EP_MSI_OFFSET 0x60 +#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT +#define IOH_EP_MSI_NR_VECTOR 2 +#define IOH_EP_EXP_OFFSET 0x90 +#define IOH_EP_AER_OFFSET 0x100 + +/* + * If two MSI vector are allocated, Advanced Error Interrupt Message Number + * is 1. otherwise 0. + * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number. + */ +static uint8_t ioh3420_aer_vector(const PCIDevice *d) +{ + switch (msi_nr_vectors_allocated(d)) { + case 1: + return 0; + case 2: + return 1; + case 4: + case 8: + case 16: + case 32: + default: + break; + } + abort(); + return 0; +} + +static void ioh3420_aer_vector_update(PCIDevice *d) +{ + pcie_aer_root_set_vector(d, ioh3420_aer_vector(d)); +} + +static void ioh3420_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + uint32_t root_cmd = + pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); + + pci_bridge_write_config(d, address, val, len); + ioh3420_aer_vector_update(d); + pcie_cap_slot_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); + pcie_aer_root_write_config(d, address, val, len, root_cmd); +} + +static void ioh3420_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + + ioh3420_aer_vector_update(d); + pcie_cap_root_reset(d); + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_aer_root_reset(d); + pci_bridge_reset(qdev); + pci_bridge_disable_base_limit(d); +} + +static int ioh3420_initfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + int rc; + + rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); + if (rc < 0) { + return rc; + } + + pcie_port_init_reg(d); + + rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET, + IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, + IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + if (rc < 0) { + goto err_bridge; + } + rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port); + if (rc < 0) { + goto err_msi; + } + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s->slot); + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + goto err_pcie_cap; + } + pcie_cap_root_init(d); + rc = pcie_aer_init(d, IOH_EP_AER_OFFSET); + if (rc < 0) { + goto err; + } + pcie_aer_root_init(d); + ioh3420_aer_vector_update(d); + return 0; + +err: + pcie_chassis_del_slot(s); +err_pcie_cap: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + pci_bridge_exitfn(d); + return rc; +} + +static void ioh3420_exitfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port, uint8_t chassis, uint16_t slot) +{ + PCIDevice *d; + PCIBridge *br; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + + qdev = &br->dev.qdev; + pci_bridge_map_irq(br, bus_name, map_irq); + qdev_prop_set_uint8(qdev, "port", port); + qdev_prop_set_uint8(qdev, "chassis", chassis); + qdev_prop_set_uint16(qdev, "slot", slot); + qdev_init_nofail(qdev); + + return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br)); +} + +static const VMStateDescription vmstate_ioh3420 = { + .name = "ioh-3240-express-root-port", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = pcie_cap_slot_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot), + VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0, + vmstate_pcie_aer_log, PCIEAERLog), + VMSTATE_END_OF_LIST() + } +}; + +static Property ioh3420_properties[] = { + DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), + DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), + DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), + DEFINE_PROP_UINT16("aer_log_max", PCIESlot, + port.br.dev.exp.aer_log.log_max, + PCIE_AER_LOG_MAX_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ioh3420_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->is_express = 1; + k->is_bridge = 1; + k->config_write = ioh3420_write_config; + k->init = ioh3420_initfn; + k->exit = ioh3420_exitfn; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_IOH_EPORT; + k->revision = PCI_DEVICE_ID_IOH_REV; + dc->desc = "Intel IOH device id 3420 PCIE Root Port"; + dc->reset = ioh3420_reset; + dc->vmsd = &vmstate_ioh3420; + dc->props = ioh3420_properties; +} + +static const TypeInfo ioh3420_info = { + .name = "ioh3420", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIESlot), + .class_init = ioh3420_class_init, +}; + +static void ioh3420_register_types(void) +{ + type_register_static(&ioh3420_info); +} + +type_init(ioh3420_register_types) + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c new file mode 100644 index 0000000000..971b432474 --- /dev/null +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -0,0 +1,158 @@ +/* + * Standard PCI Bridge Device + * + * Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin + * + * http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/shpc.h" +#include "hw/pci/slotid_cap.h" +#include "exec/memory.h" +#include "hw/pci/pci_bus.h" + +struct PCIBridgeDev { + PCIBridge bridge; + MemoryRegion bar; + uint8_t chassis_nr; +#define PCI_BRIDGE_DEV_F_MSI_REQ 0 + uint32_t flags; +}; +typedef struct PCIBridgeDev PCIBridgeDev; + +static int pci_bridge_dev_initfn(PCIDevice *dev) +{ + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br); + int err; + + err = pci_bridge_initfn(dev, TYPE_PCI_BUS); + if (err) { + goto bridge_error; + } + memory_region_init(&bridge_dev->bar, "shpc-bar", shpc_bar_size(dev)); + err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); + if (err) { + goto shpc_error; + } + err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); + if (err) { + goto slotid_error; + } + if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && + msi_supported) { + err = msi_init(dev, 0, 1, true, true); + if (err < 0) { + goto msi_error; + } + } + /* TODO: spec recommends using 64 bit prefetcheable BAR. + * Check whether that works well. */ + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); + dev->config[PCI_INTERRUPT_PIN] = 0x1; + return 0; +msi_error: + slotid_cap_cleanup(dev); +slotid_error: + shpc_cleanup(dev, &bridge_dev->bar); +shpc_error: + memory_region_destroy(&bridge_dev->bar); + pci_bridge_exitfn(dev); +bridge_error: + return err; +} + +static void pci_bridge_dev_exitfn(PCIDevice *dev) +{ + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br); + if (msi_present(dev)) { + msi_uninit(dev); + } + slotid_cap_cleanup(dev); + shpc_cleanup(dev, &bridge_dev->bar); + memory_region_destroy(&bridge_dev->bar); + pci_bridge_exitfn(dev); +} + +static void pci_bridge_dev_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + if (msi_present(d)) { + msi_write_config(d, address, val, len); + } + shpc_cap_write_config(d, address, val, len); +} + +static void qdev_pci_bridge_dev_reset(DeviceState *qdev) +{ + PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); + + pci_bridge_reset(qdev); + shpc_reset(dev); +} + +static Property pci_bridge_dev_properties[] = { + /* Note: 0 is not a legal chassis number. */ + DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0), + DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription pci_bridge_dev_vmstate = { + .name = "pci_bridge", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(bridge.dev, PCIBridgeDev), + SHPC_VMSTATE(bridge.dev.shpc, PCIBridgeDev), + VMSTATE_END_OF_LIST() + } +}; + +static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + k->init = pci_bridge_dev_initfn; + k->exit = pci_bridge_dev_exitfn; + k->config_write = pci_bridge_dev_write_config; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE; + k->class_id = PCI_CLASS_BRIDGE_PCI; + k->is_bridge = 1, + dc->desc = "Standard PCI Bridge"; + dc->reset = qdev_pci_bridge_dev_reset; + dc->props = pci_bridge_dev_properties; + dc->vmsd = &pci_bridge_dev_vmstate; +} + +static const TypeInfo pci_bridge_dev_info = { + .name = "pci-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIBridgeDev), + .class_init = pci_bridge_dev_class_init, +}; + +static void pci_bridge_dev_register(void) +{ + type_register_static(&pci_bridge_dev_info); +} + +type_init(pci_bridge_dev_register); diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c new file mode 100644 index 0000000000..b868f56ff9 --- /dev/null +++ b/hw/pci-bridge/xio3130_downstream.c @@ -0,0 +1,217 @@ +/* + * x3130_downstream.c + * TI X3130 pci express downstream port switch + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/xio3130_downstream.h" + +#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ +#define XIO3130_REVISION 0x1 +#define XIO3130_MSI_OFFSET 0x70 +#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT +#define XIO3130_MSI_NR_VECTOR 1 +#define XIO3130_SSVID_OFFSET 0x80 +#define XIO3130_SSVID_SVID 0 +#define XIO3130_SSVID_SSID 0 +#define XIO3130_EXP_OFFSET 0x90 +#define XIO3130_AER_OFFSET 0x100 + +static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_cap_slot_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); +} + +static void xio3130_downstream_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_cap_ari_reset(d); + pci_bridge_reset(qdev); +} + +static int xio3130_downstream_initfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + int rc; + + rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); + if (rc < 0) { + return rc; + } + + pcie_port_init_reg(d); + + rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + if (rc < 0) { + goto err_bridge; + } + rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, + XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, + p->port); + if (rc < 0) { + goto err_msi; + } + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s->slot); + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + goto err_pcie_cap; + } + pcie_cap_ari_init(d); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET); + if (rc < 0) { + goto err; + } + + return 0; + +err: + pcie_chassis_del_slot(s); +err_pcie_cap: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + pci_bridge_exitfn(d); + return rc; +} + +static void xio3130_downstream_exitfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port, uint8_t chassis, + uint16_t slot) +{ + PCIDevice *d; + PCIBridge *br; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, multifunction, + "xio3130-downstream"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + + qdev = &br->dev.qdev; + pci_bridge_map_irq(br, bus_name, map_irq); + qdev_prop_set_uint8(qdev, "port", port); + qdev_prop_set_uint8(qdev, "chassis", chassis); + qdev_prop_set_uint16(qdev, "slot", slot); + qdev_init_nofail(qdev); + + return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br)); +} + +static const VMStateDescription vmstate_xio3130_downstream = { + .name = "xio3130-express-downstream-port", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = pcie_cap_slot_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot), + VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0, + vmstate_pcie_aer_log, PCIEAERLog), + VMSTATE_END_OF_LIST() + } +}; + +static Property xio3130_downstream_properties[] = { + DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), + DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), + DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), + DEFINE_PROP_UINT16("aer_log_max", PCIESlot, + port.br.dev.exp.aer_log.log_max, + PCIE_AER_LOG_MAX_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xio3130_downstream_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->is_express = 1; + k->is_bridge = 1; + k->config_write = xio3130_downstream_write_config; + k->init = xio3130_downstream_initfn; + k->exit = xio3130_downstream_exitfn; + k->vendor_id = PCI_VENDOR_ID_TI; + k->device_id = PCI_DEVICE_ID_TI_XIO3130D; + k->revision = XIO3130_REVISION; + dc->desc = "TI X3130 Downstream Port of PCI Express Switch"; + dc->reset = xio3130_downstream_reset; + dc->vmsd = &vmstate_xio3130_downstream; + dc->props = xio3130_downstream_properties; +} + +static const TypeInfo xio3130_downstream_info = { + .name = "xio3130-downstream", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIESlot), + .class_init = xio3130_downstream_class_init, +}; + +static void xio3130_downstream_register_types(void) +{ + type_register_static(&xio3130_downstream_info); +} + +type_init(xio3130_downstream_register_types) + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c new file mode 100644 index 0000000000..cd5d97d211 --- /dev/null +++ b/hw/pci-bridge/xio3130_upstream.c @@ -0,0 +1,192 @@ +/* + * xio3130_upstream.c + * TI X3130 pci express upstream port switch + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/xio3130_upstream.h" + +#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ +#define XIO3130_REVISION 0x2 +#define XIO3130_MSI_OFFSET 0x70 +#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT +#define XIO3130_MSI_NR_VECTOR 1 +#define XIO3130_SSVID_OFFSET 0x80 +#define XIO3130_SSVID_SVID 0 +#define XIO3130_SSVID_SSID 0 +#define XIO3130_EXP_OFFSET 0x90 +#define XIO3130_AER_OFFSET 0x100 + +static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); +} + +static void xio3130_upstream_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + + pci_bridge_reset(qdev); + pcie_cap_deverr_reset(d); +} + +static int xio3130_upstream_initfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + int rc; + + rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); + if (rc < 0) { + return rc; + } + + pcie_port_init_reg(d); + + rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + if (rc < 0) { + goto err_bridge; + } + rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, + XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, + p->port); + if (rc < 0) { + goto err_msi; + } + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET); + if (rc < 0) { + goto err; + } + + return 0; + +err: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + pci_bridge_exitfn(d); + return rc; +} + +static void xio3130_upstream_exitfn(PCIDevice *d) +{ + pcie_aer_exit(d); + pcie_cap_exit(d); + msi_uninit(d); + pci_bridge_exitfn(d); +} + +PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port) +{ + PCIDevice *d; + PCIBridge *br; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + + qdev = &br->dev.qdev; + pci_bridge_map_irq(br, bus_name, map_irq); + qdev_prop_set_uint8(qdev, "port", port); + qdev_init_nofail(qdev); + + return DO_UPCAST(PCIEPort, br, br); +} + +static const VMStateDescription vmstate_xio3130_upstream = { + .name = "xio3130-express-upstream-port", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(br.dev, PCIEPort), + VMSTATE_STRUCT(br.dev.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log, + PCIEAERLog), + VMSTATE_END_OF_LIST() + } +}; + +static Property xio3130_upstream_properties[] = { + DEFINE_PROP_UINT8("port", PCIEPort, port, 0), + DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max, + PCIE_AER_LOG_MAX_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xio3130_upstream_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->is_express = 1; + k->is_bridge = 1; + k->config_write = xio3130_upstream_write_config; + k->init = xio3130_upstream_initfn; + k->exit = xio3130_upstream_exitfn; + k->vendor_id = PCI_VENDOR_ID_TI; + k->device_id = PCI_DEVICE_ID_TI_XIO3130U; + k->revision = XIO3130_REVISION; + dc->desc = "TI X3130 Upstream Port of PCI Express Switch"; + dc->reset = xio3130_upstream_reset; + dc->vmsd = &vmstate_xio3130_upstream; + dc->props = xio3130_upstream_properties; +} + +static const TypeInfo xio3130_upstream_info = { + .name = "x3130-upstream", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIEPort), + .class_init = xio3130_upstream_class_init, +}; + +static void xio3130_upstream_register_types(void) +{ + type_register_static(&xio3130_upstream_info); +} + +type_init(xio3130_upstream_register_types) + + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs new file mode 100644 index 0000000000..909e702eef --- /dev/null +++ b/hw/pci-host/Makefile.objs @@ -0,0 +1,18 @@ +common-obj-y += pam.o + +# PPC devices +common-obj-$(CONFIG_PREP_PCI) += prep.o +common-obj-$(CONFIG_GRACKLE_PCI) += grackle.o +# NewWorld PowerMac +common-obj-$(CONFIG_UNIN_PCI) += uninorth.o +common-obj-$(CONFIG_DEC_PCI) += dec.o +# PowerPC E500 boards +common-obj-$(CONFIG_PPCE500_PCI) += ppce500.o + +# ARM devices +common-obj-$(CONFIG_VERSATILE_PCI) += versatile.o + +common-obj-$(CONFIG_PCI_APB) += apb.o +common-obj-$(CONFIG_FULONG) += bonito.o +common-obj-$(CONFIG_PCI_PIIX) += piix.o +common-obj-$(CONFIG_PCI_Q35) += q35.o diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c new file mode 100644 index 0000000000..b4981d7005 --- /dev/null +++ b/hw/pci-host/apb.c @@ -0,0 +1,542 @@ +/* + * QEMU Ultrasparc APB PCI host + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* XXX This file and most of its contents are somewhat misnamed. The + Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is + the secondary PCI bridge. */ + +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci-host/apb.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" + +/* debug APB */ +//#define DEBUG_APB + +#ifdef DEBUG_APB +#define APB_DPRINTF(fmt, ...) \ +do { printf("APB: " fmt , ## __VA_ARGS__); } while (0) +#else +#define APB_DPRINTF(fmt, ...) +#endif + +/* + * Chipset docs: + * PBM: "UltraSPARC IIi User's Manual", + * http://www.sun.com/processors/manuals/805-0087.pdf + * + * APB: "Advanced PCI Bridge (APB) User's Manual", + * http://www.sun.com/processors/manuals/805-1251.pdf + */ + +#define PBM_PCI_IMR_MASK 0x7fffffff +#define PBM_PCI_IMR_ENABLED 0x80000000 + +#define POR (1 << 31) +#define SOFT_POR (1 << 30) +#define SOFT_XIR (1 << 29) +#define BTN_POR (1 << 28) +#define BTN_XIR (1 << 27) +#define RESET_MASK 0xf8000000 +#define RESET_WCMASK 0x98000000 +#define RESET_WMASK 0x60000000 + +#define MAX_IVEC 0x30 + +typedef struct APBState { + SysBusDevice busdev; + PCIBus *bus; + MemoryRegion apb_config; + MemoryRegion pci_config; + MemoryRegion pci_mmio; + MemoryRegion pci_ioport; + uint32_t iommu[4]; + uint32_t pci_control[16]; + uint32_t pci_irq_map[8]; + uint32_t obio_irq_map[32]; + qemu_irq *pbm_irqs; + qemu_irq *ivec_irqs; + uint32_t reset_control; + unsigned int nr_resets; +} APBState; + +static void pci_apb_set_irq(void *opaque, int irq_num, int level); + +static void apb_config_writel (void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + APBState *s = opaque; + + APB_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val); + + switch (addr & 0xffff) { + case 0x30 ... 0x4f: /* DMA error registers */ + /* XXX: not implemented yet */ + break; + case 0x200 ... 0x20b: /* IOMMU */ + s->iommu[(addr & 0xf) >> 2] = val; + break; + case 0x20c ... 0x3ff: /* IOMMU flush */ + break; + case 0xc00 ... 0xc3f: /* PCI interrupt control */ + if (addr & 4) { + s->pci_irq_map[(addr & 0x3f) >> 3] &= PBM_PCI_IMR_MASK; + s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK; + } + break; + case 0x1000 ... 0x1080: /* OBIO interrupt control */ + if (addr & 4) { + s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK; + s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK; + } + break; + case 0x1400 ... 0x143f: /* PCI interrupt clear */ + if (addr & 4) { + pci_apb_set_irq(s, (addr & 0x3f) >> 3, 0); + } + break; + case 0x1800 ... 0x1860: /* OBIO interrupt clear */ + if (addr & 4) { + pci_apb_set_irq(s, 0x20 | ((addr & 0xff) >> 3), 0); + } + break; + case 0x2000 ... 0x202f: /* PCI control */ + s->pci_control[(addr & 0x3f) >> 2] = val; + break; + case 0xf020 ... 0xf027: /* Reset control */ + if (addr & 4) { + val &= RESET_MASK; + s->reset_control &= ~(val & RESET_WCMASK); + s->reset_control |= val & RESET_WMASK; + if (val & SOFT_POR) { + s->nr_resets = 0; + qemu_system_reset_request(); + } else if (val & SOFT_XIR) { + qemu_system_reset_request(); + } + } + break; + case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ + case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ + case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ + case 0xf000 ... 0xf01f: /* FFB config, memory control */ + /* we don't care */ + default: + break; + } +} + +static uint64_t apb_config_readl (void *opaque, + hwaddr addr, unsigned size) +{ + APBState *s = opaque; + uint32_t val; + + switch (addr & 0xffff) { + case 0x30 ... 0x4f: /* DMA error registers */ + val = 0; + /* XXX: not implemented yet */ + break; + case 0x200 ... 0x20b: /* IOMMU */ + val = s->iommu[(addr & 0xf) >> 2]; + break; + case 0x20c ... 0x3ff: /* IOMMU flush */ + val = 0; + break; + case 0xc00 ... 0xc3f: /* PCI interrupt control */ + if (addr & 4) { + val = s->pci_irq_map[(addr & 0x3f) >> 3]; + } else { + val = 0; + } + break; + case 0x1000 ... 0x1080: /* OBIO interrupt control */ + if (addr & 4) { + val = s->obio_irq_map[(addr & 0xff) >> 3]; + } else { + val = 0; + } + break; + case 0x2000 ... 0x202f: /* PCI control */ + val = s->pci_control[(addr & 0x3f) >> 2]; + break; + case 0xf020 ... 0xf027: /* Reset control */ + if (addr & 4) { + val = s->reset_control; + } else { + val = 0; + } + break; + case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ + case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ + case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ + case 0xf000 ... 0xf01f: /* FFB config, memory control */ + /* we don't care */ + default: + val = 0; + break; + } + APB_DPRINTF("%s: addr " TARGET_FMT_plx " -> %x\n", __func__, addr, val); + + return val; +} + +static const MemoryRegionOps apb_config_ops = { + .read = apb_config_readl, + .write = apb_config_writel, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void apb_pci_config_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + APBState *s = opaque; + + val = qemu_bswap_len(val, size); + APB_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val); + pci_data_write(s->bus, addr, val, size); +} + +static uint64_t apb_pci_config_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint32_t ret; + APBState *s = opaque; + + ret = pci_data_read(s->bus, addr, size); + ret = qemu_bswap_len(ret, size); + APB_DPRINTF("%s: addr " TARGET_FMT_plx " -> %x\n", __func__, addr, ret); + return ret; +} + +static void pci_apb_iowriteb (void *opaque, hwaddr addr, + uint32_t val) +{ + cpu_outb(addr & IOPORTS_MASK, val); +} + +static void pci_apb_iowritew (void *opaque, hwaddr addr, + uint32_t val) +{ + cpu_outw(addr & IOPORTS_MASK, bswap16(val)); +} + +static void pci_apb_iowritel (void *opaque, hwaddr addr, + uint32_t val) +{ + cpu_outl(addr & IOPORTS_MASK, bswap32(val)); +} + +static uint32_t pci_apb_ioreadb (void *opaque, hwaddr addr) +{ + uint32_t val; + + val = cpu_inb(addr & IOPORTS_MASK); + return val; +} + +static uint32_t pci_apb_ioreadw (void *opaque, hwaddr addr) +{ + uint32_t val; + + val = bswap16(cpu_inw(addr & IOPORTS_MASK)); + return val; +} + +static uint32_t pci_apb_ioreadl (void *opaque, hwaddr addr) +{ + uint32_t val; + + val = bswap32(cpu_inl(addr & IOPORTS_MASK)); + return val; +} + +static const MemoryRegionOps pci_ioport_ops = { + .old_mmio = { + .read = { pci_apb_ioreadb, pci_apb_ioreadw, pci_apb_ioreadl }, + .write = { pci_apb_iowriteb, pci_apb_iowritew, pci_apb_iowritel, }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* The APB host has an IRQ line for each IRQ line of each slot. */ +static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return ((pci_dev->devfn & 0x18) >> 1) + irq_num; +} + +static int pci_pbm_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int bus_offset; + if (pci_dev->devfn & 1) + bus_offset = 16; + else + bus_offset = 0; + return bus_offset + irq_num; +} + +static void pci_apb_set_irq(void *opaque, int irq_num, int level) +{ + APBState *s = opaque; + + /* PCI IRQ map onto the first 32 INO. */ + if (irq_num < 32) { + if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) { + APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); + qemu_set_irq(s->ivec_irqs[irq_num], level); + } else { + APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); + qemu_irq_lower(s->ivec_irqs[irq_num]); + } + } else { + /* OBIO IRQ map onto the next 16 INO. */ + if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) { + APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); + qemu_set_irq(s->ivec_irqs[irq_num], level); + } else { + APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); + qemu_irq_lower(s->ivec_irqs[irq_num]); + } + } +} + +static int apb_pci_bridge_initfn(PCIDevice *dev) +{ + int rc; + + rc = pci_bridge_initfn(dev, TYPE_PCI_BUS); + if (rc < 0) { + return rc; + } + + /* + * command register: + * According to PCI bridge spec, after reset + * bus master bit is off + * memory space enable bit is off + * According to manual (805-1251.pdf). + * the reset value should be zero unless the boot pin is tied high + * (which is true) and thus it should be PCI_COMMAND_MEMORY. + */ + pci_set_word(dev->config + PCI_COMMAND, + PCI_COMMAND_MEMORY); + pci_set_word(dev->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | + PCI_STATUS_DEVSEL_MEDIUM); + return 0; +} + +PCIBus *pci_apb_init(hwaddr special_base, + hwaddr mem_base, + qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3, + qemu_irq **pbm_irqs) +{ + DeviceState *dev; + SysBusDevice *s; + APBState *d; + PCIDevice *pci_dev; + PCIBridge *br; + + /* Ultrasparc PBM main bus */ + dev = qdev_create(NULL, "pbm"); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + /* apb_config */ + sysbus_mmio_map(s, 0, special_base); + /* PCI configuration space */ + sysbus_mmio_map(s, 1, special_base + 0x1000000ULL); + /* pci_ioport */ + sysbus_mmio_map(s, 2, special_base + 0x2000000ULL); + d = FROM_SYSBUS(APBState, s); + + memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); + memory_region_add_subregion(get_system_memory(), mem_base, &d->pci_mmio); + + d->bus = pci_register_bus(&d->busdev.qdev, "pci", + pci_apb_set_irq, pci_pbm_map_irq, d, + &d->pci_mmio, + get_system_io(), + 0, 32, TYPE_PCI_BUS); + + *pbm_irqs = d->pbm_irqs; + d->ivec_irqs = ivec_irqs; + + pci_create_simple(d->bus, 0, "pbm-pci"); + + /* APB secondary busses */ + pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true, + "pbm-bridge"); + br = DO_UPCAST(PCIBridge, dev, pci_dev); + pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1", + pci_apb_map_irq); + qdev_init_nofail(&pci_dev->qdev); + *bus2 = pci_bridge_get_sec_bus(br); + + pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true, + "pbm-bridge"); + br = DO_UPCAST(PCIBridge, dev, pci_dev); + pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2", + pci_apb_map_irq); + qdev_init_nofail(&pci_dev->qdev); + *bus3 = pci_bridge_get_sec_bus(br); + + return d->bus; +} + +static void pci_pbm_reset(DeviceState *d) +{ + unsigned int i; + APBState *s = container_of(d, APBState, busdev.qdev); + + for (i = 0; i < 8; i++) { + s->pci_irq_map[i] &= PBM_PCI_IMR_MASK; + } + for (i = 0; i < 32; i++) { + s->obio_irq_map[i] &= PBM_PCI_IMR_MASK; + } + + if (s->nr_resets++ == 0) { + /* Power on reset */ + s->reset_control = POR; + } +} + +static const MemoryRegionOps pci_config_ops = { + .read = apb_pci_config_read, + .write = apb_pci_config_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int pci_pbm_init_device(SysBusDevice *dev) +{ + APBState *s; + unsigned int i; + + s = FROM_SYSBUS(APBState, dev); + for (i = 0; i < 8; i++) { + s->pci_irq_map[i] = (0x1f << 6) | (i << 2); + } + for (i = 0; i < 32; i++) { + s->obio_irq_map[i] = ((0x1f << 6) | 0x20) + i; + } + s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC); + + /* apb_config */ + memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config", + 0x10000); + /* at region 0 */ + sysbus_init_mmio(dev, &s->apb_config); + + memory_region_init_io(&s->pci_config, &pci_config_ops, s, "apb-pci-config", + 0x1000000); + /* at region 1 */ + sysbus_init_mmio(dev, &s->pci_config); + + /* pci_ioport */ + memory_region_init_io(&s->pci_ioport, &pci_ioport_ops, s, + "apb-pci-ioport", 0x10000); + /* at region 2 */ + sysbus_init_mmio(dev, &s->pci_ioport); + + return 0; +} + +static int pbm_pci_host_init(PCIDevice *d) +{ + pci_set_word(d->config + PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | + PCI_STATUS_DEVSEL_MEDIUM); + return 0; +} + +static void pbm_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pbm_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_SUN; + k->device_id = PCI_DEVICE_ID_SUN_SABRE; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo pbm_pci_host_info = { + .name = "pbm-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = pbm_pci_host_class_init, +}; + +static void pbm_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = pci_pbm_init_device; + dc->reset = pci_pbm_reset; +} + +static const TypeInfo pbm_host_info = { + .name = "pbm", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(APBState), + .class_init = pbm_host_class_init, +}; + +static void pbm_pci_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = apb_pci_bridge_initfn; + k->exit = pci_bridge_exitfn; + k->vendor_id = PCI_VENDOR_ID_SUN; + k->device_id = PCI_DEVICE_ID_SUN_SIMBA; + k->revision = 0x11; + k->config_write = pci_bridge_write_config; + k->is_bridge = 1; + dc->reset = pci_bridge_reset; + dc->vmsd = &vmstate_pci_device; +} + +static const TypeInfo pbm_pci_bridge_info = { + .name = "pbm-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIBridge), + .class_init = pbm_pci_bridge_class_init, +}; + +static void pbm_register_types(void) +{ + type_register_static(&pbm_host_info); + type_register_static(&pbm_pci_host_info); + type_register_static(&pbm_pci_bridge_info); +} + +type_init(pbm_register_types) diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c new file mode 100644 index 0000000000..974150bfc1 --- /dev/null +++ b/hw/pci-host/bonito.c @@ -0,0 +1,847 @@ +/* + * bonito north bridge support + * + * Copyright (c) 2008 yajin (yajin@vm-kernel.org) + * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) + * + * This code is licensed under the GNU GPL v2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +/* + * fulong 2e mini pc has a bonito north bridge. + */ + +/* what is the meaning of devfn in qemu and IDSEL in bonito northbridge? + * + * devfn pci_slot<<3 + funno + * one pci bus can have 32 devices and each device can have 8 functions. + * + * In bonito north bridge, pci slot = IDSEL bit - 12. + * For example, PCI_IDSEL_VIA686B = 17, + * pci slot = 17-12=5 + * + * so + * VT686B_FUN0's devfn = (5<<3)+0 + * VT686B_FUN1's devfn = (5<<3)+1 + * + * qemu also uses pci address for north bridge to access pci config register. + * bus_no [23:16] + * dev_no [15:11] + * fun_no [10:8] + * reg_no [7:2] + * + * so function bonito_sbridge_pciaddr for the translation from + * north bridge address to pci address. + */ + +#include + +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/i386/pc.h" +#include "hw/mips/mips.h" +#include "hw/pci/pci_host.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" + +//#define DEBUG_BONITO + +#ifdef DEBUG_BONITO +#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +/* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/ +#define BONITO_BOOT_BASE 0x1fc00000 +#define BONITO_BOOT_SIZE 0x00100000 +#define BONITO_BOOT_TOP (BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1) +#define BONITO_FLASH_BASE 0x1c000000 +#define BONITO_FLASH_SIZE 0x03000000 +#define BONITO_FLASH_TOP (BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1) +#define BONITO_SOCKET_BASE 0x1f800000 +#define BONITO_SOCKET_SIZE 0x00400000 +#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1) +#define BONITO_REG_BASE 0x1fe00000 +#define BONITO_REG_SIZE 0x00040000 +#define BONITO_REG_TOP (BONITO_REG_BASE+BONITO_REG_SIZE-1) +#define BONITO_DEV_BASE 0x1ff00000 +#define BONITO_DEV_SIZE 0x00100000 +#define BONITO_DEV_TOP (BONITO_DEV_BASE+BONITO_DEV_SIZE-1) +#define BONITO_PCILO_BASE 0x10000000 +#define BONITO_PCILO_BASE_VA 0xb0000000 +#define BONITO_PCILO_SIZE 0x0c000000 +#define BONITO_PCILO_TOP (BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1) +#define BONITO_PCILO0_BASE 0x10000000 +#define BONITO_PCILO1_BASE 0x14000000 +#define BONITO_PCILO2_BASE 0x18000000 +#define BONITO_PCIHI_BASE 0x20000000 +#define BONITO_PCIHI_SIZE 0x20000000 +#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1) +#define BONITO_PCIIO_BASE 0x1fd00000 +#define BONITO_PCIIO_BASE_VA 0xbfd00000 +#define BONITO_PCIIO_SIZE 0x00010000 +#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1) +#define BONITO_PCICFG_BASE 0x1fe80000 +#define BONITO_PCICFG_SIZE 0x00080000 +#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1) + + +#define BONITO_PCICONFIGBASE 0x00 +#define BONITO_REGBASE 0x100 + +#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE+BONITO_REG_BASE) +#define BONITO_PCICONFIG_SIZE (0x100) + +#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE+BONITO_REG_BASE) +#define BONITO_INTERNAL_REG_SIZE (0x70) + +#define BONITO_SPCICONFIG_BASE (BONITO_PCICFG_BASE) +#define BONITO_SPCICONFIG_SIZE (BONITO_PCICFG_SIZE) + + + +/* 1. Bonito h/w Configuration */ +/* Power on register */ + +#define BONITO_BONPONCFG (0x00 >> 2) /* 0x100 */ +#define BONITO_BONGENCFG_OFFSET 0x4 +#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET>>2) /*0x104 */ + +/* 2. IO & IDE configuration */ +#define BONITO_IODEVCFG (0x08 >> 2) /* 0x108 */ + +/* 3. IO & IDE configuration */ +#define BONITO_SDCFG (0x0c >> 2) /* 0x10c */ + +/* 4. PCI address map control */ +#define BONITO_PCIMAP (0x10 >> 2) /* 0x110 */ +#define BONITO_PCIMEMBASECFG (0x14 >> 2) /* 0x114 */ +#define BONITO_PCIMAP_CFG (0x18 >> 2) /* 0x118 */ + +/* 5. ICU & GPIO regs */ +/* GPIO Regs - r/w */ +#define BONITO_GPIODATA_OFFSET 0x1c +#define BONITO_GPIODATA (BONITO_GPIODATA_OFFSET >> 2) /* 0x11c */ +#define BONITO_GPIOIE (0x20 >> 2) /* 0x120 */ + +/* ICU Configuration Regs - r/w */ +#define BONITO_INTEDGE (0x24 >> 2) /* 0x124 */ +#define BONITO_INTSTEER (0x28 >> 2) /* 0x128 */ +#define BONITO_INTPOL (0x2c >> 2) /* 0x12c */ + +/* ICU Enable Regs - IntEn & IntISR are r/o. */ +#define BONITO_INTENSET (0x30 >> 2) /* 0x130 */ +#define BONITO_INTENCLR (0x34 >> 2) /* 0x134 */ +#define BONITO_INTEN (0x38 >> 2) /* 0x138 */ +#define BONITO_INTISR (0x3c >> 2) /* 0x13c */ + +/* PCI mail boxes */ +#define BONITO_PCIMAIL0_OFFSET 0x40 +#define BONITO_PCIMAIL1_OFFSET 0x44 +#define BONITO_PCIMAIL2_OFFSET 0x48 +#define BONITO_PCIMAIL3_OFFSET 0x4c +#define BONITO_PCIMAIL0 (0x40 >> 2) /* 0x140 */ +#define BONITO_PCIMAIL1 (0x44 >> 2) /* 0x144 */ +#define BONITO_PCIMAIL2 (0x48 >> 2) /* 0x148 */ +#define BONITO_PCIMAIL3 (0x4c >> 2) /* 0x14c */ + +/* 6. PCI cache */ +#define BONITO_PCICACHECTRL (0x50 >> 2) /* 0x150 */ +#define BONITO_PCICACHETAG (0x54 >> 2) /* 0x154 */ +#define BONITO_PCIBADADDR (0x58 >> 2) /* 0x158 */ +#define BONITO_PCIMSTAT (0x5c >> 2) /* 0x15c */ + +/* 7. other*/ +#define BONITO_TIMECFG (0x60 >> 2) /* 0x160 */ +#define BONITO_CPUCFG (0x64 >> 2) /* 0x164 */ +#define BONITO_DQCFG (0x68 >> 2) /* 0x168 */ +#define BONITO_MEMSIZE (0x6C >> 2) /* 0x16c */ + +#define BONITO_REGS (0x70 >> 2) + +/* PCI config for south bridge. type 0 */ +#define BONITO_PCICONF_IDSEL_MASK 0xfffff800 /* [31:11] */ +#define BONITO_PCICONF_IDSEL_OFFSET 11 +#define BONITO_PCICONF_FUN_MASK 0x700 /* [10:8] */ +#define BONITO_PCICONF_FUN_OFFSET 8 +#define BONITO_PCICONF_REG_MASK 0xFC +#define BONITO_PCICONF_REG_OFFSET 0 + + +/* idsel BIT = pci slot number +12 */ +#define PCI_SLOT_BASE 12 +#define PCI_IDSEL_VIA686B_BIT (17) +#define PCI_IDSEL_VIA686B (1<> 2; + + DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x\n", addr, val, saddr); + switch (saddr) { + case BONITO_BONPONCFG: + case BONITO_IODEVCFG: + case BONITO_SDCFG: + case BONITO_PCIMAP: + case BONITO_PCIMEMBASECFG: + case BONITO_PCIMAP_CFG: + case BONITO_GPIODATA: + case BONITO_GPIOIE: + case BONITO_INTEDGE: + case BONITO_INTSTEER: + case BONITO_INTPOL: + case BONITO_PCIMAIL0: + case BONITO_PCIMAIL1: + case BONITO_PCIMAIL2: + case BONITO_PCIMAIL3: + case BONITO_PCICACHECTRL: + case BONITO_PCICACHETAG: + case BONITO_PCIBADADDR: + case BONITO_PCIMSTAT: + case BONITO_TIMECFG: + case BONITO_CPUCFG: + case BONITO_DQCFG: + case BONITO_MEMSIZE: + s->regs[saddr] = val; + break; + case BONITO_BONGENCFG: + if (!(s->regs[saddr] & 0x04) && (val & 0x04)) { + reset = 1; /* bit 2 jump from 0 to 1 cause reset */ + } + s->regs[saddr] = val; + if (reset) { + qemu_system_reset_request(); + } + break; + case BONITO_INTENSET: + s->regs[BONITO_INTENSET] = val; + s->regs[BONITO_INTEN] |= val; + break; + case BONITO_INTENCLR: + s->regs[BONITO_INTENCLR] = val; + s->regs[BONITO_INTEN] &= ~val; + break; + case BONITO_INTEN: + case BONITO_INTISR: + DPRINTF("write to readonly bonito register %x\n", saddr); + break; + default: + DPRINTF("write to unknown bonito register %x\n", saddr); + break; + } +} + +static uint64_t bonito_readl(void *opaque, hwaddr addr, + unsigned size) +{ + PCIBonitoState *s = opaque; + uint32_t saddr; + + saddr = (addr - BONITO_REGBASE) >> 2; + + DPRINTF("bonito_readl "TARGET_FMT_plx"\n", addr); + switch (saddr) { + case BONITO_INTISR: + return s->regs[saddr]; + default: + return s->regs[saddr]; + } +} + +static const MemoryRegionOps bonito_ops = { + .read = bonito_readl, + .write = bonito_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void bonito_pciconf_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + + DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); + d->config_write(d, addr, val, 4); +} + +static uint64_t bonito_pciconf_readl(void *opaque, hwaddr addr, + unsigned size) +{ + + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + + DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr); + return d->config_read(d, addr, 4); +} + +/* north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */ + +static const MemoryRegionOps bonito_pciconf_ops = { + .read = bonito_pciconf_readl, + .write = bonito_pciconf_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr, + unsigned size) +{ + uint32_t val; + PCIBonitoState *s = opaque; + + val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)]; + + return val; +} + +static void bonito_ldma_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIBonitoState *s = opaque; + + ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff; +} + +static const MemoryRegionOps bonito_ldma_ops = { + .read = bonito_ldma_readl, + .write = bonito_ldma_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t bonito_cop_readl(void *opaque, hwaddr addr, + unsigned size) +{ + uint32_t val; + PCIBonitoState *s = opaque; + + val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)]; + + return val; +} + +static void bonito_cop_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIBonitoState *s = opaque; + + ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff; +} + +static const MemoryRegionOps bonito_cop_ops = { + .read = bonito_cop_readl, + .write = bonito_cop_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr) +{ + PCIBonitoState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t cfgaddr; + uint32_t idsel; + uint32_t devno; + uint32_t funno; + uint32_t regno; + uint32_t pciaddr; + + /* support type0 pci config */ + if ((s->regs[BONITO_PCIMAP_CFG] & 0x10000) != 0x0) { + return 0xffffffff; + } + + cfgaddr = addr & 0xffff; + cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16; + + idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET; + devno = ffs(idsel) - 1; + funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET; + regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET; + + if (idsel == 0) { + fprintf(stderr, "error in bonito pci config address " TARGET_FMT_plx + ",pcimap_cfg=%x\n", addr, s->regs[BONITO_PCIMAP_CFG]); + exit(1); + } + pciaddr = PCI_ADDR(pci_bus_num(phb->bus), devno, funno, regno); + DPRINTF("cfgaddr %x pciaddr %x busno %x devno %d funno %d regno %d\n", + cfgaddr, pciaddr, pci_bus_num(phb->bus), devno, funno, regno); + + return pciaddr; +} + +static void bonito_spciconf_writeb(void *opaque, hwaddr addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_writeb "TARGET_FMT_plx" val %x\n", addr, val); + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return; + } + + /* set the pci address in s->config_reg */ + phb->config_reg = (pciaddr) | (1u << 31); + pci_data_write(phb->bus, phb->config_reg, val & 0xff, 1); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(d->config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(d->config + PCI_STATUS, status); +} + +static void bonito_spciconf_writew(void *opaque, hwaddr addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x\n", addr, val); + assert((addr & 0x1) == 0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return; + } + + /* set the pci address in s->config_reg */ + phb->config_reg = (pciaddr) | (1u << 31); + pci_data_write(phb->bus, phb->config_reg, val, 2); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(d->config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(d->config + PCI_STATUS, status); +} + +static void bonito_spciconf_writel(void *opaque, hwaddr addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); + assert((addr & 0x3) == 0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return; + } + + /* set the pci address in s->config_reg */ + phb->config_reg = (pciaddr) | (1u << 31); + pci_data_write(phb->bus, phb->config_reg, val, 4); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(d->config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(d->config + PCI_STATUS, status); +} + +static uint32_t bonito_spciconf_readb(void *opaque, hwaddr addr) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_readb "TARGET_FMT_plx"\n", addr); + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return 0xff; + } + + /* set the pci address in s->config_reg */ + phb->config_reg = (pciaddr) | (1u << 31); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(d->config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(d->config + PCI_STATUS, status); + + return pci_data_read(phb->bus, phb->config_reg, 1); +} + +static uint32_t bonito_spciconf_readw(void *opaque, hwaddr addr) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx"\n", addr); + assert((addr & 0x1) == 0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return 0xffff; + } + + /* set the pci address in s->config_reg */ + phb->config_reg = (pciaddr) | (1u << 31); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(d->config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(d->config + PCI_STATUS, status); + + return pci_data_read(phb->bus, phb->config_reg, 2); +} + +static uint32_t bonito_spciconf_readl(void *opaque, hwaddr addr) +{ + PCIBonitoState *s = opaque; + PCIDevice *d = PCI_DEVICE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx"\n", addr); + assert((addr & 0x3) == 0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return 0xffffffff; + } + + /* set the pci address in s->config_reg */ + phb->config_reg = (pciaddr) | (1u << 31); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(d->config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(d->config + PCI_STATUS, status); + + return pci_data_read(phb->bus, phb->config_reg, 4); +} + +/* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */ +static const MemoryRegionOps bonito_spciconf_ops = { + .old_mmio = { + .read = { + bonito_spciconf_readb, + bonito_spciconf_readw, + bonito_spciconf_readl, + }, + .write = { + bonito_spciconf_writeb, + bonito_spciconf_writew, + bonito_spciconf_writel, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +#define BONITO_IRQ_BASE 32 + +static void pci_bonito_set_irq(void *opaque, int irq_num, int level) +{ + BonitoState *s = opaque; + qemu_irq *pic = s->pic; + PCIBonitoState *bonito_state = s->pci_dev; + int internal_irq = irq_num - BONITO_IRQ_BASE; + + if (bonito_state->regs[BONITO_INTEDGE] & (1 << internal_irq)) { + qemu_irq_pulse(*pic); + } else { /* level triggered */ + if (bonito_state->regs[BONITO_INTPOL] & (1 << internal_irq)) { + qemu_irq_raise(*pic); + } else { + qemu_irq_lower(*pic); + } + } +} + +/* map the original irq (0~3) to bonito irq (16~47, but 16~31 are unused) */ +static int pci_bonito_map_irq(PCIDevice * pci_dev, int irq_num) +{ + int slot; + + slot = (pci_dev->devfn >> 3); + + switch (slot) { + case 5: /* FULONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */ + return irq_num % 4 + BONITO_IRQ_BASE; + case 6: /* FULONG2E_ATI_SLOT, VGA */ + return 4 + BONITO_IRQ_BASE; + case 7: /* FULONG2E_RTL_SLOT, RTL8139 */ + return 5 + BONITO_IRQ_BASE; + case 8 ... 12: /* PCI slot 1 to 4 */ + return (slot - 8 + irq_num) + 6 + BONITO_IRQ_BASE; + default: /* Unknown device, don't do any translation */ + return irq_num; + } +} + +static void bonito_reset(void *opaque) +{ + PCIBonitoState *s = opaque; + + /* set the default value of north bridge registers */ + + s->regs[BONITO_BONPONCFG] = 0xc40; + s->regs[BONITO_BONGENCFG] = 0x1384; + s->regs[BONITO_IODEVCFG] = 0x2bff8010; + s->regs[BONITO_SDCFG] = 0x255e0091; + + s->regs[BONITO_GPIODATA] = 0x1ff; + s->regs[BONITO_GPIOIE] = 0x1ff; + s->regs[BONITO_DQCFG] = 0x8; + s->regs[BONITO_MEMSIZE] = 0x10000000; + s->regs[BONITO_PCIMAP] = 0x6140; +} + +static const VMStateDescription vmstate_bonito = { + .name = "Bonito", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, PCIBonitoState), + VMSTATE_END_OF_LIST() + } +}; + +static int bonito_pcihost_initfn(SysBusDevice *dev) +{ + PCIHostState *phb = PCI_HOST_BRIDGE(dev); + + phb->bus = pci_register_bus(DEVICE(dev), "pci", + pci_bonito_set_irq, pci_bonito_map_irq, dev, + get_system_memory(), get_system_io(), + 0x28, 32, TYPE_PCI_BUS); + + return 0; +} + +static int bonito_initfn(PCIDevice *dev) +{ + PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev); + SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); + PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); + + /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */ + pci_config_set_prog_interface(dev->config, 0x00); + + /* set the north bridge register mapping */ + memory_region_init_io(&s->iomem, &bonito_ops, s, + "north-bridge-register", BONITO_INTERNAL_REG_SIZE); + sysbus_init_mmio(sysbus, &s->iomem); + sysbus_mmio_map(sysbus, 0, BONITO_INTERNAL_REG_BASE); + + /* set the north bridge pci configure mapping */ + memory_region_init_io(&phb->conf_mem, &bonito_pciconf_ops, s, + "north-bridge-pci-config", BONITO_PCICONFIG_SIZE); + sysbus_init_mmio(sysbus, &phb->conf_mem); + sysbus_mmio_map(sysbus, 1, BONITO_PCICONFIG_BASE); + + /* set the south bridge pci configure mapping */ + memory_region_init_io(&phb->data_mem, &bonito_spciconf_ops, s, + "south-bridge-pci-config", BONITO_SPCICONFIG_SIZE); + sysbus_init_mmio(sysbus, &phb->data_mem); + sysbus_mmio_map(sysbus, 2, BONITO_SPCICONFIG_BASE); + + memory_region_init_io(&s->iomem_ldma, &bonito_ldma_ops, s, + "ldma", 0x100); + sysbus_init_mmio(sysbus, &s->iomem_ldma); + sysbus_mmio_map(sysbus, 3, 0xbfe00200); + + memory_region_init_io(&s->iomem_cop, &bonito_cop_ops, s, + "cop", 0x100); + sysbus_init_mmio(sysbus, &s->iomem_cop); + sysbus_mmio_map(sysbus, 4, 0xbfe00300); + + /* Map PCI IO Space 0x1fd0 0000 - 0x1fd1 0000 */ + s->bonito_pciio_start = BONITO_PCIIO_BASE; + s->bonito_pciio_length = BONITO_PCIIO_SIZE; + isa_mem_base = s->bonito_pciio_start; + isa_mmio_init(s->bonito_pciio_start, s->bonito_pciio_length); + + /* add pci local io mapping */ + s->bonito_localio_start = BONITO_DEV_BASE; + s->bonito_localio_length = BONITO_DEV_SIZE; + isa_mmio_init(s->bonito_localio_start, s->bonito_localio_length); + + /* set the default value of north bridge pci config */ + pci_set_word(dev->config + PCI_COMMAND, 0x0000); + pci_set_word(dev->config + PCI_STATUS, 0x0000); + pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0000); + pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000); + + pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00); + pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01); + pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c); + pci_set_byte(dev->config + PCI_MAX_LAT, 0x00); + + qemu_register_reset(bonito_reset, s); + + return 0; +} + +PCIBus *bonito_init(qemu_irq *pic) +{ + DeviceState *dev; + BonitoState *pcihost; + PCIHostState *phb; + PCIBonitoState *s; + PCIDevice *d; + + dev = qdev_create(NULL, TYPE_BONITO_PCI_HOST_BRIDGE); + phb = PCI_HOST_BRIDGE(dev); + pcihost = BONITO_PCI_HOST_BRIDGE(dev); + pcihost->pic = pic; + qdev_init_nofail(dev); + + /* set the pcihost pointer before bonito_initfn is called */ + d = pci_create(phb->bus, PCI_DEVFN(0, 0), "Bonito"); + s = DO_UPCAST(PCIBonitoState, dev, d); + s->pcihost = pcihost; + pcihost->pci_dev = s; + qdev_init_nofail(DEVICE(d)); + + return phb->bus; +} + +static void bonito_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = bonito_initfn; + k->vendor_id = 0xdf53; + k->device_id = 0x00d5; + k->revision = 0x01; + k->class_id = PCI_CLASS_BRIDGE_HOST; + dc->desc = "Host bridge"; + dc->no_user = 1; + dc->vmsd = &vmstate_bonito; +} + +static const TypeInfo bonito_info = { + .name = "Bonito", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIBonitoState), + .class_init = bonito_class_init, +}; + +static void bonito_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = bonito_pcihost_initfn; + dc->no_user = 1; +} + +static const TypeInfo bonito_pcihost_info = { + .name = TYPE_BONITO_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(BonitoState), + .class_init = bonito_pcihost_class_init, +}; + +static void bonito_register_types(void) +{ + type_register_static(&bonito_pcihost_info); + type_register_static(&bonito_info); +} + +type_init(bonito_register_types) diff --git a/hw/pci-host/dec.c b/hw/pci-host/dec.c new file mode 100644 index 0000000000..6ec3d226bd --- /dev/null +++ b/hw/pci-host/dec.c @@ -0,0 +1,156 @@ +/* + * QEMU DEC 21154 PCI bridge + * + * Copyright (c) 2006-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/dec_pci.h" +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" + +/* debug DEC */ +//#define DEBUG_DEC + +#ifdef DEBUG_DEC +#define DEC_DPRINTF(fmt, ...) \ + do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DEC_DPRINTF(fmt, ...) +#endif + +#define DEC_21154(obj) OBJECT_CHECK(DECState, (obj), TYPE_DEC_21154) + +typedef struct DECState { + PCIHostState parent_obj; +} DECState; + +static int dec_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return irq_num; +} + +static int dec_pci_bridge_initfn(PCIDevice *pci_dev) +{ + return pci_bridge_initfn(pci_dev, TYPE_PCI_BUS); +} + +static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = dec_pci_bridge_initfn; + k->exit = pci_bridge_exitfn; + k->vendor_id = PCI_VENDOR_ID_DEC; + k->device_id = PCI_DEVICE_ID_DEC_21154; + k->config_write = pci_bridge_write_config; + k->is_bridge = 1; + dc->desc = "DEC 21154 PCI-PCI bridge"; + dc->reset = pci_bridge_reset; + dc->vmsd = &vmstate_pci_device; +} + +static const TypeInfo dec_21154_pci_bridge_info = { + .name = "dec-21154-p2p-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIBridge), + .class_init = dec_21154_pci_bridge_class_init, +}; + +PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) +{ + PCIDevice *dev; + PCIBridge *br; + + dev = pci_create_multifunction(parent_bus, devfn, false, + "dec-21154-p2p-bridge"); + br = DO_UPCAST(PCIBridge, dev, dev); + pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq); + qdev_init_nofail(&dev->qdev); + return pci_bridge_get_sec_bus(br); +} + +static int pci_dec_21154_device_init(SysBusDevice *dev) +{ + PCIHostState *phb; + + phb = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops, + dev, "pci-data-idx", 0x1000); + sysbus_init_mmio(dev, &phb->conf_mem); + sysbus_init_mmio(dev, &phb->data_mem); + return 0; +} + +static int dec_21154_pci_host_init(PCIDevice *d) +{ + /* PCI2PCI bridge same values as PearPC - check this */ + return 0; +} + +static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = dec_21154_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_DEC; + k->device_id = PCI_DEVICE_ID_DEC_21154; + k->revision = 0x02; + k->class_id = PCI_CLASS_BRIDGE_PCI; + k->is_bridge = 1; +} + +static const TypeInfo dec_21154_pci_host_info = { + .name = "dec-21154", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = dec_21154_pci_host_class_init, +}; + +static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = pci_dec_21154_device_init; +} + +static const TypeInfo pci_dec_21154_device_info = { + .name = TYPE_DEC_21154, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(DECState), + .class_init = pci_dec_21154_device_class_init, +}; + +static void dec_register_types(void) +{ + type_register_static(&pci_dec_21154_device_info); + type_register_static(&dec_21154_pci_host_info); + type_register_static(&dec_21154_pci_bridge_info); +} + +type_init(dec_register_types) diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c new file mode 100644 index 0000000000..69344d9f6a --- /dev/null +++ b/hw/pci-host/grackle.c @@ -0,0 +1,165 @@ +/* + * QEMU Grackle PCI host (heathrow OldWorld PowerMac) + * + * Copyright (c) 2006-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/pci/pci_host.h" +#include "hw/ppc/mac.h" +#include "hw/pci/pci.h" + +/* debug Grackle */ +//#define DEBUG_GRACKLE + +#ifdef DEBUG_GRACKLE +#define GRACKLE_DPRINTF(fmt, ...) \ + do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0) +#else +#define GRACKLE_DPRINTF(fmt, ...) +#endif + +#define GRACKLE_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE) + +typedef struct GrackleState { + PCIHostState parent_obj; + + MemoryRegion pci_mmio; + MemoryRegion pci_hole; +} GrackleState; + +/* Don't know if this matches real hardware, but it agrees with OHW. */ +static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return (irq_num + (pci_dev->devfn >> 3)) & 3; +} + +static void pci_grackle_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level); + qemu_set_irq(pic[irq_num + 0x15], level); +} + +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io) +{ + DeviceState *dev; + SysBusDevice *s; + PCIHostState *phb; + GrackleState *d; + + dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + phb = PCI_HOST_BRIDGE(dev); + d = GRACKLE_PCI_HOST_BRIDGE(dev); + + memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); + memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, + 0x80000000ULL, 0x7e000000ULL); + memory_region_add_subregion(address_space_mem, 0x80000000ULL, + &d->pci_hole); + + phb->bus = pci_register_bus(dev, "pci", + pci_grackle_set_irq, + pci_grackle_map_irq, + pic, + &d->pci_mmio, + address_space_io, + 0, 4, TYPE_PCI_BUS); + + pci_create_simple(phb->bus, 0, "grackle"); + + sysbus_mmio_map(s, 0, base); + sysbus_mmio_map(s, 1, base + 0x00200000); + + return phb->bus; +} + +static int pci_grackle_init_device(SysBusDevice *dev) +{ + PCIHostState *phb; + + phb = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops, + dev, "pci-data-idx", 0x1000); + sysbus_init_mmio(dev, &phb->conf_mem); + sysbus_init_mmio(dev, &phb->data_mem); + + return 0; +} + +static int grackle_pci_host_init(PCIDevice *d) +{ + d->config[0x09] = 0x01; + return 0; +} + +static void grackle_pci_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = grackle_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_MOTOROLA; + k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106; + k->revision = 0x00; + k->class_id = PCI_CLASS_BRIDGE_HOST; + dc->no_user = 1; +} + +static const TypeInfo grackle_pci_info = { + .name = "grackle", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = grackle_pci_class_init, +}; + +static void pci_grackle_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = pci_grackle_init_device; + dc->no_user = 1; +} + +static const TypeInfo grackle_pci_host_info = { + .name = TYPE_GRACKLE_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(GrackleState), + .class_init = pci_grackle_class_init, +}; + +static void grackle_register_types(void) +{ + type_register_static(&grackle_pci_info); + type_register_static(&grackle_pci_host_info); +} + +type_init(grackle_register_types) diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c new file mode 100644 index 0000000000..7181bd68eb --- /dev/null +++ b/hw/pci-host/pam.c @@ -0,0 +1,87 @@ +/* + * QEMU i440FX/PIIX3 PCI Bridge Emulation + * + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2011 Isaku Yamahata + * VA Linux Systems Japan K.K. + * Copyright (c) 2012 Jason Baron + * + * Split out from piix_pci.c + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "sysemu/sysemu.h" +#include "hw/pci-host/pam.h" + +void smram_update(MemoryRegion *smram_region, uint8_t smram, + uint8_t smm_enabled) +{ + bool smram_enabled; + + smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) || + (smram & SMRAM_D_OPEN)); + memory_region_set_enabled(smram_region, !smram_enabled); +} + +void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram, + MemoryRegion *smram_region) +{ + uint8_t smm_enabled = (smm != 0); + if (*host_smm_enabled != smm_enabled) { + *host_smm_enabled = smm_enabled; + smram_update(smram_region, smram, *host_smm_enabled); + } +} + +void init_pam(MemoryRegion *ram_memory, MemoryRegion *system_memory, + MemoryRegion *pci_address_space, PAMMemoryRegion *mem, + uint32_t start, uint32_t size) +{ + int i; + + /* RAM */ + memory_region_init_alias(&mem->alias[3], "pam-ram", ram_memory, + start, size); + /* ROM (XXX: not quite correct) */ + memory_region_init_alias(&mem->alias[1], "pam-rom", ram_memory, + start, size); + memory_region_set_readonly(&mem->alias[1], true); + + /* XXX: should distinguish read/write cases */ + memory_region_init_alias(&mem->alias[0], "pam-pci", pci_address_space, + start, size); + memory_region_init_alias(&mem->alias[2], "pam-pci", pci_address_space, + start, size); + + for (i = 0; i < 4; ++i) { + memory_region_set_enabled(&mem->alias[i], false); + memory_region_add_subregion_overlap(system_memory, start, + &mem->alias[i], 1); + } + mem->current = 0; +} + +void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val) +{ + assert(0 <= idx && idx <= 12); + + memory_region_set_enabled(&pam->alias[pam->current], false); + pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK; + memory_region_set_enabled(&pam->alias[pam->current], true); +} diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c new file mode 100644 index 0000000000..f9e68c3099 --- /dev/null +++ b/hw/pci-host/piix.c @@ -0,0 +1,657 @@ +/* + * QEMU i440FX/PIIX3 PCI Bridge Emulation + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/isa/isa.h" +#include "hw/sysbus.h" +#include "qemu/range.h" +#include "hw/xen/xen.h" +#include "hw/pci-host/pam.h" +#include "sysemu/sysemu.h" + +/* + * I440FX chipset data sheet. + * http://download.intel.com/design/chipsets/datashts/29054901.pdf + */ + +typedef struct I440FXState { + PCIHostState parent_obj; +} I440FXState; + +#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ +#define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ +#define XEN_PIIX_NUM_PIRQS 128ULL +#define PIIX_PIRQC 0x60 + +/* + * Reset Control Register: PCI-accessible ISA-Compatible Register at address + * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). + */ +#define RCR_IOPORT 0xcf9 + +typedef struct PIIX3State { + PCIDevice dev; + + /* + * bitmap to track pic levels. + * The pic level is the logical OR of all the PCI irqs mapped to it + * So one PIC level is tracked by PIIX_NUM_PIRQS bits. + * + * PIRQ is mapped to PIC pins, we track it by + * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with + * pic_irq * PIIX_NUM_PIRQS + pirq + */ +#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 +#error "unable to encode pic state in 64bit in pic_levels." +#endif + uint64_t pic_levels; + + qemu_irq *pic; + + /* This member isn't used. Just for save/load compatibility */ + int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; + + /* Reset Control Register contents */ + uint8_t rcr; + + /* IO memory region for Reset Control Register (RCR_IOPORT) */ + MemoryRegion rcr_mem; +} PIIX3State; + +#define TYPE_I440FX_PCI_DEVICE "i440FX" +#define I440FX_PCI_DEVICE(obj) \ + OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) + +struct PCII440FXState { + PCIDevice dev; + MemoryRegion *system_memory; + MemoryRegion *pci_address_space; + MemoryRegion *ram_memory; + MemoryRegion pci_hole; + MemoryRegion pci_hole_64bit; + PAMMemoryRegion pam_regions[13]; + MemoryRegion smram_region; + uint8_t smm_enabled; +}; + + +#define I440FX_PAM 0x59 +#define I440FX_PAM_SIZE 7 +#define I440FX_SMRAM 0x72 + +static void piix3_set_irq(void *opaque, int pirq, int level); +static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); +static void piix3_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len); + +/* return the global irq number corresponding to a given device irq + pin. We could also use the bus number to have a more precise + mapping. */ +static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) +{ + int slot_addend; + slot_addend = (pci_dev->devfn >> 3) - 1; + return (pci_intx + slot_addend) & 3; +} + +static void i440fx_update_memory_mappings(PCII440FXState *d) +{ + int i; + + memory_region_transaction_begin(); + for (i = 0; i < 13; i++) { + pam_update(&d->pam_regions[i], i, + d->dev.config[I440FX_PAM + ((i + 1) / 2)]); + } + smram_update(&d->smram_region, d->dev.config[I440FX_SMRAM], d->smm_enabled); + memory_region_transaction_commit(); +} + +static void i440fx_set_smm(int val, void *arg) +{ + PCII440FXState *d = arg; + + memory_region_transaction_begin(); + smram_set_smm(&d->smm_enabled, val, d->dev.config[I440FX_SMRAM], + &d->smram_region); + memory_region_transaction_commit(); +} + + +static void i440fx_write_config(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + PCII440FXState *d = I440FX_PCI_DEVICE(dev); + + /* XXX: implement SMRAM.D_LOCK */ + pci_default_write_config(dev, address, val, len); + if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) || + range_covers_byte(address, len, I440FX_SMRAM)) { + i440fx_update_memory_mappings(d); + } +} + +static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) +{ + PCII440FXState *d = opaque; + int ret, i; + + ret = pci_device_load(&d->dev, f); + if (ret < 0) + return ret; + i440fx_update_memory_mappings(d); + qemu_get_8s(f, &d->smm_enabled); + + if (version_id == 2) { + for (i = 0; i < PIIX_NUM_PIRQS; i++) { + qemu_get_be32(f); /* dummy load for compatibility */ + } + } + + return 0; +} + +static int i440fx_post_load(void *opaque, int version_id) +{ + PCII440FXState *d = opaque; + + i440fx_update_memory_mappings(d); + return 0; +} + +static const VMStateDescription vmstate_i440fx = { + .name = "I440FX", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 1, + .load_state_old = i440fx_load_old, + .post_load = i440fx_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, PCII440FXState), + VMSTATE_UINT8(smm_enabled, PCII440FXState), + VMSTATE_END_OF_LIST() + } +}; + +static int i440fx_pcihost_initfn(SysBusDevice *dev) +{ + PCIHostState *s = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&s->conf_mem, &pci_host_conf_le_ops, s, + "pci-conf-idx", 4); + sysbus_add_io(dev, 0xcf8, &s->conf_mem); + sysbus_init_ioports(&s->busdev, 0xcf8, 4); + + memory_region_init_io(&s->data_mem, &pci_host_data_le_ops, s, + "pci-conf-data", 4); + sysbus_add_io(dev, 0xcfc, &s->data_mem); + sysbus_init_ioports(&s->busdev, 0xcfc, 4); + + return 0; +} + +static int i440fx_initfn(PCIDevice *dev) +{ + PCII440FXState *d = I440FX_PCI_DEVICE(dev); + + d->dev.config[I440FX_SMRAM] = 0x02; + + cpu_smm_register(&i440fx_set_smm, d); + return 0; +} + +static PCIBus *i440fx_common_init(const char *device_name, + PCII440FXState **pi440fx_state, + int *piix3_devfn, + ISABus **isa_bus, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + ram_addr_t ram_size, + hwaddr pci_hole_start, + hwaddr pci_hole_size, + hwaddr pci_hole64_start, + hwaddr pci_hole64_size, + MemoryRegion *pci_address_space, + MemoryRegion *ram_memory) +{ + DeviceState *dev; + PCIBus *b; + PCIDevice *d; + PCIHostState *s; + PIIX3State *piix3; + PCII440FXState *f; + unsigned i; + + dev = qdev_create(NULL, "i440FX-pcihost"); + s = PCI_HOST_BRIDGE(dev); + b = pci_bus_new(dev, NULL, pci_address_space, + address_space_io, 0, TYPE_PCI_BUS); + s->bus = b; + object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); + qdev_init_nofail(dev); + + d = pci_create_simple(b, 0, device_name); + *pi440fx_state = I440FX_PCI_DEVICE(d); + f = *pi440fx_state; + f->system_memory = address_space_mem; + f->pci_address_space = pci_address_space; + f->ram_memory = ram_memory; + memory_region_init_alias(&f->pci_hole, "pci-hole", f->pci_address_space, + pci_hole_start, pci_hole_size); + memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole); + memory_region_init_alias(&f->pci_hole_64bit, "pci-hole64", + f->pci_address_space, + pci_hole64_start, pci_hole64_size); + if (pci_hole64_size) { + memory_region_add_subregion(f->system_memory, pci_hole64_start, + &f->pci_hole_64bit); + } + memory_region_init_alias(&f->smram_region, "smram-region", + f->pci_address_space, 0xa0000, 0x20000); + memory_region_add_subregion_overlap(f->system_memory, 0xa0000, + &f->smram_region, 1); + memory_region_set_enabled(&f->smram_region, false); + init_pam(f->ram_memory, f->system_memory, f->pci_address_space, + &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < 12; ++i) { + init_pam(f->ram_memory, f->system_memory, f->pci_address_space, + &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, + PAM_EXPAN_SIZE); + } + + /* Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. */ + if (xen_enabled()) { + piix3 = DO_UPCAST(PIIX3State, dev, + pci_create_simple_multifunction(b, -1, true, "PIIX3-xen")); + pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, + piix3, XEN_PIIX_NUM_PIRQS); + } else { + piix3 = DO_UPCAST(PIIX3State, dev, + pci_create_simple_multifunction(b, -1, true, "PIIX3")); + pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, + PIIX_NUM_PIRQS); + pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); + } + piix3->pic = pic; + *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); + + *piix3_devfn = piix3->dev.devfn; + + ram_size = ram_size / 8 / 1024 / 1024; + if (ram_size > 255) + ram_size = 255; + (*pi440fx_state)->dev.config[0x57]=ram_size; + + i440fx_update_memory_mappings(f); + + return b; +} + +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, + ISABus **isa_bus, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + ram_addr_t ram_size, + hwaddr pci_hole_start, + hwaddr pci_hole_size, + hwaddr pci_hole64_start, + hwaddr pci_hole64_size, + MemoryRegion *pci_memory, MemoryRegion *ram_memory) + +{ + PCIBus *b; + + b = i440fx_common_init(TYPE_I440FX_PCI_DEVICE, pi440fx_state, + piix3_devfn, isa_bus, pic, + address_space_mem, address_space_io, ram_size, + pci_hole_start, pci_hole_size, + pci_hole64_start, pci_hole64_size, + pci_memory, ram_memory); + return b; +} + +/* PIIX3 PCI to ISA bridge */ +static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) +{ + qemu_set_irq(piix3->pic[pic_irq], + !!(piix3->pic_levels & + (((1ULL << PIIX_NUM_PIRQS) - 1) << + (pic_irq * PIIX_NUM_PIRQS)))); +} + +static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) +{ + int pic_irq; + uint64_t mask; + + pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; + if (pic_irq >= PIIX_NUM_PIC_IRQS) { + return; + } + + mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); + piix3->pic_levels &= ~mask; + piix3->pic_levels |= mask * !!level; + + piix3_set_irq_pic(piix3, pic_irq); +} + +static void piix3_set_irq(void *opaque, int pirq, int level) +{ + PIIX3State *piix3 = opaque; + piix3_set_irq_level(piix3, pirq, level); +} + +static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) +{ + PIIX3State *piix3 = opaque; + int irq = piix3->dev.config[PIIX_PIRQC + pin]; + PCIINTxRoute route; + + if (irq < PIIX_NUM_PIC_IRQS) { + route.mode = PCI_INTX_ENABLED; + route.irq = irq; + } else { + route.mode = PCI_INTX_DISABLED; + route.irq = -1; + } + return route; +} + +/* irq routing is changed. so rebuild bitmap */ +static void piix3_update_irq_levels(PIIX3State *piix3) +{ + int pirq; + + piix3->pic_levels = 0; + for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { + piix3_set_irq_level(piix3, pirq, + pci_bus_get_irq_level(piix3->dev.bus, pirq)); + } +} + +static void piix3_write_config(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + pci_default_write_config(dev, address, val, len); + if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { + PIIX3State *piix3 = DO_UPCAST(PIIX3State, dev, dev); + int pic_irq; + + pci_bus_fire_intx_routing_notifier(piix3->dev.bus); + piix3_update_irq_levels(piix3); + for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { + piix3_set_irq_pic(piix3, pic_irq); + } + } +} + +static void piix3_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + xen_piix_pci_write_config_client(address, val, len); + piix3_write_config(dev, address, val, len); +} + +static void piix3_reset(void *opaque) +{ + PIIX3State *d = opaque; + uint8_t *pci_conf = d->dev.config; + + pci_conf[0x04] = 0x07; /* master, memory and I/O */ + pci_conf[0x05] = 0x00; + pci_conf[0x06] = 0x00; + pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ + pci_conf[0x4c] = 0x4d; + pci_conf[0x4e] = 0x03; + pci_conf[0x4f] = 0x00; + pci_conf[0x60] = 0x80; + pci_conf[0x61] = 0x80; + pci_conf[0x62] = 0x80; + pci_conf[0x63] = 0x80; + pci_conf[0x69] = 0x02; + pci_conf[0x70] = 0x80; + pci_conf[0x76] = 0x0c; + pci_conf[0x77] = 0x0c; + pci_conf[0x78] = 0x02; + pci_conf[0x79] = 0x00; + pci_conf[0x80] = 0x00; + pci_conf[0x82] = 0x00; + pci_conf[0xa0] = 0x08; + pci_conf[0xa2] = 0x00; + pci_conf[0xa3] = 0x00; + pci_conf[0xa4] = 0x00; + pci_conf[0xa5] = 0x00; + pci_conf[0xa6] = 0x00; + pci_conf[0xa7] = 0x00; + pci_conf[0xa8] = 0x0f; + pci_conf[0xaa] = 0x00; + pci_conf[0xab] = 0x00; + pci_conf[0xac] = 0x00; + pci_conf[0xae] = 0x00; + + d->pic_levels = 0; + d->rcr = 0; +} + +static int piix3_post_load(void *opaque, int version_id) +{ + PIIX3State *piix3 = opaque; + piix3_update_irq_levels(piix3); + return 0; +} + +static void piix3_pre_save(void *opaque) +{ + int i; + PIIX3State *piix3 = opaque; + + for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { + piix3->pci_irq_levels_vmstate[i] = + pci_bus_get_irq_level(piix3->dev.bus, i); + } +} + +static bool piix3_rcr_needed(void *opaque) +{ + PIIX3State *piix3 = opaque; + + return (piix3->rcr != 0); +} + +static const VMStateDescription vmstate_piix3_rcr = { + .name = "PIIX3/rcr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(rcr, PIIX3State), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_piix3 = { + .name = "PIIX3", + .version_id = 3, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .post_load = piix3_post_load, + .pre_save = piix3_pre_save, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PIIX3State), + VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, + PIIX_NUM_PIRQS, 3), + VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_piix3_rcr, + .needed = piix3_rcr_needed, + }, + { 0 } + } +}; + + +static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) +{ + PIIX3State *d = opaque; + + if (val & 4) { + qemu_system_reset_request(); + return; + } + d->rcr = val & 2; /* keep System Reset type only */ +} + +static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) +{ + PIIX3State *d = opaque; + + return d->rcr; +} + +static const MemoryRegionOps rcr_ops = { + .read = rcr_read, + .write = rcr_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +static int piix3_initfn(PCIDevice *dev) +{ + PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); + + isa_bus_new(DEVICE(d), pci_address_space_io(dev)); + + memory_region_init_io(&d->rcr_mem, &rcr_ops, d, "piix3-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, + &d->rcr_mem, 1); + + qemu_register_reset(piix3_reset, d); + return 0; +} + +static void piix3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "ISA bridge"; + dc->vmsd = &vmstate_piix3; + dc->no_user = 1, + k->no_hotplug = 1; + k->init = piix3_initfn; + k->config_write = piix3_write_config; + k->vendor_id = PCI_VENDOR_ID_INTEL; + /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ + k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; + k->class_id = PCI_CLASS_BRIDGE_ISA; +} + +static const TypeInfo piix3_info = { + .name = "PIIX3", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PIIX3State), + .class_init = piix3_class_init, +}; + +static void piix3_xen_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "ISA bridge"; + dc->vmsd = &vmstate_piix3; + dc->no_user = 1; + k->no_hotplug = 1; + k->init = piix3_initfn; + k->config_write = piix3_write_config_xen; + k->vendor_id = PCI_VENDOR_ID_INTEL; + /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ + k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; + k->class_id = PCI_CLASS_BRIDGE_ISA; +}; + +static const TypeInfo piix3_xen_info = { + .name = "PIIX3-xen", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PIIX3State), + .class_init = piix3_xen_class_init, +}; + +static void i440fx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->no_hotplug = 1; + k->init = i440fx_initfn; + k->config_write = i440fx_write_config; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82441; + k->revision = 0x02; + k->class_id = PCI_CLASS_BRIDGE_HOST; + dc->desc = "Host bridge"; + dc->no_user = 1; + dc->vmsd = &vmstate_i440fx; +} + +static const TypeInfo i440fx_info = { + .name = TYPE_I440FX_PCI_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCII440FXState), + .class_init = i440fx_class_init, +}; + +static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = i440fx_pcihost_initfn; + dc->fw_name = "pci"; + dc->no_user = 1; +} + +static const TypeInfo i440fx_pcihost_info = { + .name = "i440FX-pcihost", + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(I440FXState), + .class_init = i440fx_pcihost_class_init, +}; + +static void i440fx_register_types(void) +{ + type_register_static(&i440fx_info); + type_register_static(&piix3_info); + type_register_static(&piix3_xen_info); + type_register_static(&i440fx_pcihost_info); +} + +type_init(i440fx_register_types) diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c new file mode 100644 index 0000000000..5e7ad94388 --- /dev/null +++ b/hw/pci-host/ppce500.c @@ -0,0 +1,427 @@ +/* + * QEMU PowerPC E500 embedded processors pci controller emulation + * + * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Yu Liu, + * + * This file is derived from hw/ppc4xx_pci.c, + * the copyright for that material belongs to the original owners. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "hw/hw.h" +#include "hw/ppc/e500-ccsr.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "qemu/bswap.h" +#include "hw/pci-host/ppce500.h" + +#ifdef DEBUG_PCI +#define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) +#else +#define pci_debug(fmt, ...) +#endif + +#define PCIE500_CFGADDR 0x0 +#define PCIE500_CFGDATA 0x4 +#define PCIE500_REG_BASE 0xC00 +#define PCIE500_ALL_SIZE 0x1000 +#define PCIE500_REG_SIZE (PCIE500_ALL_SIZE - PCIE500_REG_BASE) + +#define PCIE500_PCI_IOLEN 0x10000ULL + +#define PPCE500_PCI_CONFIG_ADDR 0x0 +#define PPCE500_PCI_CONFIG_DATA 0x4 +#define PPCE500_PCI_INTACK 0x8 + +#define PPCE500_PCI_OW1 (0xC20 - PCIE500_REG_BASE) +#define PPCE500_PCI_OW2 (0xC40 - PCIE500_REG_BASE) +#define PPCE500_PCI_OW3 (0xC60 - PCIE500_REG_BASE) +#define PPCE500_PCI_OW4 (0xC80 - PCIE500_REG_BASE) +#define PPCE500_PCI_IW3 (0xDA0 - PCIE500_REG_BASE) +#define PPCE500_PCI_IW2 (0xDC0 - PCIE500_REG_BASE) +#define PPCE500_PCI_IW1 (0xDE0 - PCIE500_REG_BASE) + +#define PPCE500_PCI_GASKET_TIMR (0xE20 - PCIE500_REG_BASE) + +#define PCI_POTAR 0x0 +#define PCI_POTEAR 0x4 +#define PCI_POWBAR 0x8 +#define PCI_POWAR 0x10 + +#define PCI_PITAR 0x0 +#define PCI_PIWBAR 0x8 +#define PCI_PIWBEAR 0xC +#define PCI_PIWAR 0x10 + +#define PPCE500_PCI_NR_POBS 5 +#define PPCE500_PCI_NR_PIBS 3 + +struct pci_outbound { + uint32_t potar; + uint32_t potear; + uint32_t powbar; + uint32_t powar; +}; + +struct pci_inbound { + uint32_t pitar; + uint32_t piwbar; + uint32_t piwbear; + uint32_t piwar; +}; + +#define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost" + +#define PPC_E500_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PPCE500PCIState, (obj), TYPE_PPC_E500_PCI_HOST_BRIDGE) + +struct PPCE500PCIState { + PCIHostState parent_obj; + + struct pci_outbound pob[PPCE500_PCI_NR_POBS]; + struct pci_inbound pib[PPCE500_PCI_NR_PIBS]; + uint32_t gasket_time; + qemu_irq irq[4]; + uint32_t first_slot; + /* mmio maps */ + MemoryRegion container; + MemoryRegion iomem; + MemoryRegion pio; +}; + +#define TYPE_PPC_E500_PCI_BRIDGE "e500-host-bridge" +#define PPC_E500_PCI_BRIDGE(obj) \ + OBJECT_CHECK(PPCE500PCIBridgeState, (obj), TYPE_PPC_E500_PCI_BRIDGE) + +struct PPCE500PCIBridgeState { + /*< private >*/ + PCIDevice parent; + /*< public >*/ + + MemoryRegion bar0; +}; + +typedef struct PPCE500PCIBridgeState PPCE500PCIBridgeState; +typedef struct PPCE500PCIState PPCE500PCIState; + +static uint64_t pci_reg_read4(void *opaque, hwaddr addr, + unsigned size) +{ + PPCE500PCIState *pci = opaque; + unsigned long win; + uint32_t value = 0; + int idx; + + win = addr & 0xfe0; + + switch (win) { + case PPCE500_PCI_OW1: + case PPCE500_PCI_OW2: + case PPCE500_PCI_OW3: + case PPCE500_PCI_OW4: + idx = (addr >> 5) & 0x7; + switch (addr & 0xC) { + case PCI_POTAR: + value = pci->pob[idx].potar; + break; + case PCI_POTEAR: + value = pci->pob[idx].potear; + break; + case PCI_POWBAR: + value = pci->pob[idx].powbar; + break; + case PCI_POWAR: + value = pci->pob[idx].powar; + break; + default: + break; + } + break; + + case PPCE500_PCI_IW3: + case PPCE500_PCI_IW2: + case PPCE500_PCI_IW1: + idx = ((addr >> 5) & 0x3) - 1; + switch (addr & 0xC) { + case PCI_PITAR: + value = pci->pib[idx].pitar; + break; + case PCI_PIWBAR: + value = pci->pib[idx].piwbar; + break; + case PCI_PIWBEAR: + value = pci->pib[idx].piwbear; + break; + case PCI_PIWAR: + value = pci->pib[idx].piwar; + break; + default: + break; + }; + break; + + case PPCE500_PCI_GASKET_TIMR: + value = pci->gasket_time; + break; + + default: + break; + } + + pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__, + win, addr, value); + return value; +} + +static void pci_reg_write4(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + PPCE500PCIState *pci = opaque; + unsigned long win; + int idx; + + win = addr & 0xfe0; + + pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n", + __func__, (unsigned)value, win, addr); + + switch (win) { + case PPCE500_PCI_OW1: + case PPCE500_PCI_OW2: + case PPCE500_PCI_OW3: + case PPCE500_PCI_OW4: + idx = (addr >> 5) & 0x7; + switch (addr & 0xC) { + case PCI_POTAR: + pci->pob[idx].potar = value; + break; + case PCI_POTEAR: + pci->pob[idx].potear = value; + break; + case PCI_POWBAR: + pci->pob[idx].powbar = value; + break; + case PCI_POWAR: + pci->pob[idx].powar = value; + break; + default: + break; + }; + break; + + case PPCE500_PCI_IW3: + case PPCE500_PCI_IW2: + case PPCE500_PCI_IW1: + idx = ((addr >> 5) & 0x3) - 1; + switch (addr & 0xC) { + case PCI_PITAR: + pci->pib[idx].pitar = value; + break; + case PCI_PIWBAR: + pci->pib[idx].piwbar = value; + break; + case PCI_PIWBEAR: + pci->pib[idx].piwbear = value; + break; + case PCI_PIWAR: + pci->pib[idx].piwar = value; + break; + default: + break; + }; + break; + + case PPCE500_PCI_GASKET_TIMR: + pci->gasket_time = value; + break; + + default: + break; + }; +} + +static const MemoryRegionOps e500_pci_reg_ops = { + .read = pci_reg_read4, + .write = pci_reg_write4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int devno = pci_dev->devfn >> 3; + int ret; + + ret = ppce500_pci_map_irq_slot(devno, irq_num); + + pci_debug("%s: devfn %x irq %d -> %d devno:%x\n", __func__, + pci_dev->devfn, irq_num, ret, devno); + + return ret; +} + +static void mpc85xx_pci_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level); + + qemu_set_irq(pic[irq_num], level); +} + +static const VMStateDescription vmstate_pci_outbound = { + .name = "pci_outbound", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(potar, struct pci_outbound), + VMSTATE_UINT32(potear, struct pci_outbound), + VMSTATE_UINT32(powbar, struct pci_outbound), + VMSTATE_UINT32(powar, struct pci_outbound), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pci_inbound = { + .name = "pci_inbound", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pitar, struct pci_inbound), + VMSTATE_UINT32(piwbar, struct pci_inbound), + VMSTATE_UINT32(piwbear, struct pci_inbound), + VMSTATE_UINT32(piwar, struct pci_inbound), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ppce500_pci = { + .name = "ppce500_pci", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1, + vmstate_pci_outbound, struct pci_outbound), + VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1, + vmstate_pci_outbound, struct pci_inbound), + VMSTATE_UINT32(gasket_time, PPCE500PCIState), + VMSTATE_END_OF_LIST() + } +}; + +#include "exec/address-spaces.h" + +static int e500_pcihost_bridge_initfn(PCIDevice *d) +{ + PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d); + PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(), + "/e500-ccsr")); + + pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI); + d->config[PCI_HEADER_TYPE] = + (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | + PCI_HEADER_TYPE_BRIDGE; + + memory_region_init_alias(&b->bar0, "e500-pci-bar0", &ccsr->ccsr_space, + 0, int128_get64(ccsr->ccsr_space.size)); + pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0); + + return 0; +} + +static int e500_pcihost_initfn(SysBusDevice *dev) +{ + PCIHostState *h; + PPCE500PCIState *s; + PCIBus *b; + int i; + MemoryRegion *address_space_mem = get_system_memory(); + + h = PCI_HOST_BRIDGE(dev); + s = PPC_E500_PCI_HOST_BRIDGE(dev); + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + + memory_region_init(&s->pio, "pci-pio", PCIE500_PCI_IOLEN); + + b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq, + mpc85xx_pci_map_irq, s->irq, address_space_mem, + &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); + h->bus = b; + + pci_create_simple(b, 0, "e500-host-bridge"); + + memory_region_init(&s->container, "pci-container", PCIE500_ALL_SIZE); + memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, h, + "pci-conf-idx", 4); + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, h, + "pci-conf-data", 4); + memory_region_init_io(&s->iomem, &e500_pci_reg_ops, s, + "pci.reg", PCIE500_REG_SIZE); + memory_region_add_subregion(&s->container, PCIE500_CFGADDR, &h->conf_mem); + memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem); + memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem); + sysbus_init_mmio(dev, &s->container); + sysbus_init_mmio(dev, &s->pio); + + return 0; +} + +static void e500_host_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = e500_pcihost_bridge_initfn; + k->vendor_id = PCI_VENDOR_ID_FREESCALE; + k->device_id = PCI_DEVICE_ID_MPC8533E; + k->class_id = PCI_CLASS_PROCESSOR_POWERPC; + dc->desc = "Host bridge"; +} + +static const TypeInfo e500_host_bridge_info = { + .name = "e500-host-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PPCE500PCIBridgeState), + .class_init = e500_host_bridge_class_init, +}; + +static Property pcihost_properties[] = { + DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), + DEFINE_PROP_END_OF_LIST(), +}; + +static void e500_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = e500_pcihost_initfn; + dc->props = pcihost_properties; + dc->vmsd = &vmstate_ppce500_pci; +} + +static const TypeInfo e500_pcihost_info = { + .name = TYPE_PPC_E500_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PPCE500PCIState), + .class_init = e500_pcihost_class_init, +}; + +static void e500_pci_register_types(void) +{ + type_register_static(&e500_pcihost_info); + type_register_static(&e500_host_bridge_info); +} + +type_init(e500_pci_register_types) diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c new file mode 100644 index 0000000000..61302539ab --- /dev/null +++ b/hw/pci-host/prep.c @@ -0,0 +1,232 @@ +/* + * QEMU PREP PCI host + * + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2011-2013 Andreas Färber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_host.h" +#include "hw/i386/pc.h" +#include "exec/address-spaces.h" + +#define TYPE_RAVEN_PCI_DEVICE "raven" +#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost" + +#define RAVEN_PCI_DEVICE(obj) \ + OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE) + +typedef struct RavenPCIState { + PCIDevice dev; +} RavenPCIState; + +#define RAVEN_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE) + +typedef struct PRePPCIState { + PCIHostState parent_obj; + + MemoryRegion intack; + qemu_irq irq[4]; + PCIBus pci_bus; + RavenPCIState pci_dev; +} PREPPCIState; + +static inline uint32_t PPC_PCIIO_config(hwaddr addr) +{ + int i; + + for (i = 0; i < 11; i++) { + if ((addr & (1 << (11 + i))) != 0) { + break; + } + } + return (addr & 0x7ff) | (i << 11); +} + +static void ppc_pci_io_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int size) +{ + PREPPCIState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + pci_data_write(phb->bus, PPC_PCIIO_config(addr), val, size); +} + +static uint64_t ppc_pci_io_read(void *opaque, hwaddr addr, + unsigned int size) +{ + PREPPCIState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + return pci_data_read(phb->bus, PPC_PCIIO_config(addr), size); +} + +static const MemoryRegionOps PPC_PCIIO_ops = { + .read = ppc_pci_io_read, + .write = ppc_pci_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t ppc_intack_read(void *opaque, hwaddr addr, + unsigned int size) +{ + return pic_read_irq(isa_pic); +} + +static const MemoryRegionOps PPC_intack_ops = { + .read = ppc_intack_read, + .valid = { + .max_access_size = 1, + }, +}; + +static int prep_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return (irq_num + (pci_dev->devfn >> 3)) & 1; +} + +static void prep_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + qemu_set_irq(pic[irq_num] , level); +} + +static void raven_pcihost_realizefn(DeviceState *d, Error **errp) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(d); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev); + MemoryRegion *address_space_mem = get_system_memory(); + int i; + + for (i = 0; i < 4; i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + + pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, s, + "pci-conf-idx", 1); + sysbus_add_io(dev, 0xcf8, &h->conf_mem); + sysbus_init_ioports(&h->busdev, 0xcf8, 1); + + memory_region_init_io(&h->data_mem, &pci_host_data_be_ops, s, + "pci-conf-data", 1); + sysbus_add_io(dev, 0xcfc, &h->data_mem); + sysbus_init_ioports(&h->busdev, 0xcfc, 1); + + memory_region_init_io(&h->mmcfg, &PPC_PCIIO_ops, s, "pciio", 0x00400000); + memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg); + + memory_region_init_io(&s->intack, &PPC_intack_ops, s, "pci-intack", 1); + memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->intack); + + /* TODO Remove once realize propagates to child devices. */ + object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); +} + +static void raven_pcihost_initfn(Object *obj) +{ + PCIHostState *h = PCI_HOST_BRIDGE(obj); + PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj); + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *address_space_io = get_system_io(); + DeviceState *pci_dev; + + pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), NULL, + address_space_mem, address_space_io, 0, TYPE_PCI_BUS); + h->bus = &s->pci_bus; + + object_initialize(&s->pci_dev, TYPE_RAVEN_PCI_DEVICE); + pci_dev = DEVICE(&s->pci_dev); + qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus)); + object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr", + NULL); + qdev_prop_set_bit(pci_dev, "multifunction", false); +} + +static int raven_init(PCIDevice *d) +{ + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x34] = 0x00; // capabilities_pointer + + return 0; +} + +static const VMStateDescription vmstate_raven = { + .name = "raven", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, RavenPCIState), + VMSTATE_END_OF_LIST() + }, +}; + +static void raven_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = raven_init; + k->vendor_id = PCI_VENDOR_ID_MOTOROLA; + k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN; + k->revision = 0x00; + k->class_id = PCI_CLASS_BRIDGE_HOST; + dc->desc = "PReP Host Bridge - Motorola Raven"; + dc->vmsd = &vmstate_raven; + dc->no_user = 1; +} + +static const TypeInfo raven_info = { + .name = TYPE_RAVEN_PCI_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(RavenPCIState), + .class_init = raven_class_init, +}; + +static void raven_pcihost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = raven_pcihost_realizefn; + dc->fw_name = "pci"; + dc->no_user = 1; +} + +static const TypeInfo raven_pcihost_info = { + .name = TYPE_RAVEN_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PREPPCIState), + .instance_init = raven_pcihost_initfn, + .class_init = raven_pcihost_class_init, +}; + +static void raven_register_types(void) +{ + type_register_static(&raven_pcihost_info); + type_register_static(&raven_info); +} + +type_init(raven_register_types) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c new file mode 100644 index 0000000000..8467f86450 --- /dev/null +++ b/hw/pci-host/q35.c @@ -0,0 +1,310 @@ +/* + * QEMU MCH/ICH9 PCI Bridge Emulation + * + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2009, 2010, 2011 + * Isaku Yamahata + * VA Linux Systems Japan K.K. + * Copyright (C) 2012 Jason Baron + * + * This is based on piix_pci.c, but heavily modified. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/pci-host/q35.h" + +/**************************************************************************** + * Q35 host + */ + +static int q35_host_init(SysBusDevice *dev) +{ + PCIBus *b; + PCIHostState *pci = FROM_SYSBUS(PCIHostState, dev); + Q35PCIHost *s = Q35_HOST_DEVICE(&dev->qdev); + + memory_region_init_io(&pci->conf_mem, &pci_host_conf_le_ops, pci, + "pci-conf-idx", 4); + sysbus_add_io(dev, MCH_HOST_BRIDGE_CONFIG_ADDR, &pci->conf_mem); + sysbus_init_ioports(&pci->busdev, MCH_HOST_BRIDGE_CONFIG_ADDR, 4); + + memory_region_init_io(&pci->data_mem, &pci_host_data_le_ops, pci, + "pci-conf-data", 4); + sysbus_add_io(dev, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); + sysbus_init_ioports(&pci->busdev, MCH_HOST_BRIDGE_CONFIG_DATA, 4); + + if (pcie_host_init(&s->host) < 0) { + return -1; + } + b = pci_bus_new(&s->host.pci.busdev.qdev, "pcie.0", + s->mch.pci_address_space, s->mch.address_space_io, + 0, TYPE_PCIE_BUS); + s->host.pci.bus = b; + qdev_set_parent_bus(DEVICE(&s->mch), BUS(b)); + qdev_init_nofail(DEVICE(&s->mch)); + + return 0; +} + +static Property mch_props[] = { + DEFINE_PROP_UINT64("MCFG", Q35PCIHost, host.base_addr, + MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void q35_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = q35_host_init; + dc->props = mch_props; +} + +static void q35_host_initfn(Object *obj) +{ + Q35PCIHost *s = Q35_HOST_DEVICE(obj); + + object_initialize(&s->mch, TYPE_MCH_PCI_DEVICE); + object_property_add_child(OBJECT(s), "mch", OBJECT(&s->mch), NULL); + qdev_prop_set_uint32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_bit(DEVICE(&s->mch), "multifunction", false); +} + +static const TypeInfo q35_host_info = { + .name = TYPE_Q35_HOST_DEVICE, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_size = sizeof(Q35PCIHost), + .instance_init = q35_host_initfn, + .class_init = q35_host_class_init, +}; + +/**************************************************************************** + * MCH D0:F0 + */ + +/* PCIe MMCFG */ +static void mch_update_pciexbar(MCHPCIState *mch) +{ + PCIDevice *pci_dev = &mch->d; + BusState *bus = qdev_get_parent_bus(&pci_dev->qdev); + DeviceState *qdev = bus->parent; + Q35PCIHost *s = Q35_HOST_DEVICE(qdev); + + uint64_t pciexbar; + int enable; + uint64_t addr; + uint64_t addr_mask; + uint32_t length; + + pciexbar = pci_get_quad(pci_dev->config + MCH_HOST_BRIDGE_PCIEXBAR); + enable = pciexbar & MCH_HOST_BRIDGE_PCIEXBAREN; + addr_mask = MCH_HOST_BRIDGE_PCIEXBAR_ADMSK; + switch (pciexbar & MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) { + case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M: + length = 256 * 1024 * 1024; + break; + case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M: + length = 128 * 1024 * 1024; + addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK | + MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; + break; + case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M: + length = 64 * 1024 * 1024; + addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; + break; + case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD: + default: + enable = 0; + length = 0; + abort(); + break; + } + addr = pciexbar & addr_mask; + pcie_host_mmcfg_update(&s->host, enable, addr, length); +} + +/* PAM */ +static void mch_update_pam(MCHPCIState *mch) +{ + int i; + + memory_region_transaction_begin(); + for (i = 0; i < 13; i++) { + pam_update(&mch->pam_regions[i], i, + mch->d.config[MCH_HOST_BRIDGE_PAM0 + ((i + 1) / 2)]); + } + memory_region_transaction_commit(); +} + +/* SMRAM */ +static void mch_update_smram(MCHPCIState *mch) +{ + memory_region_transaction_begin(); + smram_update(&mch->smram_region, mch->d.config[MCH_HOST_BRDIGE_SMRAM], + mch->smm_enabled); + memory_region_transaction_commit(); +} + +static void mch_set_smm(int smm, void *arg) +{ + MCHPCIState *mch = arg; + + memory_region_transaction_begin(); + smram_set_smm(&mch->smm_enabled, smm, mch->d.config[MCH_HOST_BRDIGE_SMRAM], + &mch->smram_region); + memory_region_transaction_commit(); +} + +static void mch_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + MCHPCIState *mch = MCH_PCI_DEVICE(d); + + /* XXX: implement SMRAM.D_LOCK */ + pci_default_write_config(d, address, val, len); + + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0, + MCH_HOST_BRIDGE_PAM_SIZE)) { + mch_update_pam(mch); + } + + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PCIEXBAR, + MCH_HOST_BRIDGE_PCIEXBAR_SIZE)) { + mch_update_pciexbar(mch); + } + + if (ranges_overlap(address, len, MCH_HOST_BRDIGE_SMRAM, + MCH_HOST_BRDIGE_SMRAM_SIZE)) { + mch_update_smram(mch); + } +} + +static void mch_update(MCHPCIState *mch) +{ + mch_update_pciexbar(mch); + mch_update_pam(mch); + mch_update_smram(mch); +} + +static int mch_post_load(void *opaque, int version_id) +{ + MCHPCIState *mch = opaque; + mch_update(mch); + return 0; +} + +static const VMStateDescription vmstate_mch = { + .name = "mch", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = mch_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(d, MCHPCIState), + VMSTATE_UINT8(smm_enabled, MCHPCIState), + VMSTATE_END_OF_LIST() + } +}; + +static void mch_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + MCHPCIState *mch = MCH_PCI_DEVICE(d); + + pci_set_quad(d->config + MCH_HOST_BRIDGE_PCIEXBAR, + MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT); + + d->config[MCH_HOST_BRDIGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; + + mch_update(mch); +} + +static int mch_init(PCIDevice *d) +{ + int i; + hwaddr pci_hole64_size; + MCHPCIState *mch = MCH_PCI_DEVICE(d); + + /* setup pci memory regions */ + memory_region_init_alias(&mch->pci_hole, "pci-hole", + mch->pci_address_space, + mch->below_4g_mem_size, + 0x100000000ULL - mch->below_4g_mem_size); + memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size, + &mch->pci_hole); + pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 : + ((uint64_t)1 << 62)); + memory_region_init_alias(&mch->pci_hole_64bit, "pci-hole64", + mch->pci_address_space, + 0x100000000ULL + mch->above_4g_mem_size, + pci_hole64_size); + if (pci_hole64_size) { + memory_region_add_subregion(mch->system_memory, + 0x100000000ULL + mch->above_4g_mem_size, + &mch->pci_hole_64bit); + } + /* smram */ + cpu_smm_register(&mch_set_smm, mch); + memory_region_init_alias(&mch->smram_region, "smram-region", + mch->pci_address_space, 0xa0000, 0x20000); + memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, + &mch->smram_region, 1); + memory_region_set_enabled(&mch->smram_region, false); + init_pam(mch->ram_memory, mch->system_memory, mch->pci_address_space, + &mch->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < 12; ++i) { + init_pam(mch->ram_memory, mch->system_memory, mch->pci_address_space, + &mch->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, + PAM_EXPAN_SIZE); + } + return 0; +} + +static void mch_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = mch_init; + k->config_write = mch_write_config; + dc->reset = mch_reset; + dc->desc = "Host bridge"; + dc->vmsd = &vmstate_mch; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH; + k->revision = MCH_HOST_BRIDGE_REVISION_DEFUALT; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo mch_info = { + .name = TYPE_MCH_PCI_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(MCHPCIState), + .class_init = mch_class_init, +}; + +static void q35_register(void) +{ + type_register_static(&mch_info); + type_register_static(&q35_host_info); +} + +type_init(q35_register); diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c new file mode 100644 index 0000000000..fff235d523 --- /dev/null +++ b/hw/pci-host/uninorth.c @@ -0,0 +1,492 @@ +/* + * QEMU Uninorth PCI host (for all Mac99 and newer machines) + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" + +/* debug UniNorth */ +//#define DEBUG_UNIN + +#ifdef DEBUG_UNIN +#define UNIN_DPRINTF(fmt, ...) \ + do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0) +#else +#define UNIN_DPRINTF(fmt, ...) +#endif + +static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; + +#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" +#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" +#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" +#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" + +#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) +#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) +#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) +#define U3_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) + +typedef struct UNINState { + PCIHostState parent_obj; + + MemoryRegion pci_mmio; + MemoryRegion pci_hole; +} UNINState; + +static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int retval; + int devfn = pci_dev->devfn & 0x00FFFFFF; + + retval = (((devfn >> 11) & 0x1F) + irq_num) & 3; + + return retval; +} + +static void pci_unin_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__, + unin_irq_line[irq_num], level); + qemu_set_irq(pic[unin_irq_line[irq_num]], level); +} + +static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) +{ + uint32_t retval; + + if (reg & (1u << 31)) { + /* XXX OpenBIOS compatibility hack */ + retval = reg | (addr & 3); + } else if (reg & 1) { + /* CFA1 style */ + retval = (reg & ~7u) | (addr & 7); + } else { + uint32_t slot, func; + + /* Grab CFA0 style values */ + slot = ffs(reg & 0xfffff800) - 1; + func = (reg >> 8) & 7; + + /* ... and then convert them to x86 format */ + /* config pointer */ + retval = (reg & (0xff - 7)) | (addr & 7); + /* slot */ + retval |= slot << 11; + /* fn */ + retval |= func << 8; + } + + + UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n", + reg, addr, retval); + + return retval; +} + +static void unin_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + UNINState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n", + addr, len, val); + pci_data_write(phb->bus, + unin_get_config_reg(phb->config_reg, addr), + val, len); +} + +static uint64_t unin_data_read(void *opaque, hwaddr addr, + unsigned len) +{ + UNINState *s = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(s); + uint32_t val; + + val = pci_data_read(phb->bus, + unin_get_config_reg(phb->config_reg, addr), + len); + UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n", + addr, len, val); + return val; +} + +static const MemoryRegionOps unin_data_ops = { + .read = unin_data_read, + .write = unin_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int pci_unin_main_init_device(SysBusDevice *dev) +{ + PCIHostState *h; + + /* Use values found on a real PowerMac */ + /* Uninorth main bus */ + h = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &unin_data_ops, dev, + "pci-conf-data", 0x1000); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); + + return 0; +} + + +static int pci_u3_agp_init_device(SysBusDevice *dev) +{ + PCIHostState *h; + + /* Uninorth U3 AGP bus */ + h = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &unin_data_ops, dev, + "pci-conf-data", 0x1000); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); + + return 0; +} + +static int pci_unin_agp_init_device(SysBusDevice *dev) +{ + PCIHostState *h; + + /* Uninorth AGP bus */ + h = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, + dev, "pci-conf-data", 0x1000); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); + return 0; +} + +static int pci_unin_internal_init_device(SysBusDevice *dev) +{ + PCIHostState *h; + + /* Uninorth internal bus */ + h = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, + dev, "pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, + dev, "pci-conf-data", 0x1000); + sysbus_init_mmio(dev, &h->conf_mem); + sysbus_init_mmio(dev, &h->data_mem); + return 0; +} + +PCIBus *pci_pmac_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io) +{ + DeviceState *dev; + SysBusDevice *s; + PCIHostState *h; + UNINState *d; + + /* Use values found on a real PowerMac */ + /* Uninorth main bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + h = PCI_HOST_BRIDGE(s); + d = UNI_NORTH_PCI_HOST_BRIDGE(dev); + memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); + memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, + 0x80000000ULL, 0x70000000ULL); + memory_region_add_subregion(address_space_mem, 0x80000000ULL, + &d->pci_hole); + + h->bus = pci_register_bus(dev, "pci", + pci_unin_set_irq, pci_unin_map_irq, + pic, + &d->pci_mmio, + address_space_io, + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + +#if 0 + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north"); +#endif + + sysbus_mmio_map(s, 0, 0xf2800000); + sysbus_mmio_map(s, 1, 0xf2c00000); + + /* DEC 21154 bridge */ +#if 0 + /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ + pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); +#endif + + /* Uninorth AGP bus */ + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); + dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + + /* Uninorth internal bus */ +#if 0 + /* XXX: not needed for now */ + pci_create_simple(h->bus, PCI_DEVFN(14, 0), + "uni-north-internal-pci"); + dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, 0xf4800000); + sysbus_mmio_map(s, 1, 0xf4c00000); +#endif + + return h->bus; +} + +PCIBus *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io) +{ + DeviceState *dev; + SysBusDevice *s; + PCIHostState *h; + UNINState *d; + + /* Uninorth AGP bus */ + + dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + h = PCI_HOST_BRIDGE(dev); + d = U3_AGP_HOST_BRIDGE(dev); + + memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); + memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, + 0x80000000ULL, 0x70000000ULL); + memory_region_add_subregion(address_space_mem, 0x80000000ULL, + &d->pci_hole); + + h->bus = pci_register_bus(dev, "pci", + pci_unin_set_irq, pci_unin_map_irq, + pic, + &d->pci_mmio, + address_space_io, + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + + pci_create_simple(h->bus, 11 << 3, "u3-agp"); + + return h->bus; +} + +static int unin_main_pci_host_init(PCIDevice *d) +{ + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x34] = 0x00; // capabilities_pointer + return 0; +} + +static int unin_agp_pci_host_init(PCIDevice *d) +{ + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + // d->config[0x34] = 0x80; // capabilities_pointer + return 0; +} + +static int u3_agp_pci_host_init(PCIDevice *d) +{ + /* cache line size */ + d->config[0x0C] = 0x08; + /* latency timer */ + d->config[0x0D] = 0x10; + return 0; +} + +static int unin_internal_pci_host_init(PCIDevice *d) +{ + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x34] = 0x00; // capabilities_pointer + return 0; +} + +static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = unin_main_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_APPLE; + k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI; + k->revision = 0x00; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo unin_main_pci_host_info = { + .name = "uni-north-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = unin_main_pci_host_class_init, +}; + +static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = u3_agp_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_APPLE; + k->device_id = PCI_DEVICE_ID_APPLE_U3_AGP; + k->revision = 0x00; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo u3_agp_pci_host_info = { + .name = "u3-agp", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = u3_agp_pci_host_class_init, +}; + +static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = unin_agp_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_APPLE; + k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP; + k->revision = 0x00; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo unin_agp_pci_host_info = { + .name = "uni-north-agp", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = unin_agp_pci_host_class_init, +}; + +static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = unin_internal_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_APPLE; + k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI; + k->revision = 0x00; + k->class_id = PCI_CLASS_BRIDGE_HOST; +} + +static const TypeInfo unin_internal_pci_host_info = { + .name = "uni-north-internal-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = unin_internal_pci_host_class_init, +}; + +static void pci_unin_main_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + + sbc->init = pci_unin_main_init_device; +} + +static const TypeInfo pci_unin_main_info = { + .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(UNINState), + .class_init = pci_unin_main_class_init, +}; + +static void pci_u3_agp_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + + sbc->init = pci_u3_agp_init_device; +} + +static const TypeInfo pci_u3_agp_info = { + .name = TYPE_U3_AGP_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(UNINState), + .class_init = pci_u3_agp_class_init, +}; + +static void pci_unin_agp_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + + sbc->init = pci_unin_agp_init_device; +} + +static const TypeInfo pci_unin_agp_info = { + .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(UNINState), + .class_init = pci_unin_agp_class_init, +}; + +static void pci_unin_internal_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + + sbc->init = pci_unin_internal_init_device; +} + +static const TypeInfo pci_unin_internal_info = { + .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(UNINState), + .class_init = pci_unin_internal_class_init, +}; + +static void unin_register_types(void) +{ + type_register_static(&unin_main_pci_host_info); + type_register_static(&u3_agp_pci_host_info); + type_register_static(&unin_agp_pci_host_info); + type_register_static(&unin_internal_pci_host_info); + + type_register_static(&pci_unin_main_info); + type_register_static(&pci_u3_agp_info); + type_register_static(&pci_unin_agp_info); + type_register_static(&pci_unin_internal_info); +} + +type_init(unin_register_types) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c new file mode 100644 index 0000000000..d67ca796fb --- /dev/null +++ b/hw/pci-host/versatile.c @@ -0,0 +1,164 @@ +/* + * ARM Versatile/PB PCI host controller + * + * Copyright (c) 2006-2009 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "exec/address-spaces.h" + +typedef struct { + SysBusDevice busdev; + qemu_irq irq[4]; + int realview; + MemoryRegion mem_config; + MemoryRegion mem_config2; + MemoryRegion isa; +} PCIVPBState; + +static inline uint32_t vpb_pci_config_addr(hwaddr addr) +{ + return addr & 0xffffff; +} + +static void pci_vpb_config_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + pci_data_write(opaque, vpb_pci_config_addr(addr), val, size); +} + +static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint32_t val; + val = pci_data_read(opaque, vpb_pci_config_addr(addr), size); + return val; +} + +static const MemoryRegionOps pci_vpb_config_ops = { + .read = pci_vpb_config_read, + .write = pci_vpb_config_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int pci_vpb_map_irq(PCIDevice *d, int irq_num) +{ + return irq_num; +} + +static void pci_vpb_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + qemu_set_irq(pic[irq_num], level); +} + +static int pci_vpb_init(SysBusDevice *dev) +{ + PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev); + PCIBus *bus; + int i; + + for (i = 0; i < 4; i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + bus = pci_register_bus(&dev->qdev, "pci", + pci_vpb_set_irq, pci_vpb_map_irq, s->irq, + get_system_memory(), get_system_io(), + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + + /* ??? Register memory space. */ + + /* Our memory regions are: + * 0 : PCI self config window + * 1 : PCI config window + * 2 : PCI IO window (realview_pci only) + */ + memory_region_init_io(&s->mem_config, &pci_vpb_config_ops, bus, + "pci-vpb-selfconfig", 0x1000000); + sysbus_init_mmio(dev, &s->mem_config); + memory_region_init_io(&s->mem_config2, &pci_vpb_config_ops, bus, + "pci-vpb-config", 0x1000000); + sysbus_init_mmio(dev, &s->mem_config2); + if (s->realview) { + isa_mmio_setup(&s->isa, 0x0100000); + sysbus_init_mmio(dev, &s->isa); + } + + pci_create_simple(bus, -1, "versatile_pci_host"); + return 0; +} + +static int pci_realview_init(SysBusDevice *dev) +{ + PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev); + s->realview = 1; + return pci_vpb_init(dev); +} + +static int versatile_pci_host_init(PCIDevice *d) +{ + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); + pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); + return 0; +} + +static void versatile_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = versatile_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_XILINX; + k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30; + k->class_id = PCI_CLASS_PROCESSOR_CO; +} + +static const TypeInfo versatile_pci_host_info = { + .name = "versatile_pci_host", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = versatile_pci_host_class_init, +}; + +static void pci_vpb_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = pci_vpb_init; +} + +static const TypeInfo pci_vpb_info = { + .name = "versatile_pci", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PCIVPBState), + .class_init = pci_vpb_class_init, +}; + +static void pci_realview_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = pci_realview_init; +} + +static const TypeInfo pci_realview_info = { + .name = "realview_pci", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PCIVPBState), + .class_init = pci_realview_class_init, +}; + +static void versatile_pci_register_types(void) +{ + type_register_static(&pci_vpb_info); + type_register_static(&pci_realview_info); + type_register_static(&versatile_pci_host_info); +} + +type_init(versatile_pci_register_types) diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index a1511abf00..a7fb9d0c11 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -8,6 +8,4 @@ common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o common-obj-$(CONFIG_NO_PCI) += pci-stub.o common-obj-$(CONFIG_ALL) += pci-stub.o -common-obj-$(CONFIG_PCI) += bridge/ host/ - obj-$(CONFIG_PCI_HOTPLUG) += pci-hotplug.o diff --git a/hw/pci/bridge/Makefile.objs b/hw/pci/bridge/Makefile.objs deleted file mode 100644 index 5dd92d28a0..0000000000 --- a/hw/pci/bridge/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -common-obj-y += pci_bridge_dev.o -common-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o -common-obj-y += i82801b11.o diff --git a/hw/pci/bridge/i82801b11.c b/hw/pci/bridge/i82801b11.c deleted file mode 100644 index 5807a92d7f..0000000000 --- a/hw/pci/bridge/i82801b11.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* - * QEMU i82801b11 dmi-to-pci Bridge Emulation - * - * Copyright (c) 2009, 2010, 2011 - * Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see - */ - -#include "hw/pci/pci.h" -#include "hw/i386/ich9.h" - - -/*****************************************************************************/ -/* ICH9 DMI-to-PCI bridge */ -#define I82801ba_SSVID_OFFSET 0x50 -#define I82801ba_SSVID_SVID 0 -#define I82801ba_SSVID_SSID 0 - -typedef struct I82801b11Bridge { - PCIBridge br; -} I82801b11Bridge; - -static int i82801b11_bridge_initfn(PCIDevice *d) -{ - int rc; - - rc = pci_bridge_initfn(d, TYPE_PCI_BUS); - if (rc < 0) { - return rc; - } - - rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET, - I82801ba_SSVID_SVID, I82801ba_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB); - return 0; - -err_bridge: - pci_bridge_exitfn(d); - - return rc; -} - -static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_bridge = 1; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11; - k->revision = ICH9_D2P_A2_REVISION; - k->init = i82801b11_bridge_initfn; -} - -static const TypeInfo i82801b11_bridge_info = { - .name = "i82801b11-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(I82801b11Bridge), - .class_init = i82801b11_bridge_class_init, -}; - -PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus) -{ - PCIDevice *d; - PCIBridge *br; - char buf[16]; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, true, "i82801b11-bridge"); - if (!d) { - return NULL; - } - br = DO_UPCAST(PCIBridge, dev, d); - qdev = &br->dev.qdev; - - snprintf(buf, sizeof(buf), "pci.%d", sec_bus); - pci_bridge_map_irq(br, buf, pci_swizzle_map_irq_fn); - qdev_init_nofail(qdev); - - return pci_bridge_get_sec_bus(br); -} - -static void d2pbr_register(void) -{ - type_register_static(&i82801b11_bridge_info); -} - -type_init(d2pbr_register); diff --git a/hw/pci/bridge/ioh3420.c b/hw/pci/bridge/ioh3420.c deleted file mode 100644 index 5cff61e095..0000000000 --- a/hw/pci/bridge/ioh3420.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * ioh3420.c - * Intel X58 north bridge IOH - * PCI Express root port device id 3420 - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/pcie.h" -#include "hw/ioh3420.h" - -#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ -#define PCI_DEVICE_ID_IOH_REV 0x2 -#define IOH_EP_SSVID_OFFSET 0x40 -#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL -#define IOH_EP_SSVID_SSID 0 -#define IOH_EP_MSI_OFFSET 0x60 -#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT -#define IOH_EP_MSI_NR_VECTOR 2 -#define IOH_EP_EXP_OFFSET 0x90 -#define IOH_EP_AER_OFFSET 0x100 - -/* - * If two MSI vector are allocated, Advanced Error Interrupt Message Number - * is 1. otherwise 0. - * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number. - */ -static uint8_t ioh3420_aer_vector(const PCIDevice *d) -{ - switch (msi_nr_vectors_allocated(d)) { - case 1: - return 0; - case 2: - return 1; - case 4: - case 8: - case 16: - case 32: - default: - break; - } - abort(); - return 0; -} - -static void ioh3420_aer_vector_update(PCIDevice *d) -{ - pcie_aer_root_set_vector(d, ioh3420_aer_vector(d)); -} - -static void ioh3420_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - uint32_t root_cmd = - pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); - - pci_bridge_write_config(d, address, val, len); - ioh3420_aer_vector_update(d); - pcie_cap_slot_write_config(d, address, val, len); - pcie_aer_write_config(d, address, val, len); - pcie_aer_root_write_config(d, address, val, len, root_cmd); -} - -static void ioh3420_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - - ioh3420_aer_vector_update(d); - pcie_cap_root_reset(d); - pcie_cap_deverr_reset(d); - pcie_cap_slot_reset(d); - pcie_aer_root_reset(d); - pci_bridge_reset(qdev); - pci_bridge_disable_base_limit(d); -} - -static int ioh3420_initfn(PCIDevice *d) -{ - PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); - PCIEPort *p = DO_UPCAST(PCIEPort, br, br); - PCIESlot *s = DO_UPCAST(PCIESlot, port, p); - int rc; - - rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); - if (rc < 0) { - return rc; - } - - pcie_port_init_reg(d); - - rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET, - IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, - IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); - if (rc < 0) { - goto err_bridge; - } - rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port); - if (rc < 0) { - goto err_msi; - } - pcie_cap_deverr_init(d); - pcie_cap_slot_init(d, s->slot); - pcie_chassis_create(s->chassis); - rc = pcie_chassis_add_slot(s); - if (rc < 0) { - goto err_pcie_cap; - } - pcie_cap_root_init(d); - rc = pcie_aer_init(d, IOH_EP_AER_OFFSET); - if (rc < 0) { - goto err; - } - pcie_aer_root_init(d); - ioh3420_aer_vector_update(d); - return 0; - -err: - pcie_chassis_del_slot(s); -err_pcie_cap: - pcie_cap_exit(d); -err_msi: - msi_uninit(d); -err_bridge: - pci_bridge_exitfn(d); - return rc; -} - -static void ioh3420_exitfn(PCIDevice *d) -{ - PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); - PCIEPort *p = DO_UPCAST(PCIEPort, br, br); - PCIESlot *s = DO_UPCAST(PCIESlot, port, p); - - pcie_aer_exit(d); - pcie_chassis_del_slot(s); - pcie_cap_exit(d); - msi_uninit(d); - pci_bridge_exitfn(d); -} - -PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port, uint8_t chassis, uint16_t slot) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420"); - if (!d) { - return NULL; - } - br = DO_UPCAST(PCIBridge, dev, d); - - qdev = &br->dev.qdev; - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_prop_set_uint8(qdev, "chassis", chassis); - qdev_prop_set_uint16(qdev, "slot", slot); - qdev_init_nofail(qdev); - - return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br)); -} - -static const VMStateDescription vmstate_ioh3420 = { - .name = "ioh-3240-express-root-port", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot), - VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0, - vmstate_pcie_aer_log, PCIEAERLog), - VMSTATE_END_OF_LIST() - } -}; - -static Property ioh3420_properties[] = { - DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), - DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), - DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), - DEFINE_PROP_UINT16("aer_log_max", PCIESlot, - port.br.dev.exp.aer_log.log_max, - PCIE_AER_LOG_MAX_DEFAULT), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ioh3420_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_express = 1; - k->is_bridge = 1; - k->config_write = ioh3420_write_config; - k->init = ioh3420_initfn; - k->exit = ioh3420_exitfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_IOH_EPORT; - k->revision = PCI_DEVICE_ID_IOH_REV; - dc->desc = "Intel IOH device id 3420 PCIE Root Port"; - dc->reset = ioh3420_reset; - dc->vmsd = &vmstate_ioh3420; - dc->props = ioh3420_properties; -} - -static const TypeInfo ioh3420_info = { - .name = "ioh3420", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIESlot), - .class_init = ioh3420_class_init, -}; - -static void ioh3420_register_types(void) -{ - type_register_static(&ioh3420_info); -} - -type_init(ioh3420_register_types) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci/bridge/pci_bridge_dev.c b/hw/pci/bridge/pci_bridge_dev.c deleted file mode 100644 index 971b432474..0000000000 --- a/hw/pci/bridge/pci_bridge_dev.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Standard PCI Bridge Device - * - * Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin - * - * http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/shpc.h" -#include "hw/pci/slotid_cap.h" -#include "exec/memory.h" -#include "hw/pci/pci_bus.h" - -struct PCIBridgeDev { - PCIBridge bridge; - MemoryRegion bar; - uint8_t chassis_nr; -#define PCI_BRIDGE_DEV_F_MSI_REQ 0 - uint32_t flags; -}; -typedef struct PCIBridgeDev PCIBridgeDev; - -static int pci_bridge_dev_initfn(PCIDevice *dev) -{ - PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); - PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br); - int err; - - err = pci_bridge_initfn(dev, TYPE_PCI_BUS); - if (err) { - goto bridge_error; - } - memory_region_init(&bridge_dev->bar, "shpc-bar", shpc_bar_size(dev)); - err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); - if (err) { - goto shpc_error; - } - err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); - if (err) { - goto slotid_error; - } - if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && - msi_supported) { - err = msi_init(dev, 0, 1, true, true); - if (err < 0) { - goto msi_error; - } - } - /* TODO: spec recommends using 64 bit prefetcheable BAR. - * Check whether that works well. */ - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); - dev->config[PCI_INTERRUPT_PIN] = 0x1; - return 0; -msi_error: - slotid_cap_cleanup(dev); -slotid_error: - shpc_cleanup(dev, &bridge_dev->bar); -shpc_error: - memory_region_destroy(&bridge_dev->bar); - pci_bridge_exitfn(dev); -bridge_error: - return err; -} - -static void pci_bridge_dev_exitfn(PCIDevice *dev) -{ - PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); - PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br); - if (msi_present(dev)) { - msi_uninit(dev); - } - slotid_cap_cleanup(dev); - shpc_cleanup(dev, &bridge_dev->bar); - memory_region_destroy(&bridge_dev->bar); - pci_bridge_exitfn(dev); -} - -static void pci_bridge_dev_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_bridge_write_config(d, address, val, len); - if (msi_present(d)) { - msi_write_config(d, address, val, len); - } - shpc_cap_write_config(d, address, val, len); -} - -static void qdev_pci_bridge_dev_reset(DeviceState *qdev) -{ - PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); - - pci_bridge_reset(qdev); - shpc_reset(dev); -} - -static Property pci_bridge_dev_properties[] = { - /* Note: 0 is not a legal chassis number. */ - DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0), - DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription pci_bridge_dev_vmstate = { - .name = "pci_bridge", - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(bridge.dev, PCIBridgeDev), - SHPC_VMSTATE(bridge.dev.shpc, PCIBridgeDev), - VMSTATE_END_OF_LIST() - } -}; - -static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pci_bridge_dev_initfn; - k->exit = pci_bridge_dev_exitfn; - k->config_write = pci_bridge_dev_write_config; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE; - k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = 1, - dc->desc = "Standard PCI Bridge"; - dc->reset = qdev_pci_bridge_dev_reset; - dc->props = pci_bridge_dev_properties; - dc->vmsd = &pci_bridge_dev_vmstate; -} - -static const TypeInfo pci_bridge_dev_info = { - .name = "pci-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIBridgeDev), - .class_init = pci_bridge_dev_class_init, -}; - -static void pci_bridge_dev_register(void) -{ - type_register_static(&pci_bridge_dev_info); -} - -type_init(pci_bridge_dev_register); diff --git a/hw/pci/bridge/xio3130_downstream.c b/hw/pci/bridge/xio3130_downstream.c deleted file mode 100644 index b868f56ff9..0000000000 --- a/hw/pci/bridge/xio3130_downstream.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * x3130_downstream.c - * TI X3130 pci express downstream port switch - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/pcie.h" -#include "hw/xio3130_downstream.h" - -#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ -#define XIO3130_REVISION 0x1 -#define XIO3130_MSI_OFFSET 0x70 -#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT -#define XIO3130_MSI_NR_VECTOR 1 -#define XIO3130_SSVID_OFFSET 0x80 -#define XIO3130_SSVID_SVID 0 -#define XIO3130_SSVID_SSID 0 -#define XIO3130_EXP_OFFSET 0x90 -#define XIO3130_AER_OFFSET 0x100 - -static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address, - uint32_t val, int len) -{ - pci_bridge_write_config(d, address, val, len); - pcie_cap_flr_write_config(d, address, val, len); - pcie_cap_slot_write_config(d, address, val, len); - pcie_aer_write_config(d, address, val, len); -} - -static void xio3130_downstream_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - - pcie_cap_deverr_reset(d); - pcie_cap_slot_reset(d); - pcie_cap_ari_reset(d); - pci_bridge_reset(qdev); -} - -static int xio3130_downstream_initfn(PCIDevice *d) -{ - PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); - PCIEPort *p = DO_UPCAST(PCIEPort, br, br); - PCIESlot *s = DO_UPCAST(PCIESlot, port, p); - int rc; - - rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); - if (rc < 0) { - return rc; - } - - pcie_port_init_reg(d); - - rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); - if (rc < 0) { - goto err_bridge; - } - rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, - XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, - p->port); - if (rc < 0) { - goto err_msi; - } - pcie_cap_flr_init(d); - pcie_cap_deverr_init(d); - pcie_cap_slot_init(d, s->slot); - pcie_chassis_create(s->chassis); - rc = pcie_chassis_add_slot(s); - if (rc < 0) { - goto err_pcie_cap; - } - pcie_cap_ari_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET); - if (rc < 0) { - goto err; - } - - return 0; - -err: - pcie_chassis_del_slot(s); -err_pcie_cap: - pcie_cap_exit(d); -err_msi: - msi_uninit(d); -err_bridge: - pci_bridge_exitfn(d); - return rc; -} - -static void xio3130_downstream_exitfn(PCIDevice *d) -{ - PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); - PCIEPort *p = DO_UPCAST(PCIEPort, br, br); - PCIESlot *s = DO_UPCAST(PCIESlot, port, p); - - pcie_aer_exit(d); - pcie_chassis_del_slot(s); - pcie_cap_exit(d); - msi_uninit(d); - pci_bridge_exitfn(d); -} - -PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port, uint8_t chassis, - uint16_t slot) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, - "xio3130-downstream"); - if (!d) { - return NULL; - } - br = DO_UPCAST(PCIBridge, dev, d); - - qdev = &br->dev.qdev; - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_prop_set_uint8(qdev, "chassis", chassis); - qdev_prop_set_uint16(qdev, "slot", slot); - qdev_init_nofail(qdev); - - return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br)); -} - -static const VMStateDescription vmstate_xio3130_downstream = { - .name = "xio3130-express-downstream-port", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot), - VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0, - vmstate_pcie_aer_log, PCIEAERLog), - VMSTATE_END_OF_LIST() - } -}; - -static Property xio3130_downstream_properties[] = { - DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), - DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), - DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), - DEFINE_PROP_UINT16("aer_log_max", PCIESlot, - port.br.dev.exp.aer_log.log_max, - PCIE_AER_LOG_MAX_DEFAULT), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xio3130_downstream_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_express = 1; - k->is_bridge = 1; - k->config_write = xio3130_downstream_write_config; - k->init = xio3130_downstream_initfn; - k->exit = xio3130_downstream_exitfn; - k->vendor_id = PCI_VENDOR_ID_TI; - k->device_id = PCI_DEVICE_ID_TI_XIO3130D; - k->revision = XIO3130_REVISION; - dc->desc = "TI X3130 Downstream Port of PCI Express Switch"; - dc->reset = xio3130_downstream_reset; - dc->vmsd = &vmstate_xio3130_downstream; - dc->props = xio3130_downstream_properties; -} - -static const TypeInfo xio3130_downstream_info = { - .name = "xio3130-downstream", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIESlot), - .class_init = xio3130_downstream_class_init, -}; - -static void xio3130_downstream_register_types(void) -{ - type_register_static(&xio3130_downstream_info); -} - -type_init(xio3130_downstream_register_types) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci/bridge/xio3130_upstream.c b/hw/pci/bridge/xio3130_upstream.c deleted file mode 100644 index cd5d97d211..0000000000 --- a/hw/pci/bridge/xio3130_upstream.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * xio3130_upstream.c - * TI X3130 pci express upstream port switch - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/pcie.h" -#include "hw/xio3130_upstream.h" - -#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ -#define XIO3130_REVISION 0x2 -#define XIO3130_MSI_OFFSET 0x70 -#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT -#define XIO3130_MSI_NR_VECTOR 1 -#define XIO3130_SSVID_OFFSET 0x80 -#define XIO3130_SSVID_SVID 0 -#define XIO3130_SSVID_SSID 0 -#define XIO3130_EXP_OFFSET 0x90 -#define XIO3130_AER_OFFSET 0x100 - -static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address, - uint32_t val, int len) -{ - pci_bridge_write_config(d, address, val, len); - pcie_cap_flr_write_config(d, address, val, len); - pcie_aer_write_config(d, address, val, len); -} - -static void xio3130_upstream_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - - pci_bridge_reset(qdev); - pcie_cap_deverr_reset(d); -} - -static int xio3130_upstream_initfn(PCIDevice *d) -{ - PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); - PCIEPort *p = DO_UPCAST(PCIEPort, br, br); - int rc; - - rc = pci_bridge_initfn(d, TYPE_PCIE_BUS); - if (rc < 0) { - return rc; - } - - pcie_port_init_reg(d); - - rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); - if (rc < 0) { - goto err_bridge; - } - rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, - XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, - p->port); - if (rc < 0) { - goto err_msi; - } - pcie_cap_flr_init(d); - pcie_cap_deverr_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET); - if (rc < 0) { - goto err; - } - - return 0; - -err: - pcie_cap_exit(d); -err_msi: - msi_uninit(d); -err_bridge: - pci_bridge_exitfn(d); - return rc; -} - -static void xio3130_upstream_exitfn(PCIDevice *d) -{ - pcie_aer_exit(d); - pcie_cap_exit(d); - msi_uninit(d); - pci_bridge_exitfn(d); -} - -PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream"); - if (!d) { - return NULL; - } - br = DO_UPCAST(PCIBridge, dev, d); - - qdev = &br->dev.qdev; - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_init_nofail(qdev); - - return DO_UPCAST(PCIEPort, br, br); -} - -static const VMStateDescription vmstate_xio3130_upstream = { - .name = "xio3130-express-upstream-port", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(br.dev, PCIEPort), - VMSTATE_STRUCT(br.dev.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log, - PCIEAERLog), - VMSTATE_END_OF_LIST() - } -}; - -static Property xio3130_upstream_properties[] = { - DEFINE_PROP_UINT8("port", PCIEPort, port, 0), - DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max, - PCIE_AER_LOG_MAX_DEFAULT), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xio3130_upstream_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_express = 1; - k->is_bridge = 1; - k->config_write = xio3130_upstream_write_config; - k->init = xio3130_upstream_initfn; - k->exit = xio3130_upstream_exitfn; - k->vendor_id = PCI_VENDOR_ID_TI; - k->device_id = PCI_DEVICE_ID_TI_XIO3130U; - k->revision = XIO3130_REVISION; - dc->desc = "TI X3130 Upstream Port of PCI Express Switch"; - dc->reset = xio3130_upstream_reset; - dc->vmsd = &vmstate_xio3130_upstream; - dc->props = xio3130_upstream_properties; -} - -static const TypeInfo xio3130_upstream_info = { - .name = "x3130-upstream", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIEPort), - .class_init = xio3130_upstream_class_init, -}; - -static void xio3130_upstream_register_types(void) -{ - type_register_static(&xio3130_upstream_info); -} - -type_init(xio3130_upstream_register_types) - - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci/host/Makefile.objs b/hw/pci/host/Makefile.objs deleted file mode 100644 index e1d6cce047..0000000000 --- a/hw/pci/host/Makefile.objs +++ /dev/null @@ -1,13 +0,0 @@ -common-obj-y += pam.o - -# PPC devices -common-obj-$(CONFIG_PREP_PCI) += prep.o -common-obj-$(CONFIG_GRACKLE_PCI) += grackle.o -# NewWorld PowerMac -common-obj-$(CONFIG_UNIN_PCI) += uninorth.o -common-obj-$(CONFIG_DEC_PCI) += dec.o -# PowerPC E500 boards -common-obj-$(CONFIG_PPCE500_PCI) += ppce500.o - -# ARM devices -common-obj-$(CONFIG_VERSATILE_PCI) += versatile.o diff --git a/hw/pci/host/dec.c b/hw/pci/host/dec.c deleted file mode 100644 index 6ec3d226bd..0000000000 --- a/hw/pci/host/dec.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * QEMU DEC 21154 PCI bridge - * - * Copyright (c) 2006-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/dec_pci.h" -#include "hw/sysbus.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" - -/* debug DEC */ -//#define DEBUG_DEC - -#ifdef DEBUG_DEC -#define DEC_DPRINTF(fmt, ...) \ - do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DEC_DPRINTF(fmt, ...) -#endif - -#define DEC_21154(obj) OBJECT_CHECK(DECState, (obj), TYPE_DEC_21154) - -typedef struct DECState { - PCIHostState parent_obj; -} DECState; - -static int dec_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return irq_num; -} - -static int dec_pci_bridge_initfn(PCIDevice *pci_dev) -{ - return pci_bridge_initfn(pci_dev, TYPE_PCI_BUS); -} - -static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = dec_pci_bridge_initfn; - k->exit = pci_bridge_exitfn; - k->vendor_id = PCI_VENDOR_ID_DEC; - k->device_id = PCI_DEVICE_ID_DEC_21154; - k->config_write = pci_bridge_write_config; - k->is_bridge = 1; - dc->desc = "DEC 21154 PCI-PCI bridge"; - dc->reset = pci_bridge_reset; - dc->vmsd = &vmstate_pci_device; -} - -static const TypeInfo dec_21154_pci_bridge_info = { - .name = "dec-21154-p2p-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIBridge), - .class_init = dec_21154_pci_bridge_class_init, -}; - -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) -{ - PCIDevice *dev; - PCIBridge *br; - - dev = pci_create_multifunction(parent_bus, devfn, false, - "dec-21154-p2p-bridge"); - br = DO_UPCAST(PCIBridge, dev, dev); - pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq); - qdev_init_nofail(&dev->qdev); - return pci_bridge_get_sec_bus(br); -} - -static int pci_dec_21154_device_init(SysBusDevice *dev) -{ - PCIHostState *phb; - - phb = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); - return 0; -} - -static int dec_21154_pci_host_init(PCIDevice *d) -{ - /* PCI2PCI bridge same values as PearPC - check this */ - return 0; -} - -static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = dec_21154_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_DEC; - k->device_id = PCI_DEVICE_ID_DEC_21154; - k->revision = 0x02; - k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = 1; -} - -static const TypeInfo dec_21154_pci_host_info = { - .name = "dec-21154", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = dec_21154_pci_host_class_init, -}; - -static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = pci_dec_21154_device_init; -} - -static const TypeInfo pci_dec_21154_device_info = { - .name = TYPE_DEC_21154, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(DECState), - .class_init = pci_dec_21154_device_class_init, -}; - -static void dec_register_types(void) -{ - type_register_static(&pci_dec_21154_device_info); - type_register_static(&dec_21154_pci_host_info); - type_register_static(&dec_21154_pci_bridge_info); -} - -type_init(dec_register_types) diff --git a/hw/pci/host/grackle.c b/hw/pci/host/grackle.c deleted file mode 100644 index 69344d9f6a..0000000000 --- a/hw/pci/host/grackle.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * QEMU Grackle PCI host (heathrow OldWorld PowerMac) - * - * Copyright (c) 2006-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/pci/pci_host.h" -#include "hw/ppc/mac.h" -#include "hw/pci/pci.h" - -/* debug Grackle */ -//#define DEBUG_GRACKLE - -#ifdef DEBUG_GRACKLE -#define GRACKLE_DPRINTF(fmt, ...) \ - do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0) -#else -#define GRACKLE_DPRINTF(fmt, ...) -#endif - -#define GRACKLE_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE) - -typedef struct GrackleState { - PCIHostState parent_obj; - - MemoryRegion pci_mmio; - MemoryRegion pci_hole; -} GrackleState; - -/* Don't know if this matches real hardware, but it agrees with OHW. */ -static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return (irq_num + (pci_dev->devfn >> 3)) & 3; -} - -static void pci_grackle_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level); - qemu_set_irq(pic[irq_num + 0x15], level); -} - -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *phb; - GrackleState *d; - - dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - phb = PCI_HOST_BRIDGE(dev); - d = GRACKLE_PCI_HOST_BRIDGE(dev); - - memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x7e000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - phb->bus = pci_register_bus(dev, "pci", - pci_grackle_set_irq, - pci_grackle_map_irq, - pic, - &d->pci_mmio, - address_space_io, - 0, 4, TYPE_PCI_BUS); - - pci_create_simple(phb->bus, 0, "grackle"); - - sysbus_mmio_map(s, 0, base); - sysbus_mmio_map(s, 1, base + 0x00200000); - - return phb->bus; -} - -static int pci_grackle_init_device(SysBusDevice *dev) -{ - PCIHostState *phb; - - phb = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); - - return 0; -} - -static int grackle_pci_host_init(PCIDevice *d) -{ - d->config[0x09] = 0x01; - return 0; -} - -static void grackle_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = grackle_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_MOTOROLA; - k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->no_user = 1; -} - -static const TypeInfo grackle_pci_info = { - .name = "grackle", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = grackle_pci_class_init, -}; - -static void pci_grackle_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = pci_grackle_init_device; - dc->no_user = 1; -} - -static const TypeInfo grackle_pci_host_info = { - .name = TYPE_GRACKLE_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(GrackleState), - .class_init = pci_grackle_class_init, -}; - -static void grackle_register_types(void) -{ - type_register_static(&grackle_pci_info); - type_register_static(&grackle_pci_host_info); -} - -type_init(grackle_register_types) diff --git a/hw/pci/host/pam.c b/hw/pci/host/pam.c deleted file mode 100644 index 7181bd68eb..0000000000 --- a/hw/pci/host/pam.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * QEMU i440FX/PIIX3 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2011 Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (c) 2012 Jason Baron - * - * Split out from piix_pci.c - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "sysemu/sysemu.h" -#include "hw/pci-host/pam.h" - -void smram_update(MemoryRegion *smram_region, uint8_t smram, - uint8_t smm_enabled) -{ - bool smram_enabled; - - smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) || - (smram & SMRAM_D_OPEN)); - memory_region_set_enabled(smram_region, !smram_enabled); -} - -void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram, - MemoryRegion *smram_region) -{ - uint8_t smm_enabled = (smm != 0); - if (*host_smm_enabled != smm_enabled) { - *host_smm_enabled = smm_enabled; - smram_update(smram_region, smram, *host_smm_enabled); - } -} - -void init_pam(MemoryRegion *ram_memory, MemoryRegion *system_memory, - MemoryRegion *pci_address_space, PAMMemoryRegion *mem, - uint32_t start, uint32_t size) -{ - int i; - - /* RAM */ - memory_region_init_alias(&mem->alias[3], "pam-ram", ram_memory, - start, size); - /* ROM (XXX: not quite correct) */ - memory_region_init_alias(&mem->alias[1], "pam-rom", ram_memory, - start, size); - memory_region_set_readonly(&mem->alias[1], true); - - /* XXX: should distinguish read/write cases */ - memory_region_init_alias(&mem->alias[0], "pam-pci", pci_address_space, - start, size); - memory_region_init_alias(&mem->alias[2], "pam-pci", pci_address_space, - start, size); - - for (i = 0; i < 4; ++i) { - memory_region_set_enabled(&mem->alias[i], false); - memory_region_add_subregion_overlap(system_memory, start, - &mem->alias[i], 1); - } - mem->current = 0; -} - -void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val) -{ - assert(0 <= idx && idx <= 12); - - memory_region_set_enabled(&pam->alias[pam->current], false); - pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK; - memory_region_set_enabled(&pam->alias[pam->current], true); -} diff --git a/hw/pci/host/ppce500.c b/hw/pci/host/ppce500.c deleted file mode 100644 index 5e7ad94388..0000000000 --- a/hw/pci/host/ppce500.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * QEMU PowerPC E500 embedded processors pci controller emulation - * - * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, - * - * This file is derived from hw/ppc4xx_pci.c, - * the copyright for that material belongs to the original owners. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include "hw/hw.h" -#include "hw/ppc/e500-ccsr.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "qemu/bswap.h" -#include "hw/pci-host/ppce500.h" - -#ifdef DEBUG_PCI -#define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) -#else -#define pci_debug(fmt, ...) -#endif - -#define PCIE500_CFGADDR 0x0 -#define PCIE500_CFGDATA 0x4 -#define PCIE500_REG_BASE 0xC00 -#define PCIE500_ALL_SIZE 0x1000 -#define PCIE500_REG_SIZE (PCIE500_ALL_SIZE - PCIE500_REG_BASE) - -#define PCIE500_PCI_IOLEN 0x10000ULL - -#define PPCE500_PCI_CONFIG_ADDR 0x0 -#define PPCE500_PCI_CONFIG_DATA 0x4 -#define PPCE500_PCI_INTACK 0x8 - -#define PPCE500_PCI_OW1 (0xC20 - PCIE500_REG_BASE) -#define PPCE500_PCI_OW2 (0xC40 - PCIE500_REG_BASE) -#define PPCE500_PCI_OW3 (0xC60 - PCIE500_REG_BASE) -#define PPCE500_PCI_OW4 (0xC80 - PCIE500_REG_BASE) -#define PPCE500_PCI_IW3 (0xDA0 - PCIE500_REG_BASE) -#define PPCE500_PCI_IW2 (0xDC0 - PCIE500_REG_BASE) -#define PPCE500_PCI_IW1 (0xDE0 - PCIE500_REG_BASE) - -#define PPCE500_PCI_GASKET_TIMR (0xE20 - PCIE500_REG_BASE) - -#define PCI_POTAR 0x0 -#define PCI_POTEAR 0x4 -#define PCI_POWBAR 0x8 -#define PCI_POWAR 0x10 - -#define PCI_PITAR 0x0 -#define PCI_PIWBAR 0x8 -#define PCI_PIWBEAR 0xC -#define PCI_PIWAR 0x10 - -#define PPCE500_PCI_NR_POBS 5 -#define PPCE500_PCI_NR_PIBS 3 - -struct pci_outbound { - uint32_t potar; - uint32_t potear; - uint32_t powbar; - uint32_t powar; -}; - -struct pci_inbound { - uint32_t pitar; - uint32_t piwbar; - uint32_t piwbear; - uint32_t piwar; -}; - -#define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost" - -#define PPC_E500_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PPCE500PCIState, (obj), TYPE_PPC_E500_PCI_HOST_BRIDGE) - -struct PPCE500PCIState { - PCIHostState parent_obj; - - struct pci_outbound pob[PPCE500_PCI_NR_POBS]; - struct pci_inbound pib[PPCE500_PCI_NR_PIBS]; - uint32_t gasket_time; - qemu_irq irq[4]; - uint32_t first_slot; - /* mmio maps */ - MemoryRegion container; - MemoryRegion iomem; - MemoryRegion pio; -}; - -#define TYPE_PPC_E500_PCI_BRIDGE "e500-host-bridge" -#define PPC_E500_PCI_BRIDGE(obj) \ - OBJECT_CHECK(PPCE500PCIBridgeState, (obj), TYPE_PPC_E500_PCI_BRIDGE) - -struct PPCE500PCIBridgeState { - /*< private >*/ - PCIDevice parent; - /*< public >*/ - - MemoryRegion bar0; -}; - -typedef struct PPCE500PCIBridgeState PPCE500PCIBridgeState; -typedef struct PPCE500PCIState PPCE500PCIState; - -static uint64_t pci_reg_read4(void *opaque, hwaddr addr, - unsigned size) -{ - PPCE500PCIState *pci = opaque; - unsigned long win; - uint32_t value = 0; - int idx; - - win = addr & 0xfe0; - - switch (win) { - case PPCE500_PCI_OW1: - case PPCE500_PCI_OW2: - case PPCE500_PCI_OW3: - case PPCE500_PCI_OW4: - idx = (addr >> 5) & 0x7; - switch (addr & 0xC) { - case PCI_POTAR: - value = pci->pob[idx].potar; - break; - case PCI_POTEAR: - value = pci->pob[idx].potear; - break; - case PCI_POWBAR: - value = pci->pob[idx].powbar; - break; - case PCI_POWAR: - value = pci->pob[idx].powar; - break; - default: - break; - } - break; - - case PPCE500_PCI_IW3: - case PPCE500_PCI_IW2: - case PPCE500_PCI_IW1: - idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0xC) { - case PCI_PITAR: - value = pci->pib[idx].pitar; - break; - case PCI_PIWBAR: - value = pci->pib[idx].piwbar; - break; - case PCI_PIWBEAR: - value = pci->pib[idx].piwbear; - break; - case PCI_PIWAR: - value = pci->pib[idx].piwar; - break; - default: - break; - }; - break; - - case PPCE500_PCI_GASKET_TIMR: - value = pci->gasket_time; - break; - - default: - break; - } - - pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__, - win, addr, value); - return value; -} - -static void pci_reg_write4(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PPCE500PCIState *pci = opaque; - unsigned long win; - int idx; - - win = addr & 0xfe0; - - pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n", - __func__, (unsigned)value, win, addr); - - switch (win) { - case PPCE500_PCI_OW1: - case PPCE500_PCI_OW2: - case PPCE500_PCI_OW3: - case PPCE500_PCI_OW4: - idx = (addr >> 5) & 0x7; - switch (addr & 0xC) { - case PCI_POTAR: - pci->pob[idx].potar = value; - break; - case PCI_POTEAR: - pci->pob[idx].potear = value; - break; - case PCI_POWBAR: - pci->pob[idx].powbar = value; - break; - case PCI_POWAR: - pci->pob[idx].powar = value; - break; - default: - break; - }; - break; - - case PPCE500_PCI_IW3: - case PPCE500_PCI_IW2: - case PPCE500_PCI_IW1: - idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0xC) { - case PCI_PITAR: - pci->pib[idx].pitar = value; - break; - case PCI_PIWBAR: - pci->pib[idx].piwbar = value; - break; - case PCI_PIWBEAR: - pci->pib[idx].piwbear = value; - break; - case PCI_PIWAR: - pci->pib[idx].piwar = value; - break; - default: - break; - }; - break; - - case PPCE500_PCI_GASKET_TIMR: - pci->gasket_time = value; - break; - - default: - break; - }; -} - -static const MemoryRegionOps e500_pci_reg_ops = { - .read = pci_reg_read4, - .write = pci_reg_write4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int devno = pci_dev->devfn >> 3; - int ret; - - ret = ppce500_pci_map_irq_slot(devno, irq_num); - - pci_debug("%s: devfn %x irq %d -> %d devno:%x\n", __func__, - pci_dev->devfn, irq_num, ret, devno); - - return ret; -} - -static void mpc85xx_pci_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level); - - qemu_set_irq(pic[irq_num], level); -} - -static const VMStateDescription vmstate_pci_outbound = { - .name = "pci_outbound", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(potar, struct pci_outbound), - VMSTATE_UINT32(potear, struct pci_outbound), - VMSTATE_UINT32(powbar, struct pci_outbound), - VMSTATE_UINT32(powar, struct pci_outbound), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_inbound = { - .name = "pci_inbound", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(pitar, struct pci_inbound), - VMSTATE_UINT32(piwbar, struct pci_inbound), - VMSTATE_UINT32(piwbear, struct pci_inbound), - VMSTATE_UINT32(piwar, struct pci_inbound), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ppce500_pci = { - .name = "ppce500_pci", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1, - vmstate_pci_outbound, struct pci_outbound), - VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1, - vmstate_pci_outbound, struct pci_inbound), - VMSTATE_UINT32(gasket_time, PPCE500PCIState), - VMSTATE_END_OF_LIST() - } -}; - -#include "exec/address-spaces.h" - -static int e500_pcihost_bridge_initfn(PCIDevice *d) -{ - PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d); - PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(), - "/e500-ccsr")); - - pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI); - d->config[PCI_HEADER_TYPE] = - (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | - PCI_HEADER_TYPE_BRIDGE; - - memory_region_init_alias(&b->bar0, "e500-pci-bar0", &ccsr->ccsr_space, - 0, int128_get64(ccsr->ccsr_space.size)); - pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0); - - return 0; -} - -static int e500_pcihost_initfn(SysBusDevice *dev) -{ - PCIHostState *h; - PPCE500PCIState *s; - PCIBus *b; - int i; - MemoryRegion *address_space_mem = get_system_memory(); - - h = PCI_HOST_BRIDGE(dev); - s = PPC_E500_PCI_HOST_BRIDGE(dev); - - for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - - memory_region_init(&s->pio, "pci-pio", PCIE500_PCI_IOLEN); - - b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, s->irq, address_space_mem, - &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); - h->bus = b; - - pci_create_simple(b, 0, "e500-host-bridge"); - - memory_region_init(&s->container, "pci-container", PCIE500_ALL_SIZE); - memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, h, - "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, h, - "pci-conf-data", 4); - memory_region_init_io(&s->iomem, &e500_pci_reg_ops, s, - "pci.reg", PCIE500_REG_SIZE); - memory_region_add_subregion(&s->container, PCIE500_CFGADDR, &h->conf_mem); - memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem); - memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); - sysbus_init_mmio(dev, &s->pio); - - return 0; -} - -static void e500_host_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = e500_pcihost_bridge_initfn; - k->vendor_id = PCI_VENDOR_ID_FREESCALE; - k->device_id = PCI_DEVICE_ID_MPC8533E; - k->class_id = PCI_CLASS_PROCESSOR_POWERPC; - dc->desc = "Host bridge"; -} - -static const TypeInfo e500_host_bridge_info = { - .name = "e500-host-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PPCE500PCIBridgeState), - .class_init = e500_host_bridge_class_init, -}; - -static Property pcihost_properties[] = { - DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), - DEFINE_PROP_END_OF_LIST(), -}; - -static void e500_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = e500_pcihost_initfn; - dc->props = pcihost_properties; - dc->vmsd = &vmstate_ppce500_pci; -} - -static const TypeInfo e500_pcihost_info = { - .name = TYPE_PPC_E500_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PPCE500PCIState), - .class_init = e500_pcihost_class_init, -}; - -static void e500_pci_register_types(void) -{ - type_register_static(&e500_pcihost_info); - type_register_static(&e500_host_bridge_info); -} - -type_init(e500_pci_register_types) diff --git a/hw/pci/host/prep.c b/hw/pci/host/prep.c deleted file mode 100644 index 61302539ab..0000000000 --- a/hw/pci/host/prep.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * QEMU PREP PCI host - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2011-2013 Andreas Färber - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pci_host.h" -#include "hw/i386/pc.h" -#include "exec/address-spaces.h" - -#define TYPE_RAVEN_PCI_DEVICE "raven" -#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost" - -#define RAVEN_PCI_DEVICE(obj) \ - OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE) - -typedef struct RavenPCIState { - PCIDevice dev; -} RavenPCIState; - -#define RAVEN_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE) - -typedef struct PRePPCIState { - PCIHostState parent_obj; - - MemoryRegion intack; - qemu_irq irq[4]; - PCIBus pci_bus; - RavenPCIState pci_dev; -} PREPPCIState; - -static inline uint32_t PPC_PCIIO_config(hwaddr addr) -{ - int i; - - for (i = 0; i < 11; i++) { - if ((addr & (1 << (11 + i))) != 0) { - break; - } - } - return (addr & 0x7ff) | (i << 11); -} - -static void ppc_pci_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - PREPPCIState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - pci_data_write(phb->bus, PPC_PCIIO_config(addr), val, size); -} - -static uint64_t ppc_pci_io_read(void *opaque, hwaddr addr, - unsigned int size) -{ - PREPPCIState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - return pci_data_read(phb->bus, PPC_PCIIO_config(addr), size); -} - -static const MemoryRegionOps PPC_PCIIO_ops = { - .read = ppc_pci_io_read, - .write = ppc_pci_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t ppc_intack_read(void *opaque, hwaddr addr, - unsigned int size) -{ - return pic_read_irq(isa_pic); -} - -static const MemoryRegionOps PPC_intack_ops = { - .read = ppc_intack_read, - .valid = { - .max_access_size = 1, - }, -}; - -static int prep_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return (irq_num + (pci_dev->devfn >> 3)) & 1; -} - -static void prep_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - qemu_set_irq(pic[irq_num] , level); -} - -static void raven_pcihost_realizefn(DeviceState *d, Error **errp) -{ - SysBusDevice *dev = SYS_BUS_DEVICE(d); - PCIHostState *h = PCI_HOST_BRIDGE(dev); - PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev); - MemoryRegion *address_space_mem = get_system_memory(); - int i; - - for (i = 0; i < 4; i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - - pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4); - - memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, s, - "pci-conf-idx", 1); - sysbus_add_io(dev, 0xcf8, &h->conf_mem); - sysbus_init_ioports(&h->busdev, 0xcf8, 1); - - memory_region_init_io(&h->data_mem, &pci_host_data_be_ops, s, - "pci-conf-data", 1); - sysbus_add_io(dev, 0xcfc, &h->data_mem); - sysbus_init_ioports(&h->busdev, 0xcfc, 1); - - memory_region_init_io(&h->mmcfg, &PPC_PCIIO_ops, s, "pciio", 0x00400000); - memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg); - - memory_region_init_io(&s->intack, &PPC_intack_ops, s, "pci-intack", 1); - memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->intack); - - /* TODO Remove once realize propagates to child devices. */ - object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); -} - -static void raven_pcihost_initfn(Object *obj) -{ - PCIHostState *h = PCI_HOST_BRIDGE(obj); - PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj); - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *address_space_io = get_system_io(); - DeviceState *pci_dev; - - pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), NULL, - address_space_mem, address_space_io, 0, TYPE_PCI_BUS); - h->bus = &s->pci_bus; - - object_initialize(&s->pci_dev, TYPE_RAVEN_PCI_DEVICE); - pci_dev = DEVICE(&s->pci_dev); - qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus)); - object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr", - NULL); - qdev_prop_set_bit(pci_dev, "multifunction", false); -} - -static int raven_init(PCIDevice *d) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer - - return 0; -} - -static const VMStateDescription vmstate_raven = { - .name = "raven", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, RavenPCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static void raven_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = raven_init; - k->vendor_id = PCI_VENDOR_ID_MOTOROLA; - k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->desc = "PReP Host Bridge - Motorola Raven"; - dc->vmsd = &vmstate_raven; - dc->no_user = 1; -} - -static const TypeInfo raven_info = { - .name = TYPE_RAVEN_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(RavenPCIState), - .class_init = raven_class_init, -}; - -static void raven_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = raven_pcihost_realizefn; - dc->fw_name = "pci"; - dc->no_user = 1; -} - -static const TypeInfo raven_pcihost_info = { - .name = TYPE_RAVEN_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PREPPCIState), - .instance_init = raven_pcihost_initfn, - .class_init = raven_pcihost_class_init, -}; - -static void raven_register_types(void) -{ - type_register_static(&raven_pcihost_info); - type_register_static(&raven_info); -} - -type_init(raven_register_types) diff --git a/hw/pci/host/uninorth.c b/hw/pci/host/uninorth.c deleted file mode 100644 index fff235d523..0000000000 --- a/hw/pci/host/uninorth.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * QEMU Uninorth PCI host (for all Mac99 and newer machines) - * - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw/hw.h" -#include "hw/ppc/mac.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" - -/* debug UniNorth */ -//#define DEBUG_UNIN - -#ifdef DEBUG_UNIN -#define UNIN_DPRINTF(fmt, ...) \ - do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0) -#else -#define UNIN_DPRINTF(fmt, ...) -#endif - -static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; - -#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" -#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" -#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" -#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" - -#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) -#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) -#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) -#define U3_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) - -typedef struct UNINState { - PCIHostState parent_obj; - - MemoryRegion pci_mmio; - MemoryRegion pci_hole; -} UNINState; - -static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int retval; - int devfn = pci_dev->devfn & 0x00FFFFFF; - - retval = (((devfn >> 11) & 0x1F) + irq_num) & 3; - - return retval; -} - -static void pci_unin_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__, - unin_irq_line[irq_num], level); - qemu_set_irq(pic[unin_irq_line[irq_num]], level); -} - -static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) -{ - uint32_t retval; - - if (reg & (1u << 31)) { - /* XXX OpenBIOS compatibility hack */ - retval = reg | (addr & 3); - } else if (reg & 1) { - /* CFA1 style */ - retval = (reg & ~7u) | (addr & 7); - } else { - uint32_t slot, func; - - /* Grab CFA0 style values */ - slot = ffs(reg & 0xfffff800) - 1; - func = (reg >> 8) & 7; - - /* ... and then convert them to x86 format */ - /* config pointer */ - retval = (reg & (0xff - 7)) | (addr & 7); - /* slot */ - retval |= slot << 11; - /* fn */ - retval |= func << 8; - } - - - UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n", - reg, addr, retval); - - return retval; -} - -static void unin_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - UNINState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n", - addr, len, val); - pci_data_write(phb->bus, - unin_get_config_reg(phb->config_reg, addr), - val, len); -} - -static uint64_t unin_data_read(void *opaque, hwaddr addr, - unsigned len) -{ - UNINState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - uint32_t val; - - val = pci_data_read(phb->bus, - unin_get_config_reg(phb->config_reg, addr), - len); - UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n", - addr, len, val); - return val; -} - -static const MemoryRegionOps unin_data_ops = { - .read = unin_data_read, - .write = unin_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static int pci_unin_main_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, &unin_data_ops, dev, - "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; -} - - -static int pci_u3_agp_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth U3 AGP bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, &unin_data_ops, dev, - "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; -} - -static int pci_unin_agp_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth AGP bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; -} - -static int pci_unin_internal_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth internal bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; -} - -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *h; - UNINState *d; - - /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(s); - d = UNI_NORTH_PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x70000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - h->bus = pci_register_bus(dev, "pci", - pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - -#if 0 - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north"); -#endif - - sysbus_mmio_map(s, 0, 0xf2800000); - sysbus_mmio_map(s, 1, 0xf2c00000); - - /* DEC 21154 bridge */ -#if 0 - /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ - pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); -#endif - - /* Uninorth AGP bus */ - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); - dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - /* Uninorth internal bus */ -#if 0 - /* XXX: not needed for now */ - pci_create_simple(h->bus, PCI_DEVFN(14, 0), - "uni-north-internal-pci"); - dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf4800000); - sysbus_mmio_map(s, 1, 0xf4c00000); -#endif - - return h->bus; -} - -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *h; - UNINState *d; - - /* Uninorth AGP bus */ - - dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(dev); - d = U3_AGP_HOST_BRIDGE(dev); - - memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x70000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - h->bus = pci_register_bus(dev, "pci", - pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - pci_create_simple(h->bus, 11 << 3, "u3-agp"); - - return h->bus; -} - -static int unin_main_pci_host_init(PCIDevice *d) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer - return 0; -} - -static int unin_agp_pci_host_init(PCIDevice *d) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - // d->config[0x34] = 0x80; // capabilities_pointer - return 0; -} - -static int u3_agp_pci_host_init(PCIDevice *d) -{ - /* cache line size */ - d->config[0x0C] = 0x08; - /* latency timer */ - d->config[0x0D] = 0x10; - return 0; -} - -static int unin_internal_pci_host_init(PCIDevice *d) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer - return 0; -} - -static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = unin_main_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo unin_main_pci_host_info = { - .name = "uni-north-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = unin_main_pci_host_class_init, -}; - -static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = u3_agp_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_U3_AGP; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo u3_agp_pci_host_info = { - .name = "u3-agp", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = u3_agp_pci_host_class_init, -}; - -static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = unin_agp_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo unin_agp_pci_host_info = { - .name = "uni-north-agp", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = unin_agp_pci_host_class_init, -}; - -static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = unin_internal_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo unin_internal_pci_host_info = { - .name = "uni-north-internal-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = unin_internal_pci_host_class_init, -}; - -static void pci_unin_main_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - sbc->init = pci_unin_main_init_device; -} - -static const TypeInfo pci_unin_main_info = { - .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_unin_main_class_init, -}; - -static void pci_u3_agp_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - sbc->init = pci_u3_agp_init_device; -} - -static const TypeInfo pci_u3_agp_info = { - .name = TYPE_U3_AGP_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_u3_agp_class_init, -}; - -static void pci_unin_agp_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - sbc->init = pci_unin_agp_init_device; -} - -static const TypeInfo pci_unin_agp_info = { - .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_unin_agp_class_init, -}; - -static void pci_unin_internal_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - sbc->init = pci_unin_internal_init_device; -} - -static const TypeInfo pci_unin_internal_info = { - .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_unin_internal_class_init, -}; - -static void unin_register_types(void) -{ - type_register_static(&unin_main_pci_host_info); - type_register_static(&u3_agp_pci_host_info); - type_register_static(&unin_agp_pci_host_info); - type_register_static(&unin_internal_pci_host_info); - - type_register_static(&pci_unin_main_info); - type_register_static(&pci_u3_agp_info); - type_register_static(&pci_unin_agp_info); - type_register_static(&pci_unin_internal_info); -} - -type_init(unin_register_types) diff --git a/hw/pci/host/versatile.c b/hw/pci/host/versatile.c deleted file mode 100644 index d67ca796fb..0000000000 --- a/hw/pci/host/versatile.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * ARM Versatile/PB PCI host controller - * - * Copyright (c) 2006-2009 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -#include "hw/sysbus.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "exec/address-spaces.h" - -typedef struct { - SysBusDevice busdev; - qemu_irq irq[4]; - int realview; - MemoryRegion mem_config; - MemoryRegion mem_config2; - MemoryRegion isa; -} PCIVPBState; - -static inline uint32_t vpb_pci_config_addr(hwaddr addr) -{ - return addr & 0xffffff; -} - -static void pci_vpb_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - pci_data_write(opaque, vpb_pci_config_addr(addr), val, size); -} - -static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t val; - val = pci_data_read(opaque, vpb_pci_config_addr(addr), size); - return val; -} - -static const MemoryRegionOps pci_vpb_config_ops = { - .read = pci_vpb_config_read, - .write = pci_vpb_config_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pci_vpb_map_irq(PCIDevice *d, int irq_num) -{ - return irq_num; -} - -static void pci_vpb_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - qemu_set_irq(pic[irq_num], level); -} - -static int pci_vpb_init(SysBusDevice *dev) -{ - PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev); - PCIBus *bus; - int i; - - for (i = 0; i < 4; i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - bus = pci_register_bus(&dev->qdev, "pci", - pci_vpb_set_irq, pci_vpb_map_irq, s->irq, - get_system_memory(), get_system_io(), - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - - /* ??? Register memory space. */ - - /* Our memory regions are: - * 0 : PCI self config window - * 1 : PCI config window - * 2 : PCI IO window (realview_pci only) - */ - memory_region_init_io(&s->mem_config, &pci_vpb_config_ops, bus, - "pci-vpb-selfconfig", 0x1000000); - sysbus_init_mmio(dev, &s->mem_config); - memory_region_init_io(&s->mem_config2, &pci_vpb_config_ops, bus, - "pci-vpb-config", 0x1000000); - sysbus_init_mmio(dev, &s->mem_config2); - if (s->realview) { - isa_mmio_setup(&s->isa, 0x0100000); - sysbus_init_mmio(dev, &s->isa); - } - - pci_create_simple(bus, -1, "versatile_pci_host"); - return 0; -} - -static int pci_realview_init(SysBusDevice *dev) -{ - PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev); - s->realview = 1; - return pci_vpb_init(dev); -} - -static int versatile_pci_host_init(PCIDevice *d) -{ - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); - pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); - return 0; -} - -static void versatile_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = versatile_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_XILINX; - k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30; - k->class_id = PCI_CLASS_PROCESSOR_CO; -} - -static const TypeInfo versatile_pci_host_info = { - .name = "versatile_pci_host", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = versatile_pci_host_class_init, -}; - -static void pci_vpb_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = pci_vpb_init; -} - -static const TypeInfo pci_vpb_info = { - .name = "versatile_pci", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PCIVPBState), - .class_init = pci_vpb_class_init, -}; - -static void pci_realview_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = pci_realview_init; -} - -static const TypeInfo pci_realview_info = { - .name = "realview_pci", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PCIVPBState), - .class_init = pci_realview_class_init, -}; - -static void versatile_pci_register_types(void) -{ - type_register_static(&pci_vpb_info); - type_register_static(&pci_realview_info); - type_register_static(&versatile_pci_host_info); -} - -type_init(versatile_pci_register_types) diff --git a/hw/piix_pci.c b/hw/piix_pci.c deleted file mode 100644 index f9e68c3099..0000000000 --- a/hw/piix_pci.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * QEMU i440FX/PIIX3 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/isa/isa.h" -#include "hw/sysbus.h" -#include "qemu/range.h" -#include "hw/xen/xen.h" -#include "hw/pci-host/pam.h" -#include "sysemu/sysemu.h" - -/* - * I440FX chipset data sheet. - * http://download.intel.com/design/chipsets/datashts/29054901.pdf - */ - -typedef struct I440FXState { - PCIHostState parent_obj; -} I440FXState; - -#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ -#define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ -#define XEN_PIIX_NUM_PIRQS 128ULL -#define PIIX_PIRQC 0x60 - -/* - * Reset Control Register: PCI-accessible ISA-Compatible Register at address - * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). - */ -#define RCR_IOPORT 0xcf9 - -typedef struct PIIX3State { - PCIDevice dev; - - /* - * bitmap to track pic levels. - * The pic level is the logical OR of all the PCI irqs mapped to it - * So one PIC level is tracked by PIIX_NUM_PIRQS bits. - * - * PIRQ is mapped to PIC pins, we track it by - * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with - * pic_irq * PIIX_NUM_PIRQS + pirq - */ -#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 -#error "unable to encode pic state in 64bit in pic_levels." -#endif - uint64_t pic_levels; - - qemu_irq *pic; - - /* This member isn't used. Just for save/load compatibility */ - int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; - - /* Reset Control Register contents */ - uint8_t rcr; - - /* IO memory region for Reset Control Register (RCR_IOPORT) */ - MemoryRegion rcr_mem; -} PIIX3State; - -#define TYPE_I440FX_PCI_DEVICE "i440FX" -#define I440FX_PCI_DEVICE(obj) \ - OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) - -struct PCII440FXState { - PCIDevice dev; - MemoryRegion *system_memory; - MemoryRegion *pci_address_space; - MemoryRegion *ram_memory; - MemoryRegion pci_hole; - MemoryRegion pci_hole_64bit; - PAMMemoryRegion pam_regions[13]; - MemoryRegion smram_region; - uint8_t smm_enabled; -}; - - -#define I440FX_PAM 0x59 -#define I440FX_PAM_SIZE 7 -#define I440FX_SMRAM 0x72 - -static void piix3_set_irq(void *opaque, int pirq, int level); -static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); -static void piix3_write_config_xen(PCIDevice *dev, - uint32_t address, uint32_t val, int len); - -/* return the global irq number corresponding to a given device irq - pin. We could also use the bus number to have a more precise - mapping. */ -static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) -{ - int slot_addend; - slot_addend = (pci_dev->devfn >> 3) - 1; - return (pci_intx + slot_addend) & 3; -} - -static void i440fx_update_memory_mappings(PCII440FXState *d) -{ - int i; - - memory_region_transaction_begin(); - for (i = 0; i < 13; i++) { - pam_update(&d->pam_regions[i], i, - d->dev.config[I440FX_PAM + ((i + 1) / 2)]); - } - smram_update(&d->smram_region, d->dev.config[I440FX_SMRAM], d->smm_enabled); - memory_region_transaction_commit(); -} - -static void i440fx_set_smm(int val, void *arg) -{ - PCII440FXState *d = arg; - - memory_region_transaction_begin(); - smram_set_smm(&d->smm_enabled, val, d->dev.config[I440FX_SMRAM], - &d->smram_region); - memory_region_transaction_commit(); -} - - -static void i440fx_write_config(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - PCII440FXState *d = I440FX_PCI_DEVICE(dev); - - /* XXX: implement SMRAM.D_LOCK */ - pci_default_write_config(dev, address, val, len); - if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) || - range_covers_byte(address, len, I440FX_SMRAM)) { - i440fx_update_memory_mappings(d); - } -} - -static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) -{ - PCII440FXState *d = opaque; - int ret, i; - - ret = pci_device_load(&d->dev, f); - if (ret < 0) - return ret; - i440fx_update_memory_mappings(d); - qemu_get_8s(f, &d->smm_enabled); - - if (version_id == 2) { - for (i = 0; i < PIIX_NUM_PIRQS; i++) { - qemu_get_be32(f); /* dummy load for compatibility */ - } - } - - return 0; -} - -static int i440fx_post_load(void *opaque, int version_id) -{ - PCII440FXState *d = opaque; - - i440fx_update_memory_mappings(d); - return 0; -} - -static const VMStateDescription vmstate_i440fx = { - .name = "I440FX", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 1, - .load_state_old = i440fx_load_old, - .post_load = i440fx_post_load, - .fields = (VMStateField []) { - VMSTATE_PCI_DEVICE(dev, PCII440FXState), - VMSTATE_UINT8(smm_enabled, PCII440FXState), - VMSTATE_END_OF_LIST() - } -}; - -static int i440fx_pcihost_initfn(SysBusDevice *dev) -{ - PCIHostState *s = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&s->conf_mem, &pci_host_conf_le_ops, s, - "pci-conf-idx", 4); - sysbus_add_io(dev, 0xcf8, &s->conf_mem); - sysbus_init_ioports(&s->busdev, 0xcf8, 4); - - memory_region_init_io(&s->data_mem, &pci_host_data_le_ops, s, - "pci-conf-data", 4); - sysbus_add_io(dev, 0xcfc, &s->data_mem); - sysbus_init_ioports(&s->busdev, 0xcfc, 4); - - return 0; -} - -static int i440fx_initfn(PCIDevice *dev) -{ - PCII440FXState *d = I440FX_PCI_DEVICE(dev); - - d->dev.config[I440FX_SMRAM] = 0x02; - - cpu_smm_register(&i440fx_set_smm, d); - return 0; -} - -static PCIBus *i440fx_common_init(const char *device_name, - PCII440FXState **pi440fx_state, - int *piix3_devfn, - ISABus **isa_bus, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - ram_addr_t ram_size, - hwaddr pci_hole_start, - hwaddr pci_hole_size, - hwaddr pci_hole64_start, - hwaddr pci_hole64_size, - MemoryRegion *pci_address_space, - MemoryRegion *ram_memory) -{ - DeviceState *dev; - PCIBus *b; - PCIDevice *d; - PCIHostState *s; - PIIX3State *piix3; - PCII440FXState *f; - unsigned i; - - dev = qdev_create(NULL, "i440FX-pcihost"); - s = PCI_HOST_BRIDGE(dev); - b = pci_bus_new(dev, NULL, pci_address_space, - address_space_io, 0, TYPE_PCI_BUS); - s->bus = b; - object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); - qdev_init_nofail(dev); - - d = pci_create_simple(b, 0, device_name); - *pi440fx_state = I440FX_PCI_DEVICE(d); - f = *pi440fx_state; - f->system_memory = address_space_mem; - f->pci_address_space = pci_address_space; - f->ram_memory = ram_memory; - memory_region_init_alias(&f->pci_hole, "pci-hole", f->pci_address_space, - pci_hole_start, pci_hole_size); - memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole); - memory_region_init_alias(&f->pci_hole_64bit, "pci-hole64", - f->pci_address_space, - pci_hole64_start, pci_hole64_size); - if (pci_hole64_size) { - memory_region_add_subregion(f->system_memory, pci_hole64_start, - &f->pci_hole_64bit); - } - memory_region_init_alias(&f->smram_region, "smram-region", - f->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(f->system_memory, 0xa0000, - &f->smram_region, 1); - memory_region_set_enabled(&f->smram_region, false); - init_pam(f->ram_memory, f->system_memory, f->pci_address_space, - &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < 12; ++i) { - init_pam(f->ram_memory, f->system_memory, f->pci_address_space, - &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, - PAM_EXPAN_SIZE); - } - - /* Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. */ - if (xen_enabled()) { - piix3 = DO_UPCAST(PIIX3State, dev, - pci_create_simple_multifunction(b, -1, true, "PIIX3-xen")); - pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, - piix3, XEN_PIIX_NUM_PIRQS); - } else { - piix3 = DO_UPCAST(PIIX3State, dev, - pci_create_simple_multifunction(b, -1, true, "PIIX3")); - pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, - PIIX_NUM_PIRQS); - pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); - } - piix3->pic = pic; - *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); - - *piix3_devfn = piix3->dev.devfn; - - ram_size = ram_size / 8 / 1024 / 1024; - if (ram_size > 255) - ram_size = 255; - (*pi440fx_state)->dev.config[0x57]=ram_size; - - i440fx_update_memory_mappings(f); - - return b; -} - -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, - ISABus **isa_bus, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - ram_addr_t ram_size, - hwaddr pci_hole_start, - hwaddr pci_hole_size, - hwaddr pci_hole64_start, - hwaddr pci_hole64_size, - MemoryRegion *pci_memory, MemoryRegion *ram_memory) - -{ - PCIBus *b; - - b = i440fx_common_init(TYPE_I440FX_PCI_DEVICE, pi440fx_state, - piix3_devfn, isa_bus, pic, - address_space_mem, address_space_io, ram_size, - pci_hole_start, pci_hole_size, - pci_hole64_start, pci_hole64_size, - pci_memory, ram_memory); - return b; -} - -/* PIIX3 PCI to ISA bridge */ -static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) -{ - qemu_set_irq(piix3->pic[pic_irq], - !!(piix3->pic_levels & - (((1ULL << PIIX_NUM_PIRQS) - 1) << - (pic_irq * PIIX_NUM_PIRQS)))); -} - -static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - uint64_t mask; - - pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); - piix3->pic_levels &= ~mask; - piix3->pic_levels |= mask * !!level; - - piix3_set_irq_pic(piix3, pic_irq); -} - -static void piix3_set_irq(void *opaque, int pirq, int level) -{ - PIIX3State *piix3 = opaque; - piix3_set_irq_level(piix3, pirq, level); -} - -static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) -{ - PIIX3State *piix3 = opaque; - int irq = piix3->dev.config[PIIX_PIRQC + pin]; - PCIINTxRoute route; - - if (irq < PIIX_NUM_PIC_IRQS) { - route.mode = PCI_INTX_ENABLED; - route.irq = irq; - } else { - route.mode = PCI_INTX_DISABLED; - route.irq = -1; - } - return route; -} - -/* irq routing is changed. so rebuild bitmap */ -static void piix3_update_irq_levels(PIIX3State *piix3) -{ - int pirq; - - piix3->pic_levels = 0; - for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { - piix3_set_irq_level(piix3, pirq, - pci_bus_get_irq_level(piix3->dev.bus, pirq)); - } -} - -static void piix3_write_config(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(dev, address, val, len); - if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { - PIIX3State *piix3 = DO_UPCAST(PIIX3State, dev, dev); - int pic_irq; - - pci_bus_fire_intx_routing_notifier(piix3->dev.bus); - piix3_update_irq_levels(piix3); - for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { - piix3_set_irq_pic(piix3, pic_irq); - } - } -} - -static void piix3_write_config_xen(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - xen_piix_pci_write_config_client(address, val, len); - piix3_write_config(dev, address, val, len); -} - -static void piix3_reset(void *opaque) -{ - PIIX3State *d = opaque; - uint8_t *pci_conf = d->dev.config; - - pci_conf[0x04] = 0x07; /* master, memory and I/O */ - pci_conf[0x05] = 0x00; - pci_conf[0x06] = 0x00; - pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ - pci_conf[0x4c] = 0x4d; - pci_conf[0x4e] = 0x03; - pci_conf[0x4f] = 0x00; - pci_conf[0x60] = 0x80; - pci_conf[0x61] = 0x80; - pci_conf[0x62] = 0x80; - pci_conf[0x63] = 0x80; - pci_conf[0x69] = 0x02; - pci_conf[0x70] = 0x80; - pci_conf[0x76] = 0x0c; - pci_conf[0x77] = 0x0c; - pci_conf[0x78] = 0x02; - pci_conf[0x79] = 0x00; - pci_conf[0x80] = 0x00; - pci_conf[0x82] = 0x00; - pci_conf[0xa0] = 0x08; - pci_conf[0xa2] = 0x00; - pci_conf[0xa3] = 0x00; - pci_conf[0xa4] = 0x00; - pci_conf[0xa5] = 0x00; - pci_conf[0xa6] = 0x00; - pci_conf[0xa7] = 0x00; - pci_conf[0xa8] = 0x0f; - pci_conf[0xaa] = 0x00; - pci_conf[0xab] = 0x00; - pci_conf[0xac] = 0x00; - pci_conf[0xae] = 0x00; - - d->pic_levels = 0; - d->rcr = 0; -} - -static int piix3_post_load(void *opaque, int version_id) -{ - PIIX3State *piix3 = opaque; - piix3_update_irq_levels(piix3); - return 0; -} - -static void piix3_pre_save(void *opaque) -{ - int i; - PIIX3State *piix3 = opaque; - - for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { - piix3->pci_irq_levels_vmstate[i] = - pci_bus_get_irq_level(piix3->dev.bus, i); - } -} - -static bool piix3_rcr_needed(void *opaque) -{ - PIIX3State *piix3 = opaque; - - return (piix3->rcr != 0); -} - -static const VMStateDescription vmstate_piix3_rcr = { - .name = "PIIX3/rcr", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField []) { - VMSTATE_UINT8(rcr, PIIX3State), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_piix3 = { - .name = "PIIX3", - .version_id = 3, - .minimum_version_id = 2, - .minimum_version_id_old = 2, - .post_load = piix3_post_load, - .pre_save = piix3_pre_save, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PIIX3State), - VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, - PIIX_NUM_PIRQS, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_piix3_rcr, - .needed = piix3_rcr_needed, - }, - { 0 } - } -}; - - -static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) -{ - PIIX3State *d = opaque; - - if (val & 4) { - qemu_system_reset_request(); - return; - } - d->rcr = val & 2; /* keep System Reset type only */ -} - -static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) -{ - PIIX3State *d = opaque; - - return d->rcr; -} - -static const MemoryRegionOps rcr_ops = { - .read = rcr_read, - .write = rcr_write, - .endianness = DEVICE_LITTLE_ENDIAN -}; - -static int piix3_initfn(PCIDevice *dev) -{ - PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); - - isa_bus_new(DEVICE(d), pci_address_space_io(dev)); - - memory_region_init_io(&d->rcr_mem, &rcr_ops, d, "piix3-reset-control", 1); - memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, - &d->rcr_mem, 1); - - qemu_register_reset(piix3_reset, d); - return 0; -} - -static void piix3_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix3; - dc->no_user = 1, - k->no_hotplug = 1; - k->init = piix3_initfn; - k->config_write = piix3_write_config; - k->vendor_id = PCI_VENDOR_ID_INTEL; - /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; - k->class_id = PCI_CLASS_BRIDGE_ISA; -} - -static const TypeInfo piix3_info = { - .name = "PIIX3", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX3State), - .class_init = piix3_class_init, -}; - -static void piix3_xen_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix3; - dc->no_user = 1; - k->no_hotplug = 1; - k->init = piix3_initfn; - k->config_write = piix3_write_config_xen; - k->vendor_id = PCI_VENDOR_ID_INTEL; - /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; - k->class_id = PCI_CLASS_BRIDGE_ISA; -}; - -static const TypeInfo piix3_xen_info = { - .name = "PIIX3-xen", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX3State), - .class_init = piix3_xen_class_init, -}; - -static void i440fx_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->no_hotplug = 1; - k->init = i440fx_initfn; - k->config_write = i440fx_write_config; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82441; - k->revision = 0x02; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->desc = "Host bridge"; - dc->no_user = 1; - dc->vmsd = &vmstate_i440fx; -} - -static const TypeInfo i440fx_info = { - .name = TYPE_I440FX_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCII440FXState), - .class_init = i440fx_class_init, -}; - -static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = i440fx_pcihost_initfn; - dc->fw_name = "pci"; - dc->no_user = 1; -} - -static const TypeInfo i440fx_pcihost_info = { - .name = "i440FX-pcihost", - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(I440FXState), - .class_init = i440fx_pcihost_class_init, -}; - -static void i440fx_register_types(void) -{ - type_register_static(&i440fx_info); - type_register_static(&piix3_info); - type_register_static(&piix3_xen_info); - type_register_static(&i440fx_pcihost_info); -} - -type_init(i440fx_register_types) diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 1e24b58282..70342c28ec 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -1,8 +1,5 @@ # IBM pSeries (sPAPR) -obj-$(CONFIG_PSERIES) += spapr_pci.o obj-$(CONFIG_PSERIES) += spapr_nvram.o -# PowerPC 4xx boards -obj-y += ppc4xx_pci.o # PowerPC OpenPIC obj-y += openpic.o @@ -13,8 +10,10 @@ obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o xics.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o +obj-$(CONFIG_PSERIES) += spapr_pci.o # PowerPC 4xx boards obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o +obj-y += ppc4xx_pci.o # PReP obj-y += prep.o # OldWorld PowerMac diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c new file mode 100644 index 0000000000..599539bc1d --- /dev/null +++ b/hw/ppc/ppc4xx_pci.c @@ -0,0 +1,414 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard + */ + +/* This file implements emulation of the 32-bit PCI controller found in some + * 4xx SoCs, such as the 440EP. */ + +#include "hw/hw.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/ppc4xx.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "exec/address-spaces.h" + +#undef DEBUG +#ifdef DEBUG +#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif /* DEBUG */ + +struct PCIMasterMap { + uint32_t la; + uint32_t ma; + uint32_t pcila; + uint32_t pciha; +}; + +struct PCITargetMap { + uint32_t ms; + uint32_t la; +}; + +#define PPC4xx_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PPC4xxPCIState, (obj), TYPE_PPC4xx_PCI_HOST_BRIDGE) + +#define PPC4xx_PCI_NR_PMMS 3 +#define PPC4xx_PCI_NR_PTMS 2 + +struct PPC4xxPCIState { + PCIHostState parent_obj; + + struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; + struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; + qemu_irq irq[4]; + + MemoryRegion container; + MemoryRegion iomem; +}; +typedef struct PPC4xxPCIState PPC4xxPCIState; + +#define PCIC0_CFGADDR 0x0 +#define PCIC0_CFGDATA 0x4 + +/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to + * PCI accesses. */ +#define PCIL0_PMM0LA 0x0 +#define PCIL0_PMM0MA 0x4 +#define PCIL0_PMM0PCILA 0x8 +#define PCIL0_PMM0PCIHA 0xc +#define PCIL0_PMM1LA 0x10 +#define PCIL0_PMM1MA 0x14 +#define PCIL0_PMM1PCILA 0x18 +#define PCIL0_PMM1PCIHA 0x1c +#define PCIL0_PMM2LA 0x20 +#define PCIL0_PMM2MA 0x24 +#define PCIL0_PMM2PCILA 0x28 +#define PCIL0_PMM2PCIHA 0x2c + +/* PCI Target Map (PTM) registers specify which PCI addresses are translated to + * PLB accesses. */ +#define PCIL0_PTM1MS 0x30 +#define PCIL0_PTM1LA 0x34 +#define PCIL0_PTM2MS 0x38 +#define PCIL0_PTM2LA 0x3c +#define PCI_REG_BASE 0x800000 +#define PCI_REG_SIZE 0x40 + +#define PCI_ALL_SIZE (PCI_REG_BASE + PCI_REG_SIZE) + +static uint64_t pci4xx_cfgaddr_read(void *opaque, hwaddr addr, + unsigned size) +{ + PPC4xxPCIState *ppc4xx_pci = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); + + return phb->config_reg; +} + +static void pci4xx_cfgaddr_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + PPC4xxPCIState *ppc4xx_pci = opaque; + PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); + + phb->config_reg = value & ~0x3; +} + +static const MemoryRegionOps pci4xx_cfgaddr_ops = { + .read = pci4xx_cfgaddr_read, + .write = pci4xx_cfgaddr_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + struct PPC4xxPCIState *pci = opaque; + + /* We ignore all target attempts at PCI configuration, effectively + * assuming a bidirectional 1:1 mapping of PLB and PCI space. */ + + switch (offset) { + case PCIL0_PMM0LA: + pci->pmm[0].la = value; + break; + case PCIL0_PMM0MA: + pci->pmm[0].ma = value; + break; + case PCIL0_PMM0PCIHA: + pci->pmm[0].pciha = value; + break; + case PCIL0_PMM0PCILA: + pci->pmm[0].pcila = value; + break; + + case PCIL0_PMM1LA: + pci->pmm[1].la = value; + break; + case PCIL0_PMM1MA: + pci->pmm[1].ma = value; + break; + case PCIL0_PMM1PCIHA: + pci->pmm[1].pciha = value; + break; + case PCIL0_PMM1PCILA: + pci->pmm[1].pcila = value; + break; + + case PCIL0_PMM2LA: + pci->pmm[2].la = value; + break; + case PCIL0_PMM2MA: + pci->pmm[2].ma = value; + break; + case PCIL0_PMM2PCIHA: + pci->pmm[2].pciha = value; + break; + case PCIL0_PMM2PCILA: + pci->pmm[2].pcila = value; + break; + + case PCIL0_PTM1MS: + pci->ptm[0].ms = value; + break; + case PCIL0_PTM1LA: + pci->ptm[0].la = value; + break; + case PCIL0_PTM2MS: + pci->ptm[1].ms = value; + break; + case PCIL0_PTM2LA: + pci->ptm[1].la = value; + break; + + default: + printf("%s: unhandled PCI internal register 0x%lx\n", __func__, + (unsigned long)offset); + break; + } +} + +static uint64_t ppc4xx_pci_reg_read4(void *opaque, hwaddr offset, + unsigned size) +{ + struct PPC4xxPCIState *pci = opaque; + uint32_t value; + + switch (offset) { + case PCIL0_PMM0LA: + value = pci->pmm[0].la; + break; + case PCIL0_PMM0MA: + value = pci->pmm[0].ma; + break; + case PCIL0_PMM0PCIHA: + value = pci->pmm[0].pciha; + break; + case PCIL0_PMM0PCILA: + value = pci->pmm[0].pcila; + break; + + case PCIL0_PMM1LA: + value = pci->pmm[1].la; + break; + case PCIL0_PMM1MA: + value = pci->pmm[1].ma; + break; + case PCIL0_PMM1PCIHA: + value = pci->pmm[1].pciha; + break; + case PCIL0_PMM1PCILA: + value = pci->pmm[1].pcila; + break; + + case PCIL0_PMM2LA: + value = pci->pmm[2].la; + break; + case PCIL0_PMM2MA: + value = pci->pmm[2].ma; + break; + case PCIL0_PMM2PCIHA: + value = pci->pmm[2].pciha; + break; + case PCIL0_PMM2PCILA: + value = pci->pmm[2].pcila; + break; + + case PCIL0_PTM1MS: + value = pci->ptm[0].ms; + break; + case PCIL0_PTM1LA: + value = pci->ptm[0].la; + break; + case PCIL0_PTM2MS: + value = pci->ptm[1].ms; + break; + case PCIL0_PTM2LA: + value = pci->ptm[1].la; + break; + + default: + printf("%s: invalid PCI internal register 0x%lx\n", __func__, + (unsigned long)offset); + value = 0; + } + + return value; +} + +static const MemoryRegionOps pci_reg_ops = { + .read = ppc4xx_pci_reg_read4, + .write = ppc4xx_pci_reg_write4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ppc4xx_pci_reset(void *opaque) +{ + struct PPC4xxPCIState *pci = opaque; + + memset(pci->pmm, 0, sizeof(pci->pmm)); + memset(pci->ptm, 0, sizeof(pci->ptm)); +} + +/* On Bamboo, all pins from each slot are tied to a single board IRQ. This + * may need further refactoring for other boards. */ +static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int slot = pci_dev->devfn >> 3; + + DPRINTF("%s: devfn %x irq %d -> %d\n", __func__, + pci_dev->devfn, irq_num, slot); + + return slot - 1; +} + +static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pci_irqs = opaque; + + DPRINTF("%s: PCI irq %d\n", __func__, irq_num); + if (irq_num < 0) { + fprintf(stderr, "%s: PCI irq %d\n", __func__, irq_num); + return; + } + qemu_set_irq(pci_irqs[irq_num], level); +} + +static const VMStateDescription vmstate_pci_master_map = { + .name = "pci_master_map", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(la, struct PCIMasterMap), + VMSTATE_UINT32(ma, struct PCIMasterMap), + VMSTATE_UINT32(pcila, struct PCIMasterMap), + VMSTATE_UINT32(pciha, struct PCIMasterMap), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pci_target_map = { + .name = "pci_target_map", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ms, struct PCITargetMap), + VMSTATE_UINT32(la, struct PCITargetMap), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ppc4xx_pci = { + .name = "ppc4xx_pci", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(pmm, PPC4xxPCIState, PPC4xx_PCI_NR_PMMS, 1, + vmstate_pci_master_map, + struct PCIMasterMap), + VMSTATE_STRUCT_ARRAY(ptm, PPC4xxPCIState, PPC4xx_PCI_NR_PTMS, 1, + vmstate_pci_target_map, + struct PCITargetMap), + VMSTATE_END_OF_LIST() + } +}; + +/* XXX Interrupt acknowledge cycles not supported. */ +static int ppc4xx_pcihost_initfn(SysBusDevice *dev) +{ + PPC4xxPCIState *s; + PCIHostState *h; + PCIBus *b; + int i; + + h = PCI_HOST_BRIDGE(dev); + s = PPC4xx_PCI_HOST_BRIDGE(dev); + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + + b = pci_register_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq, + ppc4xx_pci_map_irq, s->irq, get_system_memory(), + get_system_io(), 0, 4, TYPE_PCI_BUS); + h->bus = b; + + pci_create_simple(b, 0, "ppc4xx-host-bridge"); + + /* XXX split into 2 memory regions, one for config space, one for regs */ + memory_region_init(&s->container, "pci-container", PCI_ALL_SIZE); + memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, h, + "pci-conf-idx", 4); + memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, h, + "pci-conf-data", 4); + memory_region_init_io(&s->iomem, &pci_reg_ops, s, + "pci.reg", PCI_REG_SIZE); + memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); + memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); + memory_region_add_subregion(&s->container, PCI_REG_BASE, &s->iomem); + sysbus_init_mmio(dev, &s->container); + qemu_register_reset(ppc4xx_pci_reset, s); + + return 0; +} + +static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Host bridge"; + k->vendor_id = PCI_VENDOR_ID_IBM; + k->device_id = PCI_DEVICE_ID_IBM_440GX; + k->class_id = PCI_CLASS_BRIDGE_OTHER; +} + +static const TypeInfo ppc4xx_host_bridge_info = { + .name = "ppc4xx-host-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = ppc4xx_host_bridge_class_init, +}; + +static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = ppc4xx_pcihost_initfn; + dc->vmsd = &vmstate_ppc4xx_pci; +} + +static const TypeInfo ppc4xx_pcihost_info = { + .name = TYPE_PPC4xx_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PPC4xxPCIState), + .class_init = ppc4xx_pcihost_class_init, +}; + +static void ppc4xx_pci_register_types(void) +{ + type_register_static(&ppc4xx_pcihost_info); + type_register_static(&ppc4xx_host_bridge_info); +} + +type_init(ppc4xx_pci_register_types) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c new file mode 100644 index 0000000000..62ff323f73 --- /dev/null +++ b/hw/ppc/spapr_pci.c @@ -0,0 +1,839 @@ +/* + * QEMU sPAPR PCI host originated from Uninorth PCI host + * + * Copyright (c) 2011 Alexey Kardashevskiy, IBM Corporation. + * Copyright (C) 2011 David Gibson, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/pci/pci_host.h" +#include "hw/ppc/spapr.h" +#include "hw/pci-host/spapr.h" +#include "exec/address-spaces.h" +#include +#include "trace.h" + +#include "hw/pci/pci_bus.h" + +/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ +#define RTAS_QUERY_FN 0 +#define RTAS_CHANGE_FN 1 +#define RTAS_RESET_FN 2 +#define RTAS_CHANGE_MSI_FN 3 +#define RTAS_CHANGE_MSIX_FN 4 + +/* Interrupt types to return on RTAS_CHANGE_* */ +#define RTAS_TYPE_MSI 1 +#define RTAS_TYPE_MSIX 2 + +static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) +{ + sPAPRPHBState *sphb; + + QLIST_FOREACH(sphb, &spapr->phbs, list) { + if (sphb->buid != buid) { + continue; + } + return sphb; + } + + return NULL; +} + +static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, + uint32_t config_addr) +{ + sPAPRPHBState *sphb = find_phb(spapr, buid); + PCIHostState *phb = PCI_HOST_BRIDGE(sphb); + BusState *bus = BUS(phb->bus); + BusChild *kid; + int devfn = (config_addr >> 8) & 0xFF; + + if (!phb) { + return NULL; + } + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + PCIDevice *dev = (PCIDevice *)kid->child; + if (dev->devfn == devfn) { + return dev; + } + } + + return NULL; +} + +static uint32_t rtas_pci_cfgaddr(uint32_t arg) +{ + /* This handles the encoding of extended config space addresses */ + return ((arg >> 20) & 0xf00) | (arg & 0xff); +} + +static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, + uint32_t addr, uint32_t size, + target_ulong rets) +{ + PCIDevice *pci_dev; + uint32_t val; + + if ((size != 1) && (size != 2) && (size != 4)) { + /* access must be 1, 2 or 4 bytes */ + rtas_st(rets, 0, -1); + return; + } + + pci_dev = find_dev(spapr, buid, addr); + addr = rtas_pci_cfgaddr(addr); + + if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { + /* Access must be to a valid device, within bounds and + * naturally aligned */ + rtas_st(rets, 0, -1); + return; + } + + val = pci_host_config_read_common(pci_dev, addr, + pci_config_size(pci_dev), size); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, val); +} + +static void rtas_ibm_read_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint64_t buid; + uint32_t size, addr; + + if ((nargs != 4) || (nret != 2)) { + rtas_st(rets, 0, -1); + return; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + size = rtas_ld(args, 3); + addr = rtas_ld(args, 0); + + finish_read_pci_config(spapr, buid, addr, size, rets); +} + +static void rtas_read_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t size, addr; + + if ((nargs != 2) || (nret != 2)) { + rtas_st(rets, 0, -1); + return; + } + + size = rtas_ld(args, 1); + addr = rtas_ld(args, 0); + + finish_read_pci_config(spapr, 0, addr, size, rets); +} + +static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, + uint32_t addr, uint32_t size, + uint32_t val, target_ulong rets) +{ + PCIDevice *pci_dev; + + if ((size != 1) && (size != 2) && (size != 4)) { + /* access must be 1, 2 or 4 bytes */ + rtas_st(rets, 0, -1); + return; + } + + pci_dev = find_dev(spapr, buid, addr); + addr = rtas_pci_cfgaddr(addr); + + if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { + /* Access must be to a valid device, within bounds and + * naturally aligned */ + rtas_st(rets, 0, -1); + return; + } + + pci_host_config_write_common(pci_dev, addr, pci_config_size(pci_dev), + val, size); + + rtas_st(rets, 0, 0); +} + +static void rtas_ibm_write_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint64_t buid; + uint32_t val, size, addr; + + if ((nargs != 5) || (nret != 1)) { + rtas_st(rets, 0, -1); + return; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + val = rtas_ld(args, 4); + size = rtas_ld(args, 3); + addr = rtas_ld(args, 0); + + finish_write_pci_config(spapr, buid, addr, size, val, rets); +} + +static void rtas_write_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t val, size, addr; + + if ((nargs != 3) || (nret != 1)) { + rtas_st(rets, 0, -1); + return; + } + + + val = rtas_ld(args, 2); + size = rtas_ld(args, 1); + addr = rtas_ld(args, 0); + + finish_write_pci_config(spapr, 0, addr, size, val, rets); +} + +/* + * Find an entry with config_addr or returns the empty one if not found AND + * alloc_new is set. + * At the moment the msi_table entries are never released so there is + * no point to look till the end of the list if we need to find the free entry. + */ +static int spapr_msicfg_find(sPAPRPHBState *phb, uint32_t config_addr, + bool alloc_new) +{ + int i; + + for (i = 0; i < SPAPR_MSIX_MAX_DEVS; ++i) { + if (!phb->msi_table[i].nvec) { + break; + } + if (phb->msi_table[i].config_addr == config_addr) { + return i; + } + } + if ((i < SPAPR_MSIX_MAX_DEVS) && alloc_new) { + trace_spapr_pci_msi("Allocating new MSI config", i, config_addr); + return i; + } + + return -1; +} + +/* + * Set MSI/MSIX message data. + * This is required for msi_notify()/msix_notify() which + * will write at the addresses via spapr_msi_write(). + */ +static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, + bool msix, unsigned req_num) +{ + unsigned i; + MSIMessage msg = { .address = addr, .data = 0 }; + + if (!msix) { + msi_set_message(pdev, msg); + trace_spapr_pci_msi_setup(pdev->name, 0, msg.address); + return; + } + + for (i = 0; i < req_num; ++i) { + msg.address = addr | (i << 2); + msix_set_message(pdev, i, msg); + trace_spapr_pci_msi_setup(pdev->name, i, msg.address); + } +} + +static void rtas_ibm_change_msi(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint32_t config_addr = rtas_ld(args, 0); + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + unsigned int func = rtas_ld(args, 3); + unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */ + unsigned int seq_num = rtas_ld(args, 5); + unsigned int ret_intr_type; + int ndev, irq; + sPAPRPHBState *phb = NULL; + PCIDevice *pdev = NULL; + + switch (func) { + case RTAS_CHANGE_MSI_FN: + case RTAS_CHANGE_FN: + ret_intr_type = RTAS_TYPE_MSI; + break; + case RTAS_CHANGE_MSIX_FN: + ret_intr_type = RTAS_TYPE_MSIX; + break; + default: + fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func); + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Fins sPAPRPHBState */ + phb = find_phb(spapr, buid); + if (phb) { + pdev = find_dev(spapr, buid, config_addr); + } + if (!phb || !pdev) { + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Releasing MSIs */ + if (!req_num) { + ndev = spapr_msicfg_find(phb, config_addr, false); + if (ndev < 0) { + trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + trace_spapr_pci_msi("Released MSIs", ndev, config_addr); + rtas_st(rets, 0, 0); + rtas_st(rets, 1, 0); + return; + } + + /* Enabling MSI */ + + /* Find a device number in the map to add or reuse the existing one */ + ndev = spapr_msicfg_find(phb, config_addr, true); + if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) { + fprintf(stderr, "No free entry for a new MSI device\n"); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + trace_spapr_pci_msi("Configuring MSI", ndev, config_addr); + + /* Check if there is an old config and MSI number has not changed */ + if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) { + /* Unexpected behaviour */ + fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + + /* There is no cached config, allocate MSIs */ + if (!phb->msi_table[ndev].nvec) { + irq = spapr_allocate_irq_block(req_num, false); + if (irq < 0) { + fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + phb->msi_table[ndev].irq = irq; + phb->msi_table[ndev].nvec = req_num; + phb->msi_table[ndev].config_addr = config_addr; + } + + /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ + spapr_msi_setmsg(pdev, phb->msi_win_addr | (ndev << 16), + ret_intr_type == RTAS_TYPE_MSIX, req_num); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, req_num); + rtas_st(rets, 2, ++seq_num); + rtas_st(rets, 3, ret_intr_type); + + trace_spapr_pci_rtas_ibm_change_msi(func, req_num); +} + +static void rtas_ibm_query_interrupt_source_number(sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, + target_ulong args, + uint32_t nret, + target_ulong rets) +{ + uint32_t config_addr = rtas_ld(args, 0); + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); + int ndev; + sPAPRPHBState *phb = NULL; + + /* Fins sPAPRPHBState */ + phb = find_phb(spapr, buid); + if (!phb) { + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Find device descriptor and start IRQ */ + ndev = spapr_msicfg_find(phb, config_addr, false); + if (ndev < 0) { + trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + + intr_src_num = phb->msi_table[ndev].irq + ioa_intr_num; + trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, + intr_src_num); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, intr_src_num); + rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ +} + +static int pci_spapr_swizzle(int slot, int pin) +{ + return (slot + pin) % PCI_NUM_PINS; +} + +static int pci_spapr_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* + * Here we need to convert pci_dev + irq_num to some unique value + * which is less than number of IRQs on the specific bus (4). We + * use standard PCI swizzling, that is (slot number + pin number) + * % 4. + */ + return pci_spapr_swizzle(PCI_SLOT(pci_dev->devfn), irq_num); +} + +static void pci_spapr_set_irq(void *opaque, int irq_num, int level) +{ + /* + * Here we use the number returned by pci_spapr_map_irq to find a + * corresponding qemu_irq. + */ + sPAPRPHBState *phb = opaque; + + trace_spapr_pci_lsi_set(phb->dtbusname, irq_num, phb->lsi_table[irq_num].irq); + qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); +} + +static uint64_t spapr_io_read(void *opaque, hwaddr addr, + unsigned size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + } + assert(0); +} + +static void spapr_io_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + switch (size) { + case 1: + cpu_outb(addr, data); + return; + case 2: + cpu_outw(addr, data); + return; + case 4: + cpu_outl(addr, data); + return; + } + assert(0); +} + +static const MemoryRegionOps spapr_io_ops = { + .endianness = DEVICE_LITTLE_ENDIAN, + .read = spapr_io_read, + .write = spapr_io_write +}; + +/* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + * For MSI-X, the vector number is encoded as a part of the address, + * data is set to 0. + * For MSI, the vector number is encoded in least bits in data. + */ +static void spapr_msi_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + sPAPRPHBState *phb = opaque; + int ndev = addr >> 16; + int vec = ((addr & 0xFFFF) >> 2) | data; + uint32_t irq = phb->msi_table[ndev].irq + vec; + + trace_spapr_pci_msi_write(addr, data, irq); + + qemu_irq_pulse(xics_get_qirq(spapr->icp, irq)); +} + +static const MemoryRegionOps spapr_msi_ops = { + /* There is no .read as the read result is undefined by PCI spec */ + .read = NULL, + .write = spapr_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +/* + * PHB PCI device + */ +static DMAContext *spapr_pci_dma_context_fn(PCIBus *bus, void *opaque, + int devfn) +{ + sPAPRPHBState *phb = opaque; + + return phb->dma; +} + +static int spapr_phb_init(SysBusDevice *s) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); + PCIHostState *phb = PCI_HOST_BRIDGE(s); + const char *busname; + char *namebuf; + int i; + PCIBus *bus; + + if (sphb->index != -1) { + hwaddr windows_base; + + if ((sphb->buid != -1) || (sphb->dma_liobn != -1) + || (sphb->mem_win_addr != -1) + || (sphb->io_win_addr != -1) + || (sphb->msi_win_addr != -1)) { + fprintf(stderr, "Either \"index\" or other parameters must" + " be specified for PAPR PHB, not both\n"); + return -1; + } + + sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; + sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index; + + windows_base = SPAPR_PCI_WINDOW_BASE + + sphb->index * SPAPR_PCI_WINDOW_SPACING; + sphb->mem_win_addr = windows_base + SPAPR_PCI_MMIO_WIN_OFF; + sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; + sphb->msi_win_addr = windows_base + SPAPR_PCI_MSI_WIN_OFF; + } + + if (sphb->buid == -1) { + fprintf(stderr, "BUID not specified for PHB\n"); + return -1; + } + + if (sphb->dma_liobn == -1) { + fprintf(stderr, "LIOBN not specified for PHB\n"); + return -1; + } + + if (sphb->mem_win_addr == -1) { + fprintf(stderr, "Memory window address not specified for PHB\n"); + return -1; + } + + if (sphb->io_win_addr == -1) { + fprintf(stderr, "IO window address not specified for PHB\n"); + return -1; + } + + if (sphb->msi_win_addr == -1) { + fprintf(stderr, "MSI window address not specified for PHB\n"); + return -1; + } + + if (find_phb(spapr, sphb->buid)) { + fprintf(stderr, "PCI host bridges must have unique BUIDs\n"); + return -1; + } + + sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); + + namebuf = alloca(strlen(sphb->dtbusname) + 32); + + /* Initialize memory regions */ + sprintf(namebuf, "%s.mmio", sphb->dtbusname); + memory_region_init(&sphb->memspace, namebuf, INT64_MAX); + + sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname); + memory_region_init_alias(&sphb->memwindow, namebuf, &sphb->memspace, + SPAPR_PCI_MEM_WIN_BUS_OFFSET, sphb->mem_win_size); + memory_region_add_subregion(get_system_memory(), sphb->mem_win_addr, + &sphb->memwindow); + + /* On ppc, we only have MMIO no specific IO space from the CPU + * perspective. In theory we ought to be able to embed the PCI IO + * memory region direction in the system memory space. However, + * if any of the IO BAR subregions use the old_portio mechanism, + * that won't be processed properly unless accessed from the + * system io address space. This hack to bounce things via + * system_io works around the problem until all the users of + * old_portion are updated */ + sprintf(namebuf, "%s.io", sphb->dtbusname); + memory_region_init(&sphb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); + /* FIXME: fix to support multiple PHBs */ + memory_region_add_subregion(get_system_io(), 0, &sphb->iospace); + + sprintf(namebuf, "%s.io-alias", sphb->dtbusname); + memory_region_init_io(&sphb->iowindow, &spapr_io_ops, sphb, + namebuf, SPAPR_PCI_IO_WIN_SIZE); + memory_region_add_subregion(get_system_memory(), sphb->io_win_addr, + &sphb->iowindow); + + /* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, + * we need to allocate some memory to catch those writes coming + * from msi_notify()/msix_notify() */ + if (msi_supported) { + sprintf(namebuf, "%s.msi", sphb->dtbusname); + memory_region_init_io(&sphb->msiwindow, &spapr_msi_ops, sphb, + namebuf, SPAPR_MSIX_MAX_DEVS * 0x10000); + memory_region_add_subregion(get_system_memory(), sphb->msi_win_addr, + &sphb->msiwindow); + } + + /* + * Selecting a busname is more complex than you'd think, due to + * interacting constraints. If the user has specified an id + * explicitly for the phb , then we want to use the qdev default + * of naming the bus based on the bridge device (so the user can + * then assign devices to it in the way they expect). For the + * first / default PCI bus (index=0) we want to use just "pci" + * because libvirt expects there to be a bus called, simply, + * "pci". Otherwise, we use the same name as in the device tree, + * since it's unique by construction, and makes the guest visible + * BUID clear. + */ + if (s->qdev.id) { + busname = NULL; + } else if (sphb->index == 0) { + busname = "pci"; + } else { + busname = sphb->dtbusname; + } + bus = pci_register_bus(DEVICE(s), busname, + pci_spapr_set_irq, pci_spapr_map_irq, sphb, + &sphb->memspace, &sphb->iospace, + PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); + phb->bus = bus; + + sphb->dma_window_start = 0; + sphb->dma_window_size = 0x40000000; + sphb->dma = spapr_tce_new_dma_context(sphb->dma_liobn, sphb->dma_window_size); + if (!sphb->dma) { + fprintf(stderr, "Unable to create TCE table for %s\n", sphb->dtbusname); + return -1; + } + pci_setup_iommu(bus, spapr_pci_dma_context_fn, sphb); + + QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); + + /* Initialize the LSI table */ + for (i = 0; i < PCI_NUM_PINS; i++) { + uint32_t irq; + + irq = spapr_allocate_lsi(0); + if (!irq) { + return -1; + } + + sphb->lsi_table[i].irq = irq; + } + + return 0; +} + +static void spapr_phb_reset(DeviceState *qdev) +{ + SysBusDevice *s = SYS_BUS_DEVICE(qdev); + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); + + /* Reset the IOMMU state */ + spapr_tce_reset(sphb->dma); +} + +static Property spapr_phb_properties[] = { + DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), + DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), + DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), + DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), + DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, + SPAPR_PCI_MMIO_WIN_SIZE), + DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, -1), + DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, + SPAPR_PCI_IO_WIN_SIZE), + DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_phb_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + sdc->init = spapr_phb_init; + dc->props = spapr_phb_properties; + dc->reset = spapr_phb_reset; +} + +static const TypeInfo spapr_phb_info = { + .name = TYPE_SPAPR_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(sPAPRPHBState), + .class_init = spapr_phb_class_init, +}; + +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) +{ + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); + qdev_prop_set_uint32(dev, "index", index); + qdev_init_nofail(dev); + + return PCI_HOST_BRIDGE(dev); +} + +/* Macros to operate with address in OF binding to PCI */ +#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) +#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ +#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ +#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ +#define b_ss(x) b_x((x), 24, 2) /* the space code */ +#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ +#define b_ddddd(x) b_x((x), 11, 5) /* device number */ +#define b_fff(x) b_x((x), 8, 3) /* function number */ +#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ + +int spapr_populate_pci_dt(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt) +{ + int bus_off, i, j; + char nodename[256]; + uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; + struct { + uint32_t hi; + uint64_t child; + uint64_t parent; + uint64_t size; + } QEMU_PACKED ranges[] = { + { + cpu_to_be32(b_ss(1)), cpu_to_be64(0), + cpu_to_be64(phb->io_win_addr), + cpu_to_be64(memory_region_size(&phb->iospace)), + }, + { + cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET), + cpu_to_be64(phb->mem_win_addr), + cpu_to_be64(memory_region_size(&phb->memwindow)), + }, + }; + uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 }; + uint32_t interrupt_map_mask[] = { + cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; + uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7]; + + /* Start populating the FDT */ + sprintf(nodename, "pci@%" PRIx64, phb->buid); + bus_off = fdt_add_subnode(fdt, 0, nodename); + if (bus_off < 0) { + return bus_off; + } + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + return ret; \ + } \ + } while (0) + + /* Write PHB properties */ + _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); + _FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); + _FDT(fdt_setprop_cell(fdt, bus_off, "#address-cells", 0x3)); + _FDT(fdt_setprop_cell(fdt, bus_off, "#size-cells", 0x2)); + _FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1)); + _FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0)); + _FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range))); + _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof(ranges))); + _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); + _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); + + /* Build the interrupt-map, this must matches what is done + * in pci_spapr_map_irq + */ + _FDT(fdt_setprop(fdt, bus_off, "interrupt-map-mask", + &interrupt_map_mask, sizeof(interrupt_map_mask))); + for (i = 0; i < PCI_SLOT_MAX; i++) { + for (j = 0; j < PCI_NUM_PINS; j++) { + uint32_t *irqmap = interrupt_map[i*PCI_NUM_PINS + j]; + int lsi_num = pci_spapr_swizzle(i, j); + + irqmap[0] = cpu_to_be32(b_ddddd(i)|b_fff(0)); + irqmap[1] = 0; + irqmap[2] = 0; + irqmap[3] = cpu_to_be32(j+1); + irqmap[4] = cpu_to_be32(xics_phandle); + irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].irq); + irqmap[6] = cpu_to_be32(0x8); + } + } + /* Write interrupt map */ + _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, + sizeof(interrupt_map))); + + spapr_dma_dt(fdt, bus_off, "ibm,dma-window", + phb->dma_liobn, phb->dma_window_start, + phb->dma_window_size); + + return 0; +} + +void spapr_pci_rtas_init(void) +{ + spapr_rtas_register("read-pci-config", rtas_read_pci_config); + spapr_rtas_register("write-pci-config", rtas_write_pci_config); + spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); + spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); + if (msi_supported) { + spapr_rtas_register("ibm,query-interrupt-source-number", + rtas_ibm_query_interrupt_source_number); + spapr_rtas_register("ibm,change-msi", rtas_ibm_change_msi); + } +} + +static void spapr_pci_register_types(void) +{ + type_register_static(&spapr_phb_info); +} + +type_init(spapr_pci_register_types) diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c deleted file mode 100644 index 599539bc1d..0000000000 --- a/hw/ppc4xx_pci.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -/* This file implements emulation of the 32-bit PCI controller found in some - * 4xx SoCs, such as the 440EP. */ - -#include "hw/hw.h" -#include "hw/ppc/ppc.h" -#include "hw/ppc/ppc4xx.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "exec/address-spaces.h" - -#undef DEBUG -#ifdef DEBUG -#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif /* DEBUG */ - -struct PCIMasterMap { - uint32_t la; - uint32_t ma; - uint32_t pcila; - uint32_t pciha; -}; - -struct PCITargetMap { - uint32_t ms; - uint32_t la; -}; - -#define PPC4xx_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PPC4xxPCIState, (obj), TYPE_PPC4xx_PCI_HOST_BRIDGE) - -#define PPC4xx_PCI_NR_PMMS 3 -#define PPC4xx_PCI_NR_PTMS 2 - -struct PPC4xxPCIState { - PCIHostState parent_obj; - - struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; - struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; - qemu_irq irq[4]; - - MemoryRegion container; - MemoryRegion iomem; -}; -typedef struct PPC4xxPCIState PPC4xxPCIState; - -#define PCIC0_CFGADDR 0x0 -#define PCIC0_CFGDATA 0x4 - -/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to - * PCI accesses. */ -#define PCIL0_PMM0LA 0x0 -#define PCIL0_PMM0MA 0x4 -#define PCIL0_PMM0PCILA 0x8 -#define PCIL0_PMM0PCIHA 0xc -#define PCIL0_PMM1LA 0x10 -#define PCIL0_PMM1MA 0x14 -#define PCIL0_PMM1PCILA 0x18 -#define PCIL0_PMM1PCIHA 0x1c -#define PCIL0_PMM2LA 0x20 -#define PCIL0_PMM2MA 0x24 -#define PCIL0_PMM2PCILA 0x28 -#define PCIL0_PMM2PCIHA 0x2c - -/* PCI Target Map (PTM) registers specify which PCI addresses are translated to - * PLB accesses. */ -#define PCIL0_PTM1MS 0x30 -#define PCIL0_PTM1LA 0x34 -#define PCIL0_PTM2MS 0x38 -#define PCIL0_PTM2LA 0x3c -#define PCI_REG_BASE 0x800000 -#define PCI_REG_SIZE 0x40 - -#define PCI_ALL_SIZE (PCI_REG_BASE + PCI_REG_SIZE) - -static uint64_t pci4xx_cfgaddr_read(void *opaque, hwaddr addr, - unsigned size) -{ - PPC4xxPCIState *ppc4xx_pci = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); - - return phb->config_reg; -} - -static void pci4xx_cfgaddr_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PPC4xxPCIState *ppc4xx_pci = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(ppc4xx_pci); - - phb->config_reg = value & ~0x3; -} - -static const MemoryRegionOps pci4xx_cfgaddr_ops = { - .read = pci4xx_cfgaddr_read, - .write = pci4xx_cfgaddr_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - struct PPC4xxPCIState *pci = opaque; - - /* We ignore all target attempts at PCI configuration, effectively - * assuming a bidirectional 1:1 mapping of PLB and PCI space. */ - - switch (offset) { - case PCIL0_PMM0LA: - pci->pmm[0].la = value; - break; - case PCIL0_PMM0MA: - pci->pmm[0].ma = value; - break; - case PCIL0_PMM0PCIHA: - pci->pmm[0].pciha = value; - break; - case PCIL0_PMM0PCILA: - pci->pmm[0].pcila = value; - break; - - case PCIL0_PMM1LA: - pci->pmm[1].la = value; - break; - case PCIL0_PMM1MA: - pci->pmm[1].ma = value; - break; - case PCIL0_PMM1PCIHA: - pci->pmm[1].pciha = value; - break; - case PCIL0_PMM1PCILA: - pci->pmm[1].pcila = value; - break; - - case PCIL0_PMM2LA: - pci->pmm[2].la = value; - break; - case PCIL0_PMM2MA: - pci->pmm[2].ma = value; - break; - case PCIL0_PMM2PCIHA: - pci->pmm[2].pciha = value; - break; - case PCIL0_PMM2PCILA: - pci->pmm[2].pcila = value; - break; - - case PCIL0_PTM1MS: - pci->ptm[0].ms = value; - break; - case PCIL0_PTM1LA: - pci->ptm[0].la = value; - break; - case PCIL0_PTM2MS: - pci->ptm[1].ms = value; - break; - case PCIL0_PTM2LA: - pci->ptm[1].la = value; - break; - - default: - printf("%s: unhandled PCI internal register 0x%lx\n", __func__, - (unsigned long)offset); - break; - } -} - -static uint64_t ppc4xx_pci_reg_read4(void *opaque, hwaddr offset, - unsigned size) -{ - struct PPC4xxPCIState *pci = opaque; - uint32_t value; - - switch (offset) { - case PCIL0_PMM0LA: - value = pci->pmm[0].la; - break; - case PCIL0_PMM0MA: - value = pci->pmm[0].ma; - break; - case PCIL0_PMM0PCIHA: - value = pci->pmm[0].pciha; - break; - case PCIL0_PMM0PCILA: - value = pci->pmm[0].pcila; - break; - - case PCIL0_PMM1LA: - value = pci->pmm[1].la; - break; - case PCIL0_PMM1MA: - value = pci->pmm[1].ma; - break; - case PCIL0_PMM1PCIHA: - value = pci->pmm[1].pciha; - break; - case PCIL0_PMM1PCILA: - value = pci->pmm[1].pcila; - break; - - case PCIL0_PMM2LA: - value = pci->pmm[2].la; - break; - case PCIL0_PMM2MA: - value = pci->pmm[2].ma; - break; - case PCIL0_PMM2PCIHA: - value = pci->pmm[2].pciha; - break; - case PCIL0_PMM2PCILA: - value = pci->pmm[2].pcila; - break; - - case PCIL0_PTM1MS: - value = pci->ptm[0].ms; - break; - case PCIL0_PTM1LA: - value = pci->ptm[0].la; - break; - case PCIL0_PTM2MS: - value = pci->ptm[1].ms; - break; - case PCIL0_PTM2LA: - value = pci->ptm[1].la; - break; - - default: - printf("%s: invalid PCI internal register 0x%lx\n", __func__, - (unsigned long)offset); - value = 0; - } - - return value; -} - -static const MemoryRegionOps pci_reg_ops = { - .read = ppc4xx_pci_reg_read4, - .write = ppc4xx_pci_reg_write4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void ppc4xx_pci_reset(void *opaque) -{ - struct PPC4xxPCIState *pci = opaque; - - memset(pci->pmm, 0, sizeof(pci->pmm)); - memset(pci->ptm, 0, sizeof(pci->ptm)); -} - -/* On Bamboo, all pins from each slot are tied to a single board IRQ. This - * may need further refactoring for other boards. */ -static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int slot = pci_dev->devfn >> 3; - - DPRINTF("%s: devfn %x irq %d -> %d\n", __func__, - pci_dev->devfn, irq_num, slot); - - return slot - 1; -} - -static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pci_irqs = opaque; - - DPRINTF("%s: PCI irq %d\n", __func__, irq_num); - if (irq_num < 0) { - fprintf(stderr, "%s: PCI irq %d\n", __func__, irq_num); - return; - } - qemu_set_irq(pci_irqs[irq_num], level); -} - -static const VMStateDescription vmstate_pci_master_map = { - .name = "pci_master_map", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(la, struct PCIMasterMap), - VMSTATE_UINT32(ma, struct PCIMasterMap), - VMSTATE_UINT32(pcila, struct PCIMasterMap), - VMSTATE_UINT32(pciha, struct PCIMasterMap), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_target_map = { - .name = "pci_target_map", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ms, struct PCITargetMap), - VMSTATE_UINT32(la, struct PCITargetMap), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ppc4xx_pci = { - .name = "ppc4xx_pci", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(pmm, PPC4xxPCIState, PPC4xx_PCI_NR_PMMS, 1, - vmstate_pci_master_map, - struct PCIMasterMap), - VMSTATE_STRUCT_ARRAY(ptm, PPC4xxPCIState, PPC4xx_PCI_NR_PTMS, 1, - vmstate_pci_target_map, - struct PCITargetMap), - VMSTATE_END_OF_LIST() - } -}; - -/* XXX Interrupt acknowledge cycles not supported. */ -static int ppc4xx_pcihost_initfn(SysBusDevice *dev) -{ - PPC4xxPCIState *s; - PCIHostState *h; - PCIBus *b; - int i; - - h = PCI_HOST_BRIDGE(dev); - s = PPC4xx_PCI_HOST_BRIDGE(dev); - - for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - - b = pci_register_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq, - ppc4xx_pci_map_irq, s->irq, get_system_memory(), - get_system_io(), 0, 4, TYPE_PCI_BUS); - h->bus = b; - - pci_create_simple(b, 0, "ppc4xx-host-bridge"); - - /* XXX split into 2 memory regions, one for config space, one for regs */ - memory_region_init(&s->container, "pci-container", PCI_ALL_SIZE); - memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops, h, - "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, h, - "pci-conf-data", 4); - memory_region_init_io(&s->iomem, &pci_reg_ops, s, - "pci.reg", PCI_REG_SIZE); - memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); - memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); - memory_region_add_subregion(&s->container, PCI_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); - qemu_register_reset(ppc4xx_pci_reset, s); - - return 0; -} - -static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "Host bridge"; - k->vendor_id = PCI_VENDOR_ID_IBM; - k->device_id = PCI_DEVICE_ID_IBM_440GX; - k->class_id = PCI_CLASS_BRIDGE_OTHER; -} - -static const TypeInfo ppc4xx_host_bridge_info = { - .name = "ppc4xx-host-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = ppc4xx_host_bridge_class_init, -}; - -static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = ppc4xx_pcihost_initfn; - dc->vmsd = &vmstate_ppc4xx_pci; -} - -static const TypeInfo ppc4xx_pcihost_info = { - .name = TYPE_PPC4xx_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PPC4xxPCIState), - .class_init = ppc4xx_pcihost_class_init, -}; - -static void ppc4xx_pci_register_types(void) -{ - type_register_static(&ppc4xx_pcihost_info); - type_register_static(&ppc4xx_host_bridge_info); -} - -type_init(ppc4xx_pci_register_types) diff --git a/hw/q35.c b/hw/q35.c deleted file mode 100644 index 8467f86450..0000000000 --- a/hw/q35.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * QEMU MCH/ICH9 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2009, 2010, 2011 - * Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This is based on piix_pci.c, but heavily modified. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw/hw.h" -#include "hw/pci-host/q35.h" - -/**************************************************************************** - * Q35 host - */ - -static int q35_host_init(SysBusDevice *dev) -{ - PCIBus *b; - PCIHostState *pci = FROM_SYSBUS(PCIHostState, dev); - Q35PCIHost *s = Q35_HOST_DEVICE(&dev->qdev); - - memory_region_init_io(&pci->conf_mem, &pci_host_conf_le_ops, pci, - "pci-conf-idx", 4); - sysbus_add_io(dev, MCH_HOST_BRIDGE_CONFIG_ADDR, &pci->conf_mem); - sysbus_init_ioports(&pci->busdev, MCH_HOST_BRIDGE_CONFIG_ADDR, 4); - - memory_region_init_io(&pci->data_mem, &pci_host_data_le_ops, pci, - "pci-conf-data", 4); - sysbus_add_io(dev, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); - sysbus_init_ioports(&pci->busdev, MCH_HOST_BRIDGE_CONFIG_DATA, 4); - - if (pcie_host_init(&s->host) < 0) { - return -1; - } - b = pci_bus_new(&s->host.pci.busdev.qdev, "pcie.0", - s->mch.pci_address_space, s->mch.address_space_io, - 0, TYPE_PCIE_BUS); - s->host.pci.bus = b; - qdev_set_parent_bus(DEVICE(&s->mch), BUS(b)); - qdev_init_nofail(DEVICE(&s->mch)); - - return 0; -} - -static Property mch_props[] = { - DEFINE_PROP_UINT64("MCFG", Q35PCIHost, host.base_addr, - MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), - DEFINE_PROP_END_OF_LIST(), -}; - -static void q35_host_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = q35_host_init; - dc->props = mch_props; -} - -static void q35_host_initfn(Object *obj) -{ - Q35PCIHost *s = Q35_HOST_DEVICE(obj); - - object_initialize(&s->mch, TYPE_MCH_PCI_DEVICE); - object_property_add_child(OBJECT(s), "mch", OBJECT(&s->mch), NULL); - qdev_prop_set_uint32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0)); - qdev_prop_set_bit(DEVICE(&s->mch), "multifunction", false); -} - -static const TypeInfo q35_host_info = { - .name = TYPE_Q35_HOST_DEVICE, - .parent = TYPE_PCIE_HOST_BRIDGE, - .instance_size = sizeof(Q35PCIHost), - .instance_init = q35_host_initfn, - .class_init = q35_host_class_init, -}; - -/**************************************************************************** - * MCH D0:F0 - */ - -/* PCIe MMCFG */ -static void mch_update_pciexbar(MCHPCIState *mch) -{ - PCIDevice *pci_dev = &mch->d; - BusState *bus = qdev_get_parent_bus(&pci_dev->qdev); - DeviceState *qdev = bus->parent; - Q35PCIHost *s = Q35_HOST_DEVICE(qdev); - - uint64_t pciexbar; - int enable; - uint64_t addr; - uint64_t addr_mask; - uint32_t length; - - pciexbar = pci_get_quad(pci_dev->config + MCH_HOST_BRIDGE_PCIEXBAR); - enable = pciexbar & MCH_HOST_BRIDGE_PCIEXBAREN; - addr_mask = MCH_HOST_BRIDGE_PCIEXBAR_ADMSK; - switch (pciexbar & MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) { - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M: - length = 256 * 1024 * 1024; - break; - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M: - length = 128 * 1024 * 1024; - addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK | - MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; - break; - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M: - length = 64 * 1024 * 1024; - addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; - break; - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD: - default: - enable = 0; - length = 0; - abort(); - break; - } - addr = pciexbar & addr_mask; - pcie_host_mmcfg_update(&s->host, enable, addr, length); -} - -/* PAM */ -static void mch_update_pam(MCHPCIState *mch) -{ - int i; - - memory_region_transaction_begin(); - for (i = 0; i < 13; i++) { - pam_update(&mch->pam_regions[i], i, - mch->d.config[MCH_HOST_BRIDGE_PAM0 + ((i + 1) / 2)]); - } - memory_region_transaction_commit(); -} - -/* SMRAM */ -static void mch_update_smram(MCHPCIState *mch) -{ - memory_region_transaction_begin(); - smram_update(&mch->smram_region, mch->d.config[MCH_HOST_BRDIGE_SMRAM], - mch->smm_enabled); - memory_region_transaction_commit(); -} - -static void mch_set_smm(int smm, void *arg) -{ - MCHPCIState *mch = arg; - - memory_region_transaction_begin(); - smram_set_smm(&mch->smm_enabled, smm, mch->d.config[MCH_HOST_BRDIGE_SMRAM], - &mch->smram_region); - memory_region_transaction_commit(); -} - -static void mch_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - /* XXX: implement SMRAM.D_LOCK */ - pci_default_write_config(d, address, val, len); - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0, - MCH_HOST_BRIDGE_PAM_SIZE)) { - mch_update_pam(mch); - } - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PCIEXBAR, - MCH_HOST_BRIDGE_PCIEXBAR_SIZE)) { - mch_update_pciexbar(mch); - } - - if (ranges_overlap(address, len, MCH_HOST_BRDIGE_SMRAM, - MCH_HOST_BRDIGE_SMRAM_SIZE)) { - mch_update_smram(mch); - } -} - -static void mch_update(MCHPCIState *mch) -{ - mch_update_pciexbar(mch); - mch_update_pam(mch); - mch_update_smram(mch); -} - -static int mch_post_load(void *opaque, int version_id) -{ - MCHPCIState *mch = opaque; - mch_update(mch); - return 0; -} - -static const VMStateDescription vmstate_mch = { - .name = "mch", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = mch_post_load, - .fields = (VMStateField []) { - VMSTATE_PCI_DEVICE(d, MCHPCIState), - VMSTATE_UINT8(smm_enabled, MCHPCIState), - VMSTATE_END_OF_LIST() - } -}; - -static void mch_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - pci_set_quad(d->config + MCH_HOST_BRIDGE_PCIEXBAR, - MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT); - - d->config[MCH_HOST_BRDIGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; - - mch_update(mch); -} - -static int mch_init(PCIDevice *d) -{ - int i; - hwaddr pci_hole64_size; - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - /* setup pci memory regions */ - memory_region_init_alias(&mch->pci_hole, "pci-hole", - mch->pci_address_space, - mch->below_4g_mem_size, - 0x100000000ULL - mch->below_4g_mem_size); - memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size, - &mch->pci_hole); - pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 : - ((uint64_t)1 << 62)); - memory_region_init_alias(&mch->pci_hole_64bit, "pci-hole64", - mch->pci_address_space, - 0x100000000ULL + mch->above_4g_mem_size, - pci_hole64_size); - if (pci_hole64_size) { - memory_region_add_subregion(mch->system_memory, - 0x100000000ULL + mch->above_4g_mem_size, - &mch->pci_hole_64bit); - } - /* smram */ - cpu_smm_register(&mch_set_smm, mch); - memory_region_init_alias(&mch->smram_region, "smram-region", - mch->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, - &mch->smram_region, 1); - memory_region_set_enabled(&mch->smram_region, false); - init_pam(mch->ram_memory, mch->system_memory, mch->pci_address_space, - &mch->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < 12; ++i) { - init_pam(mch->ram_memory, mch->system_memory, mch->pci_address_space, - &mch->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, - PAM_EXPAN_SIZE); - } - return 0; -} - -static void mch_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = mch_init; - k->config_write = mch_write_config; - dc->reset = mch_reset; - dc->desc = "Host bridge"; - dc->vmsd = &vmstate_mch; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH; - k->revision = MCH_HOST_BRIDGE_REVISION_DEFUALT; - k->class_id = PCI_CLASS_BRIDGE_HOST; -} - -static const TypeInfo mch_info = { - .name = TYPE_MCH_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(MCHPCIState), - .class_init = mch_class_init, -}; - -static void q35_register(void) -{ - type_register_static(&mch_info); - type_register_static(&q35_host_info); -} - -type_init(q35_register); diff --git a/hw/sh4/Makefile.objs b/hw/sh4/Makefile.objs index 76b37bbf6b..0387b96b4b 100644 --- a/hw/sh4/Makefile.objs +++ b/hw/sh4/Makefile.objs @@ -1,7 +1,8 @@ -obj-y += sh_intc.o sh_pci.o +obj-y += sh_intc.o obj-y := $(addprefix ../,$(obj-y)) obj-y += shix.o r2d.o obj-y += sh7750.o sh7750_regnames.o +obj-y += sh_pci.o diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c new file mode 100644 index 0000000000..d213a90580 --- /dev/null +++ b/hw/sh4/sh_pci.c @@ -0,0 +1,186 @@ +/* + * SuperH on-chip PCIC emulation. + * + * Copyright (c) 2008 Takashi YOSHII + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/sysbus.h" +#include "hw/sh4/sh.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "qemu/bswap.h" +#include "exec/address-spaces.h" + +typedef struct SHPCIState { + SysBusDevice busdev; + PCIBus *bus; + PCIDevice *dev; + qemu_irq irq[4]; + MemoryRegion memconfig_p4; + MemoryRegion memconfig_a7; + MemoryRegion isa; + uint32_t par; + uint32_t mbr; + uint32_t iobr; +} SHPCIState; + +static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, + unsigned size) +{ + SHPCIState *pcic = p; + switch(addr) { + case 0 ... 0xfc: + cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val); + break; + case 0x1c0: + pcic->par = val; + break; + case 0x1c4: + pcic->mbr = val & 0xff000001; + break; + case 0x1c8: + if ((val & 0xfffc0000) != (pcic->iobr & 0xfffc0000)) { + memory_region_del_subregion(get_system_memory(), &pcic->isa); + pcic->iobr = val & 0xfffc0001; + memory_region_add_subregion(get_system_memory(), + pcic->iobr & 0xfffc0000, &pcic->isa); + } + break; + case 0x220: + pci_data_write(pcic->bus, pcic->par, val, 4); + break; + } +} + +static uint64_t sh_pci_reg_read (void *p, hwaddr addr, + unsigned size) +{ + SHPCIState *pcic = p; + switch(addr) { + case 0 ... 0xfc: + return le32_to_cpup((uint32_t*)(pcic->dev->config + addr)); + case 0x1c0: + return pcic->par; + case 0x1c4: + return pcic->mbr; + case 0x1c8: + return pcic->iobr; + case 0x220: + return pci_data_read(pcic->bus, pcic->par, 4); + } + return 0; +} + +static const MemoryRegionOps sh_pci_reg_ops = { + .read = sh_pci_reg_read, + .write = sh_pci_reg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static int sh_pci_map_irq(PCIDevice *d, int irq_num) +{ + return (d->devfn >> 3); +} + +static void sh_pci_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + qemu_set_irq(pic[irq_num], level); +} + +static int sh_pci_device_init(SysBusDevice *dev) +{ + SHPCIState *s; + int i; + + s = FROM_SYSBUS(SHPCIState, dev); + for (i = 0; i < 4; i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + s->bus = pci_register_bus(&s->busdev.qdev, "pci", + sh_pci_set_irq, sh_pci_map_irq, + s->irq, + get_system_memory(), + get_system_io(), + PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); + memory_region_init_io(&s->memconfig_p4, &sh_pci_reg_ops, s, + "sh_pci", 0x224); + memory_region_init_alias(&s->memconfig_a7, "sh_pci.2", &s->memconfig_p4, + 0, 0x224); + isa_mmio_setup(&s->isa, 0x40000); + sysbus_init_mmio(dev, &s->memconfig_p4); + sysbus_init_mmio(dev, &s->memconfig_a7); + s->iobr = 0xfe240000; + memory_region_add_subregion(get_system_memory(), s->iobr, &s->isa); + + s->dev = pci_create_simple(s->bus, PCI_DEVFN(0, 0), "sh_pci_host"); + return 0; +} + +static int sh_pci_host_init(PCIDevice *d) +{ + pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); + pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + return 0; +} + +static void sh_pci_host_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = sh_pci_host_init; + k->vendor_id = PCI_VENDOR_ID_HITACHI; + k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R; +} + +static const TypeInfo sh_pci_host_info = { + .name = "sh_pci_host", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = sh_pci_host_class_init, +}; + +static void sh_pci_device_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = sh_pci_device_init; +} + +static const TypeInfo sh_pci_device_info = { + .name = "sh_pci", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SHPCIState), + .class_init = sh_pci_device_class_init, +}; + +static void sh_pci_register_types(void) +{ + type_register_static(&sh_pci_device_info); + type_register_static(&sh_pci_host_info); +} + +type_init(sh_pci_register_types) diff --git a/hw/sh_pci.c b/hw/sh_pci.c deleted file mode 100644 index d213a90580..0000000000 --- a/hw/sh_pci.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * SuperH on-chip PCIC emulation. - * - * Copyright (c) 2008 Takashi YOSHII - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw/sysbus.h" -#include "hw/sh4/sh.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "qemu/bswap.h" -#include "exec/address-spaces.h" - -typedef struct SHPCIState { - SysBusDevice busdev; - PCIBus *bus; - PCIDevice *dev; - qemu_irq irq[4]; - MemoryRegion memconfig_p4; - MemoryRegion memconfig_a7; - MemoryRegion isa; - uint32_t par; - uint32_t mbr; - uint32_t iobr; -} SHPCIState; - -static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, - unsigned size) -{ - SHPCIState *pcic = p; - switch(addr) { - case 0 ... 0xfc: - cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val); - break; - case 0x1c0: - pcic->par = val; - break; - case 0x1c4: - pcic->mbr = val & 0xff000001; - break; - case 0x1c8: - if ((val & 0xfffc0000) != (pcic->iobr & 0xfffc0000)) { - memory_region_del_subregion(get_system_memory(), &pcic->isa); - pcic->iobr = val & 0xfffc0001; - memory_region_add_subregion(get_system_memory(), - pcic->iobr & 0xfffc0000, &pcic->isa); - } - break; - case 0x220: - pci_data_write(pcic->bus, pcic->par, val, 4); - break; - } -} - -static uint64_t sh_pci_reg_read (void *p, hwaddr addr, - unsigned size) -{ - SHPCIState *pcic = p; - switch(addr) { - case 0 ... 0xfc: - return le32_to_cpup((uint32_t*)(pcic->dev->config + addr)); - case 0x1c0: - return pcic->par; - case 0x1c4: - return pcic->mbr; - case 0x1c8: - return pcic->iobr; - case 0x220: - return pci_data_read(pcic->bus, pcic->par, 4); - } - return 0; -} - -static const MemoryRegionOps sh_pci_reg_ops = { - .read = sh_pci_reg_read, - .write = sh_pci_reg_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static int sh_pci_map_irq(PCIDevice *d, int irq_num) -{ - return (d->devfn >> 3); -} - -static void sh_pci_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - qemu_set_irq(pic[irq_num], level); -} - -static int sh_pci_device_init(SysBusDevice *dev) -{ - SHPCIState *s; - int i; - - s = FROM_SYSBUS(SHPCIState, dev); - for (i = 0; i < 4; i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - s->bus = pci_register_bus(&s->busdev.qdev, "pci", - sh_pci_set_irq, sh_pci_map_irq, - s->irq, - get_system_memory(), - get_system_io(), - PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); - memory_region_init_io(&s->memconfig_p4, &sh_pci_reg_ops, s, - "sh_pci", 0x224); - memory_region_init_alias(&s->memconfig_a7, "sh_pci.2", &s->memconfig_p4, - 0, 0x224); - isa_mmio_setup(&s->isa, 0x40000); - sysbus_init_mmio(dev, &s->memconfig_p4); - sysbus_init_mmio(dev, &s->memconfig_a7); - s->iobr = 0xfe240000; - memory_region_add_subregion(get_system_memory(), s->iobr, &s->isa); - - s->dev = pci_create_simple(s->bus, PCI_DEVFN(0, 0), "sh_pci_host"); - return 0; -} - -static int sh_pci_host_init(PCIDevice *d) -{ - pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); - pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | - PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - return 0; -} - -static void sh_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = sh_pci_host_init; - k->vendor_id = PCI_VENDOR_ID_HITACHI; - k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R; -} - -static const TypeInfo sh_pci_host_info = { - .name = "sh_pci_host", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = sh_pci_host_class_init, -}; - -static void sh_pci_device_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = sh_pci_device_init; -} - -static const TypeInfo sh_pci_device_info = { - .name = "sh_pci", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SHPCIState), - .class_init = sh_pci_device_class_init, -}; - -static void sh_pci_register_types(void) -{ - type_register_static(&sh_pci_device_info); - type_register_static(&sh_pci_host_info); -} - -type_init(sh_pci_register_types) diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c deleted file mode 100644 index 62ff323f73..0000000000 --- a/hw/spapr_pci.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * QEMU sPAPR PCI host originated from Uninorth PCI host - * - * Copyright (c) 2011 Alexey Kardashevskiy, IBM Corporation. - * Copyright (C) 2011 David Gibson, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "hw/pci/pci_host.h" -#include "hw/ppc/spapr.h" -#include "hw/pci-host/spapr.h" -#include "exec/address-spaces.h" -#include -#include "trace.h" - -#include "hw/pci/pci_bus.h" - -/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ -#define RTAS_QUERY_FN 0 -#define RTAS_CHANGE_FN 1 -#define RTAS_RESET_FN 2 -#define RTAS_CHANGE_MSI_FN 3 -#define RTAS_CHANGE_MSIX_FN 4 - -/* Interrupt types to return on RTAS_CHANGE_* */ -#define RTAS_TYPE_MSI 1 -#define RTAS_TYPE_MSIX 2 - -static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) -{ - sPAPRPHBState *sphb; - - QLIST_FOREACH(sphb, &spapr->phbs, list) { - if (sphb->buid != buid) { - continue; - } - return sphb; - } - - return NULL; -} - -static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, - uint32_t config_addr) -{ - sPAPRPHBState *sphb = find_phb(spapr, buid); - PCIHostState *phb = PCI_HOST_BRIDGE(sphb); - BusState *bus = BUS(phb->bus); - BusChild *kid; - int devfn = (config_addr >> 8) & 0xFF; - - if (!phb) { - return NULL; - } - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - PCIDevice *dev = (PCIDevice *)kid->child; - if (dev->devfn == devfn) { - return dev; - } - } - - return NULL; -} - -static uint32_t rtas_pci_cfgaddr(uint32_t arg) -{ - /* This handles the encoding of extended config space addresses */ - return ((arg >> 20) & 0xf00) | (arg & 0xff); -} - -static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, - uint32_t addr, uint32_t size, - target_ulong rets) -{ - PCIDevice *pci_dev; - uint32_t val; - - if ((size != 1) && (size != 2) && (size != 4)) { - /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, -1); - return; - } - - pci_dev = find_dev(spapr, buid, addr); - addr = rtas_pci_cfgaddr(addr); - - if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { - /* Access must be to a valid device, within bounds and - * naturally aligned */ - rtas_st(rets, 0, -1); - return; - } - - val = pci_host_config_read_common(pci_dev, addr, - pci_config_size(pci_dev), size); - - rtas_st(rets, 0, 0); - rtas_st(rets, 1, val); -} - -static void rtas_ibm_read_pci_config(sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint64_t buid; - uint32_t size, addr; - - if ((nargs != 4) || (nret != 2)) { - rtas_st(rets, 0, -1); - return; - } - - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - size = rtas_ld(args, 3); - addr = rtas_ld(args, 0); - - finish_read_pci_config(spapr, buid, addr, size, rets); -} - -static void rtas_read_pci_config(sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint32_t size, addr; - - if ((nargs != 2) || (nret != 2)) { - rtas_st(rets, 0, -1); - return; - } - - size = rtas_ld(args, 1); - addr = rtas_ld(args, 0); - - finish_read_pci_config(spapr, 0, addr, size, rets); -} - -static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, - uint32_t addr, uint32_t size, - uint32_t val, target_ulong rets) -{ - PCIDevice *pci_dev; - - if ((size != 1) && (size != 2) && (size != 4)) { - /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, -1); - return; - } - - pci_dev = find_dev(spapr, buid, addr); - addr = rtas_pci_cfgaddr(addr); - - if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { - /* Access must be to a valid device, within bounds and - * naturally aligned */ - rtas_st(rets, 0, -1); - return; - } - - pci_host_config_write_common(pci_dev, addr, pci_config_size(pci_dev), - val, size); - - rtas_st(rets, 0, 0); -} - -static void rtas_ibm_write_pci_config(sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint64_t buid; - uint32_t val, size, addr; - - if ((nargs != 5) || (nret != 1)) { - rtas_st(rets, 0, -1); - return; - } - - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - val = rtas_ld(args, 4); - size = rtas_ld(args, 3); - addr = rtas_ld(args, 0); - - finish_write_pci_config(spapr, buid, addr, size, val, rets); -} - -static void rtas_write_pci_config(sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint32_t val, size, addr; - - if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, -1); - return; - } - - - val = rtas_ld(args, 2); - size = rtas_ld(args, 1); - addr = rtas_ld(args, 0); - - finish_write_pci_config(spapr, 0, addr, size, val, rets); -} - -/* - * Find an entry with config_addr or returns the empty one if not found AND - * alloc_new is set. - * At the moment the msi_table entries are never released so there is - * no point to look till the end of the list if we need to find the free entry. - */ -static int spapr_msicfg_find(sPAPRPHBState *phb, uint32_t config_addr, - bool alloc_new) -{ - int i; - - for (i = 0; i < SPAPR_MSIX_MAX_DEVS; ++i) { - if (!phb->msi_table[i].nvec) { - break; - } - if (phb->msi_table[i].config_addr == config_addr) { - return i; - } - } - if ((i < SPAPR_MSIX_MAX_DEVS) && alloc_new) { - trace_spapr_pci_msi("Allocating new MSI config", i, config_addr); - return i; - } - - return -1; -} - -/* - * Set MSI/MSIX message data. - * This is required for msi_notify()/msix_notify() which - * will write at the addresses via spapr_msi_write(). - */ -static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, - bool msix, unsigned req_num) -{ - unsigned i; - MSIMessage msg = { .address = addr, .data = 0 }; - - if (!msix) { - msi_set_message(pdev, msg); - trace_spapr_pci_msi_setup(pdev->name, 0, msg.address); - return; - } - - for (i = 0; i < req_num; ++i) { - msg.address = addr | (i << 2); - msix_set_message(pdev, i, msg); - trace_spapr_pci_msi_setup(pdev->name, i, msg.address); - } -} - -static void rtas_ibm_change_msi(sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint32_t config_addr = rtas_ld(args, 0); - uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - unsigned int func = rtas_ld(args, 3); - unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */ - unsigned int seq_num = rtas_ld(args, 5); - unsigned int ret_intr_type; - int ndev, irq; - sPAPRPHBState *phb = NULL; - PCIDevice *pdev = NULL; - - switch (func) { - case RTAS_CHANGE_MSI_FN: - case RTAS_CHANGE_FN: - ret_intr_type = RTAS_TYPE_MSI; - break; - case RTAS_CHANGE_MSIX_FN: - ret_intr_type = RTAS_TYPE_MSIX; - break; - default: - fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func); - rtas_st(rets, 0, -3); /* Parameter error */ - return; - } - - /* Fins sPAPRPHBState */ - phb = find_phb(spapr, buid); - if (phb) { - pdev = find_dev(spapr, buid, config_addr); - } - if (!phb || !pdev) { - rtas_st(rets, 0, -3); /* Parameter error */ - return; - } - - /* Releasing MSIs */ - if (!req_num) { - ndev = spapr_msicfg_find(phb, config_addr, false); - if (ndev < 0) { - trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ - return; - } - trace_spapr_pci_msi("Released MSIs", ndev, config_addr); - rtas_st(rets, 0, 0); - rtas_st(rets, 1, 0); - return; - } - - /* Enabling MSI */ - - /* Find a device number in the map to add or reuse the existing one */ - ndev = spapr_msicfg_find(phb, config_addr, true); - if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) { - fprintf(stderr, "No free entry for a new MSI device\n"); - rtas_st(rets, 0, -1); /* Hardware error */ - return; - } - trace_spapr_pci_msi("Configuring MSI", ndev, config_addr); - - /* Check if there is an old config and MSI number has not changed */ - if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) { - /* Unexpected behaviour */ - fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev); - rtas_st(rets, 0, -1); /* Hardware error */ - return; - } - - /* There is no cached config, allocate MSIs */ - if (!phb->msi_table[ndev].nvec) { - irq = spapr_allocate_irq_block(req_num, false); - if (irq < 0) { - fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev); - rtas_st(rets, 0, -1); /* Hardware error */ - return; - } - phb->msi_table[ndev].irq = irq; - phb->msi_table[ndev].nvec = req_num; - phb->msi_table[ndev].config_addr = config_addr; - } - - /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ - spapr_msi_setmsg(pdev, phb->msi_win_addr | (ndev << 16), - ret_intr_type == RTAS_TYPE_MSIX, req_num); - - rtas_st(rets, 0, 0); - rtas_st(rets, 1, req_num); - rtas_st(rets, 2, ++seq_num); - rtas_st(rets, 3, ret_intr_type); - - trace_spapr_pci_rtas_ibm_change_msi(func, req_num); -} - -static void rtas_ibm_query_interrupt_source_number(sPAPREnvironment *spapr, - uint32_t token, - uint32_t nargs, - target_ulong args, - uint32_t nret, - target_ulong rets) -{ - uint32_t config_addr = rtas_ld(args, 0); - uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); - unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); - int ndev; - sPAPRPHBState *phb = NULL; - - /* Fins sPAPRPHBState */ - phb = find_phb(spapr, buid); - if (!phb) { - rtas_st(rets, 0, -3); /* Parameter error */ - return; - } - - /* Find device descriptor and start IRQ */ - ndev = spapr_msicfg_find(phb, config_addr, false); - if (ndev < 0) { - trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ - return; - } - - intr_src_num = phb->msi_table[ndev].irq + ioa_intr_num; - trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, - intr_src_num); - - rtas_st(rets, 0, 0); - rtas_st(rets, 1, intr_src_num); - rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ -} - -static int pci_spapr_swizzle(int slot, int pin) -{ - return (slot + pin) % PCI_NUM_PINS; -} - -static int pci_spapr_map_irq(PCIDevice *pci_dev, int irq_num) -{ - /* - * Here we need to convert pci_dev + irq_num to some unique value - * which is less than number of IRQs on the specific bus (4). We - * use standard PCI swizzling, that is (slot number + pin number) - * % 4. - */ - return pci_spapr_swizzle(PCI_SLOT(pci_dev->devfn), irq_num); -} - -static void pci_spapr_set_irq(void *opaque, int irq_num, int level) -{ - /* - * Here we use the number returned by pci_spapr_map_irq to find a - * corresponding qemu_irq. - */ - sPAPRPHBState *phb = opaque; - - trace_spapr_pci_lsi_set(phb->dtbusname, irq_num, phb->lsi_table[irq_num].irq); - qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); -} - -static uint64_t spapr_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return cpu_inb(addr); - case 2: - return cpu_inw(addr); - case 4: - return cpu_inl(addr); - } - assert(0); -} - -static void spapr_io_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - switch (size) { - case 1: - cpu_outb(addr, data); - return; - case 2: - cpu_outw(addr, data); - return; - case 4: - cpu_outl(addr, data); - return; - } - assert(0); -} - -static const MemoryRegionOps spapr_io_ops = { - .endianness = DEVICE_LITTLE_ENDIAN, - .read = spapr_io_read, - .write = spapr_io_write -}; - -/* - * MSI/MSIX memory region implementation. - * The handler handles both MSI and MSIX. - * For MSI-X, the vector number is encoded as a part of the address, - * data is set to 0. - * For MSI, the vector number is encoded in least bits in data. - */ -static void spapr_msi_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - sPAPRPHBState *phb = opaque; - int ndev = addr >> 16; - int vec = ((addr & 0xFFFF) >> 2) | data; - uint32_t irq = phb->msi_table[ndev].irq + vec; - - trace_spapr_pci_msi_write(addr, data, irq); - - qemu_irq_pulse(xics_get_qirq(spapr->icp, irq)); -} - -static const MemoryRegionOps spapr_msi_ops = { - /* There is no .read as the read result is undefined by PCI spec */ - .read = NULL, - .write = spapr_msi_write, - .endianness = DEVICE_LITTLE_ENDIAN -}; - -/* - * PHB PCI device - */ -static DMAContext *spapr_pci_dma_context_fn(PCIBus *bus, void *opaque, - int devfn) -{ - sPAPRPHBState *phb = opaque; - - return phb->dma; -} - -static int spapr_phb_init(SysBusDevice *s) -{ - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s); - const char *busname; - char *namebuf; - int i; - PCIBus *bus; - - if (sphb->index != -1) { - hwaddr windows_base; - - if ((sphb->buid != -1) || (sphb->dma_liobn != -1) - || (sphb->mem_win_addr != -1) - || (sphb->io_win_addr != -1) - || (sphb->msi_win_addr != -1)) { - fprintf(stderr, "Either \"index\" or other parameters must" - " be specified for PAPR PHB, not both\n"); - return -1; - } - - sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; - sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index; - - windows_base = SPAPR_PCI_WINDOW_BASE - + sphb->index * SPAPR_PCI_WINDOW_SPACING; - sphb->mem_win_addr = windows_base + SPAPR_PCI_MMIO_WIN_OFF; - sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; - sphb->msi_win_addr = windows_base + SPAPR_PCI_MSI_WIN_OFF; - } - - if (sphb->buid == -1) { - fprintf(stderr, "BUID not specified for PHB\n"); - return -1; - } - - if (sphb->dma_liobn == -1) { - fprintf(stderr, "LIOBN not specified for PHB\n"); - return -1; - } - - if (sphb->mem_win_addr == -1) { - fprintf(stderr, "Memory window address not specified for PHB\n"); - return -1; - } - - if (sphb->io_win_addr == -1) { - fprintf(stderr, "IO window address not specified for PHB\n"); - return -1; - } - - if (sphb->msi_win_addr == -1) { - fprintf(stderr, "MSI window address not specified for PHB\n"); - return -1; - } - - if (find_phb(spapr, sphb->buid)) { - fprintf(stderr, "PCI host bridges must have unique BUIDs\n"); - return -1; - } - - sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); - - namebuf = alloca(strlen(sphb->dtbusname) + 32); - - /* Initialize memory regions */ - sprintf(namebuf, "%s.mmio", sphb->dtbusname); - memory_region_init(&sphb->memspace, namebuf, INT64_MAX); - - sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname); - memory_region_init_alias(&sphb->memwindow, namebuf, &sphb->memspace, - SPAPR_PCI_MEM_WIN_BUS_OFFSET, sphb->mem_win_size); - memory_region_add_subregion(get_system_memory(), sphb->mem_win_addr, - &sphb->memwindow); - - /* On ppc, we only have MMIO no specific IO space from the CPU - * perspective. In theory we ought to be able to embed the PCI IO - * memory region direction in the system memory space. However, - * if any of the IO BAR subregions use the old_portio mechanism, - * that won't be processed properly unless accessed from the - * system io address space. This hack to bounce things via - * system_io works around the problem until all the users of - * old_portion are updated */ - sprintf(namebuf, "%s.io", sphb->dtbusname); - memory_region_init(&sphb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); - /* FIXME: fix to support multiple PHBs */ - memory_region_add_subregion(get_system_io(), 0, &sphb->iospace); - - sprintf(namebuf, "%s.io-alias", sphb->dtbusname); - memory_region_init_io(&sphb->iowindow, &spapr_io_ops, sphb, - namebuf, SPAPR_PCI_IO_WIN_SIZE); - memory_region_add_subregion(get_system_memory(), sphb->io_win_addr, - &sphb->iowindow); - - /* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, - * we need to allocate some memory to catch those writes coming - * from msi_notify()/msix_notify() */ - if (msi_supported) { - sprintf(namebuf, "%s.msi", sphb->dtbusname); - memory_region_init_io(&sphb->msiwindow, &spapr_msi_ops, sphb, - namebuf, SPAPR_MSIX_MAX_DEVS * 0x10000); - memory_region_add_subregion(get_system_memory(), sphb->msi_win_addr, - &sphb->msiwindow); - } - - /* - * Selecting a busname is more complex than you'd think, due to - * interacting constraints. If the user has specified an id - * explicitly for the phb , then we want to use the qdev default - * of naming the bus based on the bridge device (so the user can - * then assign devices to it in the way they expect). For the - * first / default PCI bus (index=0) we want to use just "pci" - * because libvirt expects there to be a bus called, simply, - * "pci". Otherwise, we use the same name as in the device tree, - * since it's unique by construction, and makes the guest visible - * BUID clear. - */ - if (s->qdev.id) { - busname = NULL; - } else if (sphb->index == 0) { - busname = "pci"; - } else { - busname = sphb->dtbusname; - } - bus = pci_register_bus(DEVICE(s), busname, - pci_spapr_set_irq, pci_spapr_map_irq, sphb, - &sphb->memspace, &sphb->iospace, - PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); - phb->bus = bus; - - sphb->dma_window_start = 0; - sphb->dma_window_size = 0x40000000; - sphb->dma = spapr_tce_new_dma_context(sphb->dma_liobn, sphb->dma_window_size); - if (!sphb->dma) { - fprintf(stderr, "Unable to create TCE table for %s\n", sphb->dtbusname); - return -1; - } - pci_setup_iommu(bus, spapr_pci_dma_context_fn, sphb); - - QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); - - /* Initialize the LSI table */ - for (i = 0; i < PCI_NUM_PINS; i++) { - uint32_t irq; - - irq = spapr_allocate_lsi(0); - if (!irq) { - return -1; - } - - sphb->lsi_table[i].irq = irq; - } - - return 0; -} - -static void spapr_phb_reset(DeviceState *qdev) -{ - SysBusDevice *s = SYS_BUS_DEVICE(qdev); - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); - - /* Reset the IOMMU state */ - spapr_tce_reset(sphb->dma); -} - -static Property spapr_phb_properties[] = { - DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), - DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), - DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), - DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), - DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, - SPAPR_PCI_MMIO_WIN_SIZE), - DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, -1), - DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, - SPAPR_PCI_IO_WIN_SIZE), - DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void spapr_phb_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - sdc->init = spapr_phb_init; - dc->props = spapr_phb_properties; - dc->reset = spapr_phb_reset; -} - -static const TypeInfo spapr_phb_info = { - .name = TYPE_SPAPR_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(sPAPRPHBState), - .class_init = spapr_phb_class_init, -}; - -PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); - qdev_prop_set_uint32(dev, "index", index); - qdev_init_nofail(dev); - - return PCI_HOST_BRIDGE(dev); -} - -/* Macros to operate with address in OF binding to PCI */ -#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) -#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ -#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ -#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ -#define b_ss(x) b_x((x), 24, 2) /* the space code */ -#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ -#define b_ddddd(x) b_x((x), 11, 5) /* device number */ -#define b_fff(x) b_x((x), 8, 3) /* function number */ -#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ - -int spapr_populate_pci_dt(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt) -{ - int bus_off, i, j; - char nodename[256]; - uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; - struct { - uint32_t hi; - uint64_t child; - uint64_t parent; - uint64_t size; - } QEMU_PACKED ranges[] = { - { - cpu_to_be32(b_ss(1)), cpu_to_be64(0), - cpu_to_be64(phb->io_win_addr), - cpu_to_be64(memory_region_size(&phb->iospace)), - }, - { - cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET), - cpu_to_be64(phb->mem_win_addr), - cpu_to_be64(memory_region_size(&phb->memwindow)), - }, - }; - uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 }; - uint32_t interrupt_map_mask[] = { - cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; - uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7]; - - /* Start populating the FDT */ - sprintf(nodename, "pci@%" PRIx64, phb->buid); - bus_off = fdt_add_subnode(fdt, 0, nodename); - if (bus_off < 0) { - return bus_off; - } - -#define _FDT(exp) \ - do { \ - int ret = (exp); \ - if (ret < 0) { \ - return ret; \ - } \ - } while (0) - - /* Write PHB properties */ - _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); - _FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); - _FDT(fdt_setprop_cell(fdt, bus_off, "#address-cells", 0x3)); - _FDT(fdt_setprop_cell(fdt, bus_off, "#size-cells", 0x2)); - _FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1)); - _FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0)); - _FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range))); - _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof(ranges))); - _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); - _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); - - /* Build the interrupt-map, this must matches what is done - * in pci_spapr_map_irq - */ - _FDT(fdt_setprop(fdt, bus_off, "interrupt-map-mask", - &interrupt_map_mask, sizeof(interrupt_map_mask))); - for (i = 0; i < PCI_SLOT_MAX; i++) { - for (j = 0; j < PCI_NUM_PINS; j++) { - uint32_t *irqmap = interrupt_map[i*PCI_NUM_PINS + j]; - int lsi_num = pci_spapr_swizzle(i, j); - - irqmap[0] = cpu_to_be32(b_ddddd(i)|b_fff(0)); - irqmap[1] = 0; - irqmap[2] = 0; - irqmap[3] = cpu_to_be32(j+1); - irqmap[4] = cpu_to_be32(xics_phandle); - irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].irq); - irqmap[6] = cpu_to_be32(0x8); - } - } - /* Write interrupt map */ - _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, - sizeof(interrupt_map))); - - spapr_dma_dt(fdt, bus_off, "ibm,dma-window", - phb->dma_liobn, phb->dma_window_start, - phb->dma_window_size); - - return 0; -} - -void spapr_pci_rtas_init(void) -{ - spapr_rtas_register("read-pci-config", rtas_read_pci_config); - spapr_rtas_register("write-pci-config", rtas_write_pci_config); - spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); - spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); - if (msi_supported) { - spapr_rtas_register("ibm,query-interrupt-source-number", - rtas_ibm_query_interrupt_source_number); - spapr_rtas_register("ibm,change-msi", rtas_ibm_change_msi); - } -} - -static void spapr_pci_register_types(void) -{ - type_register_static(&spapr_phb_info); -} - -type_init(spapr_pci_register_types) diff --git a/hw/sparc64/Makefile.objs b/hw/sparc64/Makefile.objs index 178464ba54..a84cfe3ec7 100644 --- a/hw/sparc64/Makefile.objs +++ b/hw/sparc64/Makefile.objs @@ -1,5 +1 @@ -obj-y = apb_pci.o - -obj-y := $(addprefix ../,$(obj-y)) - obj-y += sun4u.o diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 6b50b5f34a..e182c820ac 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -28,7 +28,6 @@ #include "hw/sysbus.h" #include "hw/i386/pc.h" #include "hw/isa/apm.h" -#include "hw/i386/apic.h" #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" #include "hw/acpi/acpi.h" -- cgit v1.2.3-55-g7522