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

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

display error if device claimed by other driver; update BIOS MMIO address if resource manager rejects it; ICH5 does NOT support AHCI -> removed

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