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 /* 26 * Common PCI configuration space access routines 27 */ 28 29 #include <sys/types.h> 30 #include <sys/ddi.h> 31 #include <sys/promif.h> 32 #include <sys/sunddi.h> 33 #include <sys/sunndi.h> 34 #include <sys/kmem.h> 35 #include <sys/obpdefs.h> 36 #include <sys/sysmacros.h> 37 #include <sys/pci.h> 38 #include <sys/spl.h> 39 #include <sys/pcie_impl.h> 40 #include <sys/pci_cfgacc_4v.h> 41 42 #define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) 43 44 /* RC BDF Shift in a Phyiscal Address */ 45 #define RC_RA_BDF_SHIFT 8 46 47 static boolean_t 48 pci_cfgacc_valid(pci_cfgacc_req_t *req) 49 { 50 int sz = req->size; 51 52 if (IS_P2ALIGNED(req->offset, sz) && 53 (req->offset < PCIE_CFG_SPACE_SIZE) && 54 ((sz & 0xf) && ISP2(sz))) 55 return (B_TRUE); 56 57 cmn_err(CE_WARN, "illegal PCI request: offset = %x, size = %d", 58 req->offset, sz); 59 return (B_FALSE); 60 } 61 62 /* 63 * Unprotected raw reads/writes of fabric device's config space. 64 */ 65 static uint64_t 66 pci_cfgacc_get(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size) 67 { 68 pcie_bus_t *bus_p; 69 uint64_t devhdl; 70 uint64_t devaddr; 71 uint64_t data = 0; 72 73 bus_p = PCIE_DIP2DOWNBUS(dip); 74 ASSERT(bus_p != NULL); 75 76 devhdl = bus_p->bus_cfgacc_base; 77 devaddr = ((uint64_t)bdf) << RC_RA_BDF_SHIFT; 78 79 (void) hvio_config_get(devhdl, devaddr, 80 offset, size, (pci_cfg_data_t *)&data); 81 82 return (data); 83 } 84 85 static void 86 pci_cfgacc_set(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size, 87 uint64_t val) 88 { 89 pcie_bus_t *bus_p; 90 uint64_t devhdl; 91 uint64_t devaddr; 92 pci_cfg_data_t wdata = { 0 }; 93 94 bus_p = PCIE_DIP2DOWNBUS(dip); 95 ASSERT(bus_p != NULL); 96 97 devhdl = bus_p->bus_cfgacc_base; 98 devaddr = ((uint64_t)bdf) << RC_RA_BDF_SHIFT; 99 100 wdata.qw = val; 101 (void) hvio_config_put(devhdl, devaddr, offset, size, wdata); 102 } 103 104 void 105 pci_cfgacc_acc(pci_cfgacc_req_t *req) 106 { 107 if (!req->write) 108 VAL64(req) = (uint64_t)-1; 109 110 if (!pci_cfgacc_valid(req)) 111 return; 112 113 if (req->write) { 114 pci_cfgacc_set(req->rcdip, req->bdf, req->offset, 115 req->size, VAL64(req)); 116 } else { 117 VAL64(req) = pci_cfgacc_get(req->rcdip, req->bdf, 118 req->offset, req->size); 119 switch (req->size) { 120 case 1: 121 VAL8(req) = (uint8_t)VAL64(req); 122 break; 123 case 2: 124 VAL16(req) = (uint16_t)VAL64(req); 125 break; 126 case 4: 127 VAL32(req) = (uint32_t)VAL64(req); 128 break; 129 case 8: 130 /* fall through, no special handling needed */ 131 default: 132 break; 133 } 134 } 135 } 136