00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <agnix/agnix.h>
00015 #include <agnix/adi/adi.h>
00016 #include <agnix/list.h>
00017 #include <agnix/spinlock.h>
00018 #include <agnix/console.h>
00019 #include <asm/pgtable.h>
00020 #include "pcibus_define_names.h"
00021
00022 #define MOD_NAME "PCI: \t\t"
00023 #define PCIBUS_ROUTING_TABLE_START phys_to_virt(0xf0000)
00024 #define PCIBUS_ROUTING_TABLE_END phys_to_virt(0x100000)
00025
00026 #define pcibus_irq_router_get(pirq, irq_ptr) pcidev_irq_router_ops->pirq_get(pirq, irq_ptr)
00027 #define pcibus_irq_router_set(pirq, irq) pcidev_irq_router_ops->pirq_set(pirq, irq)
00028
00029 #define PCI_DEBUG 0
00030
00031 extern struct list_head pcidev_list;
00032 extern spinlock_t pcidev_list_lock;
00033
00034 struct pcibus_irq_table *pirq_table;
00035 struct pcidev_s *pcidev_irq_router;
00036 struct pcibus_irq_router_ops *pcidev_irq_router_ops;
00037
00038 int pcibus_irq_piix_get(int pirq, u8 *irq);
00039 int pcibus_irq_piix_set(int pirq, u8 irq);
00040
00041 struct pcibus_irq_router irq_routers[] = {
00042 { PCIBUS_VENDOR_INTEL_ID, PCIBUS_DEVICE_82371AB_ID, { pcibus_irq_piix_get, pcibus_irq_piix_set } },
00043 { PCIBUS_VENDOR_INTEL_ID, PCIBUS_DEVICE_82371SB_ID, { pcibus_irq_piix_get, pcibus_irq_piix_set } },
00044 { 0 ,}
00045 };
00046
00047 int pcibus_irq_piix_get(int pirq, u8 *irq)
00048 {
00049 pci_read_config_byte(pcidev_irq_router, pirq, irq);
00050
00051 return 0;
00052 }
00053
00054 int pcibus_irq_piix_set(int pirq, u8 irq)
00055 {
00056 pci_write_config_byte(pcidev_irq_router, pirq, irq);
00057
00058 return 0;
00059 }
00060
00061 u8 pcibus_irq_checksum(struct pcibus_irq_table *pirq_table)
00062 {
00063 u8 *pirq_table_ptr;
00064 u16 i;
00065 u8 checksum = 0;
00066
00067 pirq_table_ptr = (u8 *)pirq_table;
00068 for (i = 0; i < pirq_table->size; i++)
00069 checksum += pirq_table_ptr[i];
00070
00071 return checksum;
00072 }
00073
00074 struct pcibus_irq_table *pcibus_irq_find_routing_table(void)
00075 {
00076 u32 table_start = PCIBUS_ROUTING_TABLE_START;
00077 u32 table_end = PCIBUS_ROUTING_TABLE_END;
00078 u32 table_ptr;
00079 struct pcibus_irq_table *pirq_table;
00080
00081 for (table_ptr = table_start; table_ptr < table_end; table_ptr += 16) {
00082 pirq_table = (struct pcibus_irq_table *)table_ptr;
00083
00084 if (pirq_table->signature == PCIBUS_PIRQ_SIGNATURE &&
00085 pirq_table->version == PCIBUS_PIRQ_VERSION &&
00086 pirq_table->size > sizeof(struct pcibus_irq_table)) {
00087
00088 if (pcibus_irq_checksum(pirq_table) == 0) {
00089 printk(MOD_NAME "irq routing table found (at address 0x%08x)\n", virt_to_phys(table_ptr));
00090 return pirq_table;
00091 } else {
00092 printk(MOD_NAME "irq routing table checksum failed! (at address 0x%08x)\n", virt_to_phys(table_ptr));
00093 return NULL;
00094 }
00095 }
00096 }
00097
00098 printk(MOD_NAME "irq routing table not found\n");
00099
00100 return NULL;
00101 }
00102
00103 struct pcidev_s *pcibus_irq_find_router(struct pcibus_irq_table *pirq_table)
00104 {
00105 struct pcidev_s *pcidev_1 = NULL;
00106 struct pcidev_s *pcidev_2 = NULL;
00107
00108 if (pirq_table->router_vendor != 0x0000 && pirq_table->router_vendor != 0xffff &&
00109 pirq_table->router_device != 0x0000 && pirq_table->router_device != 0xffff)
00110 pcidev_1 = pcibus_find_device_by_id(pirq_table->router_vendor, pirq_table->router_device);
00111
00112 pcidev_2 = pcibus_find_device_by_fn(pirq_table->router_bus, pirq_table->router_function);
00113
00114 if (pcidev_1 != NULL && pcidev_2 != NULL && pcidev_1 == pcidev_2)
00115 return pcidev_1;
00116
00117 if (pcidev_1 == NULL)
00118 return pcidev_2;
00119
00120 return pcidev_1;
00121 }
00122
00123 struct pcibus_irq_router_ops *pcibus_irq_find_router_ops(struct pcidev_s *pcidev)
00124 {
00125 int i = 0;
00126
00127 for (i = 0; irq_routers[i].router_vendor_id; i++) {
00128 if (pcidev->dev_vendor_id == irq_routers[i].router_vendor_id &&
00129 pcidev->dev_device_id == irq_routers[i].router_device_id)
00130 return &(irq_routers[i].router_ops);
00131 }
00132
00133 return NULL;
00134 }
00135
00136 struct pcibus_irq_table_slot *pcibus_irq_find_slot(struct pcidev_s *pcidev)
00137 {
00138 int i;
00139 int slots_nr = (pirq_table->size - sizeof(struct pcibus_irq_table)) / sizeof(struct pcibus_irq_table_slot);
00140 struct pcibus_irq_table_slot *table_slot =
00141 (struct pcibus_irq_table_slot *)(((void *)pirq_table) + sizeof(struct pcibus_irq_table));
00142
00143 for (i = 0; i <= slots_nr + 2; i++) {
00144 if ((table_slot[i].dev_bus == pcidev->dev_bus->bus_nr) &&
00145 (PCIDEV_FN_TO_SLOT(table_slot[i].dev_function) == PCIDEV_SLOT(pcidev)))
00146 return &table_slot[i];
00147 }
00148
00149 return NULL;
00150 }
00151
00152 int pcibus_irq_dev_set(struct pcidev_s *pcidev)
00153 {
00154 struct pcibus_irq_table_slot *slot;
00155 struct pcibus_irq_table_slot_irq *slot_irq;
00156 struct pcidev_s *pcidev_tmp;
00157 struct list_head *tmp;
00158 u8 dev_pin;
00159 u8 dev_irq;
00160 u8 router_irq_ptr;
00161 u16 router_irq_bitmap;
00162 int sharing_with = 0;
00163
00164 pci_read_config_byte(pcidev, PCIDEV_INTERRUPT_PIN, &dev_pin);
00165
00166 dev_irq = pcidev->dev_irq;
00167
00168
00169 if (dev_irq >= 16) {
00170 #if PCI_DEBUG
00171 printk(MOD_NAME "%02x.%02x has irq >= 16, setting to 0\n", PCIDEV_SLOT(pcidev), PCIDEV_FN(pcidev));
00172 #endif
00173 dev_irq = 0;
00174 }
00175
00176
00177 if (dev_pin == 0) {
00178 pcidev->dev_irq = dev_irq;
00179 return 0;
00180 }
00181
00182 dev_pin--;
00183
00184 if (dev_pin > PCIDEV_MAX_IRQS) {
00185 #if PCI_DEBUG
00186 printk("dev_pin > MAX\n");
00187 #endif
00188 return 0;
00189 }
00190
00191 if ((slot = pcibus_irq_find_slot(pcidev)) == NULL) {
00192 #if PCI_DEBUG
00193 printk("slot not found\n");
00194 #endif
00195 return 0;
00196 }
00197
00198 slot_irq = slot->dev_irq;
00199
00200 router_irq_ptr = slot_irq[dev_pin].link;
00201 router_irq_bitmap = slot_irq[dev_pin].bitmap;
00202
00203 if (dev_irq == 0)
00204 pcibus_irq_router_get(router_irq_ptr, &dev_irq);
00205 else
00206 pcibus_irq_router_set(router_irq_ptr, dev_irq);
00207
00208 pcidev->dev_irq = dev_irq;
00209
00210 printk(MOD_NAME "found irq %d for %02x.%02x", dev_irq, PCIDEV_SLOT(pcidev), PCIDEV_FN(pcidev));
00211
00212 list_for_each(tmp, &pcidev_list) {
00213 pcidev_tmp = list_entry(tmp, struct pcidev_s, dev_list);
00214
00215 if (pcidev_tmp == pcidev)
00216 continue;
00217
00218 pci_read_config_byte(pcidev_tmp, PCIDEV_INTERRUPT_PIN, &dev_pin);
00219
00220 if (!dev_pin)
00221 continue;
00222
00223 dev_pin--;
00224
00225 if ((slot = pcibus_irq_find_slot(pcidev_tmp)) == NULL)
00226 continue;
00227
00228 if (slot->dev_irq[dev_pin].link == router_irq_ptr) {
00229
00230 if (pcidev_tmp->dev_irq >= 16)
00231 pcidev_tmp->dev_irq = 0;
00232
00233 if (pcidev_tmp->dev_irq && (pcidev_tmp->dev_irq != pcidev->dev_irq)) {
00234 printk(", conflict with %02x.%02x", PCIDEV_SLOT(pcidev_tmp), PCIDEV_FN(pcidev_tmp));
00235 return -1;
00236 }
00237
00238 pcidev_tmp->dev_irq = dev_irq;
00239 if (!sharing_with) {
00240 sharing_with = 1;
00241 printk(", sharing with");
00242 }
00243 printk(" %02x.%02x", PCIDEV_SLOT(pcidev_tmp), PCIDEV_FN(pcidev_tmp));
00244 }
00245 }
00246
00247 printk("\n");
00248
00249 return 0;
00250 }
00251
00252 int pcibus_irq_all_set(void)
00253 {
00254 struct list_head *tmp;
00255 struct pcidev_s *pcidev;
00256 int ret = 0;
00257 u32 flags;
00258
00259 spin_lock_irqsave(&pcidev_list_lock, flags);
00260
00261 list_for_each(tmp, &pcidev_list) {
00262 pcidev = list_entry(tmp, struct pcidev_s, dev_list);
00263 pcibus_irq_dev_set(pcidev);
00264 }
00265
00266 spin_unlock_irqrestore(&pcidev_list_lock, flags);
00267
00268 return ret;
00269 }
00270
00271 int pcibus_irq_init(void)
00272 {
00273 pirq_table = pcibus_irq_find_routing_table();
00274 pcidev_irq_router = pcibus_irq_find_router(pirq_table);
00275 pcidev_irq_router_ops = pcibus_irq_find_router_ops(pcidev_irq_router);
00276
00277 if (pcidev_irq_router != NULL)
00278 printk(MOD_NAME "found %s irq router\n", pcidev_irq_router->dev_name);
00279 else
00280 printk(MOD_NAME "irq router not found\n");
00281
00282 if (pcidev_irq_router_ops == NULL)
00283 printk(MOD_NAME "irq router ops not found\n");
00284 else {
00285 printk(MOD_NAME "setting pirqs.... \n");
00286
00287 if (!pcibus_irq_all_set())
00288 return -1;
00289 }
00290
00291 return 0;
00292 }