From patchwork Tue Jun 25 11:27:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Cameron X-Patchwork-Id: 11015359 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D459F1398 for ; Tue, 25 Jun 2019 11:34:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C1203289C6 for ; Tue, 25 Jun 2019 11:34:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B44DF28B68; Tue, 25 Jun 2019 11:34:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 4E048289D3 for ; Tue, 25 Jun 2019 11:34:03 +0000 (UTC) Received: from localhost ([::1]:59028 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hfji6-0004V5-LS for patchwork-qemu-devel@patchwork.kernel.org; Tue, 25 Jun 2019 07:34:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:53826) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hfjdR-0007Vo-2R for qemu-devel@nongnu.org; Tue, 25 Jun 2019 07:29:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hfjdM-0004Oo-66 for qemu-devel@nongnu.org; Tue, 25 Jun 2019 07:29:12 -0400 Received: from szxga07-in.huawei.com ([45.249.212.35]:52888 helo=huawei.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hfjd5-0003xV-KU; Tue, 25 Jun 2019 07:28:57 -0400 Received: from DGGEMS406-HUB.china.huawei.com (unknown [172.30.72.60]) by Forcepoint Email with ESMTP id 4702790BD5F52C0CB68A; Tue, 25 Jun 2019 19:28:48 +0800 (CST) Received: from lhrphicprd00229.huawei.com (10.123.41.22) by DGGEMS406-HUB.china.huawei.com (10.3.19.206) with Microsoft SMTP Server id 14.3.439.0; Tue, 25 Jun 2019 19:28:37 +0800 From: Jonathan Cameron To: QEMU Developers Date: Tue, 25 Jun 2019 19:27:48 +0800 Message-ID: <20190625112752.83188-4-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190625112752.83188-1-Jonathan.Cameron@huawei.com> References: <20190625112752.83188-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.123.41.22] X-CFilter-Loop: Reflected X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 45.249.212.35 Subject: [Qemu-devel] [RFC PATCH 3/7] pci: CCIX config space emulation library. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , jcm@redhat.com, linuxarm@huawei.com, Auger Eric , qemu-arm , Jonathan Cameron Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP The nature of the complex topologies supported by CCIX means that it will be sometime before it is possible to construct many of the interesting cases in hardware, and it will be extermely hard to exercise all of the combinations whilst developing firwmare and drivers. To that end, the intent of this library and following device emulations is to allow the construction of complex CCIX toplogies via their overlaying on PCIe. The CCIX topologies are configured through CCIX specific PCIe DVSEC capabillity and control structures. A typical mesh capable CCIX device will overlay onto N upstream PCIe switch ports, M downstream PCIe switch ports, function 0 EPs which are not PCIe switch upstream ports, P functions 1+ which have additional CCIX protocol elements described and Q functions 1+ which respresent acceleration functions, with no CCIX protocol elements (these look to be normal PCIe functions so are not covered by this patch set). Signed-off-by: Jonathan Cameron --- hw/pci/Kconfig | 3 + hw/pci/Makefile.objs | 1 + hw/pci/ccix_lib.c | 1299 ++++++++++++++++++++++++++++++++++++++ include/hw/misc/ccix.h | 28 + include/hw/pci/pci_ids.h | 2 + 5 files changed, 1333 insertions(+) diff --git a/hw/pci/Kconfig b/hw/pci/Kconfig index 77f8b005ff..605f8dcd18 100644 --- a/hw/pci/Kconfig +++ b/hw/pci/Kconfig @@ -13,3 +13,6 @@ config MSI_NONBROKEN # or support it and have a good implementation. See commit # 47d2b0f33c664533b8dbd5cb17faa8e6a01afe1f. bool + +config CCIX_LIB + bool diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index 8642fab622..efc17ff4dd 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -13,3 +13,4 @@ common-obj-$(CONFIG_PCI) += ccix_per.o common-obj-$(call lnot,$(CONFIG_PCI)) += pci-stub.o common-obj-$(CONFIG_ALL) += pci-stub.o +common-obj-$(CONFIG_CCIX_LIB) += ccix_lib.o diff --git a/hw/pci/ccix_lib.c b/hw/pci/ccix_lib.c new file mode 100644 index 0000000000..544f90077d --- /dev/null +++ b/hw/pci/ccix_lib.c @@ -0,0 +1,1299 @@ +/* + * CCIX configuration space creation library + * + * Copyright (c) 2019 Huawei + * Author: Jonathan Cameron + * + * Portions copied from pci-testdev.c + * Copyright (c) 2012 Red Hat Inc. + * Author: Michael S. Tsirkin + * + * 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "qemu/event_notifier.h" +#include "sysemu/kvm.h" +#include "hw/misc/ccix.h" + +/* Leave space for the SR-IDM and SW portal if enabled */ +#define CCIX_COMMON_CAP_MAX_SIZE 10 * sizeof(uint32_t) + +#define CM_CAP_DW1_MULTIPORT_CAP_OFF 0 +#define CM_CAP_DW1_MULTIPORT_CAP_M 0x00000007 +#define CM_CAP_DW1_VERSION_CAP_OFF 22 +#define CM_CAP_DW1_VERSION_CAP_M 0x00C00000 +#define CM_CAP_DW1_DEVID_OFF 24 +#define CM_CAP_DW1_DEVID_M 0xFF000000 + +#define CM_CAP_DW2_DISC_READY_CAP_OFF 0 +#define CM_CAP_DW2_PART_CS_CAP_OFF 1 +#define CM_CAP_DW2_PORT_AG_CAP_OFF 2 +#define CM_CAP_DW2_CL_SIZE_CAP_OFF 3 +#define CM_CAP_DW2_ADDR_W_CAP_OFF 4 +#define CM_CAP_DW2_MH_CAP_OFF 7 +#define CM_CAP_DW2_SW_PORT_CAP_OFF 8 +#define CM_CAP_DW2_SAM_ALIGN_CAP_OFF 9 +#define CM_CAP_DW2_READY_TIME_VAL_OFF 19 +#define CM_CAP_DW2_READY_TIME_SCALE_OFF 28 + +/* COMMON_CAP III is reserved */ +#define CM_CAP_DW4_DEV_ERR_LOG_OFFSET_OFF 20 +#define CM_CAP_DW5_IDM_OFFSET_OFF 20 +#define CM_CAP_DW6_RSAM_SIZE_OFF 0 +#define CM_CAP_DW6_RSAM_OFFSET_OFF 20 +#define CM_CAP_DW7_HSAM_SIZE_OFF 0 +#define CM_CAP_DW7_HSAM_OFFSET_OFF 20 +#define CM_CAP_DW8_SR_OFFSET_OFF 20 +#define CM_CAP_DW9_SW_PORTAL_OFF 0 + +#define CM_CTL_DW1_DEVICE_EN_OFF 0 +#define CM_CTL_DW1_PRIMARY_PORT_EN_OFF 1 +#define CM_CTL_DW1_MESH_EN_OFF 2 +#define CM_CTL_DW1_PORT_AG_EN_OFF 4 +#define CM_CTL_DW1_IDM_TABLE_VALID_OFF 5 +#define CM_CTL_DW1_RSAM_TABLE_VALID_OFF 6 +#define CM_CTL_DW1_HSAM_TABLE_VALID_OFF 7 +#define CM_CTL_DW1_SW_PORT_ENABLE_OFF 8 +#define CM_CTL_DW1_ERR_AGENT_ID_OFF 16 +#define CM_CTL_DW1_ERR_AGENT_ID_M 0x003F0000 +#define CM_CTL_DW1_DEVID_OFF 24 +#define CM_CTL_DW1_DEVID_M 0xFF000000 + +#define CM_CTL_DW2_PART_CS_EN_OFF 1 +#define CM_CTL_DW2_CL_SIZE_EN_OFF 3 +#define CM_CTL_DW2_ADDR_W_EN_OFF 4 +#define CM_CTL_DW2_ADDR_W_EN_M 0x00000070 + +#define CCIX_PORT_CAP_SIZE 5 * sizeof(uint32_t) + +#define PT_CAP_DW1_DISC_READY_OFF 0 +#define PT_CAP_DW1_OPT_HEADER_OFF 1 +#define PT_CAP_DW1_P2P_FORWARDING_OFF 5 +#define PT_CAP_DW1_LINKS_OFF 7 +#define PT_CAP_DW1_LINKS_M 0x00001F80 +#define PT_CAP_DW1_PSAM_ENTRIES_OFF 13 +#define PT_CAP_DW1_PSAM_ENTRIES_M 0x0007E000 +#define PT_CAP_DW1_PORTID_OFF 27 +#define PT_CAP_DW1_PORTID_M 0xF8000000 + +/* not including PSAM */ +#define CCIX_PORT_CTL_SIZE 5 * sizeof(uint32_t) + +#define PT_CTL_DW1_PORT_EN_OFF 0 +#define PT_CTL_DW1_OPT_HEADER_EN_OFF 1 +#define PT_CTL_DW1_LINKS_EN_OFF 7 +#define PT_CTL_DW1_LINKS_EN_M 0x00001F80 +#define PT_CTL_DW1_PSAM_ENTRIES_EN_OFF 13 +#define PT_CTL_DW1_PSAM_ENTRIES_EN_M 0x0007E000 + +#define CCIX_LINK_CAP_SIZE 6 * sizeof(uint32_t) + +#define LK_CAP_DW1_DISC_READY_OFF 0 +#define LK_CAP_DW1_CREDIT_TYPE_OFF 1 +#define LK_CAP_DW1_MESSAGE_PACKING_OFF 2 +#define LK_CAP_DW1_NOCOMPACK_OFF 6 +#define LK_CAP_DW1_MAX_PKT_SIZE_OFF 7 +#define LK_CAP_DW1_MAX_PKT_SIZE_M 0x00000038 +#define LK_CAP_DW2_MAX_MEM_REQ_SEND_OFF 0 +#define LK_CAP_DW2_MAX_MEM_REQ_SEND_M 0x000003FF +#define LK_CAP_DW2_MAX_SNP_REQ_SEND_OFF 10 +#define LK_CAP_DW2_MAX_SNP_REQ_SEND_M 0x000FFC00 +#define LK_CAP_DW2_MAX_DAT_REQ_SEND_OFF 20 +#define LK_CAP_DW2_MAX_DAT_REQ_SEND_M 0x3FF00000 +#define LK_CAP_DW3_MAX_MEM_REQ_RECV_OFF 0 +#define LK_CAP_DW3_MAX_MEM_REQ_RECV_M 0x000003FF +#define LK_CAP_DW3_MAX_SNP_REQ_RECV_OFF 10 +#define LK_CAP_DW3_MAX_SNP_REQ_RECV_M 0x000FFC00 +#define LK_CAP_DW3_MAX_DAT_REQ_RECV_OFF 20 +#define LK_CAP_DW3_MAX_DAT_REQ_RECV_M 0x3FF00000 +#define LK_CAP_DW4_MAX_MISC_REQ_SEND_CAP_OFF 0 +#define LK_CAP_DW4_MAX_MISC_REQ_RECV_CAP_OFF 10 + +/* Not including per link */ +#define CCIX_LINK_CTL_SIZE 1 * sizeof(uint32_t) +/* Not including BcastFwdCntlVctr, including Tranport ID map */ +#define CCIX_LINK_CTL_PER_LINK_SIZE (6 + 1) * sizeof(uint32_t) + +/* Per link controls */ +#define LK_CTL_DW1_LINK_EN_OFF 0 +#define LK_CTL_DW1_CREDIT_EN_OFF 1 +#define LK_CTL_DW1_MESSAGE_PACKING_EN_OFF 2 +#define LK_CTL_DW1_NO_COMP_ACK_EN_OFF 6 +#define LK_CTL_DW1_MAX_PKT_SIZE_EN_OFF 7 +#define LK_CTL_DW1_MAX_PKT_SIZE_EN_M 0x00000380 +#define LK_CTL_DW1_LINK_ENT_ADDR_TYPE_OFF 10 + +#define CCIX_RA_CAP_SIZE 3 * sizeof(uint32_t) + +#define RA_CAP_DW1_RA_DISC_RDY_STAT_OFF 0 +#define RA_CAP_DW1_RA_CACHE_FLUSH_TIME_VALUE_OFF 19 +#define RA_CAP_DW1_RA_CACHE_FLUSH_TIME_VALUE_M 0x0FF80000 +#define RA_CAP_DW1_RA_CACHE_FLUSH_TIME_SCALE_OFF 28 +#define RA_CAP_DW1_RA_CACHE_FLUSH_TIME_SCALE_M 0x70000000 +#define RA_CAP_DW1_RA_CACHE_FLUSH_STA_OFF 31 + +#define RA_CAP_DW2_RA_ERROR_LOG_OFFSET_OFF 20 + +#define CCIX_RA_CTL_SIZE 4 * sizeof(uint32_t) + +#define RA_CTL_DW1_EN_OFF 0 +#define RA_CTL_DW1_SNOOP_RESP_EN_OFF 1 +#define RA_CTL_DW1_CACHE_FLUSH_EN_OFF 14 +#define RA_CTL_DW1_CACHE_EN_OFF 15 +#define RA_CTL_DW1_RAID_OFF 26 +#define RA_CTL_DW1_RAID_M 0xFC000000 + +/* Excluding pool entries */ +#define CCIX_HA_CAP_SIZE 3 * sizeof(uint32_t) + +#define HA_CAP_DW1_HA_DISC_RD_STAT_OFF 0 +#define HA_CAP_DW1_NUM_HA_IDS_OFF 1 +#define HA_CAP_DW1_HA_MEM_POOL_CAP_OFF 8 +#define HA_CAP_DW1_HA_QACK_OFF 14 +#define HA_CAP_DW1_HA_HW_QACK_CAP_OFF 15 +#define HA_CAP_DW1_HA_MEM_EXP_CAP_OFF 16 +#define HA_CAP_DW1_HA_EVICT_HINT_CAP_OFF 17 +#define HA_CAP_DW1_HA_WRITE_EVICT_FULL_HIT_CAP_OFF 18 +#define HA_CAP_DW1_MEM_POOL_READY_TIME_VALUE_OFF 19 +#define HA_CAP_DW1_MEM_POOL_READY_TIME_VALUE_M 0x0FF80000 +#define HA_CAP_DW1_MEM_POOL_READY_TIME_SCALE_OFF 28 +#define HA_CAP_DW1_MEM_POOL_READY_TIME_SCALE_M 0x70000000 +#define HA_CAP_DW1_MEM_POOL_READY_STA_OFF 31 + +#define HA_CAP_DW2_HA_ERROR_LOG_OFF 20 + +#define MEM_POOL_CAP_DW1_READY_STA_OFF 0 +#define MEM_POOL_CAP_DW1_GEN_MEM_TYPE_OFF 1 +enum { + mem_type_other = 0, + mem_type_expan, + mem_type_hole, + mem_type_rom, + mem_type_volatile, + mem_type_non_volatile, + mem_type_device, +}; + +#define MEM_POOL_CAP_DW1_SPEC_MEM_TYPE_OFF 4 +enum { + mem_spec_other = 0, + mem_spec_sram, + mem_spec_ddr, + mem_spec_nvdimm_f, + mem_spec_nvdimm_n, + mem_spec_hbm, + mem_spec_flash, +}; +#define MEM_POOL_CAP_DW1_ADDR_CAP_OFF 7 +#define MEM_POOL_CAP_DW1_MEM_ATTR_OFF 8 +#define MEM_POOL_CAP_DW1_MEM_ATTR_DEV 0x0 +#define MEM_POOL_CAP_DW1_MEM_ATTR_NONCACHE 0x4 +#define MEM_POOL_CAP_DW1_MEM_ATTR_NORMAL 0x5 +#define MEM_POOL_CAP_DW1_MEM_EXT_ATTR_OFF 11 +#define MEM_POOL_CAP_DW1_MEM_EXT_ATTR_NONE 0x0 +#define MEM_POOL_CAP_DW1_MEM_EXT_ATTR_PRIVATE 0x1 +#define MEM_POOL_CAP_DW1_MEM_POOL_SIZE_L_CAP_OFF 16 + +#define MEM_POOL_CAP_DW2_MEM_POOL_SIZE_H_CAP_OFF 0 + +/* Not including HAID or HBAT entries */ +#define CCIX_HA_CTL_SIZE 6 * sizeof(uint32_t) +#define CCIX_HAID_ENTRY_SIZE 1 * sizeof(uint32_t) +#define CCIX_BAT_ENTRY_SIZE 2 * sizeof(uint32_t) + +#define CCIX_POOL_CAP_SIZE 2 * sizeof(uint32_t) +#define CCIX_GUID_SIZE 5 * sizeof(uint32_t) +#define CCIX_SAM_ENTRY_SIZE 3 * sizeof(uint32_t) +#define CCIX_GUID_DW0 0xC3CB993B +#define CCIX_GUID_DW1 0x02C4436F +#define CCIX_GUID_DW2 0x9B68D271 +#define CCIX_GUID_DW3 0xF2E8CA31 +#define CCIX_GUID_DW4_VERSION_OFF 0 + +/* HACK - What is the best way to do this in Qemu? */ +CCIXState *CCIXFuncs[256]; + +typedef void (*am_cb_t)(PCIDevice *d, CCIXState *s, uint16_t offset, + uint16_t cap_start_offset, uint32_t val); +struct am { + uint16_t offset; + uint16_t cap_start_offset; + am_cb_t am_cb; +}; + +static int am_cmp(const void *ap, const void *bp) +{ + const uint16_t *a; + const uint16_t *b; + + a = ap; + b = bp; + + return *a - *b; +} + +void initialize_ccixstate(CCIXState *s, PCIDevice *d) +{ + s->am_tree = g_tree_new(am_cmp); + s->pci_dev = d; +} + +static void am_table_add(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start_offset, am_cb_t cb) +{ + struct am *entry = g_malloc0(sizeof *entry); + + if (entry) { + entry->offset = offset; + entry->cap_start_offset = cap_start_offset; + entry->am_cb = cb; + g_tree_insert(s->am_tree, &entry->offset, entry); + } +} + +void ccix_write_config(PCIDevice *pci_dev, CCIXState *s, uint32_t addr, uint32_t val_in, int l) +{ + uint64_t key = addr; + struct am *entry = g_tree_lookup(s->am_tree, &key); + + if (entry && entry->offset == addr) + entry->am_cb(pci_dev, s, addr, entry->cap_start_offset, val_in); +} + +#define CCIX_COMP_ID_GENERAL 0x0000 +#define CCIX_COMP_ID_TDL_DVSEC 0x0001 +#define CCIX_COMP_ID_PRL_DVSEC 0x0002 +#define CCIX_COMP_ID_PRL_COMMON 0x0003 +#define CCIX_COMP_ID_PRL_PORT 0x0004 +#define CCIX_COMP_ID_PRL_LINK 0x0005 +#define CCIX_COMP_ID_HA 0x0006 +#define CCIX_COMP_ID_RA 0x0008 +#define CCIX_COMP_ID_SA 0x000A + +#define CCIX_DVSEC_CAP_POS_OFFSET 0x0C +#define CCIX_DVSEC_CTR_POS_OFFSET 0x10 + +static void ccix_dvsec_fill_dvsec_header(PCIDevice *pci_dev, uint16_t offset, + uint16_t cap_size, + uint16_t cap_offset, + uint16_t control_size, + uint16_t control_offset) +{ + uint32_t *dword_addr; + + dword_addr = (uint32_t *)(pci_dev->config + offset + + CCIX_DVSEC_CAP_POS_OFFSET); + *dword_addr = cap_size | (cap_offset << 20); + dword_addr = (uint32_t *)(pci_dev->config + offset + + CCIX_DVSEC_CTR_POS_OFFSET); + *dword_addr = control_size | (control_offset << 20); +} + +static void ccix_fill_cap_header(PCIDevice *pci_dev, uint16_t cap_offset, + uint16_t ccix_comp, uint16_t next_offset) +{ + uint8_t ver = 1; + uint32_t *dword_addr; + + dword_addr = (uint32_t *)(pci_dev->config + cap_offset); + *dword_addr = ccix_comp | (ver << 16) | (next_offset << 20); +} + +static void ra_ctl_dw1_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t cur_val, new_val = 0; + bool ra_en_cur, ra_en_req; + uint32_t capval; + bool ra_snoop_resp_en_cur, ra_snoop_resp_en_req; + bool ra_cache_flush_en_cur, ra_cache_flush_en_req; + bool ra_cache_en_cur, ra_cache_en_req; + uint8_t raid_cur, raid_req; + + cur_val = pci_get_long(pci_dev->config + offset); + ra_en_cur = cur_val & (1 << RA_CTL_DW1_EN_OFF); + ra_en_req = req_val & (1 << RA_CTL_DW1_EN_OFF); + + if (ra_en_cur != ra_en_req) + printf("Changing RA Enable to %d\n", ra_en_req); + + if (ra_en_req) + new_val = (1 << RA_CTL_DW1_EN_OFF); + + ra_snoop_resp_en_cur = cur_val & (1 << RA_CTL_DW1_SNOOP_RESP_EN_OFF); + ra_snoop_resp_en_req = req_val & (1 << RA_CTL_DW1_SNOOP_RESP_EN_OFF); + + if (ra_snoop_resp_en_cur != ra_snoop_resp_en_req) + printf("Changing RA Snoop REsp Enabled to %d\n", + ra_snoop_resp_en_req); + + if (ra_snoop_resp_en_req) + new_val |= (1 << RA_CTL_DW1_SNOOP_RESP_EN_OFF); + + ra_cache_flush_en_cur = cur_val & (1 << RA_CTL_DW1_CACHE_FLUSH_EN_OFF); + ra_cache_flush_en_req = req_val & (1 << RA_CTL_DW1_CACHE_FLUSH_EN_OFF); + if (ra_cache_flush_en_cur != ra_cache_flush_en_req) { + printf("Enabling or disabling a cache flush %d\n", + ra_cache_flush_en_req); + + //Make the cache flush status update instantaneous for now + capval = pci_get_long(pci_dev->config + cap_start + 0x4); + if (ra_cache_flush_en_req) + capval |= (1 << RA_CAP_DW1_RA_CACHE_FLUSH_STA_OFF); + else + capval &= ~ (1 << RA_CAP_DW1_RA_CACHE_FLUSH_STA_OFF); + pci_set_long(pci_dev->config + cap_start + 0x4, capval); + } + if (ra_cache_flush_en_req) + new_val |= (1 << RA_CTL_DW1_CACHE_FLUSH_EN_OFF); + + ra_cache_en_cur = cur_val & (1 << RA_CTL_DW1_CACHE_EN_OFF); + ra_cache_en_req = req_val & (1 << RA_CTL_DW1_CACHE_EN_OFF); + if (ra_cache_en_cur != ra_cache_en_req) + printf("Enabling of disabling the RA cache %d\n", ra_cache_en_req); + + if (ra_cache_en_req) + new_val |= (1 << RA_CTL_DW1_CACHE_EN_OFF); + + raid_cur = (cur_val & RA_CTL_DW1_RAID_M) >> RA_CTL_DW1_RAID_OFF; + raid_req = (req_val & RA_CTL_DW1_RAID_M) >> RA_CTL_DW1_RAID_OFF; + if (raid_cur != raid_req) + printf("RA ID changing from %u to %u\n", raid_cur, raid_req); + + new_val |= (raid_req << RA_CTL_DW1_RAID_OFF) & RA_CTL_DW1_RAID_M; + + pci_set_long(pci_dev->config + offset, new_val); +} + + +static void comncntl1_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t cur_val, new_val = 0; + bool dev_en_req, dev_en_cur; + bool pp_en_req, pp_en_cur; + bool id_val_req, id_val_cur; + bool rsam_val_req, rsam_val_cur; + uint8_t eaid_req, eaid_cur; + uint8_t devid_req, devid_cur; + int i; + uint32_t capval; + + cur_val = pci_get_long(pci_dev->config + offset); + + dev_en_req = req_val & (1 << CM_CTL_DW1_DEVICE_EN_OFF); + dev_en_cur = cur_val & (1 << CM_CTL_DW1_DEVICE_EN_OFF); + if (dev_en_req != dev_en_cur) { + printf("Dev enable changing to %d\n", dev_en_req); + /* Now I need to set it on all devices */ + if (s->ccix_dev_name) + for (i = 0; i < sizeof(CCIXFuncs)/sizeof(*CCIXFuncs); i++) { + if (!CCIXFuncs[i] || !CCIXFuncs[i]->ccix_dev_name) + continue; + if (CCIXFuncs[i] != s && !strcmp(CCIXFuncs[i]->ccix_dev_name, s->ccix_dev_name)) { + pci_set_long(CCIXFuncs[i]->pci_dev->config + CCIXFuncs[i]->enable_offset, + pci_get_word(CCIXFuncs[i]->pci_dev->config + CCIXFuncs[i]->enable_offset) | 0x1); + } + } + } + if (dev_en_req) + new_val |= (1 << CM_CTL_DW1_DEVICE_EN_OFF); + + pp_en_req = req_val & (1 << CM_CTL_DW1_PRIMARY_PORT_EN_OFF); + pp_en_cur = cur_val & (1 << CM_CTL_DW1_PRIMARY_PORT_EN_OFF); + if (pp_en_req != pp_en_cur) + printf("Primary port enable changing to %d\n", pp_en_req); + if (pp_en_req) + new_val |= (1 << CM_CTL_DW1_PRIMARY_PORT_EN_OFF); + /* NOT DOING MESH ENABLE FOR NOW */ + /* NOT DOING PORT AG FOR NOW */ + + id_val_req = req_val & (1 << CM_CTL_DW1_IDM_TABLE_VALID_OFF); + id_val_cur = cur_val & (1 << CM_CTL_DW1_IDM_TABLE_VALID_OFF); + if (id_val_req != id_val_cur) + printf("Validity of IDM changing to %d\n", id_val_req); + if (id_val_req) + new_val |= (1 << CM_CTL_DW1_IDM_TABLE_VALID_OFF); + + rsam_val_req = req_val & (1 << CM_CTL_DW1_RSAM_TABLE_VALID_OFF); + rsam_val_cur = cur_val & (1 << CM_CTL_DW1_RSAM_TABLE_VALID_OFF); + if (rsam_val_req != rsam_val_cur) + printf("RSAM valid changing to %d\n", rsam_val_req); + if (rsam_val_req) + new_val |= (1 << CM_CTL_DW1_RSAM_TABLE_VALID_OFF); + /* NOT DOING HSAM FOR NOW */ + /* NOT DOING SW SERVICES PORTAL FOR NOW */ + + eaid_req = (req_val & CM_CTL_DW1_ERR_AGENT_ID_M) >> + CM_CTL_DW1_ERR_AGENT_ID_OFF; + eaid_cur = (cur_val & CM_CTL_DW1_ERR_AGENT_ID_M) >> + CM_CTL_DW1_ERR_AGENT_ID_OFF; + if (eaid_req != eaid_cur) + printf("EAID for device changing from %d to %d\n", + eaid_cur, eaid_req); + new_val |= eaid_req << CM_CTL_DW1_ERR_AGENT_ID_OFF; + + devid_req = (req_val & CM_CTL_DW1_DEVID_M) >> CM_CTL_DW1_DEVID_OFF; + devid_cur = (cur_val & CM_CTL_DW1_DEVID_M) >> CM_CTL_DW1_DEVID_OFF; + if (devid_req != devid_cur) { + printf("DEVID changing from %d to %d, updating status\n", + devid_cur, devid_req); + + capval = pci_get_long(pci_dev->config + cap_start + 0x4); + capval &= ~CM_CAP_DW1_DEVID_M; + capval |= (uint32_t)devid_req << CM_CAP_DW1_DEVID_OFF; + pci_set_long(pci_dev->config + cap_start + 4, capval); + + if (s->ccix_dev_name) + for (i = 0; i < sizeof(CCIXFuncs)/sizeof(*CCIXFuncs); i++) { + if (!CCIXFuncs[i] || !CCIXFuncs[i]->ccix_dev_name) + continue; + if (CCIXFuncs[i] != s && !strcmp(CCIXFuncs[i]->ccix_dev_name, s->ccix_dev_name)) { + capval = pci_get_long(CCIXFuncs[i]->pci_dev->config + CCIXFuncs[i]->enable_offset); + capval &= ~CM_CAP_DW1_DEVID_M; + capval |= (uint32_t)devid_req << CM_CAP_DW1_DEVID_OFF; + pci_set_long(CCIXFuncs[i]->pci_dev->config + CCIXFuncs[i]->enable_offset, capval); + } + } + } + new_val |= (uint32_t)devid_req << CM_CTL_DW1_DEVID_OFF; + + pci_set_long(pci_dev->config + offset, new_val); +} + +static void comncntl2_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t cur_val, new_val = 0; + bool partial_cur, partial_req; + uint32_t addrw_cur, addrw_req; + + cur_val = pci_get_long(pci_dev->config + offset); + + partial_cur = cur_val & (1 << CM_CTL_DW2_PART_CS_EN_OFF); + partial_req = req_val & (1 << CM_CTL_DW2_PART_CS_EN_OFF); + if (partial_cur != partial_req) + printf("Changing Partial Cache enable to %d\n", partial_req); + if (partial_req) + new_val = (1 << CM_CTL_DW2_PART_CS_EN_OFF); + /* Cacheline size 128 not yet supported */ + + addrw_cur = (cur_val & CM_CTL_DW2_ADDR_W_EN_M) >> CM_CTL_DW2_ADDR_W_EN_OFF; + addrw_req = (req_val & CM_CTL_DW2_ADDR_W_EN_M) >> CM_CTL_DW2_ADDR_W_EN_OFF; + /* No sanity checking yet */ + if (addrw_cur != addrw_req) + printf("Changing Address Width to %d\n", addrw_req); + new_val = (addrw_req << CM_CTL_DW2_ADDR_W_EN_OFF); + + pci_set_long(pci_dev->config + offset, new_val); +} + +static void port_ctl_dw1_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t cur_val; + uint32_t new_val = 0; + bool en_req, en_cur; + bool opt_req, opt_cur; + uint32_t links_req, links_cur; + uint32_t psam_req, psam_cur; + + cur_val = pci_get_long(pci_dev->config + offset); + en_cur = cur_val & (1 << PT_CTL_DW1_PORT_EN_OFF); + en_req = req_val & (1 << PT_CTL_DW1_PORT_EN_OFF); + if (en_cur != en_req) + printf("Enabling of port changing to %d\n", en_req); + if (en_req) + new_val |= (1 << PT_CTL_DW1_PORT_EN_OFF); + + opt_cur = cur_val & (1 << PT_CTL_DW1_OPT_HEADER_EN_OFF); + opt_req = req_val & (1 << PT_CTL_DW1_OPT_HEADER_EN_OFF); + if (opt_cur != opt_req) + printf("Enabling of optimized header changing to %d\n", opt_req); + if (opt_req) + new_val |= (1 << PT_CTL_DW1_OPT_HEADER_EN_OFF); + + links_cur = (cur_val & PT_CTL_DW1_LINKS_EN_M) >> PT_CTL_DW1_LINKS_EN_OFF; + links_req = (req_val & PT_CTL_DW1_LINKS_EN_M) >> PT_CTL_DW1_LINKS_EN_OFF; + if (links_cur != links_req) + printf("Number of enabled links changing from %d to %d\n", + links_cur, links_req); + new_val |= (links_req << PT_CTL_DW1_LINKS_EN_OFF); + + psam_cur = (cur_val & PT_CTL_DW1_PSAM_ENTRIES_EN_M) >> + PT_CTL_DW1_PSAM_ENTRIES_EN_OFF; + psam_req = (req_val & PT_CTL_DW1_PSAM_ENTRIES_EN_M) >> + PT_CTL_DW1_PSAM_ENTRIES_EN_OFF; + if (psam_cur != psam_req) + printf("Number of psam entries changing from %d to %d\n", + psam_cur, psam_req); + new_val |= (psam_req << PT_CTL_DW1_PSAM_ENTRIES_EN_OFF); + + pci_set_long(pci_dev->config + offset, new_val); +} + +static void lk_ctl_dw1_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t cur_val; + uint32_t new_val = 0; + bool en_cur, en_req; + bool cred_en_cur, cred_en_req; + bool mpack_en_cur, mpack_en_req; + bool nocompack_cur, nocompack_req; + uint32_t maxpkt_cur, maxpkt_req; + bool addr_type_cur, addr_type_req; + + cur_val = pci_get_long(pci_dev->config + offset); + + en_cur = cur_val & (1 << LK_CTL_DW1_LINK_EN_OFF); + en_req = req_val & (1 << LK_CTL_DW1_LINK_EN_OFF); + if (en_cur != en_req) + printf("Changing link enabled status to %d\n", en_req); + if (en_req) + new_val |= (1 << LK_CTL_DW1_LINK_EN_OFF); + + cred_en_cur = cur_val & (1 << LK_CTL_DW1_CREDIT_EN_OFF); + cred_en_req = req_val & (1 << LK_CTL_DW1_CREDIT_EN_OFF); + if (cred_en_cur != cred_en_req) + printf("Changing link credit enable status to %d\n", cred_en_cur); + if (cred_en_req) + new_val |= (1 << LK_CTL_DW1_CREDIT_EN_OFF); + + mpack_en_cur = cur_val & (1 << LK_CTL_DW1_MESSAGE_PACKING_EN_OFF); + mpack_en_req = req_val & (1 << LK_CTL_DW1_MESSAGE_PACKING_EN_OFF); + if (mpack_en_cur != mpack_en_req) + printf("Changing message packing enable to %d\n", mpack_en_req); + if (mpack_en_req) + new_val |= (1 << LK_CTL_DW1_MESSAGE_PACKING_EN_OFF); + nocompack_cur = cur_val & (1 << LK_CTL_DW1_NO_COMP_ACK_EN_OFF); + nocompack_req = req_val & (1 << LK_CTL_DW1_NO_COMP_ACK_EN_OFF); + if (nocompack_cur != nocompack_req) + printf("Setting nocompack for link to %d\n", nocompack_req); + if (nocompack_req) + new_val |= (1 << LK_CTL_DW1_NO_COMP_ACK_EN_OFF); + + maxpkt_cur = (cur_val & LK_CTL_DW1_MAX_PKT_SIZE_EN_M) >> + LK_CTL_DW1_MAX_PKT_SIZE_EN_OFF; + maxpkt_req = (req_val & LK_CTL_DW1_MAX_PKT_SIZE_EN_M) >> + LK_CTL_DW1_MAX_PKT_SIZE_EN_OFF; + if (maxpkt_cur != maxpkt_req) + printf("Changing max packet size on link from %d to %d\n", + maxpkt_cur, maxpkt_req); + new_val |= maxpkt_req << LK_CTL_DW1_MAX_PKT_SIZE_EN_OFF; + + addr_type_cur = cur_val & (1 << LK_CTL_DW1_LINK_ENT_ADDR_TYPE_OFF); + addr_type_req = req_val & (1 << LK_CTL_DW1_LINK_ENT_ADDR_TYPE_OFF); + if (addr_type_cur != addr_type_req) + printf("Link Entry Address Type changed to %d\n", addr_type_req); + if (addr_type_req) + new_val |= (1 << LK_CTL_DW1_LINK_ENT_ADDR_TYPE_OFF); + + pci_set_long(pci_dev->config + offset, new_val); +} + +static void lk_ctl_dw2_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t new_val = req_val; + /* TODO */ + pci_set_long(pci_dev->config + offset, new_val); +} + +static void lk_ctl_dw3_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t new_val = req_val; + /* TODO */ + pci_set_long(pci_dev->config + offset, new_val); +} + +static void lk_ctl_dw4_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + uint32_t new_val = req_val; + /* TODO */ + pci_set_long(pci_dev->config + offset, new_val); +} + +static void idm_entry_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting idm entry\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + +static void psam_entry_dw0_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting psam entry dw0\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + +static void psam_entry_dw1_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting psam entry dw1\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + +static void psam_entry_dw2_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting psam entry dw2\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + + +static void sam_entry_dw0_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting sam entry dw0\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + +static void sam_entry_dw1_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting sam entry dw1\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + +static void sam_entry_dw2_set(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, + uint16_t cap_start, uint32_t req_val) +{ + /* No sanity checking */ + printf("Setting sam entry dw2\n"); + pci_set_long(pci_dev->config + offset, req_val); +} + +static void ccix_prl_common_cap(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_cap_offset, + uint16_t next_cap_offset, + uint16_t device_err_log_offset, + uint16_t idm_table_offset, + uint16_t rsam_offset, uint16_t rsam_size, + uint16_t hsam_offset, uint16_t hsam_size, + uint16_t sr_table_offset, + uint16_t sw_portal_offset) +{ + uint32_t *cap_start = (uint32_t *)(pci_dev->config + this_cap_offset); + uint32_t multiport_dev_cap; + + /* Put in our first capability */ + ccix_fill_cap_header(pci_dev, this_cap_offset, CCIX_COMP_ID_PRL_COMMON, + next_cap_offset); + /* ComnCapStat1 */ + if (s->flags & (1 << PRIMARY_PORT_BIT)) + multiport_dev_cap = 0x7; + else + multiport_dev_cap = 0x5; + + *(cap_start + 1) = (multiport_dev_cap << CM_CAP_DW1_MULTIPORT_CAP_OFF) | + (0 << CM_CAP_DW1_VERSION_CAP_OFF) | + (0 << CM_CAP_DW1_DEVID_OFF); + /* ComnCapStat2 */ + *(cap_start + 2) = (1 << CM_CAP_DW2_DISC_READY_CAP_OFF) | + (0 << CM_CAP_DW2_PART_CS_CAP_OFF) | + (0 << CM_CAP_DW2_PORT_AG_CAP_OFF) | + /* 64 byte only */ + (0 << CM_CAP_DW2_CL_SIZE_CAP_OFF) | + /* 48 bit addressing only */ + (0 << CM_CAP_DW2_ADDR_W_CAP_OFF) | + /* No multihop port aggregation */ + (0 << CM_CAP_DW2_MH_CAP_OFF) | + ((sw_portal_offset ? 1 : 0) << CM_CAP_DW2_SW_PORT_CAP_OFF) | + /* Natural alignment over 4GB */ + (1 << CM_CAP_DW2_SAM_ALIGN_CAP_OFF) | + /* random time values */ + (3 << CM_CAP_DW2_READY_TIME_VAL_OFF) | + (1 << CM_CAP_DW2_READY_TIME_SCALE_OFF); + /* ComnCapStat3 */ + *(cap_start + 3) = 0; + /* Device Error Log Offset */ + *(cap_start + 4) = device_err_log_offset << CM_CAP_DW4_DEV_ERR_LOG_OFFSET_OFF; + *(cap_start + 5) = idm_table_offset << CM_CAP_DW5_IDM_OFFSET_OFF; + *(cap_start + 6) = (rsam_size << CM_CAP_DW6_RSAM_SIZE_OFF) | + (rsam_offset << CM_CAP_DW6_RSAM_OFFSET_OFF); + *(cap_start + 7) = (hsam_size << CM_CAP_DW7_HSAM_SIZE_OFF) | + (hsam_offset << CM_CAP_DW7_HSAM_OFFSET_OFF); + *(cap_start + 8) = sr_table_offset << CM_CAP_DW8_SR_OFFSET_OFF; + *(cap_start + 9) = sw_portal_offset << CM_CAP_DW9_SW_PORTAL_OFF; +} + +static void ccix_prl_ra_cap(PCIDevice *pci_dev, + uint16_t this_cap_offset, + uint16_t next_cap_offset, + uint16_t error_log_offset) +{ + uint32_t *cap_start = (uint32_t *)(pci_dev->config + this_cap_offset); + + ccix_fill_cap_header(pci_dev, this_cap_offset, CCIX_COMP_ID_RA, next_cap_offset); + *(cap_start + 1) = (1 << RA_CAP_DW1_RA_DISC_RDY_STAT_OFF) | + /* Some example values follow */ + (3 << RA_CAP_DW1_RA_CACHE_FLUSH_TIME_VALUE_OFF) | + (1 << RA_CAP_DW1_RA_CACHE_FLUSH_TIME_SCALE_OFF) | + (0 << RA_CAP_DW1_RA_CACHE_FLUSH_STA_OFF); + + *(cap_start + 2) = (error_log_offset << RA_CAP_DW2_RA_ERROR_LOG_OFFSET_OFF); +} + +static void ccix_prl_ha_cap(PCIDevice *pci_dev, + uint16_t this_cap_offset, + uint16_t next_cap_offset, + uint16_t error_log_offset, + uint8_t num_ids, + uint8_t num_pools) +{ + uint32_t *cap_start = (uint32_t *)(pci_dev->config + this_cap_offset); + int i; + + ccix_fill_cap_header(pci_dev, this_cap_offset, CCIX_COMP_ID_HA, next_cap_offset); + *(cap_start + 1) = (1 << HA_CAP_DW1_HA_DISC_RD_STAT_OFF) | + ((num_ids - 1) << HA_CAP_DW1_NUM_HA_IDS_OFF) | + (num_pools << HA_CAP_DW1_HA_MEM_POOL_CAP_OFF) | + (0 << HA_CAP_DW1_HA_QACK_OFF) | + (0 << HA_CAP_DW1_HA_HW_QACK_CAP_OFF) | + (0 << HA_CAP_DW1_HA_MEM_EXP_CAP_OFF) | /* No support for SAs being homed here */ + (0 << HA_CAP_DW1_HA_EVICT_HINT_CAP_OFF) | + (0 << HA_CAP_DW1_HA_WRITE_EVICT_FULL_HIT_CAP_OFF) | + (3 << HA_CAP_DW1_MEM_POOL_READY_TIME_VALUE_OFF) | + (4 << HA_CAP_DW1_MEM_POOL_READY_TIME_SCALE_OFF) | + (1 << HA_CAP_DW1_MEM_POOL_READY_STA_OFF); + *(cap_start + 2) = error_log_offset << HA_CAP_DW2_HA_ERROR_LOG_OFF; + + for (i = 0; i < num_pools; i++) { + *(cap_start + 3 + i * 2) = (1 << MEM_POOL_CAP_DW1_READY_STA_OFF) | + (mem_type_volatile << MEM_POOL_CAP_DW1_GEN_MEM_TYPE_OFF) | + (mem_spec_hbm << MEM_POOL_CAP_DW1_SPEC_MEM_TYPE_OFF) | + (0 << MEM_POOL_CAP_DW1_ADDR_CAP_OFF) | + (MEM_POOL_CAP_DW1_MEM_ATTR_NORMAL << MEM_POOL_CAP_DW1_MEM_ATTR_OFF) | + (MEM_POOL_CAP_DW1_MEM_EXT_ATTR_NONE << MEM_POOL_CAP_DW1_MEM_EXT_ATTR_OFF) | + (0xFFFF << MEM_POOL_CAP_DW1_MEM_POOL_SIZE_L_CAP_OFF); + *(cap_start + 3 + i * 2 + 1) = 0xF << MEM_POOL_CAP_DW2_MEM_POOL_SIZE_H_CAP_OFF; + } +} + +static void ccix_prl_port_cap(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_cap_offset, + uint16_t next_cap_offset, + uint16_t error_log_offset) +{ + uint32_t *cap_start = (uint32_t *)(pci_dev->config + this_cap_offset); + + ccix_fill_cap_header(pci_dev, this_cap_offset, CCIX_COMP_ID_PRL_PORT, + next_cap_offset); + *(cap_start + 1) = (1 << PT_CAP_DW1_DISC_READY_OFF) | + (1 << PT_CAP_DW1_OPT_HEADER_OFF) | + (0 << PT_CAP_DW1_P2P_FORWARDING_OFF) | + (s->num_links << PT_CAP_DW1_LINKS_OFF) | + (s->psam_entries << PT_CAP_DW1_PSAM_ENTRIES_OFF) | + ((s->port_id & 0xf) << PT_CAP_DW1_PORTID_OFF); + *(cap_start + 2) = 0; /* No Port AG */ + *(cap_start + 3) = 0; /* No Port FW */ + *(cap_start + 4) = error_log_offset << 20; +} + +static void ccix_prl_link_cap(PCIDevice *pci_dev, + uint16_t this_cap_offset, + uint16_t next_cap_offset, + uint16_t error_log_offset) +{ + uint32_t *cap_start = (uint32_t *)(pci_dev->config + this_cap_offset); + + ccix_fill_cap_header(pci_dev, this_cap_offset, CCIX_COMP_ID_PRL_LINK, + next_cap_offset); + + *(cap_start + 1) = (1 << LK_CAP_DW1_DISC_READY_OFF) | + (0 << LK_CAP_DW1_CREDIT_TYPE_OFF) | + (1 << LK_CAP_DW1_MESSAGE_PACKING_OFF) | + (0 << LK_CAP_DW1_NOCOMPACK_OFF) | + (2 << LK_CAP_DW1_MAX_PKT_SIZE_OFF); + + *(cap_start + 2) = (3 << LK_CAP_DW2_MAX_MEM_REQ_SEND_OFF) | + (4 << LK_CAP_DW2_MAX_SNP_REQ_SEND_OFF) | + (5 << LK_CAP_DW2_MAX_DAT_REQ_SEND_OFF); + *(cap_start + 3) = (6 << LK_CAP_DW3_MAX_MEM_REQ_RECV_OFF) | + (7 << LK_CAP_DW3_MAX_SNP_REQ_RECV_OFF) | + (8 << LK_CAP_DW3_MAX_DAT_REQ_RECV_OFF); + *(cap_start + 4) = (9 << LK_CAP_DW4_MAX_MISC_REQ_SEND_CAP_OFF) | + (10 << LK_CAP_DW4_MAX_MISC_REQ_RECV_CAP_OFF); + *(cap_start + 5) = error_log_offset << 20; +} + +static void ccix_prl_common_ctl(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_ctl_offset, + uint16_t next_ctl_offset) +{ + uint32_t *ctl_start = (uint32_t *)(pci_dev->config + this_ctl_offset); + + ccix_fill_cap_header(pci_dev, this_ctl_offset, CCIX_COMP_ID_PRL_COMMON, + next_ctl_offset); + /* ComnCntl1 */ + *(ctl_start + 1) = (0 << CM_CTL_DW1_DEVICE_EN_OFF) | + (0 << CM_CTL_DW1_PRIMARY_PORT_EN_OFF) | + (0 << CM_CTL_DW1_MESH_EN_OFF) | + (0 << CM_CTL_DW1_PORT_AG_EN_OFF) | + (0 << CM_CTL_DW1_IDM_TABLE_VALID_OFF) | + (0 << CM_CTL_DW1_RSAM_TABLE_VALID_OFF) | + (0 << CM_CTL_DW1_HSAM_TABLE_VALID_OFF) | + (0 << CM_CTL_DW1_SW_PORT_ENABLE_OFF) | + (0 << CM_CTL_DW1_ERR_AGENT_ID_OFF) | + (0 << CM_CTL_DW1_DEVID_OFF); + am_table_add(pci_dev, s, this_ctl_offset + 4, this_ctl_offset, comncntl1_set); + s->enable_offset = this_ctl_offset + 4; + /* ComnCtl2 */ + *(ctl_start + 2) = 0; // No support for anything in here yet. + am_table_add(pci_dev, s, this_ctl_offset + 8, this_ctl_offset, comncntl2_set); + /* No snoop request hash mask yet */ + /* No SW Service Portal yet */ +} + +static void ccix_prl_ra_ctl(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_ctl_offset, + uint16_t next_ctl_offset, + uint16_t ra_id) +{ + uint32_t *ctl_start = (uint32_t *)(pci_dev->config + this_ctl_offset); + + ccix_fill_cap_header(pci_dev, this_ctl_offset, CCIX_COMP_ID_RA, + next_ctl_offset); + + *(ctl_start + 1) = (0 << RA_CTL_DW1_EN_OFF) | + (0 << RA_CTL_DW1_SNOOP_RESP_EN_OFF) | + (0 << RA_CTL_DW1_CACHE_FLUSH_EN_OFF) | + (0 << RA_CTL_DW1_CACHE_EN_OFF) | + (0 << RA_CTL_DW1_RAID_OFF); + am_table_add(pci_dev, s, this_ctl_offset + 4, this_ctl_offset, ra_ctl_dw1_set); +} + +static void ccix_prl_ha_ctl(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_ctl_offset, + uint16_t next_ctl_offset, + uint16_t num_ids, + uint16_t num_pools) +{ + uint32_t *ctl_start = (uint32_t *)(pci_dev->config + this_ctl_offset); + + ccix_fill_cap_header(pci_dev, this_ctl_offset, CCIX_COMP_ID_HA, + next_ctl_offset); + *(ctl_start + 1) = 0; +} + +static void ccix_prl_port_ctl(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_ctl_offset, + uint16_t next_ctl_offset, + uint8_t psam_table_entries) +{ + uint8_t i; + uint32_t *ctl_start = (uint32_t *)(pci_dev->config + this_ctl_offset); + + ccix_fill_cap_header(pci_dev, this_ctl_offset, CCIX_COMP_ID_PRL_PORT, + next_ctl_offset); + /* Port Control */ + *(ctl_start + 1) = 0; + am_table_add(pci_dev, s, this_ctl_offset + 4, this_ctl_offset, port_ctl_dw1_set); + + for (i = 0; i < psam_table_entries; i++) { + am_table_add(pci_dev, s, this_ctl_offset + 0x14 + (3 * i + 0) * 4, + 0, psam_entry_dw0_set); + am_table_add(pci_dev, s, this_ctl_offset + 0x14 + (3 * i + 1) * 4, + 0, psam_entry_dw1_set); + am_table_add(pci_dev, s, this_ctl_offset + 0x14 + (3 * i + 2) * 4, + 0, psam_entry_dw2_set); + } +} + +static void ccix_prl_link_ctl(PCIDevice *pci_dev, + CCIXState *s, + uint16_t this_ctl_offset, + uint16_t next_ctl_offset, + uint8_t num_links) +{ + uint32_t *ctl_start = (uint32_t *)(pci_dev->config + this_ctl_offset); + uint8_t i; + + ccix_fill_cap_header(pci_dev, this_ctl_offset, CCIX_COMP_ID_PRL_LINK, + next_ctl_offset); + + for (i = 0; i < num_links; i++) { + /* LinkAttrCntl for link i */ + *(ctl_start + 1 + i * 6) = 0; + am_table_add(pci_dev, s, this_ctl_offset + 4 + i * 24, 0, lk_ctl_dw1_set); + /* LinkMaxCreditCntl for link i */ + *(ctl_start + 1 + i * 6 + 1) = 0; + am_table_add(pci_dev, s, this_ctl_offset + 4 + i * 24 + 4, 0, lk_ctl_dw2_set); + *(ctl_start + 1 + i * 6 + 2) = 0; + am_table_add(pci_dev, s, this_ctl_offset + 4 + i * 24 + 8, 0, lk_ctl_dw3_set); + *(ctl_start + 1 + i * 6 + 3) = 0; + am_table_add(pci_dev, s, this_ctl_offset + 4 + i * 24 + 0xC, 0, lk_ctl_dw4_set); + + /* error fields - to do */ + *(ctl_start + 1 + i * 24 + 4) = 0; + *(ctl_start + 1 + i * 24 + 5) = 0; + } + /* transport id */ + for (i = 0; i < num_links; i++) + //to check + *(ctl_start + 1 + num_links * 6 + 4 * i) = 0; +} + +static void ccix_idm(PCIDevice *pci_dev, CCIXState *s, uint16_t offset) +{ + int i; + + for (i = 0; i < 64; i++) + am_table_add(pci_dev, s, offset + i * 4, 0, idm_entry_set); +} + +static void ccix_sam(PCIDevice *pci_dev, CCIXState *s, uint16_t offset, uint8_t entries) +{ + int i; + + for (i = 0; i < entries; i++) { + am_table_add(pci_dev, s, offset + 3 * i * 4, 0, sam_entry_dw0_set); + am_table_add(pci_dev, s, offset + (3 * i + 1) * 4, 0, sam_entry_dw1_set); + am_table_add(pci_dev, s, offset + (3 * i + 2) * 4, 0, sam_entry_dw2_set); + } +} +static void ccix_guid(PCIDevice *pci_dev, uint16_t offset) +{ + uint32_t *start = (uint32_t *)(pci_dev->config + offset); + + *(start) = CCIX_GUID_DW0; + *(start + 1) = CCIX_GUID_DW1; + *(start + 2) = CCIX_GUID_DW2; + *(start + 3) = CCIX_GUID_DW3; + *(start + 4) = 1 << CCIX_GUID_DW4_VERSION_OFF; +} + +/*FIXME: Is this generic enough that we should put it in the PCIe core */ + +#define CCIX_DVSEC_HEADER_SIZE 0x14 +#define PCIE_DVSEC_HEADER_OFFSET 0x4 /* Offset from start of extend cap */ +#define PCIE_DVSEC_ID_OFFSET 0x8 +static void pcie_add_dvsec(PCIDevice *pci_dev, uint16_t offset, + uint16_t size, uint16_t vendor_id, uint16_t dvsec_id, + uint16_t ccix_guid_offset) +{ + uint16_t *word_addr; + + pcie_add_capability(pci_dev, PCI_EXT_CAP_ID_DVSEC, 1, offset, size); + pci_set_word(pci_dev->config + offset + PCIE_DVSEC_HEADER_OFFSET, + vendor_id); + word_addr = (uint16_t *)(pci_dev->config + offset + PCIE_DVSEC_HEADER_OFFSET + 2); + *word_addr = 0x1 | (size << 4); + + word_addr = (uint16_t *)(pci_dev->config + offset + PCIE_DVSEC_ID_OFFSET); + *word_addr = dvsec_id; + word_addr = (uint16_t *)(pci_dev->config + offset + PCIE_DVSEC_ID_OFFSET + 2); + *word_addr = (ccix_guid_offset << 4); +} + +void ccix_set_port(CCIXState *s) +{ + s->flags |= (1 << CCIX_IS_PORT); + /* Enforce rule of how many psam entries if links is greater than 1 */ + if ((s->num_links > 1) && + (s->psam_entries < s->num_links + 1)) { + printf("Increased psam entries to minimum allowed\n"); + s->psam_entries = s->num_links + 1; + } +} + +enum ccix_entry_type { + common_cap, + ra_cap, + ha_cap, + port_cap, + link_cap, + end_cap, + common_ctl, + ra_ctl, + ha_ctl, + port_ctl, + link_ctl, + end_ctl, + idm_table, + sr_table, + rsam, + hsam, + guid, +}; + +struct ccix_cap { + uint16_t size; + uint16_t offset; + enum ccix_entry_type type; +}; + +static void ccix_add_cap(GList **l, enum ccix_entry_type type, uint16_t size) +{ + struct ccix_cap *c; + + c = malloc(sizeof(*c)); + c->type = type; + c->size = size; + *l = g_list_append(*l, c); +} + +uint16_t ccix_add_prldvsec(PCIDevice *pci_dev, CCIXState *s, uint16_t offset) +{ + /* Runtime configuration of the following is not yet implemented */ + uint16_t ha_num_pools = 2; + uint16_t ha_num_ids = 8; + /* Error log not yet implemented */ + uint16_t error_log_offset = 0; + uint16_t idm_offset = 0; + uint16_t sr_offset = 0; + uint16_t rsam_offset = 0; + uint16_t hsam_offset = 0; + uint16_t guid_offset = 0; + uint16_t max_offset = 0; + uint16_t next_offset; + uint16_t cap_size = 0; + uint16_t cap_offset; + uint16_t ctl_size = 0; + uint16_t ctl_offset; + struct ccix_cap *c; + GList *li; + GList *cap_list = NULL; + GList *ctl_list = NULL; + GList *other_list = NULL; + int i; + + /* + * Build up lists of the CCIX capabilities, controls and other blocks so that we + * can work out their layout in config space before writing the header. + */ + ccix_add_cap(&cap_list, common_cap, CCIX_COMMON_CAP_MAX_SIZE); + if (s->flags & (1 << PRIMARY_PORT_BIT)) + ccix_add_cap(&ctl_list, common_ctl, 0x10); + for (i = 0; i < s->num_ras; i++) { + ccix_add_cap(&cap_list, ra_cap, CCIX_RA_CAP_SIZE); + ccix_add_cap(&ctl_list, ra_ctl, CCIX_RA_CTL_SIZE); + } + for (i = 0; i < s->num_has; i++) { + ccix_add_cap(&cap_list, ha_cap, CCIX_HA_CAP_SIZE + CCIX_POOL_CAP_SIZE * ha_num_pools); + ccix_add_cap(&ctl_list, ha_ctl, + CCIX_HA_CTL_SIZE + + (ha_num_ids + 3) / 4 * CCIX_HAID_ENTRY_SIZE + + ha_num_pools * CCIX_BAT_ENTRY_SIZE); + } + + /* Only functions with CCIX ports may have port and link structures */ + if (s->flags & (1 << CCIX_IS_PORT)) { + ccix_add_cap(&cap_list, port_cap, CCIX_PORT_CAP_SIZE); + ccix_add_cap(&ctl_list, port_ctl, + CCIX_PORT_CTL_SIZE + 12 * s->psam_entries); + ccix_add_cap(&cap_list, link_cap, CCIX_LINK_CAP_SIZE); + ccix_add_cap(&ctl_list, link_ctl, + CCIX_LINK_CTL_SIZE + CCIX_LINK_CTL_PER_LINK_SIZE * s->num_links); + } + + if (s->flags & (1 << PRIMARY_PORT_BIT)) { + ccix_add_cap(&other_list, idm_table, 64 * 4); + if (s->rsam_entries) + ccix_add_cap(&other_list, rsam, CCIX_SAM_ENTRY_SIZE * s->rsam_entries); + if (s->hsam_entries) + ccix_add_cap(&other_list, hsam, CCIX_SAM_ENTRY_SIZE * s->hsam_entries); + ccix_add_cap(&other_list, guid, CCIX_GUID_SIZE); + } + + cap_offset = offset + CCIX_DVSEC_HEADER_SIZE; + next_offset = cap_offset; + for (li = cap_list; li != NULL; li = li->next) { + c = (struct ccix_cap *)li->data; + c->offset = next_offset; + if (li->next == NULL) + next_offset = 0; + else { + next_offset = c->offset + c->size; + } + cap_size += c->size; + max_offset = c->offset + c->size; + } + + next_offset = max_offset; + ctl_offset = max_offset; + for (li = ctl_list; li != NULL; li = li->next) { + c = (struct ccix_cap *)li->data; + c->offset = next_offset; + if (li->next == NULL) + next_offset = 0; + else + next_offset = c->offset + c->size; + + max_offset = c->offset + c->size; + ctl_size += c->size; + } + + next_offset = max_offset; + for (li = other_list; li != NULL; li = li->next) { + c = (struct ccix_cap *)li->data; + c->offset = next_offset; + switch (c->type) { + case + idm_table: idm_offset = c->offset; + break; + case sr_table: + sr_offset = c->offset; + break; + case hsam: + hsam_offset = c->offset; + break; + case rsam: + rsam_offset = c->offset; + break; + case guid: + guid_offset = c->offset; + break; + default: + break; + } + if (li->next == NULL) + next_offset = 0; + else + next_offset = c->offset + c->size; + max_offset = c->offset + c->size; + } + + /* Will eventually take some description and make a prl. For now one RA */ + pcie_add_dvsec(pci_dev, offset, max_offset - offset, PCI_VENDOR_ID_CCIX, + CCIX_COMP_ID_PRL_DVSEC, guid_offset); + + ccix_dvsec_fill_dvsec_header(pci_dev, offset, cap_size, cap_offset, + ctl_size, ctl_offset); + + for (li = cap_list; li != NULL; li = li->next) { + c = (struct ccix_cap *)li->data; + if (li->next) + next_offset = ((struct ccix_cap *)(li->next->data))->offset; + else + next_offset = 0; + switch (c->type) { + case common_cap: + ccix_prl_common_cap(pci_dev, s, c->offset, next_offset, + 0, /* No support for device error log */ + idm_offset, + rsam_offset, CCIX_SAM_ENTRY_SIZE * s->rsam_entries, + hsam_offset, CCIX_SAM_ENTRY_SIZE * s->hsam_entries, + sr_offset, + 0 /* No support for sw portal */); + break; + case ra_cap: + ccix_prl_ra_cap(pci_dev, c->offset, next_offset, error_log_offset); + break; + case ha_cap: + ccix_prl_ha_cap(pci_dev, c->offset, next_offset, error_log_offset, + ha_num_ids, ha_num_pools); + break; + case port_cap: + ccix_prl_port_cap(pci_dev, s, c->offset, next_offset, error_log_offset); + break; + case link_cap: + ccix_prl_link_cap(pci_dev, c->offset, next_offset, error_log_offset); + break; + default: + break; + } + } + + for (li = ctl_list; li != NULL; li = li->next) { + c = (struct ccix_cap *)li->data; + if (li->next) + next_offset = ((struct ccix_cap *)(li->next->data))->offset; + else + next_offset =0; + switch (c->type) { + case common_ctl: + ccix_prl_common_ctl(pci_dev, s, c->offset, next_offset); + break; + case ra_ctl: + ccix_prl_ra_ctl(pci_dev, s, c->offset, next_offset, 0); + break; + case ha_ctl: + ccix_prl_ha_ctl(pci_dev, s, c->offset, next_offset, ha_num_ids, ha_num_pools); + break; + case port_ctl: + ccix_prl_port_ctl(pci_dev, s, c->offset, next_offset, s->psam_entries); + break; + case link_ctl: + ccix_prl_link_ctl(pci_dev, s, c->offset, next_offset, s->num_links); + break; + default: + break; + } + } + + for (li = other_list; li != NULL; li = li->next) { + c = (struct ccix_cap *)li->data; + switch(c->type) { + case idm_table: + ccix_idm(pci_dev, s, c->offset); + break; + case sr_table: + ccix_idm(pci_dev, s, c->offset); + break; + case rsam: + ccix_sam(pci_dev, s, c->offset, s->rsam_entries); + break; + case hsam: + ccix_sam(pci_dev, s, c->offset, s->hsam_entries); + break; + case guid: + ccix_guid(pci_dev, c->offset); + break; + default: + break; + } + } + return max_offset; +} + +uint16_t ccix_add_tdldvsec(PCIDevice *pci_dev, uint16_t offset) +{ + const uint16_t dvsec_size = 17 * sizeof(uint32_t); + + pcie_add_dvsec(pci_dev, offset, dvsec_size, PCI_VENDOR_ID_CCIX, + CCIX_COMP_ID_TDL_DVSEC, 0); + + return offset + dvsec_size; +} + +void ccix_register(CCIXState *s) +{ + int i; + + if (s->ccix_dev_name) + for (i = 0; i < sizeof(CCIXFuncs)/sizeof(*CCIXFuncs); i++) + if (!CCIXFuncs[i]) { + CCIXFuncs[i] = s; + break; + } +} diff --git a/include/hw/misc/ccix.h b/include/hw/misc/ccix.h new file mode 100644 index 0000000000..94aa904b53 --- /dev/null +++ b/include/hw/misc/ccix.h @@ -0,0 +1,28 @@ + +#include "hw/pci/pcie_port.h" +typedef struct CCIXState { + GTree *am_tree; +#define PRIMARY_PORT_BIT 0 +#define CCIX_IS_PORT 1 + uint32_t flags; + char *ccix_dev_name; + uint8_t port_id; + uint8_t num_links; + uint8_t psam_entries; + uint8_t num_ras; + uint8_t num_has; + uint8_t rsam_entries; + uint8_t hsam_entries; + PCIDevice *pci_dev; + uint16_t enable_offset; +} CCIXState; + +extern CCIXState *CCIXFuncs[256]; + +uint16_t ccix_add_prldvsec(PCIDevice *pci_dev, CCIXState *s, uint16_t offset); +void ccix_write_config(PCIDevice *pci_dev, CCIXState *s, uint32_t addr, uint32_t val_in, int l); +uint16_t ccix_add_tdldvsec(PCIDevice *pci_dev, uint16_t offset); +void initialize_ccixstate(CCIXState *s, PCIDevice *pci_dev); +void ccix_register(CCIXState *s); + +void ccix_set_port(CCIXState *s); diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index f49be07328..19675fdac1 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -225,6 +225,8 @@ #define PCI_DEVICE_ID_HUAWEI_CCIX_DOWN 0xA261 #define PCI_DEVICE_ID_HUAWEI_CCIX_EP 0xA262 +#define PCI_VENDOR_ID_CCIX 0x1e2c + #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_82378 0x0484 #define PCI_DEVICE_ID_INTEL_82441 0x1237