/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #define PCI_CFG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) #define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) /* RC BDF Shift in a Phyiscal Address */ #define RC_PA_BDF_SHIFT 12 #define RC_BDF_TO_CFGADDR(bdf, offset) (((bdf) << RC_PA_BDF_SHIFT) + (offset)) static boolean_t pci_cfgacc_valid(pci_cfgacc_req_t *req) { int sz = req->size; if (IS_P2ALIGNED(req->offset, sz) && (req->offset + sz - 1 < PCIE_CFG_SPACE_SIZE) && ((sz & 0xf) && ISP2(sz))) return (B_TRUE); cmn_err(CE_WARN, "illegal PCI request: offset = %x, size = %d", req->offset, sz); return (B_FALSE); } /* * Unprotected raw reads/writes of fabric device's config space. */ static uint64_t pci_cfgacc_get(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size) { pcie_bus_t *bus_p; uint64_t base_addr; uint64_t val; bus_p = PCIE_DIP2DOWNBUS(dip); ASSERT(bus_p != NULL); base_addr = bus_p->bus_cfgacc_base; base_addr += RC_BDF_TO_CFGADDR(bdf, offset); switch (size) { case 1: val = ldbphysio(base_addr); break; case 2: val = ldhphysio(base_addr); break; case 4: val = ldphysio(base_addr); break; case 8: val = lddphysio(base_addr); break; default: return ((uint64_t)-1); } return (LE_64(val)); } static void pci_cfgacc_set(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size, uint64_t val) { pcie_bus_t *bus_p; uint64_t base_addr; bus_p = PCIE_DIP2DOWNBUS(dip); ASSERT(bus_p != NULL); base_addr = bus_p->bus_cfgacc_base; base_addr += RC_BDF_TO_CFGADDR(bdf, offset); val = LE_64(val); switch (size) { case 1: stbphysio(base_addr, val); break; case 2: sthphysio(base_addr, val); break; case 4: stphysio(base_addr, val); break; case 8: stdphysio(base_addr, val); break; default: break; } } void pci_cfgacc_acc(pci_cfgacc_req_t *req) { if (!req->write) VAL64(req) = (uint64_t)-1; if (!pci_cfgacc_valid(req)) return; if (req->write) { pci_cfgacc_set(req->rcdip, req->bdf, req->offset, req->size, VAL64(req)); } else { VAL64(req) = pci_cfgacc_get(req->rcdip, req->bdf, req->offset, req->size); } }