Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 1 | // Initialize PCI devices (on emulators) |
| 2 | // |
| 3 | // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> |
| 4 | // Copyright (C) 2006 Fabrice Bellard |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 7 | |
| 8 | #include "util.h" // dprintf |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 9 | #include "pci.h" // pci_config_readl |
Kevin O'Connor | 9521e26 | 2008-07-04 13:04:29 -0400 | [diff] [blame] | 10 | #include "biosvar.h" // GET_EBDA |
Kevin O'Connor | 2ed2f58 | 2008-11-08 15:53:36 -0500 | [diff] [blame] | 11 | #include "pci_ids.h" // PCI_VENDOR_ID_INTEL |
| 12 | #include "pci_regs.h" // PCI_COMMAND |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 13 | #include "dev-i440fx.h" |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 14 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 15 | #define PCI_ROM_SLOT 6 |
| 16 | #define PCI_NUM_REGIONS 7 |
| 17 | |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 18 | static void pci_bios_init_device_in_bus(int bus); |
| 19 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 20 | static struct pci_region pci_bios_io_region; |
| 21 | static struct pci_region pci_bios_mem_region; |
| 22 | static struct pci_region pci_bios_prefmem_region; |
| 23 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 24 | /* host irqs corresponding to PCI irqs A-D */ |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 25 | const u8 pci_irqs[4] = { |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 26 | 10, 10, 11, 11 |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 27 | }; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 28 | |
Isaku Yamahata | a65821d | 2010-06-22 17:57:50 +0900 | [diff] [blame] | 29 | static u32 pci_bar(u16 bdf, int region_num) |
| 30 | { |
| 31 | if (region_num != PCI_ROM_SLOT) { |
| 32 | return PCI_BASE_ADDRESS_0 + region_num * 4; |
| 33 | } |
Isaku Yamahata | 5d0de15 | 2010-06-22 17:57:51 +0900 | [diff] [blame] | 34 | |
| 35 | #define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 |
| 36 | u8 type = pci_config_readb(bdf, PCI_HEADER_TYPE); |
| 37 | type &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; |
| 38 | return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; |
Isaku Yamahata | a65821d | 2010-06-22 17:57:50 +0900 | [diff] [blame] | 39 | } |
| 40 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 41 | static void pci_set_io_region_addr(u16 bdf, int region_num, u32 addr) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 42 | { |
Isaku Yamahata | eebe949 | 2010-08-30 11:32:01 +0900 | [diff] [blame] | 43 | u32 ofs; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 44 | |
Isaku Yamahata | a65821d | 2010-06-22 17:57:50 +0900 | [diff] [blame] | 45 | ofs = pci_bar(bdf, region_num); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 46 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 47 | pci_config_writel(bdf, ofs, addr); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 48 | dprintf(1, "region %d: 0x%08x\n", region_num, addr); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 49 | } |
| 50 | |
Isaku Yamahata | dfd94fa | 2010-06-22 17:57:48 +0900 | [diff] [blame] | 51 | /* |
| 52 | * return value |
| 53 | * 0: 32bit BAR |
| 54 | * non 0: 64bit BAR |
| 55 | */ |
| 56 | static int pci_bios_allocate_region(u16 bdf, int region_num) |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 57 | { |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 58 | struct pci_region *r; |
Isaku Yamahata | a65821d | 2010-06-22 17:57:50 +0900 | [diff] [blame] | 59 | u32 ofs = pci_bar(bdf, region_num); |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 60 | |
| 61 | u32 old = pci_config_readl(bdf, ofs); |
| 62 | u32 mask; |
| 63 | if (region_num == PCI_ROM_SLOT) { |
| 64 | mask = PCI_ROM_ADDRESS_MASK; |
| 65 | pci_config_writel(bdf, ofs, mask); |
| 66 | } else { |
| 67 | if (old & PCI_BASE_ADDRESS_SPACE_IO) |
| 68 | mask = PCI_BASE_ADDRESS_IO_MASK; |
| 69 | else |
| 70 | mask = PCI_BASE_ADDRESS_MEM_MASK; |
| 71 | pci_config_writel(bdf, ofs, ~0); |
| 72 | } |
| 73 | u32 val = pci_config_readl(bdf, ofs); |
| 74 | pci_config_writel(bdf, ofs, old); |
| 75 | |
Isaku Yamahata | 0a8eada | 2010-06-22 17:57:49 +0900 | [diff] [blame] | 76 | u32 size = (~(val & mask)) + 1; |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 77 | if (val != 0) { |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 78 | const char *type; |
| 79 | const char *msg; |
Isaku Yamahata | 0a8eada | 2010-06-22 17:57:49 +0900 | [diff] [blame] | 80 | if (val & PCI_BASE_ADDRESS_SPACE_IO) { |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 81 | r = &pci_bios_io_region; |
| 82 | type = "io"; |
| 83 | msg = ""; |
Isaku Yamahata | 0a8eada | 2010-06-22 17:57:49 +0900 | [diff] [blame] | 84 | } else if ((val & PCI_BASE_ADDRESS_MEM_PREFETCH) && |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 85 | /* keep behaviour on bus = 0 */ |
| 86 | pci_bdf_to_bus(bdf) != 0 && |
| 87 | /* If pci_bios_prefmem_addr == 0, keep old behaviour */ |
| 88 | pci_region_addr(&pci_bios_prefmem_region) != 0) { |
| 89 | r = &pci_bios_prefmem_region; |
| 90 | type = "prefmem"; |
| 91 | msg = "decrease BUILD_PCIMEM_SIZE and recompile. size %x"; |
Isaku Yamahata | 0a8eada | 2010-06-22 17:57:49 +0900 | [diff] [blame] | 92 | } else { |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 93 | r = &pci_bios_mem_region; |
| 94 | type = "mem"; |
| 95 | msg = "increase BUILD_PCIMEM_SIZE and recompile."; |
Isaku Yamahata | 0a8eada | 2010-06-22 17:57:49 +0900 | [diff] [blame] | 96 | } |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 97 | u32 addr = pci_region_alloc(r, size); |
| 98 | if (addr > 0) { |
| 99 | pci_set_io_region_addr(bdf, region_num, addr); |
| 100 | } else { |
| 101 | size = 0; |
| 102 | dprintf(1, |
| 103 | "%s region of (bdf 0x%x bar %d) can't be mapped. " |
| 104 | "%s size %x\n", |
| 105 | type, bdf, region_num, msg, pci_region_size(r)); |
Isaku Yamahata | 0a8eada | 2010-06-22 17:57:49 +0900 | [diff] [blame] | 106 | } |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 107 | } |
Isaku Yamahata | dfd94fa | 2010-06-22 17:57:48 +0900 | [diff] [blame] | 108 | |
| 109 | int is_64bit = !(val & PCI_BASE_ADDRESS_SPACE_IO) && |
| 110 | (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64; |
Isaku Yamahata | 1d5c333 | 2010-07-26 14:02:45 +0900 | [diff] [blame] | 111 | if (is_64bit && size > 0) { |
| 112 | pci_config_writel(bdf, ofs + 4, 0); |
Isaku Yamahata | dfd94fa | 2010-06-22 17:57:48 +0900 | [diff] [blame] | 113 | } |
| 114 | return is_64bit; |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 115 | } |
| 116 | |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 117 | void pci_bios_allocate_regions(u16 bdf, void *arg) |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 118 | { |
| 119 | int i; |
| 120 | for (i = 0; i < PCI_NUM_REGIONS; i++) { |
Isaku Yamahata | dfd94fa | 2010-06-22 17:57:48 +0900 | [diff] [blame] | 121 | int is_64bit = pci_bios_allocate_region(bdf, i); |
| 122 | if (is_64bit){ |
| 123 | i++; |
| 124 | } |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 125 | } |
| 126 | } |
| 127 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 128 | /* return the global irq number corresponding to a given device irq |
| 129 | pin. We could also use the bus number to have a more precise |
| 130 | mapping. */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 131 | static int pci_slot_get_pirq(u16 bdf, int irq_num) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 132 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 133 | int slot_addend = pci_bdf_to_dev(bdf) - 1; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 134 | return (irq_num + slot_addend) & 3; |
| 135 | } |
| 136 | |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 137 | static const struct pci_device_id pci_isa_bridge_tbl[] = { |
| 138 | /* PIIX3/PIIX4 PCI to ISA bridge */ |
| 139 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, |
| 140 | piix_isa_bridge_init), |
| 141 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, |
| 142 | piix_isa_bridge_init), |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 143 | |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 144 | PCI_DEVICE_END |
| 145 | }; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 146 | |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 147 | #define PCI_IO_ALIGN 4096 |
| 148 | #define PCI_IO_SHIFT 8 |
| 149 | #define PCI_MEMORY_ALIGN (1UL << 20) |
| 150 | #define PCI_MEMORY_SHIFT 16 |
| 151 | #define PCI_PREF_MEMORY_ALIGN (1UL << 20) |
| 152 | #define PCI_PREF_MEMORY_SHIFT 16 |
| 153 | |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 154 | static void pci_bios_init_device_bridge(u16 bdf, void *arg) |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 155 | { |
| 156 | pci_bios_allocate_region(bdf, 0); |
| 157 | pci_bios_allocate_region(bdf, 1); |
| 158 | pci_bios_allocate_region(bdf, PCI_ROM_SLOT); |
| 159 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 160 | u32 io_old = pci_region_addr(&pci_bios_io_region); |
| 161 | u32 mem_old = pci_region_addr(&pci_bios_mem_region); |
| 162 | u32 prefmem_old = pci_region_addr(&pci_bios_prefmem_region); |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 163 | |
| 164 | /* IO BASE is assumed to be 16 bit */ |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 165 | if (pci_region_align(&pci_bios_io_region, PCI_IO_ALIGN) == 0) { |
| 166 | pci_region_disable(&pci_bios_io_region); |
| 167 | } |
| 168 | if (pci_region_align(&pci_bios_mem_region, PCI_MEMORY_ALIGN) == 0) { |
| 169 | pci_region_disable(&pci_bios_mem_region); |
| 170 | } |
| 171 | if (pci_region_align(&pci_bios_prefmem_region, |
| 172 | PCI_PREF_MEMORY_ALIGN) == 0) { |
| 173 | pci_region_disable(&pci_bios_prefmem_region); |
| 174 | } |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 175 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 176 | u32 io_base = pci_region_addr(&pci_bios_io_region); |
| 177 | u32 mem_base = pci_region_addr(&pci_bios_mem_region); |
| 178 | u32 prefmem_base = pci_region_addr(&pci_bios_prefmem_region); |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 179 | |
| 180 | u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); |
| 181 | if (secbus > 0) { |
| 182 | pci_bios_init_device_in_bus(secbus); |
| 183 | } |
| 184 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 185 | u32 io_end = pci_region_align(&pci_bios_io_region, PCI_IO_ALIGN); |
| 186 | if (io_end == 0) { |
| 187 | pci_region_revert(&pci_bios_io_region, io_old); |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 188 | io_base = 0xffff; |
| 189 | io_end = 1; |
| 190 | } |
| 191 | pci_config_writeb(bdf, PCI_IO_BASE, io_base >> PCI_IO_SHIFT); |
| 192 | pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); |
| 193 | pci_config_writeb(bdf, PCI_IO_LIMIT, (io_end - 1) >> PCI_IO_SHIFT); |
| 194 | pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0); |
| 195 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 196 | u32 mem_end = pci_region_align(&pci_bios_mem_region, PCI_MEMORY_ALIGN); |
| 197 | if (mem_end == 0) { |
| 198 | pci_region_revert(&pci_bios_mem_region, mem_old); |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 199 | mem_base = 0xffffffff; |
| 200 | mem_end = 1; |
| 201 | } |
| 202 | pci_config_writew(bdf, PCI_MEMORY_BASE, mem_base >> PCI_MEMORY_SHIFT); |
| 203 | pci_config_writew(bdf, PCI_MEMORY_LIMIT, (mem_end -1) >> PCI_MEMORY_SHIFT); |
| 204 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 205 | u32 prefmem_end = pci_region_align(&pci_bios_prefmem_region, |
| 206 | PCI_PREF_MEMORY_ALIGN); |
| 207 | if (prefmem_end == 0) { |
| 208 | pci_region_revert(&pci_bios_prefmem_region, prefmem_old); |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 209 | prefmem_base = 0xffffffff; |
| 210 | prefmem_end = 1; |
| 211 | } |
| 212 | pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, |
| 213 | prefmem_base >> PCI_PREF_MEMORY_SHIFT); |
| 214 | pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, |
| 215 | (prefmem_end - 1) >> PCI_PREF_MEMORY_SHIFT); |
| 216 | pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, 0); |
| 217 | pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, 0); |
| 218 | |
| 219 | dprintf(1, "PCI: br io = [0x%x, 0x%x)\n", io_base, io_end); |
| 220 | dprintf(1, "PCI: br mem = [0x%x, 0x%x)\n", mem_base, mem_end); |
| 221 | dprintf(1, "PCI: br pref = [0x%x, 0x%x)\n", prefmem_base, prefmem_end); |
| 222 | |
| 223 | u16 cmd = pci_config_readw(bdf, PCI_COMMAND); |
| 224 | cmd &= ~PCI_COMMAND_IO; |
| 225 | if (io_end > io_base) { |
| 226 | cmd |= PCI_COMMAND_IO; |
| 227 | } |
| 228 | cmd &= ~PCI_COMMAND_MEMORY; |
| 229 | if (mem_end > mem_base || prefmem_end > prefmem_base) { |
| 230 | cmd |= PCI_COMMAND_MEMORY; |
| 231 | } |
| 232 | cmd |= PCI_COMMAND_MASTER; |
| 233 | pci_config_writew(bdf, PCI_COMMAND, cmd); |
| 234 | |
| 235 | pci_config_maskw(bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_SERR); |
| 236 | } |
| 237 | |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 238 | static void storage_ide_init(u16 bdf, void *arg) |
| 239 | { |
| 240 | /* IDE: we map it as in ISA mode */ |
| 241 | pci_set_io_region_addr(bdf, 0, PORT_ATA1_CMD_BASE); |
| 242 | pci_set_io_region_addr(bdf, 1, PORT_ATA1_CTRL_BASE); |
| 243 | pci_set_io_region_addr(bdf, 2, PORT_ATA2_CMD_BASE); |
| 244 | pci_set_io_region_addr(bdf, 3, PORT_ATA2_CTRL_BASE); |
| 245 | } |
| 246 | |
| 247 | static void pic_ibm_init(u16 bdf, void *arg) |
| 248 | { |
| 249 | /* PIC, IBM, MPIC & MPIC2 */ |
| 250 | pci_set_io_region_addr(bdf, 0, 0x80800000 + 0x00040000); |
| 251 | } |
| 252 | |
| 253 | static void apple_macio_init(u16 bdf, void *arg) |
| 254 | { |
| 255 | /* macio bridge */ |
| 256 | pci_set_io_region_addr(bdf, 0, 0x80800000); |
| 257 | } |
| 258 | |
| 259 | static const struct pci_device_id pci_class_tbl[] = { |
| 260 | /* STORAGE IDE */ |
| 261 | PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, |
| 262 | PCI_CLASS_STORAGE_IDE, piix_ide_init), |
| 263 | PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, |
| 264 | PCI_CLASS_STORAGE_IDE, piix_ide_init), |
| 265 | PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, |
| 266 | storage_ide_init), |
| 267 | |
| 268 | /* PIC, IBM, MIPC & MPIC2 */ |
| 269 | PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0x0046, PCI_CLASS_SYSTEM_PIC, |
| 270 | pic_ibm_init), |
| 271 | PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0xFFFF, PCI_CLASS_SYSTEM_PIC, |
| 272 | pic_ibm_init), |
| 273 | |
| 274 | /* 0xff00 */ |
| 275 | PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_init), |
| 276 | PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_init), |
| 277 | |
| 278 | /* PCI bridge */ |
| 279 | PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, |
| 280 | pci_bios_init_device_bridge), |
| 281 | |
| 282 | /* default */ |
| 283 | PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID, pci_bios_allocate_regions), |
| 284 | |
| 285 | PCI_DEVICE_END, |
| 286 | }; |
| 287 | |
| 288 | static const struct pci_device_id pci_device_tbl[] = { |
| 289 | /* PIIX4 Power Management device (for ACPI) */ |
| 290 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, |
| 291 | piix4_pm_init), |
| 292 | |
| 293 | PCI_DEVICE_END, |
| 294 | }; |
| 295 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 296 | static void pci_bios_init_device(u16 bdf) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 297 | { |
Isaku Yamahata | b9e4721 | 2010-06-22 17:57:47 +0900 | [diff] [blame] | 298 | int pin, pic_irq, vendor_id, device_id; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 299 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 300 | vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID); |
| 301 | device_id = pci_config_readw(bdf, PCI_DEVICE_ID); |
| 302 | dprintf(1, "PCI: bus=%d devfn=0x%02x: vendor_id=0x%04x device_id=0x%04x\n" |
| 303 | , pci_bdf_to_bus(bdf), pci_bdf_to_devfn(bdf), vendor_id, device_id); |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 304 | pci_init_device(pci_class_tbl, bdf, NULL); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 305 | |
Kevin O'Connor | b82a1e4 | 2009-10-12 10:34:51 -0400 | [diff] [blame] | 306 | /* enable memory mappings */ |
| 307 | pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); |
| 308 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 309 | /* map the interrupt */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 310 | pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 311 | if (pin != 0) { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 312 | pin = pci_slot_get_pirq(bdf, pin - 1); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 313 | pic_irq = pci_irqs[pin]; |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 314 | pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pic_irq); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 315 | } |
| 316 | |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 317 | pci_init_device(pci_device_tbl, bdf, NULL); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 318 | } |
| 319 | |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 320 | static void pci_bios_init_device_in_bus(int bus) |
| 321 | { |
| 322 | int bdf, max; |
| 323 | foreachpci_in_bus(bdf, max, bus) { |
| 324 | pci_bios_init_device(bdf); |
| 325 | } |
| 326 | } |
| 327 | |
Isaku Yamahata | f441666 | 2010-06-22 17:57:52 +0900 | [diff] [blame] | 328 | static void |
| 329 | pci_bios_init_bus_rec(int bus, u8 *pci_bus) |
| 330 | { |
| 331 | int bdf, max; |
| 332 | u16 class; |
| 333 | |
| 334 | dprintf(1, "PCI: %s bus = 0x%x\n", __func__, bus); |
| 335 | |
| 336 | /* prevent accidental access to unintended devices */ |
| 337 | foreachpci_in_bus(bdf, max, bus) { |
| 338 | class = pci_config_readw(bdf, PCI_CLASS_DEVICE); |
| 339 | if (class == PCI_CLASS_BRIDGE_PCI) { |
| 340 | pci_config_writeb(bdf, PCI_SECONDARY_BUS, 255); |
| 341 | pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | foreachpci_in_bus(bdf, max, bus) { |
| 346 | class = pci_config_readw(bdf, PCI_CLASS_DEVICE); |
| 347 | if (class != PCI_CLASS_BRIDGE_PCI) { |
| 348 | continue; |
| 349 | } |
| 350 | dprintf(1, "PCI: %s bdf = 0x%x\n", __func__, bdf); |
| 351 | |
| 352 | u8 pribus = pci_config_readb(bdf, PCI_PRIMARY_BUS); |
| 353 | if (pribus != bus) { |
| 354 | dprintf(1, "PCI: primary bus = 0x%x -> 0x%x\n", pribus, bus); |
| 355 | pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus); |
| 356 | } else { |
| 357 | dprintf(1, "PCI: primary bus = 0x%x\n", pribus); |
| 358 | } |
| 359 | |
| 360 | u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); |
| 361 | (*pci_bus)++; |
| 362 | if (*pci_bus != secbus) { |
| 363 | dprintf(1, "PCI: secondary bus = 0x%x -> 0x%x\n", |
| 364 | secbus, *pci_bus); |
| 365 | secbus = *pci_bus; |
| 366 | pci_config_writeb(bdf, PCI_SECONDARY_BUS, secbus); |
| 367 | } else { |
| 368 | dprintf(1, "PCI: secondary bus = 0x%x\n", secbus); |
| 369 | } |
| 370 | |
| 371 | /* set to max for access to all subordinate buses. |
| 372 | later set it to accurate value */ |
| 373 | u8 subbus = pci_config_readb(bdf, PCI_SUBORDINATE_BUS); |
| 374 | pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255); |
| 375 | |
| 376 | pci_bios_init_bus_rec(secbus, pci_bus); |
| 377 | |
| 378 | if (subbus != *pci_bus) { |
| 379 | dprintf(1, "PCI: subordinate bus = 0x%x -> 0x%x\n", |
| 380 | subbus, *pci_bus); |
| 381 | subbus = *pci_bus; |
| 382 | } else { |
| 383 | dprintf(1, "PCI: subordinate bus = 0x%x\n", subbus); |
| 384 | } |
| 385 | pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, subbus); |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | static void |
| 390 | pci_bios_init_bus(void) |
| 391 | { |
| 392 | u8 pci_bus = 0; |
| 393 | pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); |
| 394 | } |
| 395 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 396 | void |
Kevin O'Connor | 40f5b5a | 2009-09-13 10:46:57 -0400 | [diff] [blame] | 397 | pci_setup(void) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 398 | { |
| 399 | if (CONFIG_COREBOOT) |
| 400 | // Already done by coreboot. |
| 401 | return; |
| 402 | |
Kevin O'Connor | 40f5b5a | 2009-09-13 10:46:57 -0400 | [diff] [blame] | 403 | dprintf(3, "pci setup\n"); |
| 404 | |
Isaku Yamahata | e2623fc | 2010-10-28 15:54:36 +0900 | [diff] [blame^] | 405 | pci_region_init(&pci_bios_io_region, 0xc000, 64 * 1024); |
| 406 | pci_region_init(&pci_bios_mem_region, |
| 407 | BUILD_PCIMEM_START, BUILD_PCIMEM_END - 1); |
| 408 | pci_region_init(&pci_bios_prefmem_region, |
| 409 | BUILD_PCIPREFMEM_START, BUILD_PCIPREFMEM_END - 1); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 410 | |
Isaku Yamahata | f441666 | 2010-06-22 17:57:52 +0900 | [diff] [blame] | 411 | pci_bios_init_bus(); |
| 412 | |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 413 | int bdf, max; |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 414 | foreachpci(bdf, max) { |
Kevin O'Connor | 0d6b8d5 | 2010-07-10 13:12:37 -0400 | [diff] [blame] | 415 | pci_init_device(pci_isa_bridge_tbl, bdf, NULL); |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 416 | } |
Isaku Yamahata | af0963d | 2010-06-22 17:57:53 +0900 | [diff] [blame] | 417 | pci_bios_init_device_in_bus(0 /* host bus */); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 418 | } |