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