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