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