/* * Copyright (C) 2012-2015 Apple Inc. All rights reserved. * * This document is the property of Apple Inc. * It is considered confidential and proprietary. * * This document may not be reproduced or transmitted in any form, * in whole or in part, without the express written permission of * Apple Inc. */ #ifdef APCIE_DEBUG_LEVEL #define DEBUG_LEVEL APCIE_DEBUG_LEVEL #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct apcie_link_status apcie_link_statuses[APCIE_NUM_LINKS]; struct apcie_link_config *apcie_link_configs; static pci_device_t host_bridge; static pci_device_t port_bridges[APCIE_NUM_LINKS]; void apcie_config_read_raw(void *dst, uint32_t bdfo, int size) { uintptr_t addr = PCI_CONFIG_BASE | bdfo; uint32_t value; dprintf(DEBUG_SPEW, "apcie_config_read[0x%lx, %d]", addr, size); switch (size) { case 1: *(uint8_t *)dst = *(volatile uint8_t *)addr; value = *(uint8_t *)dst; break; case 2: *(uint16_t *)dst = *(volatile uint16_t *)addr; value = *(uint16_t *)dst; break; case 4: *(uint32_t *)dst = *(volatile uint32_t *)addr; value = *(uint32_t *)dst; break; default: panic("apcie_config_read: invalid size %d", size); } dprintf(DEBUG_SPEW, " = 0x%x\n", value); } static void apcie_config_read(void *priv, void *dst, uint32_t bdfo, int size) { struct apcie_link_status *link_status = (struct apcie_link_status *)priv; if (link_status == NULL && apcie_get_link_enable_count() == 0) { panic("apcie: config read with no links enabled"); memset(dst, 0xff, size); return; } if (link_status != NULL && !link_status->enabled) { panic("apcie: config read from disabled link"); memset(dst, 0xff, size); return; } apcie_config_read_raw(dst, bdfo, size); } void apcie_config_write_raw(const void *src, uint32_t bdfo, int size) { uintptr_t addr = PCI_CONFIG_BASE | bdfo; switch (size) { case 1: dprintf(DEBUG_SPEW, "apcie_config_write[0x%lx, %d] = 0x%x\n", addr, size, *(uint8_t *)src); *(volatile uint8_t *)addr = *(uint8_t *)src; break; case 2: dprintf(DEBUG_SPEW, "apcie_config_write[0x%lx, %d] = 0x%x\n", addr, size, *(uint16_t *)src); *(volatile uint16_t *)addr = *(uint16_t *)src; break; case 4: dprintf(DEBUG_SPEW, "apcie_config_write[0x%lx, %d] = 0x%x\n", addr, size, *(uint32_t *)src); *(volatile uint32_t *)addr = *(uint32_t *)src; break; default: panic("apcie_config_read: invalid size %d", size); } } static void apcie_config_write(void *priv, const void *src, uint32_t bdfo, int size) { struct apcie_link_status *link_status = (struct apcie_link_status *)priv; if (link_status == NULL && apcie_get_link_enable_count() == 0) { panic("apcie: config write with no links enabled"); } if (link_status != NULL && !link_status->enabled) { panic("apcie: config write to disabled link"); } apcie_config_write_raw(src, bdfo, size); } /* Configure the virtual PCI2PCI bridge associated with the port. */ void apcie_setup_root_port_bridge(uint32_t link, struct apcie_link_config *config) { pci_device_t bridge; uint8_t header_type; dprintf(DEBUG_INFO, "apcie: Probing port bridge for link %d\n", link); ASSERT(port_bridges[link] == 0); bridge = pci_bridge_probe(host_bridge, link, 100); if (bridge == NULL) { panic("apcie: failed to probe port bridge on link %u\n", link); } header_type = pci_get_header_type(bridge); if (header_type != 1) { panic("PCI2PCI bridge has wrong header type %d (expected 1)", header_type); } pci_bridge_assign_secondary_bus(bridge); pci_bus_master_enable(bridge, true); pci_memory_space_enable(bridge, true); pci_bridge_serr_enable(bridge, true); port_bridges[link] = bridge; } void apcie_disable_all(void) { for (int i = 0; i < APCIE_NUM_LINKS; i++) apcie_disable_link(i); } void apcie_init(void) { uint32_t memory_base; // At some point, we may allow targets to override the PCI // configuration, but this will do for now apcie_link_configs = platform_apcie_link_configs; memory_base = platform_map_host_to_pci_addr(PCI_32BIT_BASE); host_bridge = pci_create_host_bridge("host bridge", 0, memory_base, PCI_32BIT_LEN, NULL, apcie_config_read, apcie_config_write); } pci_device_t apcie_get_port_bridge(uint32_t link) { ASSERT(link < APCIE_NUM_LINKS); return port_bridges[link]; } void apcie_free_port_bridge(uint32_t link) { ASSERT(link < APCIE_NUM_LINKS); if (port_bridges[link] != NULL) { pci_free(port_bridges[link]); port_bridges[link] = NULL; } } static int do_apcie_debug_cmd(int argc, struct cmd_arg *args) { int result = -1; int link; const char *cmd = args[1].str; if (cmd == NULL) { result = -1; } else { if (strcmp(cmd, "enable") == 0 && argc == 3) { link = args[2].n; if (link >= 0 && link < APCIE_NUM_LINKS) { apcie_enable_link(link); result = 0; } } else if (strcmp(cmd, "disable") == 0 && argc == 3) { link = args[2].n; if (link >= 0 && link < APCIE_NUM_LINKS) { apcie_disable_link(link); result = 0; } } else if (strcmp(cmd, "counters") == 0 && argc == 4) { link = args[3].n; if (strcmp(args[2].str, "read") == 0 && link >= 0 && link < APCIE_NUM_LINKS) { if (apcie_link_statuses[link].enabled) { rAPCIE_COUNTER_COMMAND(link) = APCIE_COUNTER_ENABLE | APCIE_COUNTER_CAPTURE; printf("time_L0:\t%u\n", rAPCIE_COUNTER_VALUE(link, 0)); printf("time_L1:\t%u\n", rAPCIE_COUNTER_VALUE(link, 1)); printf("time_L1.1:\t%u\n", rAPCIE_COUNTER_VALUE(link, 2)); printf("time_L1.2:\t%u\n", rAPCIE_COUNTER_VALUE(link, 3)); printf("entry_L0:\t%u\n", rAPCIE_COUNTER_VALUE(link, 4)); printf("entry_L1:\t%u\n", rAPCIE_COUNTER_VALUE(link, 5)); printf("entry_L1.1:\t%u\n", rAPCIE_COUNTER_VALUE(link, 6)); printf("entry_L1.2:\t%u\n", rAPCIE_COUNTER_VALUE(link, 7)); } else { printf("link not enabled\n"); } result = 0; } else if(strcmp(args[2].str, "reset") == 0 && link >= 0 && link < APCIE_NUM_LINKS) { if (apcie_link_statuses[link].enabled) { rAPCIE_COUNTER_COMMAND(link) = APCIE_COUNTER_ENABLE | APCIE_COUNTER_CLEAR; } else { printf("link not enabled\n"); } result = 0; } } } if (result < 0) { printf("Usage:\n"); printf("\tenable \t\t - Enables the specified link\n"); printf("\tdisable \t\t - Disables the specified link\n"); printf("\tcounters read \t - Reads counters for the specified link\n"); printf("\tcounters reset \t - Resets counters for the specified link\n"); } return result; } MENU_COMMAND_DEBUG(apcie, do_apcie_debug_cmd, "APCIe debug commands", NULL);