Main Page | Directories | File List | Globals

pcibus_irq.c

Go to the documentation of this file.
00001 /*
00002  * kernel_drivers/adi_buses/pci/pcibus_irq.c
00003  *
00004  * Copyright (c) 2003-2004 Lukasz Dembinski <dembol@nasa.com.pl>
00005  * All Rights Reserved
00006  * 
00007  * Date:        2004/06
00008  * Author:      Lukasz Dembinski
00009  * Info:        pcibus_irq.c core file
00010  * Contact:     mailto: <dembol@nasa.com.pl>
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     /* some devices has irq = 255 */
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     /* device has not irq pin */
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 }
Dokumentacje wygenerowano programem Doxygen 1.4.2 dla projektu Agnix