blob: 3df1dae41d2b15de5a1948b6b3d3cf2a1e200b3e [file] [log] [blame]
Kevin O'Connor061d1372008-06-11 22:39:46 -04001// PCI config space access functions.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor061d1372008-06-11 22:39:46 -04007
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04008#include "output.h" // dprintf
9#include "pci.h" // pci_config_writel
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "util.h" // udelay
Kevin O'Connor71036f82014-04-07 20:36:02 -040012#include "x86.h" // outl
Kevin O'Connora0dc2962008-03-16 14:29:32 -040013
Kevin O'Connor4d8510c2016-02-03 01:28:20 -050014#define PORT_PCI_CMD 0x0cf8
Kevin O'Connor4d8510c2016-02-03 01:28:20 -050015#define PORT_PCI_DATA 0x0cfc
16
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010017static u32 mmconfig;
18
19static void *mmconfig_addr(u16 bdf, u32 addr)
20{
21 return (void*)(mmconfig + ((u32)bdf << 12) + addr);
22}
23
Gerd Hoffmann63a44af2020-03-23 15:59:10 +010024static u32 ioconfig_cmd(u16 bdf, u32 addr)
25{
26 return 0x80000000 | (bdf << 8) | (addr & 0xfc);
27}
28
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050029void pci_config_writel(u16 bdf, u32 addr, u32 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040030{
Gerd Hoffmann2e3de622020-05-25 11:06:27 +020031 if (!MODESEGMENT && mmconfig) {
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010032 writel(mmconfig_addr(bdf, addr), val);
33 } else {
34 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
35 outl(val, PORT_PCI_DATA);
36 }
Kevin O'Connora0dc2962008-03-16 14:29:32 -040037}
38
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050039void pci_config_writew(u16 bdf, u32 addr, u16 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040040{
Gerd Hoffmann2e3de622020-05-25 11:06:27 +020041 if (!MODESEGMENT && mmconfig) {
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010042 writew(mmconfig_addr(bdf, addr), val);
43 } else {
44 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
45 outw(val, PORT_PCI_DATA + (addr & 2));
46 }
Kevin O'Connora0dc2962008-03-16 14:29:32 -040047}
48
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050049void pci_config_writeb(u16 bdf, u32 addr, u8 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040050{
Gerd Hoffmann2e3de622020-05-25 11:06:27 +020051 if (!MODESEGMENT && mmconfig) {
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010052 writeb(mmconfig_addr(bdf, addr), val);
53 } else {
54 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
55 outb(val, PORT_PCI_DATA + (addr & 3));
56 }
Kevin O'Connora0dc2962008-03-16 14:29:32 -040057}
58
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050059u32 pci_config_readl(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040060{
Gerd Hoffmann2e3de622020-05-25 11:06:27 +020061 if (!MODESEGMENT && mmconfig) {
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010062 return readl(mmconfig_addr(bdf, addr));
63 } else {
64 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
65 return inl(PORT_PCI_DATA);
66 }
Kevin O'Connora0dc2962008-03-16 14:29:32 -040067}
68
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050069u16 pci_config_readw(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040070{
Gerd Hoffmann2e3de622020-05-25 11:06:27 +020071 if (!MODESEGMENT && mmconfig) {
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010072 return readw(mmconfig_addr(bdf, addr));
73 } else {
74 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
75 return inw(PORT_PCI_DATA + (addr & 2));
76 }
Kevin O'Connora0dc2962008-03-16 14:29:32 -040077}
78
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050079u8 pci_config_readb(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040080{
Gerd Hoffmann2e3de622020-05-25 11:06:27 +020081 if (!MODESEGMENT && mmconfig) {
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010082 return readb(mmconfig_addr(bdf, addr));
83 } else {
84 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
85 return inb(PORT_PCI_DATA + (addr & 3));
86 }
Kevin O'Connor0f803e42008-05-24 23:07:16 -040087}
88
Kevin O'Connor59f02832009-10-12 10:09:15 -040089void
90pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
91{
92 u16 val = pci_config_readw(bdf, addr);
93 val = (val & ~off) | on;
94 pci_config_writew(bdf, addr, val);
95}
96
Gerd Hoffmann6a3b59a2020-03-23 15:59:11 +010097void
98pci_enable_mmconfig(u64 addr, const char *name)
99{
100 if (addr >= 0x100000000ll)
101 return;
102 dprintf(1, "PCIe: using %s mmconfig at 0x%llx\n", name, addr);
103 mmconfig = addr;
104}
105
Aleksandr Bezzubikov7de1f652017-08-18 02:33:19 +0300106u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
107{
108 int i;
109 u16 status = pci_config_readw(bdf, PCI_STATUS);
110
111 if (!(status & PCI_STATUS_CAP_LIST))
112 return 0;
113
114 if (cap == 0) {
115 /* find first */
116 cap = pci_config_readb(bdf, PCI_CAPABILITY_LIST);
117 } else {
118 /* find next */
119 cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
120 }
121 for (i = 0; cap && i <= 0xff; i++) {
122 if (pci_config_readb(bdf, cap + PCI_CAP_LIST_ID) == cap_id)
123 return cap;
124 cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
125 }
126
127 return 0;
128}
129
Kevin O'Connorbaac6b62011-06-19 10:46:28 -0400130// Helper function for foreachbdf() macro - return next device
Kevin O'Connore6338322008-11-29 20:31:49 -0500131int
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400132pci_next(int bdf, int bus)
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400133{
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400134 if (pci_bdf_to_fn(bdf) == 0
135 && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
Kevin O'Connore6338322008-11-29 20:31:49 -0500136 // Last found device wasn't a multi-function device - skip to
137 // the next device.
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400138 bdf += 8;
139 else
140 bdf += 1;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500141
Kevin O'Connore6338322008-11-29 20:31:49 -0500142 for (;;) {
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400143 if (pci_bdf_to_bus(bdf) != bus)
144 return -1;
Kevin O'Connore6338322008-11-29 20:31:49 -0500145
146 u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
147 if (v != 0x0000 && v != 0xffff)
148 // Device is present.
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400149 return bdf;
Kevin O'Connore6338322008-11-29 20:31:49 -0500150
151 if (pci_bdf_to_fn(bdf) == 0)
152 bdf += 8;
153 else
154 bdf += 1;
155 }
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500156}
157
Jan Kiszka58e6b3f2011-09-21 08:16:21 +0200158// Check if PCI is available at all
159int
160pci_probe_host(void)
161{
162 outl(0x80000000, PORT_PCI_CMD);
163 if (inl(PORT_PCI_CMD) != 0x80000000) {
164 dprintf(1, "Detected non-PCI system\n");
165 return -1;
166 }
167 return 0;
168}
169
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400170void
171pci_reboot(void)
172{
173 u8 v = inb(PORT_PCI_REBOOT) & ~6;
174 outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
175 udelay(50);
176 outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
177 udelay(50);
178}