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