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