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.
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.
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]
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/systm.h>
27 #include <sys/pci_cfgacc.h>
28 #include <sys/pci_cfgspace.h>
29 #include <sys/pci_cfgspace_impl.h>
30 #include <sys/sunddi.h>
31 #include <sys/sysmacros.h>
32 #include <sys/x86_archext.h>
34 #include <sys/cmn_err.h>
35 #include <vm/hat_i86.h>
36 #include <vm/seg_kmem.h>
37 #include <vm/kboot_mmu.h>
39 #define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4)
40 #define PCI_BDF_BUS(bdf) ((((uint16_t)bdf) & 0xff00) >> 8)
41 #define PCI_BDF_DEV(bdf) ((((uint16_t)bdf) & 0xf8) >> 3)
42 #define PCI_BDF_FUNC(bdf) (((uint16_t)bdf) & 0x7)
44 /* patchable variables */
45 volatile boolean_t pci_cfgacc_force_io
= B_FALSE
;
47 extern uintptr_t alloc_vaddr(size_t, paddr_t
);
49 void pci_cfgacc_acc(pci_cfgacc_req_t
*);
51 boolean_t
pci_cfgacc_find_workaround(uint16_t);
53 * IS_P2ALIGNED() is used to make sure offset is 'size'-aligned, so
54 * it's guaranteed that the access will not cross 4k page boundary.
55 * Thus only 1 page is allocated for all config space access, and the
56 * virtual address of that page is cached in pci_cfgacc_virt_base.
58 static caddr_t pci_cfgacc_virt_base
= NULL
;
61 pci_cfgacc_map(paddr_t phys_addr
)
64 pfn_t pfn
= mmu_btop(phys_addr
);
66 * pci_cfgacc_virt_base may hold address left from early
67 * boot, which points to low mem. Realloc virtual address
68 * in kernel space since it's already late in boot now.
69 * Note: no need to unmap first, clear_boot_mappings() will
72 if (pci_cfgacc_virt_base
< (caddr_t
)kernelbase
)
73 pci_cfgacc_virt_base
= vmem_alloc(heap_arena
,
74 MMU_PAGESIZE
, VM_SLEEP
);
76 hat_devload(kas
.a_hat
, pci_cfgacc_virt_base
,
77 MMU_PAGESIZE
, pfn
, PROT_READ
| PROT_WRITE
|
78 HAT_STRICTORDER
, HAT_LOAD_LOCK
);
80 paddr_t pa_base
= P2ALIGN(phys_addr
, MMU_PAGESIZE
);
82 if (pci_cfgacc_virt_base
== NULL
)
83 pci_cfgacc_virt_base
=
84 (caddr_t
)alloc_vaddr(MMU_PAGESIZE
, MMU_PAGESIZE
);
86 kbm_map((uintptr_t)pci_cfgacc_virt_base
, pa_base
, 0, 0);
89 return (pci_cfgacc_virt_base
+ (phys_addr
& MMU_PAGEOFFSET
));
96 hat_unload(kas
.a_hat
, pci_cfgacc_virt_base
, MMU_PAGESIZE
,
101 pci_cfgacc_io(pci_cfgacc_req_t
*req
)
103 uint8_t bus
, dev
, func
;
104 uint16_t ioacc_offset
; /* 4K config access with IO ECS */
106 bus
= PCI_BDF_BUS(req
->bdf
);
107 dev
= PCI_BDF_DEV(req
->bdf
);
108 func
= PCI_BDF_FUNC(req
->bdf
);
109 ioacc_offset
= req
->offset
;
114 (*pci_putb_func
)(bus
, dev
, func
,
115 ioacc_offset
, VAL8(req
));
117 VAL8(req
) = (*pci_getb_func
)(bus
, dev
, func
,
122 (*pci_putw_func
)(bus
, dev
, func
,
123 ioacc_offset
, VAL16(req
));
125 VAL16(req
) = (*pci_getw_func
)(bus
, dev
, func
,
130 (*pci_putl_func
)(bus
, dev
, func
,
131 ioacc_offset
, VAL32(req
));
133 VAL32(req
) = (*pci_getl_func
)(bus
, dev
, func
,
138 (*pci_putl_func
)(bus
, dev
, func
,
139 ioacc_offset
, VAL64(req
) & 0xffffffff);
140 (*pci_putl_func
)(bus
, dev
, func
,
141 ioacc_offset
+ 4, VAL64(req
) >> 32);
143 VAL64(req
) = (*pci_getl_func
)(bus
, dev
, func
,
145 VAL64(req
) |= (uint64_t)(*pci_getl_func
)(bus
, dev
, func
,
146 ioacc_offset
+ 4) << 32;
153 pci_cfgacc_mmio(pci_cfgacc_req_t
*req
)
158 paddr
= (paddr_t
)req
->bdf
<< 12;
159 paddr
+= mcfg_mem_base
+ req
->offset
;
161 mutex_enter(&pcicfg_mmio_mutex
);
162 vaddr
= pci_cfgacc_map(paddr
);
167 *((uint8_t *)vaddr
) = VAL8(req
);
169 VAL8(req
) = *((uint8_t *)vaddr
);
173 *((uint16_t *)vaddr
) = VAL16(req
);
175 VAL16(req
) = *((uint16_t *)vaddr
);
179 *((uint32_t *)vaddr
) = VAL32(req
);
181 VAL32(req
) = *((uint32_t *)vaddr
);
185 *((uint64_t *)vaddr
) = VAL64(req
);
187 VAL64(req
) = *((uint64_t *)vaddr
);
191 mutex_exit(&pcicfg_mmio_mutex
);
195 pci_cfgacc_valid(pci_cfgacc_req_t
*req
, uint16_t cfgspc_size
)
199 if (IS_P2ALIGNED(req
->offset
, sz
) &&
200 (req
->offset
+ sz
- 1 < cfgspc_size
) &&
201 ((sz
& 0xf) && ISP2(sz
)))
204 cmn_err(CE_WARN
, "illegal PCI request: offset = %x, size = %d",
210 pci_cfgacc_check_io(pci_cfgacc_req_t
*req
)
214 bus
= PCI_BDF_BUS(req
->bdf
);
216 if (pci_cfgacc_force_io
|| (mcfg_mem_base
== (uintptr_t)NULL
) ||
217 (bus
< mcfg_bus_start
) || (bus
> mcfg_bus_end
) ||
218 pci_cfgacc_find_workaround(req
->bdf
))
223 pci_cfgacc_acc(pci_cfgacc_req_t
*req
)
225 extern uint_t pci_iocfg_max_offset
;
228 VAL64(req
) = (uint64_t)-1;
230 pci_cfgacc_check_io(req
);
233 if (pci_cfgacc_valid(req
, pci_iocfg_max_offset
+ 1))
236 if (pci_cfgacc_valid(req
, PCIE_CFG_SPACE_SIZE
))
237 pci_cfgacc_mmio(req
);
241 typedef struct cfgacc_bus_range
{
242 struct cfgacc_bus_range
*next
;
246 } cfgacc_bus_range_t
;
248 cfgacc_bus_range_t
*pci_cfgacc_bus_head
= NULL
;
250 #define BUS_INSERT(prev, el) \
254 #define BUS_REMOVE(prev, el) \
258 * This function is only supposed to be called in device tree setup time,
259 * thus no lock is needed.
262 pci_cfgacc_add_workaround(uint16_t bdf
, uchar_t secbus
, uchar_t subbus
)
264 cfgacc_bus_range_t
*entry
;
266 entry
= kmem_zalloc(sizeof (cfgacc_bus_range_t
), KM_SLEEP
);
268 entry
->secbus
= secbus
;
269 entry
->subbus
= subbus
;
270 BUS_INSERT(&pci_cfgacc_bus_head
, entry
);
274 pci_cfgacc_find_workaround(uint16_t bdf
)
276 cfgacc_bus_range_t
*entry
;
279 for (entry
= pci_cfgacc_bus_head
; entry
!= NULL
;
280 entry
= entry
->next
) {
281 if (bdf
== entry
->bdf
) {
282 /* found a device which is known to be broken */
286 bus
= PCI_BDF_BUS(bdf
);
287 if ((bus
!= 0) && (bus
>= entry
->secbus
) &&
288 (bus
<= entry
->subbus
)) {
290 * found a device whose parent/grandparent is
291 * known to be broken.