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