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
pci_cfgacc_valid(pci_cfgacc_req_t * req)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 + sz - 1 < 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
pci_cfgacc_get(dev_info_t * dip,uint16_t bdf,uint16_t offset,uint8_t size)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
pci_cfgacc_set(dev_info_t * dip,uint16_t bdf,uint16_t offset,uint8_t size,uint64_t val)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
pci_cfgacc_acc(pci_cfgacc_req_t * req)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