Subversion Repositories HelenOS-historic

Rev

Rev 1302 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  *  The PCI Library -- Direct Configuration access via i386 Ports
  3.  *
  4.  *  Copyright (c) 1997--2004 Martin Mares <mj@ucw.cz>
  5.  *
  6.  *  Modified and ported to HelenOS by Jakub Jermar.
  7.  *
  8.  *  Can be freely distributed and used under the terms of the GNU GPL.
  9.  */
  10.  
  11. #include <unistd.h>
  12.  
  13. #include "internal.h"
  14.  
  15. static inline void outb(u8 b, u16 port)
  16. {
  17.     asm volatile ("outb %0, %1\n" : : "a" (b), "d" (port));
  18. }
  19.  
  20. static inline void outw(u16 w, u16 port)
  21. {
  22.     asm volatile ("outw %0, %1\n" : : "a" (w), "d" (port));
  23. }
  24.  
  25. static inline void outl(u32 l, u16 port)
  26. {
  27.     asm volatile ("outl %0, %1\n" : : "a" (l), "d" (port));
  28. }
  29.  
  30. static inline u8 inb(u16 port)
  31. {
  32.     u8 val;
  33.    
  34.     asm volatile ("inb %1, %0 \n" : "=a" (val) : "d" (port));
  35.     return val;
  36. }
  37.  
  38. static inline u16 inw(u16 port)
  39. {
  40.     u16 val;
  41.    
  42.     asm volatile ("inw %1, %0 \n" : "=a" (val) : "d" (port));
  43.     return val;
  44. }
  45.  
  46. static inline u32 inl(u16 port)
  47. {
  48.     u32 val;
  49.    
  50.     asm volatile ("inl %1, %0 \n" : "=a" (val) : "d" (port));
  51.     return val;
  52. }
  53.  
  54. static void
  55. conf12_init(struct pci_access *a)
  56. {
  57. }
  58.  
  59. static void
  60. conf12_cleanup(struct pci_access *a UNUSED)
  61. {
  62. }
  63.  
  64. /*
  65.  * Before we decide to use direct hardware access mechanisms, we try to do some
  66.  * trivial checks to ensure it at least _seems_ to be working -- we just test
  67.  * whether bus 00 contains a host bridge (this is similar to checking
  68.  * techniques used in XFree86, but ours should be more reliable since we
  69.  * attempt to make use of direct access hints provided by the PCI BIOS).
  70.  *
  71.  * This should be close to trivial, but it isn't, because there are buggy
  72.  * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
  73.  */
  74.  
  75. static int
  76. intel_sanity_check(struct pci_access *a, struct pci_methods *m)
  77. {
  78.   struct pci_dev d;
  79.  
  80.   a->debug("...sanity check");
  81.   d.bus = 0;
  82.   d.func = 0;
  83.   for(d.dev = 0; d.dev < 32; d.dev++)
  84.     {
  85.       u16 class, vendor;
  86.       if (m->read(&d, PCI_CLASS_DEVICE, (byte *) &class, sizeof(class)) &&
  87.       (class == cpu_to_le16(PCI_CLASS_BRIDGE_HOST) || class == cpu_to_le16(PCI_CLASS_DISPLAY_VGA)) ||
  88.       m->read(&d, PCI_VENDOR_ID, (byte *) &vendor, sizeof(vendor)) &&
  89.       (vendor == cpu_to_le16(PCI_VENDOR_ID_INTEL) || vendor == cpu_to_le16(PCI_VENDOR_ID_COMPAQ)))
  90.     {
  91.       a->debug("...outside the Asylum at 0/%02x/0", d.dev);
  92.       return 1;
  93.     }
  94.     }
  95.   a->debug("...insane");
  96.   return 0;
  97. }
  98.  
  99. /*
  100.  *  Configuration type 1
  101.  */
  102.  
  103. #define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
  104.  
  105. static int
  106. conf1_detect(struct pci_access *a)
  107. {
  108.   unsigned int tmp;
  109.   int res = 0;
  110.  
  111.   outb (0x01, 0xCFB);
  112.   tmp = inl (0xCF8);
  113.   outl (0x80000000, 0xCF8);
  114.   if (inl (0xCF8) == 0x80000000)
  115.     res = 1;
  116.   outl (tmp, 0xCF8);
  117.   if (res)
  118.     res = intel_sanity_check(a, &pm_intel_conf1);
  119.   return res;
  120. }
  121.  
  122. static int
  123. conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
  124. {
  125.   int addr = 0xcfc + (pos&3);
  126.  
  127.   if (pos >= 256)
  128.     return 0;
  129.  
  130.   outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
  131.  
  132.   switch (len)
  133.     {
  134.     case 1:
  135.       buf[0] = inb(addr);
  136.       break;
  137.     case 2:
  138.       ((u16 *) buf)[0] = cpu_to_le16(inw(addr));
  139.       break;
  140.     case 4:
  141.       ((u32 *) buf)[0] = cpu_to_le32(inl(addr));
  142.       break;
  143.     default:
  144.       return pci_generic_block_read(d, pos, buf, len);
  145.     }
  146.   return 1;
  147. }
  148.  
  149. static int
  150. conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
  151. {
  152.   int addr = 0xcfc + (pos&3);
  153.  
  154.   if (pos >= 256)
  155.     return 0;
  156.  
  157.   outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8);
  158.  
  159.   switch (len)
  160.     {
  161.     case 1:
  162.       outb(buf[0], addr);
  163.       break;
  164.     case 2:
  165.       outw(le16_to_cpu(((u16 *) buf)[0]), addr);
  166.       break;
  167.     case 4:
  168.       outl(le32_to_cpu(((u32 *) buf)[0]), addr);
  169.       break;
  170.     default:
  171.       return pci_generic_block_write(d, pos, buf, len);
  172.     }
  173.   return 1;
  174. }
  175.  
  176. /*
  177.  *  Configuration type 2. Obsolete and brain-damaged, but existing.
  178.  */
  179.  
  180. static int
  181. conf2_detect(struct pci_access *a)
  182. {
  183.   /* This is ugly and tends to produce false positives. Beware. */
  184.  
  185.   outb(0x00, 0xCFB);
  186.   outb(0x00, 0xCF8);
  187.   outb(0x00, 0xCFA);
  188.   if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00)
  189.     return intel_sanity_check(a, &pm_intel_conf2);
  190.   else
  191.     return 0;
  192. }
  193.  
  194. static int
  195. conf2_read(struct pci_dev *d, int pos, byte *buf, int len)
  196. {
  197.   int addr = 0xc000 | (d->dev << 8) | pos;
  198.  
  199.   if (pos >= 256)
  200.     return 0;
  201.  
  202.   if (d->dev >= 16)
  203.     /* conf2 supports only 16 devices per bus */
  204.     return 0;
  205.   outb((d->func << 1) | 0xf0, 0xcf8);
  206.   outb(d->bus, 0xcfa);
  207.   switch (len)
  208.     {
  209.     case 1:
  210.       buf[0] = inb(addr);
  211.       break;
  212.     case 2:
  213.       ((u16 *) buf)[0] = cpu_to_le16(inw(addr));
  214.       break;
  215.     case 4:
  216.       ((u32 *) buf)[0] = cpu_to_le32(inl(addr));
  217.       break;
  218.     default:
  219.       outb(0, 0xcf8);
  220.       return pci_generic_block_read(d, pos, buf, len);
  221.     }
  222.   outb(0, 0xcf8);
  223.   return 1;
  224. }
  225.  
  226. static int
  227. conf2_write(struct pci_dev *d, int pos, byte *buf, int len)
  228. {
  229.   int addr = 0xc000 | (d->dev << 8) | pos;
  230.  
  231.   if (pos >= 256)
  232.     return 0;
  233.  
  234.   if (d->dev >= 16)
  235.     d->access->error("conf2_write: only first 16 devices exist.");
  236.   outb((d->func << 1) | 0xf0, 0xcf8);
  237.   outb(d->bus, 0xcfa);
  238.   switch (len)
  239.     {
  240.     case 1:
  241.       outb(buf[0], addr);
  242.       break;
  243.     case 2:
  244.       outw(le16_to_cpu(* (u16 *) buf), addr);
  245.       break;
  246.     case 4:
  247.       outl(le32_to_cpu(* (u32 *) buf), addr);
  248.       break;
  249.     default:
  250.       outb(0, 0xcf8);
  251.       return pci_generic_block_write(d, pos, buf, len);
  252.     }
  253.   outb(0, 0xcf8);
  254.   return 1;
  255. }
  256.  
  257. struct pci_methods pm_intel_conf1 = {
  258.   "Intel-conf1",
  259.   NULL,                 /* config */
  260.   conf1_detect,
  261.   conf12_init,
  262.   conf12_cleanup,
  263.   pci_generic_scan,
  264.   pci_generic_fill_info,
  265.   conf1_read,
  266.   conf1_write,
  267.   NULL,                 /* init_dev */
  268.   NULL                  /* cleanup_dev */
  269. };
  270.  
  271. struct pci_methods pm_intel_conf2 = {
  272.   "Intel-conf2",
  273.   NULL,                 /* config */
  274.   conf2_detect,
  275.   conf12_init,
  276.   conf12_cleanup,
  277.   pci_generic_scan,
  278.   pci_generic_fill_info,
  279.   conf2_read,
  280.   conf2_write,
  281.   NULL,                 /* init_dev */
  282.   NULL                  /* cleanup_dev */
  283. };
  284.