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 | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 13 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 14 | #define PCI_ROM_SLOT 6 |
| 15 | #define PCI_NUM_REGIONS 7 |
| 16 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 17 | static u32 pci_bios_io_addr; |
| 18 | static u32 pci_bios_mem_addr; |
| 19 | static u32 pci_bios_bigmem_addr; |
| 20 | /* host irqs corresponding to PCI irqs A-D */ |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 21 | static u8 pci_irqs[4] = { |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 22 | 10, 10, 11, 11 |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 23 | }; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 24 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 25 | 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] | 26 | { |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 27 | u32 ofs, old_addr; |
| 28 | |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 29 | if (region_num == PCI_ROM_SLOT) { |
| 30 | ofs = PCI_ROM_ADDRESS; |
| 31 | } else { |
| 32 | ofs = PCI_BASE_ADDRESS_0 + region_num * 4; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 33 | } |
| 34 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 35 | old_addr = pci_config_readl(bdf, ofs); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 36 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 37 | pci_config_writel(bdf, ofs, addr); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 38 | dprintf(1, "region %d: 0x%08x\n", region_num, addr); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 39 | } |
| 40 | |
| 41 | /* return the global irq number corresponding to a given device irq |
| 42 | pin. We could also use the bus number to have a more precise |
| 43 | mapping. */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 44 | static int pci_slot_get_pirq(u16 bdf, int irq_num) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 45 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 46 | int slot_addend = pci_bdf_to_dev(bdf) - 1; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 47 | return (irq_num + slot_addend) & 3; |
| 48 | } |
| 49 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 50 | static void pci_bios_init_bridges(u16 bdf) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 51 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 52 | u16 vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID); |
| 53 | u16 device_id = pci_config_readw(bdf, PCI_DEVICE_ID); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 54 | |
Kevin O'Connor | 415c2dc | 2008-10-25 14:35:59 -0400 | [diff] [blame] | 55 | if (vendor_id == PCI_VENDOR_ID_INTEL |
Kevin O'Connor | 2f840e3 | 2008-10-25 15:23:23 -0400 | [diff] [blame] | 56 | && (device_id == PCI_DEVICE_ID_INTEL_82371SB_0 |
| 57 | || device_id == PCI_DEVICE_ID_INTEL_82371AB_0)) { |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 58 | int i, irq; |
| 59 | u8 elcr[2]; |
| 60 | |
Kevin O'Connor | 2f840e3 | 2008-10-25 15:23:23 -0400 | [diff] [blame] | 61 | /* PIIX3/PIIX4 PCI to ISA bridge */ |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 62 | |
| 63 | elcr[0] = 0x00; |
| 64 | elcr[1] = 0x00; |
Kevin O'Connor | 40f5b5a | 2009-09-13 10:46:57 -0400 | [diff] [blame] | 65 | for (i = 0; i < 4; i++) { |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 66 | irq = pci_irqs[i]; |
| 67 | /* set to trigger level */ |
| 68 | elcr[irq >> 3] |= (1 << (irq & 7)); |
| 69 | /* activate irq remapping in PIIX */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 70 | pci_config_writeb(bdf, 0x60 + i, irq); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 71 | } |
| 72 | outb(elcr[0], 0x4d0); |
| 73 | outb(elcr[1], 0x4d1); |
Kevin O'Connor | 2f840e3 | 2008-10-25 15:23:23 -0400 | [diff] [blame] | 74 | dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 75 | elcr[0], elcr[1]); |
| 76 | } |
| 77 | } |
| 78 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 79 | static void pci_bios_init_device(u16 bdf) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 80 | { |
| 81 | int class; |
| 82 | u32 *paddr; |
| 83 | int i, pin, pic_irq, vendor_id, device_id; |
| 84 | |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 85 | class = pci_config_readw(bdf, PCI_CLASS_DEVICE); |
| 86 | vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID); |
| 87 | device_id = pci_config_readw(bdf, PCI_DEVICE_ID); |
| 88 | dprintf(1, "PCI: bus=%d devfn=0x%02x: vendor_id=0x%04x device_id=0x%04x\n" |
| 89 | , pci_bdf_to_bus(bdf), pci_bdf_to_devfn(bdf), vendor_id, device_id); |
Kevin O'Connor | 4dde532 | 2009-10-08 21:34:45 -0400 | [diff] [blame] | 90 | switch (class) { |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 91 | case PCI_CLASS_STORAGE_IDE: |
Kevin O'Connor | 415c2dc | 2008-10-25 14:35:59 -0400 | [diff] [blame] | 92 | if (vendor_id == PCI_VENDOR_ID_INTEL |
Kevin O'Connor | 2f840e3 | 2008-10-25 15:23:23 -0400 | [diff] [blame] | 93 | && (device_id == PCI_DEVICE_ID_INTEL_82371SB_1 |
| 94 | || device_id == PCI_DEVICE_ID_INTEL_82371AB)) { |
| 95 | /* PIIX3/PIIX4 IDE */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 96 | pci_config_writew(bdf, 0x40, 0x8000); // enable IDE0 |
| 97 | pci_config_writew(bdf, 0x42, 0x8000); // enable IDE1 |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 98 | goto default_map; |
| 99 | } else { |
| 100 | /* IDE: we map it as in ISA mode */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 101 | pci_set_io_region_addr(bdf, 0, 0x1f0); |
| 102 | pci_set_io_region_addr(bdf, 1, 0x3f4); |
| 103 | pci_set_io_region_addr(bdf, 2, 0x170); |
| 104 | pci_set_io_region_addr(bdf, 3, 0x374); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 105 | } |
| 106 | break; |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 107 | case PCI_CLASS_SYSTEM_PIC: |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 108 | /* PIC */ |
Kevin O'Connor | 415c2dc | 2008-10-25 14:35:59 -0400 | [diff] [blame] | 109 | if (vendor_id == PCI_VENDOR_ID_IBM) { |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 110 | /* IBM */ |
| 111 | if (device_id == 0x0046 || device_id == 0xFFFF) { |
| 112 | /* MPIC & MPIC2 */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 113 | pci_set_io_region_addr(bdf, 0, 0x80800000 + 0x00040000); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 114 | } |
| 115 | } |
| 116 | break; |
| 117 | case 0xff00: |
Kevin O'Connor | 415c2dc | 2008-10-25 14:35:59 -0400 | [diff] [blame] | 118 | if (vendor_id == PCI_VENDOR_ID_APPLE && |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 119 | (device_id == 0x0017 || device_id == 0x0022)) { |
| 120 | /* macio bridge */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 121 | pci_set_io_region_addr(bdf, 0, 0x80800000); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 122 | } |
| 123 | break; |
| 124 | default: |
| 125 | default_map: |
| 126 | /* default memory mappings */ |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 127 | for (i = 0; i < PCI_NUM_REGIONS; i++) { |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 128 | int ofs; |
Kevin O'Connor | e06363e | 2008-08-29 21:12:03 -0400 | [diff] [blame] | 129 | u32 val, size; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 130 | |
| 131 | if (i == PCI_ROM_SLOT) |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 132 | ofs = PCI_ROM_ADDRESS; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 133 | else |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 134 | ofs = PCI_BASE_ADDRESS_0 + i * 4; |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 135 | pci_config_writel(bdf, ofs, 0xffffffff); |
| 136 | val = pci_config_readl(bdf, ofs); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 137 | if (val != 0) { |
| 138 | size = (~(val & ~0xf)) + 1; |
Kevin O'Connor | 9649a96 | 2008-12-10 20:53:35 -0500 | [diff] [blame] | 139 | if (val & PCI_BASE_ADDRESS_SPACE_IO) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 140 | paddr = &pci_bios_io_addr; |
| 141 | else if (size >= 0x04000000) |
| 142 | paddr = &pci_bios_bigmem_addr; |
| 143 | else |
| 144 | paddr = &pci_bios_mem_addr; |
Kevin O'Connor | e06363e | 2008-08-29 21:12:03 -0400 | [diff] [blame] | 145 | *paddr = ALIGN(*paddr, size); |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 146 | pci_set_io_region_addr(bdf, i, *paddr); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 147 | *paddr += size; |
| 148 | } |
| 149 | } |
| 150 | break; |
| 151 | } |
| 152 | |
Kevin O'Connor | b82a1e4 | 2009-10-12 10:34:51 -0400 | [diff] [blame^] | 153 | /* enable memory mappings */ |
| 154 | pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); |
| 155 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 156 | /* map the interrupt */ |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 157 | pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 158 | if (pin != 0) { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 159 | pin = pci_slot_get_pirq(bdf, pin - 1); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 160 | pic_irq = pci_irqs[pin]; |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 161 | pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pic_irq); |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 162 | } |
| 163 | |
Kevin O'Connor | 415c2dc | 2008-10-25 14:35:59 -0400 | [diff] [blame] | 164 | if (vendor_id == PCI_VENDOR_ID_INTEL |
| 165 | && device_id == PCI_DEVICE_ID_INTEL_82371AB_3) { |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 166 | /* PIIX4 Power Management device (for ACPI) */ |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 167 | |
Kevin O'Connor | 8740e3a | 2009-06-21 09:35:10 -0400 | [diff] [blame] | 168 | // acpi sci is hardwired to 9 |
| 169 | pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 170 | |
Kevin O'Connor | e682cbc | 2008-12-06 23:11:56 -0500 | [diff] [blame] | 171 | pci_config_writel(bdf, 0x40, PORT_ACPI_PM_BASE | 1); |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 172 | pci_config_writeb(bdf, 0x80, 0x01); /* enable PM io space */ |
Kevin O'Connor | e682cbc | 2008-12-06 23:11:56 -0500 | [diff] [blame] | 173 | pci_config_writel(bdf, 0x90, PORT_SMB_BASE | 1); |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 174 | pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */ |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 175 | } |
| 176 | } |
| 177 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 178 | void |
Kevin O'Connor | 40f5b5a | 2009-09-13 10:46:57 -0400 | [diff] [blame] | 179 | pci_setup(void) |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 180 | { |
| 181 | if (CONFIG_COREBOOT) |
| 182 | // Already done by coreboot. |
| 183 | return; |
| 184 | |
Kevin O'Connor | 40f5b5a | 2009-09-13 10:46:57 -0400 | [diff] [blame] | 185 | dprintf(3, "pci setup\n"); |
| 186 | |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 187 | pci_bios_io_addr = 0xc000; |
Kevin O'Connor | d1c4d64 | 2009-10-12 10:22:45 -0400 | [diff] [blame] | 188 | pci_bios_mem_addr = 0xf0000000; |
Kevin O'Connor | e791636 | 2008-12-28 22:03:17 -0500 | [diff] [blame] | 189 | pci_bios_bigmem_addr = RamSize; |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 190 | if (pci_bios_bigmem_addr < 0x90000000) |
| 191 | pci_bios_bigmem_addr = 0x90000000; |
| 192 | |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 193 | int bdf, max; |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 194 | foreachpci(bdf, max) { |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 195 | pci_bios_init_bridges(bdf); |
| 196 | } |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 197 | foreachpci(bdf, max) { |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 198 | pci_bios_init_device(bdf); |
| 199 | } |
Kevin O'Connor | 0525d29 | 2008-07-04 06:18:30 -0400 | [diff] [blame] | 200 | } |