1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/sunddi.h> 26 #include <sys/sunndi.h> 27 #include <sys/promif.h> 28 #include <sys/pci.h> 29 #include <sys/sysmacros.h> 30 #include <sys/pcie_impl.h> 31 #include <sys/machsystm.h> 32 #include <sys/byteorder.h> 33 #include <sys/pci_cfgacc.h> 34 35 #define PCI_CFG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 36 #define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) 37 38 /* RC BDF Shift in a Phyiscal Address */ 39 #define RC_PA_BDF_SHIFT 12 40 #define RC_BDF_TO_CFGADDR(bdf, offset) (((bdf) << RC_PA_BDF_SHIFT) + (offset)) 41 42 static boolean_t 43 pci_cfgacc_valid(pci_cfgacc_req_t *req) 44 { 45 int sz = req->size; 46 47 if (IS_P2ALIGNED(req->offset, sz) && 48 (req->offset < PCIE_CFG_SPACE_SIZE) && 49 ((sz & 0xf) && ISP2(sz))) 50 return (B_TRUE); 51 52 cmn_err(CE_WARN, "illegal PCI request: offset = %x, size = %d", 53 req->offset, sz); 54 return (B_FALSE); 55 } 56 57 /* 58 * Unprotected raw reads/writes of fabric device's config space. 59 */ 60 static uint64_t 61 pci_cfgacc_get(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size) 62 { 63 pcie_bus_t *bus_p; 64 uint64_t base_addr; 65 uint64_t val; 66 67 bus_p = PCIE_DIP2DOWNBUS(dip); 68 ASSERT(bus_p != NULL); 69 70 base_addr = bus_p->bus_cfgacc_base; 71 base_addr += RC_BDF_TO_CFGADDR(bdf, offset); 72 73 switch (size) { 74 case 1: 75 val = ldbphysio(base_addr); 76 break; 77 case 2: 78 val = ldhphysio(base_addr); 79 break; 80 case 4: 81 val = ldphysio(base_addr); 82 break; 83 case 8: 84 val = lddphysio(base_addr); 85 break; 86 default: 87 return ((uint64_t)-1); 88 } 89 90 return (LE_64(val)); 91 } 92 93 static void 94 pci_cfgacc_set(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size, 95 uint64_t val) 96 { 97 pcie_bus_t *bus_p; 98 uint64_t base_addr; 99 100 bus_p = PCIE_DIP2DOWNBUS(dip); 101 ASSERT(bus_p != NULL); 102 103 base_addr = bus_p->bus_cfgacc_base; 104 base_addr += RC_BDF_TO_CFGADDR(bdf, offset); 105 106 val = LE_64(val); 107 108 switch (size) { 109 case 1: 110 stbphysio(base_addr, val); 111 break; 112 case 2: 113 sthphysio(base_addr, val); 114 break; 115 case 4: 116 stphysio(base_addr, val); 117 break; 118 case 8: 119 stdphysio(base_addr, val); 120 break; 121 default: 122 break; 123 } 124 } 125 126 void 127 pci_cfgacc_acc(pci_cfgacc_req_t *req) 128 { 129 if (!req->write) 130 VAL64(req) = (uint64_t)-1; 131 132 if (!pci_cfgacc_valid(req)) 133 return; 134 135 if (req->write) { 136 pci_cfgacc_set(req->rcdip, req->bdf, req->offset, 137 req->size, VAL64(req)); 138 } else { 139 VAL64(req) = pci_cfgacc_get(req->rcdip, req->bdf, 140 req->offset, req->size); 141 } 142 } 143