source: trunk/src/os2ahci/pci.c@ 14

Last change on this file since 14 was 14, checked in by markus, 15 years ago

merged last NCQ related changes with Watcom related changes

File size: 34.0 KB
Line 
1/******************************************************************************
2 * pci.c - PCI constants and detection code for os2ahci driver
3 *
4 * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
5 * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "os2ahci.h"
23
24/* -------------------------- macros and constants ------------------------- */
25
26/* offset of PCI base address register (BAR) in the PCI config space */
27#define PCI_BAR(reg) (0x10 + (reg) * sizeof(u32))
28
29/* offset of MMIO base address register (BAR) for AHCI adapters */
30#define AHCI_MMIO PCI_BAR(AHCI_PCI_BAR)
31
32/******************************************************************************
33 * OEMHLP constants for PCI access
34 */
35#define GENERIC_IOCTL 0x10
36#define OH_CATEGORY 0x00
37#define OH_FUNC_PCI 0x0b
38
39/* subfunctions */
40#define OH_BIOS_INFO 0x00
41#define OH_FIND_DEVICE 0x01
42#define OH_FIND_CLASS 0x02
43#define OH_READ_CONFIG 0x03
44#define OH_WRITE_CONFIG 0x04
45
46/* return codes */
47#define OH_SUCCESS 0x00
48#define OH_NOT_SUPPORTED 0x81
49#define OH_BAD_VENDOR 0x83
50#define OH_NOT_FOUND 0x86
51#define OH_BAD_REGISTER 0x87
52
53/* ------------------------ typedefs and structures ------------------------ */
54
55/******************************************************************************
56 * OEMHLP IOCtl parameter union. The parameter area is generally used as input
57 * to the OEMHLP IOCtl calls.
58 */
59typedef union {
60
61 /* query PCI BIOS information" */
62 struct {
63 UCHAR subfunction;
64 } bios_info;
65
66 /* find PCI device */
67 struct {
68 UCHAR subfunction;
69 USHORT device;
70 USHORT vendor;
71 UCHAR index;
72 } find_device;
73
74 /* find PCI class code */
75 struct {
76 UCHAR subfunction;
77 ULONG class;
78 UCHAR index;
79 } find_class;
80
81 /* read PCI configuration space */
82 struct {
83 UCHAR subfunction;
84 UCHAR bus;
85 UCHAR dev_func;
86 UCHAR reg;
87 UCHAR size;
88 } read_config;
89
90 /* write PCI configuration space */
91 struct {
92 UCHAR subfunction;
93 UCHAR bus;
94 UCHAR dev_func;
95 UCHAR reg;
96 UCHAR size;
97 ULONG data;
98 } write_config;
99
100} OH_PARM;
101
102/******************************************************************************
103 * OEMHLP IOCtl data union. The data area is generally used as output from the
104 * OEMHLP IOCtl calls.
105 */
106typedef union {
107
108 /* query PCI BIOS information" */
109 struct {
110 UCHAR rc;
111 UCHAR hw_mech;
112 UCHAR major_version;
113 UCHAR minor_version;
114 UCHAR last_bus;
115 } bios_info;
116
117 /* find PCI device */
118 struct {
119 UCHAR rc;
120 UCHAR bus;
121 UCHAR dev_func;
122 } find_device;
123
124 /* find PCI class code */
125 struct {
126 UCHAR rc;
127 UCHAR bus;
128 UCHAR dev_func;
129 } find_class;
130
131 /* read PCI confguration space */
132 struct {
133 UCHAR rc;
134 ULONG data;
135 } read_config;
136
137 /* write PCI confguration space */
138 struct {
139 UCHAR rc;
140 } write_config;
141
142} OH_DATA;
143
144/* -------------------------- function prototypes -------------------------- */
145
146static void add_pci_device (PCI_ID *pci_id, OH_DATA _far *data);
147static UCHAR pci_read_conf (UCHAR bus, UCHAR dev_func, UCHAR indx,
148 UCHAR size, ULONG _far *val);
149static UCHAR pci_write_conf (UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
150 ULONG val);
151static int oemhlp_call (UCHAR subfunction, OH_PARM _far *parm,
152 OH_DATA _far *data);
153
154/* ------------------------ global/static variables ------------------------ */
155
156/******************************************************************************
157 * PCI vendor and device IDs for known AHCI adapters. Copied from the Linux
158 * AHCI driver.
159 */
160
161PCI_ID pci_ids[] = {
162
163 /* Intel */
164 { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */
165 { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */
166 { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */
167 { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */
168 { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */
169 { PCI_VDEVICE(AL, 0x5288), board_ahci_ign_iferr }, /* ULi M5288 */
170 { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */
171 { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
172 { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
173 { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
174 { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
175 { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */
176 { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
177 { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
178 { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
179 { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
180 { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
181 { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
182 { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
183 { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
184 { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
185 { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
186 { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
187 { PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */
188 { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
189 { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
190 { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
191 { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
192 { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
193 { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
194 { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
195 { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
196 { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
197 { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
198 { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
199 { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
200 { PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */
201 { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
202 { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
203 { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
204
205 /* JMicron 360/1/3/5/6, match class to avoid IDE function */
206 { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
207 PCI_CLASS_STORAGE_SATA_AHCI, 0xffffffL, board_ahci_ign_iferr },
208
209 /* ATI */
210 { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
211 { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb700 }, /* ATI SB700/800 */
212 { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb700 }, /* ATI SB700/800 */
213 { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb700 }, /* ATI SB700/800 */
214 { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb700 }, /* ATI SB700/800 */
215 { PCI_VDEVICE(ATI, 0x4394), board_ahci_sb700 }, /* ATI SB700/800 */
216 { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */
217
218 /* AMD */
219 { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */
220 /* AMD is using RAID class only for ahci controllers */
221 { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
222 PCI_CLASS_STORAGE_RAID << 8, 0xffffffL, board_ahci },
223
224 /* VIA */
225 { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
226 { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
227
228 /* NVIDIA */
229 { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 }, /* MCP65 */
230 { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 }, /* MCP65 */
231 { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 }, /* MCP65 */
232 { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 }, /* MCP65 */
233 { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 }, /* MCP65 */
234 { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */
235 { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */
236 { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */
237 { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_yesncq }, /* MCP67 */
238 { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_yesncq }, /* MCP67 */
239 { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_yesncq }, /* MCP67 */
240 { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_yesncq }, /* MCP67 */
241 { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_yesncq }, /* MCP67 */
242 { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_yesncq }, /* MCP67 */
243 { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_yesncq }, /* MCP67 */
244 { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_yesncq }, /* MCP67 */
245 { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_yesncq }, /* MCP67 */
246 { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq }, /* MCP67 */
247 { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq }, /* MCP67 */
248 { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq }, /* MCP67 */
249 { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_yesncq }, /* Linux ID */
250 { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq }, /* MCP73 */
251 { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq }, /* MCP73 */
252 { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq }, /* MCP73 */
253 { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_yesncq }, /* MCP73 */
254 { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_yesncq }, /* MCP73 */
255 { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_yesncq }, /* MCP73 */
256 { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_yesncq }, /* MCP73 */
257 { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_yesncq }, /* MCP73 */
258 { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_yesncq }, /* MCP73 */
259 { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_yesncq }, /* MCP73 */
260 { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_yesncq }, /* MCP73 */
261 { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_yesncq }, /* MCP73 */
262 { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci }, /* MCP77 */
263 { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci }, /* MCP77 */
264 { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci }, /* MCP77 */
265 { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci }, /* MCP77 */
266 { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci }, /* MCP77 */
267 { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci }, /* MCP77 */
268 { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci }, /* MCP77 */
269 { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci }, /* MCP77 */
270 { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci }, /* MCP77 */
271 { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci }, /* MCP77 */
272 { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci }, /* MCP77 */
273 { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci }, /* MCP77 */
274 { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci }, /* MCP79 */
275 { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci }, /* MCP79 */
276 { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci }, /* MCP79 */
277 { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci }, /* MCP79 */
278 { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci }, /* MCP79 */
279 { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci }, /* MCP79 */
280 { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci }, /* MCP79 */
281 { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci }, /* MCP79 */
282 { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci }, /* MCP79 */
283 { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci }, /* MCP79 */
284 { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci }, /* MCP79 */
285 { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci }, /* MCP79 */
286 { PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci }, /* MCP89 */
287 { PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci }, /* MCP89 */
288 { PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci }, /* MCP89 */
289 { PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci }, /* MCP89 */
290 { PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci }, /* MCP89 */
291 { PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci }, /* MCP89 */
292 { PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci }, /* MCP89 */
293 { PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci }, /* MCP89 */
294 { PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci }, /* MCP89 */
295 { PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci }, /* MCP89 */
296 { PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci }, /* MCP89 */
297 { PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci }, /* MCP89 */
298
299 /* SiS */
300 { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */
301 { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 968 */
302 { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */
303
304 /* Marvell */
305 { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv }, /* 6145 */
306 { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv }, /* 6121 */
307
308 /* Promise */
309 { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */
310
311 /* Generic, PCI class code for AHCI */
312 { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
313 PCI_CLASS_STORAGE_SATA_AHCI, 0xffffffL, board_ahci },
314
315 /* end of list, including a few slots to define custom adapters (10) */
316 { 0, 0, 0, 0, 0, 0, 0 },
317 { 0, 0, 0, 0, 0, 0, 0 },
318 { 0, 0, 0, 0, 0, 0, 0 },
319 { 0, 0, 0, 0, 0, 0, 0 },
320 { 0, 0, 0, 0, 0, 0, 0 },
321 { 0, 0, 0, 0, 0, 0, 0 },
322 { 0, 0, 0, 0, 0, 0, 0 },
323 { 0, 0, 0, 0, 0, 0, 0 },
324 { 0, 0, 0, 0, 0, 0, 0 },
325 { 0, 0, 0, 0, 0, 0, 0 },
326
327 { 0, 0, 0, 0, 0, 0, 0 }
328};
329
330/******************************************************************************
331 * OEMHLP$ is used by OS/2 to provide access to OEM-specific machine resources
332 * like PCI BIOS access. We're using this to enumerate the PCI bus. Due to
333 * BIOS bugs, it may be necessary to use I/O operations for this purpose but
334 * so far I think this is only relevant for rather old PCs and SATA is not
335 * expected to be a priority on those machines.
336 */
337static IDCTABLE oemhlp; /* OEMHLP$ IDC entry point */
338
339/* ----------------------------- start of code ----------------------------- */
340
341/******************************************************************************
342 * Add specified PCI vendor and device ID to the list of supported AHCI
343 * controllers. Please note that the last slot in pci_ids needs to remain
344 * empty because it's used as end marker.
345 */
346int add_pci_id(u16 vendor, u16 device)
347{
348 int max_slot = sizeof(pci_ids) / sizeof(*pci_ids) - 2;
349 int i;
350
351 /* search for last used slot in 'pci_ids' */
352 for (i = max_slot; i >= 0 && pci_ids[i].vendor == 0; i--);
353 if (i >= max_slot) {
354 /* all slots in use */
355 return(-1);
356 }
357
358 /* use slot after the last used slot */
359 i++;
360 pci_ids[i].vendor = vendor;
361 pci_ids[i].device = device;
362 pci_ids[i].board = board_ahci;
363 return(0);
364}
365
366/******************************************************************************
367 * Scan PCI bus using OEMHLP$ IOCTLs and build adapter list.
368 */
369void scan_pci_bus(void)
370{
371 OH_PARM parm;
372 OH_DATA data;
373 UCHAR index;
374 UCHAR rc;
375 int i;
376
377 ddprintf("scanning PCI bus...\n");
378
379 /* verify that we have a PCI system */
380 memset(&parm, 0x00, sizeof(parm));
381 if (oemhlp_call(OH_BIOS_INFO, &parm, &data) != OH_SUCCESS) {
382 cprintf("couldn't get PCI BIOS information\n");
383 return;
384 }
385
386 /* Go through the list of PCI IDs and search for each device
387 *
388 * NOTES:
389 *
390 * - When searching via class code, the OEMHLP$ interface doesn't allow
391 * setting a bitmask to look for individual portions of class code,
392 * subclass code and programming interface. However, all bitmasks in the
393 * PCI list currently use 0xffffff, thus this should not be a problem at
394 * this point in time.
395 *
396 * - Scanning via OEMHLP$ seems rather slow, at least in the virtual
397 * machine I'm currenly using to test this driver. Thus, class code
398 * scans are preferred unless the option "-t" (thorough_scan) has been
399 * specified. The assumption is that most, if not all, modern AHCI
400 * adapters have the correct class code (PCI_CLASS_STORAGE_SATA_AHCI).
401 */
402 for (i = 0; pci_ids[i].vendor != 0; i++) {
403 index = 0;
404 do {
405 if (pci_ids[i].device == PCI_ANY_ID || pci_ids[i].vendor == PCI_ANY_ID) {
406 /* look for class code */
407 memset(&parm, 0x00, sizeof(parm));
408 parm.find_class.class = pci_ids[i].class;
409 parm.find_class.index = index;
410 rc = oemhlp_call(OH_FIND_CLASS, &parm, &data);
411
412 } else if (thorough_scan) {
413 /* look for this specific vendor and device ID */
414 memset(&parm, 0x00, sizeof(parm));
415 parm.find_device.device = pci_ids[i].device;
416 parm.find_device.vendor = pci_ids[i].vendor;
417 parm.find_device.index = index;
418 rc = oemhlp_call(OH_FIND_DEVICE, &parm, &data);
419
420 } else {
421 rc = OH_NOT_FOUND;
422 }
423
424 if (rc == OH_SUCCESS) {
425 /* found a device */
426 add_pci_device(pci_ids + i, &data);
427 if (++index > 180) {
428 /* something's wrong here... */
429 return;
430 }
431 }
432
433 } while (rc == OH_SUCCESS);
434 }
435}
436
437/******************************************************************************
438 * Enable interrupt generation. PCI 2.3 added a bit which allows disabling
439 * interrupt generation for a device. This function clears the corresponding
440 * bit in the configuration space command register.
441 */
442int pci_enable_int(UCHAR bus, UCHAR dev_func)
443{
444 ULONG tmp;
445
446 if (pci_read_conf (bus, dev_func, 4, sizeof(u32), &tmp) != OH_SUCCESS ||
447 pci_write_conf(bus, dev_func, 4, sizeof(u32), tmp & ~(1UL << 10)) != OH_SUCCESS) {
448 return(-1);
449 }
450 return(0);
451}
452
453/******************************************************************************
454 * Hack to set up proper IRQ mappings in the emulated PIIX3 ISA bridge in
455 * VirtualBox (for some reason, the first mapped IRQ is 0x80 without this
456 * hack).
457 */
458void pci_hack_virtualbox(void)
459{
460 ULONG irq = 0;
461
462 if (pci_read_conf(0, 0x08, 0x60, 1, &irq) == OH_SUCCESS && irq == 0x80) {
463 /* set IRQ for first device/func to 11 */
464 dprintf("hacking virtualbox PIIX3 PCI to ISA bridge IRQ mapping\n");
465 irq = ad_infos[0].irq;
466 pci_write_conf(0, 0x08, 0x60, 1, irq);
467 }
468}
469
470/******************************************************************************
471 * Add a single PCI device to the list of adapters.
472 */
473static void add_pci_device(PCI_ID *pci_id, OH_DATA _far *data)
474{
475 char rc_list_buf[sizeof(AHRESOURCE) + sizeof(HRESOURCE) * 4];
476 AHRESOURCE _far *rc_list = (AHRESOURCE _far *) rc_list_buf;
477 RESOURCESTRUCT resource;
478 ADAPTERSTRUCT adapter;
479 ADJUNCT adj;
480 AD_INFO *ad_info;
481 APIRET ret;
482 UCHAR bus = data->find_class.bus;
483 UCHAR dev_func = data->find_class.dev_func;
484 ULONG val;
485 SEL gdt[PORT_DMA_BUF_SEGS + 1];
486 char tmp[40];
487 u16 device;
488 u16 vendor;
489 u32 class;
490 u32 mmio_bios = 0;
491 u32 mmio_size;
492 u32 mmio_rqd;
493 int irq;
494 int pin;
495 int i;
496
497 /*****************************************************************************
498 * Part 1: Get further information about the device to be added; PCI ID...
499 */
500 if (pci_read_conf(bus, dev_func, 0x00, sizeof(ULONG), &val) != OH_SUCCESS) {
501 return;
502 }
503 device = (u16) (val >> 16);
504 vendor = (u16) (val & 0xffff);
505
506 /* ... and class code */
507 if (pci_read_conf(bus, dev_func, 0x08, sizeof(ULONG), &val) != OH_SUCCESS) {
508 return;
509 }
510 class = (u32) (val >> 8);
511
512 if (pci_id->device == PCI_ANY_ID) {
513 /* We found this device in a wildcard search. There are two possible
514 * reasons which require a different handling:
515 *
516 * 1) This device uses a non-standard PCI class and has been identified
517 * with the corresponding class in pci_ids[] (e.g. the entry
518 * PCI_VENDOR_ID_JMICRON), but there is a vendor ID in pci_ids[]. In
519 * this case, we need to verify that the vendor is correct (see
520 * comments regarding OEMHLP limitations in 'scan_pci_bus()')
521 *
522 * 2) This device was identified using a generic PCI class for AHCI
523 * adapters such as PCI_CLASS_STORAGE_SATA_AHCI and we need to map
524 * the device and vendor ID to the corresponding index in pci_ids[]
525 * if there is such an entry; the index passed to this function will
526 * be the generic class-based index which is fine as long as there's
527 * not special treatment required as indicated by the board_*
528 * constants in pci_ids[]...
529 *
530 * The main reason for this kludge is that it seems as if OEMHLP$
531 * is rather slow searching for PCI devices, adding around 30s
532 * to the boot time when scanning for individual AHCI PCI IDs. Thus,
533 * the OS2AHCI driver avoids this kind of scan in favor of a class-
534 * based scan (unless overridden with the "/T" option).
535 */
536 if (pci_id->vendor != PCI_ANY_ID) {
537 /* case 1: the vendor is known but we found the PCI device using a class
538 * search; verify vendor matches the one in pci_ids[]
539 */
540 if (pci_id->vendor != vendor) {
541 /* vendor doesn't match */
542 return;
543 }
544
545 } else {
546 /* case 2: we found this device using a generic class search; if the
547 * device/vendor is listed in pci_ids[], use this entry in favor of the
548 * one passed in 'pci_id'
549 */
550 for (i = 0; pci_ids[i].vendor != 0; i++) {
551 if (pci_ids[i].device == device && pci_ids[i].vendor == vendor) {
552 pci_id = pci_ids + i;
553 break;
554 }
555 }
556 }
557 }
558
559 /* found a supported AHCI device */
560 cprintf("found AHCI device: %04x:%04x (class = 0x%06lx, bus = %d, dev_func = 0x%02x)\n",
561 vendor, device, class, bus, dev_func);
562
563 /* make sure we got room in the adapter information array */
564 if (ad_info_cnt >= MAX_AD - 1) {
565 cprintf("error: too many AHCI devices\n");
566 return;
567 }
568
569 /****************************************************************************
570 * Part 2: Determine resource requirements and allocate resources with the
571 * OS/2 resource manager. While doing so, some of the entries of the
572 * corresponding slot in the AD_INFO array, namely resource manager
573 * handles, are initialized so we need prepare the slot.
574 *
575 * NOTE: While registering resources with the resource manager, each new
576 * resource is added to the corresponding rc_list.hResource[] slot.
577 * rc_list is used further down to associate resources to adapters
578 * whe the adapter itself is registered with the OS/2 resource manager.
579 */
580 ad_info = ad_infos + ad_info_cnt;
581 memset(ad_info, 0x00, sizeof(*ad_info));
582 rc_list->NumResource = 0;
583
584 /* Register IRQ with resource manager
585 *
586 * NOTE: We rely on the IRQ number saved in the PCI config space by the PCI
587 * BIOS. There's no reliable way to find out the IRQ number in any
588 * other way unless we start using message-driven interrupts (which
589 * is out of scope for the time being).
590 */
591 if (pci_read_conf(bus, dev_func, 0x3c, sizeof(u32), &val) != OH_SUCCESS) {
592 return;
593 }
594 irq = (int) (val & 0xff);
595 pin = (int) ((val >> 8) & 0xff);
596
597 memset(&resource, 0x00, sizeof(resource));
598 resource.ResourceType = RS_TYPE_IRQ;
599 resource.IRQResource.IRQLevel = irq;
600 resource.IRQResource.PCIIrqPin = pin;
601 resource.IRQResource.IRQFlags = RS_IRQ_SHARED;
602
603 ret = RMAllocResource(rm_drvh, &ad_info->rm_irq, &resource);
604 if (ret != RMRC_SUCCESS) {
605 cprintf("error: couldn't register IRQ %d (rc = %d)\n", irq, ret);
606 return;
607 }
608 rc_list->hResource[rc_list->NumResource++] = ad_info->rm_irq;
609
610 /* Determine MMIO size for this device
611 *
612 * NOTE: In order to do this, we need to temporarily write 0xffffffff to
613 * the MMIO base address register (BAR), read back the resulting value
614 * and check the 0 bits from the right end. After doing this, we must
615 * restore the original value set up by the BIOS because we're not yet
616 * ready to take over.
617 *
618 * The least significant 4 bits are not relevant for the MMIO address, thus
619 * we'll start at 0x10:
620 *
621 * 31 4 3 2 1 0
622 * -------------------------------------------------------------------
623 * base address P T T I
624 * P = prefetchable
625 * T = type
626 * I = I/O (1) or memory (0)
627 */
628 if (pci_read_conf (bus, dev_func, AHCI_MMIO, sizeof(u32), &mmio_bios) != OH_SUCCESS ||
629 pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), ~(0UL)) != OH_SUCCESS ||
630 pci_read_conf (bus, dev_func, AHCI_MMIO, sizeof(u32), &mmio_rqd) != OH_SUCCESS ||
631 pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), mmio_bios) != OH_SUCCESS) {
632
633 cprintf("error: couldn't determine MMIO size\n");
634 if (mmio_bios != 0) {
635 cprintf("restoring BIOS MMIO address\n");
636 pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), mmio_bios);
637 }
638 goto add_pci_fail;
639 }
640 for (mmio_size = 0x00000010UL;
641 mmio_size < 0x10000000UL && (mmio_rqd & mmio_size) == 0;
642 mmio_size <<= 1);
643
644 ddprintf("MMIO size = %ld\n", mmio_size);
645 ddprintf("MMIO address (BIOS) = 0x%08lx\n", mmio_bios & 0xfffffff0UL);
646
647 /* register BIOS MMIO address space with resource manager */
648 memset(&resource, 0x00, sizeof(resource));
649 resource.ResourceType = RS_TYPE_MEM;
650 resource.MEMResource.MemBase = mmio_bios & 0xfffffff0UL;
651 resource.MEMResource.MemSize = mmio_size;
652 resource.MEMResource.MemFlags = RS_MEM_EXCLUSIVE;
653
654 ret = RMAllocResource(rm_drvh, &ad_info->rm_mmio, &resource);
655
656 if (ret != RMRC_SUCCESS) {
657 /* didn't work; try to find another MMIO region */
658 cprintf("warning: BIOS MMIO address not accepted by resource manager\n");
659 memset(&resource, 0x00, sizeof(resource));
660 resource.ResourceType = RS_TYPE_MEM;
661 resource.MEMResource.MemSize = mmio_size;
662 resource.MEMResource.MemFlags = RS_MEM_EXCLUSIVE | RS_SEARCH;
663
664 ret = RMAllocResource(rm_drvh, &ad_info->rm_mmio, &resource);
665 }
666
667 if (ret != RMRC_SUCCESS) {
668 cprintf("error: couldn't register MMIO region (rc = %d)\n", ret);
669 goto add_pci_fail;
670 }
671 rc_list->hResource[rc_list->NumResource++] = ad_info->rm_mmio;
672 ddprintf("MMIO address (final) = 0x%08lx\n", resource.MEMResource.MemBase);
673
674 /****************************************************************************
675 * Part 3: Fill in the remaining fields in the AD_INFO slot and allocate
676 * memory and GDT selectors for the adapter. Finally, register the adapter
677 * itself with the OS/2 resource manager
678 */
679 ad_info->pci = pci_ids + i;
680 ad_info->bus = bus;
681 ad_info->dev_func = dev_func;
682 ad_info->irq = irq;
683 ad_info->mmio_phys = resource.MEMResource.MemBase;
684
685 /* allocate memory for port-specific DMA scratch buffers */
686 if (DevHelp_AllocPhys((long) AHCI_PORT_PRIV_DMA_SZ * AHCI_MAX_PORTS,
687 MEMTYPE_ABOVE_1M, &ad_info->dma_buf_phys) != 0) {
688 cprintf("error: couldn't allocate DMA scratch buffers for AHCI ports\n");
689 ad_info->dma_buf_phys = 0;
690 goto add_pci_fail;
691 }
692
693 /* allocate GDT selectors for memory-mapped I/O and DMA scratch buffers */
694 if (DevHelp_AllocGDTSelector(gdt, PORT_DMA_BUF_SEGS + 1) != 0) {
695 cprintf("error: couldn't allocate GDT selectors\n");
696 memset(gdt, 0x00, sizeof(gdt));
697 goto add_pci_fail;
698 }
699
700 /* map MMIO address to first GDT selector */
701 if (DevHelp_PhysToGDTSelector(ad_info->mmio_phys, (USHORT) mmio_size,
702 gdt[0]) != 0) {
703 cprintf("error: couldn't map MMIO address to GDT selector\n");
704 goto add_pci_fail;
705 }
706
707 /* map DMA scratch buffers to remaining GDT selectors */
708 for (i = 0; i < PORT_DMA_BUF_SEGS; i++) {
709 ULONG addr = ad_info->dma_buf_phys + i * PORT_DMA_SEG_SIZE;
710 USHORT len = AHCI_PORT_PRIV_DMA_SZ * PORT_DMA_BUFS_PER_SEG;
711
712 if (DevHelp_PhysToGDTSelector(addr, len, gdt[i+1]) != 0) {
713 cprintf("error: couldn't map DMA scratch buffer to GDT selector\n");
714 goto add_pci_fail;
715 }
716 }
717
718 /* fill in MMIO and DMA scratch buffer addresses in adapter info */
719 ad_info->mmio = (u8 _far *) ((u32) gdt[0] << 16);
720 for (i = 0; i < PORT_DMA_BUF_SEGS; i++) {
721 ad_info->dma_buf[i] = (u8 _far *) ((u32) gdt[i+1] << 16);
722 }
723
724 /* register adapter with resource manager */
725 memset(&adj, 0x00, sizeof(adj));
726 adj.pNextAdj = NULL;
727 adj.AdjLength = sizeof(adj);
728 adj.AdjType = ADJ_ADAPTER_NUMBER;
729 adj.Adapter_Number = ad_info_cnt;
730
731 memset(&adapter, 0x00, sizeof(adapter));
732 sprintf(tmp, "AHCI_%d Controller", ad_info_cnt);
733 adapter.AdaptDescriptName = tmp;
734 adapter.AdaptFlags = 0;
735 adapter.BaseType = AS_BASE_MSD;
736 adapter.SubType = AS_SUB_IDE;
737 adapter.InterfaceType = AS_INTF_GENERIC;
738 adapter.HostBusType = AS_HOSTBUS_PCI;
739 adapter.HostBusWidth = AS_BUSWIDTH_32BIT;
740 adapter.pAdjunctList = &adj;
741
742 ret = RMCreateAdapter(rm_drvh, &ad_info->rm_adh, &adapter, NULL, rc_list);
743 if (ret != RMRC_SUCCESS) {
744 cprintf("error: couldn't register adapter (rc = %d)\n", ret);
745 goto add_pci_fail;
746 }
747
748 /* Successfully added the adapter and reserved its resources; the adapter
749 * is still under BIOS control so we're not going to do anything else at
750 * this point.
751 */
752 ad_info_cnt++;
753 return;
754
755add_pci_fail:
756 /* something went wrong; try to clean up as far as possible */
757 if (ad_info->rm_mmio != 0) {
758 RMDeallocResource(rm_drvh, ad_info->rm_mmio);
759 }
760 if (ad_info->rm_irq != 0) {
761 RMDeallocResource(rm_drvh, ad_info->rm_irq);
762 }
763 if (&ad_info->dma_buf_phys != 0) {
764 DevHelp_FreePhys(ad_info->dma_buf_phys);
765 }
766 for (i = 0; i < sizeof(gdt) / sizeof(*gdt); i++) {
767 if (gdt[i] != 0) {
768 DevHelp_FreeGDTSelector(gdt[i]);
769 }
770 }
771}
772
773/******************************************************************************
774 * Read PCI configuration space register
775 */
776static UCHAR pci_read_conf(UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
777 ULONG _far *val)
778{
779 OH_PARM parm;
780 OH_DATA data;
781 UCHAR rc;
782
783 memset(&parm, 0x00, sizeof(parm));
784 parm.read_config.bus = bus;
785 parm.read_config.dev_func = dev_func;
786 parm.read_config.reg = indx;
787 parm.read_config.size = size;
788 if ((rc = oemhlp_call(OH_READ_CONFIG, &parm, &data) != OH_SUCCESS)) {
789 cprintf("error: couldn't read config space (bus = %d, dev_func = 0x%02x, indx = 0x%02x, rc = %d)\n",
790 bus, dev_func, indx, rc);
791 return(rc);
792 }
793
794 *val = data.read_config.data;
795 return(OH_SUCCESS);
796}
797
798/******************************************************************************
799 * Write PCI configuration space register
800 */
801static UCHAR pci_write_conf(UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
802 ULONG val)
803{
804 OH_PARM parm;
805 OH_DATA data;
806 UCHAR rc;
807
808 memset(&parm, 0x00, sizeof(parm));
809 parm.write_config.bus = bus;
810 parm.write_config.dev_func = dev_func;
811 parm.write_config.reg = indx;
812 parm.write_config.size = size;
813 parm.write_config.data = val;
814
815 if ((rc = oemhlp_call(OH_WRITE_CONFIG, &parm, &data) != OH_SUCCESS)) {
816 cprintf("error: couldn't write config space (bus = %d, dev_func = 0x%02x, indx = 0x%02x, rc = %d)\n",
817 bus, dev_func, indx, rc);
818 return(rc);
819 }
820
821 return(OH_SUCCESS);
822}
823/******************************************************************************
824 * Call OEMHLP$ IDC entry point with the specified IOCtl parameter and data
825 * packets.
826 */
827static int oemhlp_call(UCHAR subfunction, OH_PARM _far *parm,
828 OH_DATA _far *data)
829{
830 void (_far *func)(void);
831 RP_GENIOCTL ioctl;
832 unsigned short prot_idc_ds;
833
834 if (oemhlp.ProtIDCEntry == NULL || oemhlp.ProtIDC_DS == 0) {
835 /* attach to OEMHLP$ device driver */
836 if (DevHelp_AttachDD("OEMHLP$ ", (NPBYTE) &oemhlp) ||
837 oemhlp.ProtIDCEntry == NULL ||
838 oemhlp.ProtIDC_DS == 0) {
839 cprintf("couldn't attach to OEMHLP$\n");
840 return(OH_NOT_SUPPORTED);
841 }
842 }
843
844 /* store subfuntion in first byte of pararameter packet */
845 parm->bios_info.subfunction = subfunction;
846 memset(data, 0x00, sizeof(*data));
847
848 /* assemble IOCtl request */
849 memset(&ioctl, 0x00, sizeof(ioctl));
850 ioctl.rph.Len = sizeof(ioctl);
851 ioctl.rph.Unit = 0;
852 ioctl.rph.Cmd = GENERIC_IOCTL;
853 ioctl.rph.Status = 0;
854
855 ioctl.Category = OH_CATEGORY;
856 ioctl.Function = OH_FUNC_PCI;
857 ioctl.ParmPacket = (PUCHAR) parm;
858 ioctl.DataPacket = (PUCHAR) data;
859 ioctl.ParmLen = sizeof(*parm);
860 ioctl.DataLen = sizeof(*data);
861
862 /* Call OEMHLP's IDC routine. Before doing so, we need to assign the address
863 * to be called to a stack variable because the inter-device driver calling
864 * convention forces us to set DS to the device driver's data segment and ES
865 * to the segment of the request packet.
866 */
867 func = oemhlp.ProtIDCEntry;
868
869 /* The WATCOM compiler does not support struct references in inline
870 * assembler code, so we pass it in a stack variable
871 */
872 prot_idc_ds = oemhlp.ProtIDC_DS;
873
874 _asm {
875 push ds;
876 push es;
877 push bx;
878 push si;
879 push di;
880
881 push ss
882 pop es
883 lea bx, ioctl;
884 mov ds, prot_idc_ds;
885 call dword ptr [func];
886
887 pop di;
888 pop si;
889 pop bx;
890 pop es;
891 pop ds;
892 }
893
894 dddphex(parm, sizeof(*parm), "oemhlp_parm: ");
895 dddphex(data, sizeof(*data), "oemhlp_data: ");
896
897 if (ioctl.rph.Status & STERR) {
898 return(OH_NOT_SUPPORTED);
899 }
900 return(data->bios_info.rc);
901}
Note: See TracBrowser for help on using the repository browser.