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