/** * PCI.c - PCI constants and detection code for os2ahci driver * * Copyright (c) 2011 thi.guten Software Development * Copyright (c) 2011 Mensys B.V. * Copyright (c) 2013-2023 David Azarewicz * * Authors: Christian Mueller, Markus Thielen * * Parts copied from/inspired by the Linux AHCI driver; * those parts are (c) Linux AHCI/ATA maintainers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "os2ahci.h" #include "pci_regs.h" /* offset of PCI base address register (BAR) in the PCI config space */ #define PCI_BAR(reg) (UCHAR) (0x10 + (reg) * sizeof(u32)) static void add_pci_device(PCI_ID *pci_id, USHORT BusDevFunc); static long bar_resource(USHORT BusDevFunc, RESOURCESTRUCT *resource, int i); static char *rmerr(APIRET ret); /****************************************************************************** * PCI vendor and device IDs for known AHCI adapters. Copied from the Linux * AHCI driver. */ PCI_ID pci_ids[] = { /* Intel * NOTE: ICH5 controller does NOT support AHCI, so we do not add it here! */ { PCI_VDEVICE(INTEL, 0x2652), "ICH6" , 0 }, /* ICH6 */ { PCI_VDEVICE(INTEL, 0x2653), "ICH6M", 0 }, /* ICH6M */ { PCI_VDEVICE(INTEL, 0x27c1), "ICH7" , 0 }, /* ICH7 */ { PCI_VDEVICE(INTEL, 0x27c5), "ICH7M", 0 }, /* ICH7M */ { PCI_VDEVICE(INTEL, 0x27c3), "ICH7R", 0 }, /* ICH7R */ { PCI_VDEVICE(AL, 0x5288), "ULiM5288", AHCI_HFLAG_IGN_IRQ_IF_ERR }, /* ULi M5288 */ { PCI_VDEVICE(INTEL, 0x2681), "ESB2" , 0 }, /* ESB2 */ { PCI_VDEVICE(INTEL, 0x2682), "ESB2" , 0 }, /* ESB2 */ { PCI_VDEVICE(INTEL, 0x2683), "ESB2" , 0 }, /* ESB2 */ { PCI_VDEVICE(INTEL, 0x27c6), "ICH7MDH", 0 }, /* ICH7-M DH */ { PCI_VDEVICE(INTEL, 0x2821), "ICH8" , 0 }, /* ICH8 */ { PCI_VDEVICE(INTEL, 0x2822), "ICH8", AHCI_HFLAG_NO_SNTF }, /* ICH8 */ { PCI_VDEVICE(INTEL, 0x2824), "ICH8" , 0 }, /* ICH8 */ { PCI_VDEVICE(INTEL, 0x2829), "ICH8M" , 0 }, /* ICH8M */ { PCI_VDEVICE(INTEL, 0x282a), "ICH8M" , 0 }, /* ICH8M */ { PCI_VDEVICE(INTEL, 0x2922), "ICH9" , 0 }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2923), "ICH9" , 0 }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2924), "ICH9" , 0 }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2925), "ICH9" , 0 }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2927), "ICH9" , 0 }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x2929), "ICH9M" , 0 }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292a), "ICH9M" , 0 }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292b), "ICH9M" , 0 }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292c), "ICH9M" , 0 }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292f), "ICH9M" , 0 }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x294d), "ICH9" , 0 }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x294e), "ICH9M" , 0 }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x502a), "Tolapai" , 0 }, /* Tolapai */ { PCI_VDEVICE(INTEL, 0x502b), "Tolapai" , 0 }, /* Tolapai */ { PCI_VDEVICE(INTEL, 0x3a05), "ICH10" , 0 }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3a22), "ICH10" , 0 }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3a25), "ICH10" , 0 }, /* ICH10 */ { PCI_VDEVICE(INTEL, 0x3b22), "PCH AHCI", 0 }, /* PCH AHCI */ { PCI_VDEVICE(INTEL, 0x3b23), "PCH AHCI", 0 }, /* PCH AHCI */ { PCI_VDEVICE(INTEL, 0x3b24), "PCH RAID", 0 }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b25), "PCH RAID", 0 }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b29), "PCH AHCI", 0 }, /* PCH AHCI */ { PCI_VDEVICE(INTEL, 0x3b2b), "PCH RAID", 0 }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2c), "PCH RAID", 0 }, /* PCH RAID */ { PCI_VDEVICE(INTEL, 0x3b2f), "PCH AHCI", 0 }, /* PCH AHCI */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, "360", AHCI_HFLAG_IGN_IRQ_IF_ERR }, /* ATI */ { PCI_VDEVICE(ATI, 0x4380), "SB600", AHCI_HFLAG_IGN_SERR_INTERNAL | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_SECT255 | AHCI_HFLAG_32BIT_ONLY }, /* ATI SB600 */ { PCI_VDEVICE(ATI, 0x4390), "SB700/800", AHCI_HFLAG_IGN_SERR_INTERNAL }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4391), "SB700/800", AHCI_HFLAG_IGN_SERR_INTERNAL }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4392), "SB700/800", AHCI_HFLAG_IGN_SERR_INTERNAL }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4393), "SB700/800", AHCI_HFLAG_IGN_SERR_INTERNAL }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4394), "SB700/800", AHCI_HFLAG_IGN_SERR_INTERNAL }, /* ATI SB700/800 */ { PCI_VDEVICE(ATI, 0x4395), "SB700/800", AHCI_HFLAG_IGN_SERR_INTERNAL }, /* ATI SB700/800 */ /* AMD */ { PCI_VDEVICE(AMD, 0x7800), "Hudson-2", 0 }, /* AMD Hudson-2 */ /* AMD is using RAID class only for ahci controllers */ { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffffff, "Hudson2", 0 }, /* VIA */ { PCI_VDEVICE(VIA, 0x3349), "VT8251", AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP }, /* VIA VT8251 */ { PCI_VDEVICE(VIA, 0x6287), "VT8251", AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP }, /* VIA VT8251 */ /* NVIDIA */ { PCI_VDEVICE(NVIDIA, 0x044c), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044d), "MCB65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044e), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x044f), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045c), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045d), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045e), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045f), "MCP65", AHCI_HFLAG_YES_NCQ }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x0550), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0551), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0552), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0553), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0554), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0555), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0556), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0557), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0558), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0559), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x055a), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x055b), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* MCP67 */ { PCI_VDEVICE(NVIDIA, 0x0580), "MCP67" , AHCI_HFLAG_YES_NCQ }, /* Linux ID */ { PCI_VDEVICE(NVIDIA, 0x07f0), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f1), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f2), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f3), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f4), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f5), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f6), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f7), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f8), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07f9), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07fa), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x07fb), "MCP73" , AHCI_HFLAG_YES_NCQ }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x0ad0), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad1), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad2), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad3), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad4), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad5), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad6), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad7), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad8), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad9), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ada), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0adb), "MCP77", 0 }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ab4), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab5), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab6), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab7), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab8), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0ab9), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0aba), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abb), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abc), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abd), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abe), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0abf), "MCP79", 0 }, /* MCP79 */ { PCI_VDEVICE(NVIDIA, 0x0d84), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d85), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d86), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d87), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d88), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d89), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d8a), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d8b), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d8c), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d8d), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d8e), "MCP89", 0 }, /* MCP89 */ { PCI_VDEVICE(NVIDIA, 0x0d8f), "MCP89", 0 }, /* MCP89 */ /* SiS */ { PCI_VDEVICE(SI, 0x1184), "966", 0 }, /* SiS 966 */ { PCI_VDEVICE(SI, 0x1185), "968", 0 }, /* SiS 968 */ { PCI_VDEVICE(SI, 0x0186), "968", 0 }, /* SiS 968 */ /* Marvell */ { PCI_VDEVICE(MARVELL, 0x6145), "6145", AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP }, /* 6145 */ { PCI_VDEVICE(MARVELL, 0x6121), "6121", AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_MSI | AHCI_HFLAG_MV_PATA | AHCI_HFLAG_NO_PMP }, /* 6121 */ /* Promise */ { PCI_VDEVICE(PROMISE, 0x3f20), "PDC42819", 0 }, /* PDC42819 */ /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, "Generic", 0 }, /* end of list, including a few slots to define custom adapters (10) */ { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 }, { 0, 0, 0, 0, 0, 0, NULL, 0 } }; /****************************************************************************** * Add specified PCI vendor and device ID to the list of supported AHCI * controllers. Please note that the last slot in pci_ids needs to remain * empty because it's used as end marker. */ int add_pci_id(u16 vendor, u16 device) { int max_slot = sizeof(pci_ids) / sizeof(*pci_ids) - 2; int i; /* search for last used slot in 'pci_ids' */ for (i = max_slot; i >= 0 && pci_ids[i].vendor == 0; i--); if (i >= max_slot) return(-1); /* all slots in use */ /* use slot after the last used slot */ i++; pci_ids[i].vendor = vendor; pci_ids[i].device = device; pci_ids[i].pChipName = "Specified"; pci_ids[i].quirks = 0; return(0); } /****************************************************************************** * Scan PCI bus using OEMHLP$ IOCTLs and build adapter list. */ void scan_pci_bus(void) { UCHAR index; int ad_indx = 0; int i; int n; USHORT BusDevFunc; DPRINTF(DBG_INIT|DBG_FUNCBEG, DBG_PREFIX": scanning PCI bus...\n"); /* Go through the list of PCI IDs and search for each device * * NOTES: * * - When searching via class code, the OEMHLP$ interface doesn't allow * setting a bitmask to look for individual portions of class code, * subclass code and programming interface. However, all bitmasks in the * PCI list currently use 0xffffff, thus this should not be a problem at * this point in time. * * - Scanning via OEMHLP$ seems rather slow, at least in the virtual * machine I'm currenly using to test this driver. Thus, class code * scans are preferred unless the option "-t" (thorough_scan) has been * specified. The assumption is that most, if not all, modern AHCI * adapters have the correct class code (PCI_CLASS_STORAGE_SATA_AHCI). */ for (i = 0; pci_ids[i].vendor != 0; i++) { index = 0; do { if (pci_ids[i].device == PCI_ANY_ID || pci_ids[i].vendor == PCI_ANY_ID) { /* look for class code */ BusDevFunc = PciFindClass(pci_ids[i].class, index); } else if (thorough_scan) { /* look for this specific vendor and device ID */ BusDevFunc = PciFindDevice( pci_ids[i].vendor, pci_ids[i].device, index); } else { BusDevFunc = 0xffff; } if (BusDevFunc != 0xffff) { /* found a device */ int already_found = 0; /* increment index for next loop */ if (++index > 180) return; /* something's wrong here... */ /* check whether we already found this device */ for (n = 0; n < ad_info_cnt; n++) { if (ad_infos[n].BusDevFunc == BusDevFunc) { /* this device has already been found (e.g. via thorough scan) */ already_found = 1; break; } } if (already_found || (ad_ignore & (1U << ad_indx++))) { /* ignore this device; it has either already been found via a * thorough scan or has been specified to be ignored via command * line option */ continue; } /* add this PCI device to ad_infos[] */ add_pci_device(pci_ids + i, BusDevFunc); } } while (BusDevFunc != 0xffff); } } /****************************************************************************** * Hack to set up proper IRQ mappings in the emulated PIIX3 ISA bridge in * VirtualBox (for some reason, the first mapped IRQ is 0x80 without this * hack). */ void pci_hack_virtualbox(void) { UCHAR irq = 0; if (!PciReadConfig(0x0008, 0x60, sizeof(irq), &irq) && irq == 0x80) { /* set IRQ for first device/func to 11 */ DPRINTF(DBG_INIT, DBG_PREFIX": hacking virtualbox PIIX3 PCI to ISA bridge IRQ mapping\n"); irq = ad_infos[0].irq; PciWriteConfig(0x0008, 0x60, sizeof(irq), irq); } } /****************************************************************************** * Add a single PCI device to the list of adapters. */ static void add_pci_device(PCI_ID *pci_id, USHORT BusDevFunc) { char rc_list_buf[sizeof(AHRESOURCE) + sizeof(HRESOURCE) * 15]; AHRESOURCE *rc_list = (AHRESOURCE *) rc_list_buf; RESOURCESTRUCT resource; ADAPTERSTRUCT adapter; ADJUNCT adj; AD_INFO *ad_info; APIRET ret; ULONG val; char tmp[40]; u16 device; u16 vendor; u32 class; int irq; int pin; int i; /***************************************************************************** * Part 1: Get further information about the device to be added; PCI ID... */ if (PciReadConfig(BusDevFunc, 0x00, sizeof(ULONG), &val)) return; device = (val >> 16); vendor = (val & 0xffff); /* ... and class code */ if (PciReadConfig(BusDevFunc, 0x08, sizeof(ULONG), &val)) return; class = (val >> 8); if (pci_id->device == PCI_ANY_ID) { /* We found this device in a wildcard search. There are two possible * reasons which require a different handling: * * 1) This device uses a non-standard PCI class and has been identified * with the corresponding class in pci_ids[] (e.g. the entry * PCI_VENDOR_ID_JMICRON), but there is a vendor ID in pci_ids[]. In * this case, we need to verify that the vendor is correct (see * comments regarding OEMHLP limitations in 'scan_pci_bus()') * * 2) This device was identified using a generic PCI class for AHCI * adapters such as PCI_CLASS_STORAGE_SATA_AHCI and we need to map * the device and vendor ID to the corresponding index in pci_ids[] * if there is such an entry; the index passed to this function will * be the generic class-based index which is fine as long as there's * not special treatment required as indicated by the board_* * constants in pci_ids[]... * * The main reason for this kludge is that it seems as if OEMHLP$ * is rather slow searching for PCI devices, adding around 30s * to the boot time when scanning for individual AHCI PCI IDs. Thus, * the OS2AHCI driver avoids this kind of scan in favor of a class- * based scan (unless overridden with the "/T" option). */ if (pci_id->vendor != PCI_ANY_ID) { /* case 1: the vendor is known but we found the PCI device using a class * search; verify vendor matches the one in pci_ids[] */ if (pci_id->vendor != vendor) return; /* vendor doesn't match */ } else { /* case 2: we found this device using a generic class search; if the * device/vendor is listed in pci_ids[], use this entry in favor of the * one passed in 'pci_id' */ for (i = 0; pci_ids[i].vendor != 0; i++) { if (pci_ids[i].device == device && pci_ids[i].vendor == vendor) { pci_id = pci_ids + i; break; } } } } /* found a supported AHCI device */ if (PciReadConfig(BusDevFunc, 0x3c, sizeof(u32), &val)) return; irq = (int) (val & 0xff); pin = (int) ((val >> 8) & 0xff); /* make sure we got room in the adapter information array */ if (ad_info_cnt >= MAX_AD - 1) { iprintf("OS2AHCI.ADD: too many AHCI devices"); return; } /**************************************************************************** * Part 2: Determine resource requirements and allocate resources with the * OS/2 resource manager. While doing so, some of the entries of the * corresponding slot in the AD_INFO array, namely resource manager * handles, are initialized so we need prepare the slot. * * NOTE: While registering resources with the resource manager, each new * resource is added to the corresponding rc_list.hResource[] slot. * rc_list is used further down to associate resources to adapters * when the adapter itself is registered with the OS/2 resource * manager. */ ad_info = ad_infos + ad_info_cnt; memset(ad_info, 0x00, sizeof(*ad_info)); rc_list->NumResource = 0; /* Allocate all I/O and MMIO addresses offered by this device. In theory, * we need only BAR #5, the AHCI MMIO BAR, but in order to prevent any * other driver from hijacking our device and accessing it via legacy * registers we'll reserve anything we can find. */ if (iVerbose) { iprintf("OS2AHCI.ADD: Adapter %d PCI=%d:%d:%d ID=%04x:%04x", ad_info_cnt, PCI_BUS_FROM_BDF(BusDevFunc), PCI_DEV_FROM_BDF(BusDevFunc), PCI_FUNC_FROM_BDF(BusDevFunc), vendor, device); } DPRINTF(DBG_INIT, DBG_PREFIX": Adapter %d PCI=%d:%d:%d ID=%04x:%04x\n", ad_info_cnt, PCI_BUS_FROM_BDF(BusDevFunc), PCI_DEV_FROM_BDF(BusDevFunc), PCI_FUNC_FROM_BDF(BusDevFunc), vendor, device); for (i = 0; i < sizeof(ad_info->rm_bars) / sizeof(*ad_info->rm_bars); i++) { long len = bar_resource(BusDevFunc, &resource, i); if (len < 0) { /* something went wrong */ goto add_pci_fail; } if (len == 0) { /* this BAR is unused */ continue; } if (i == AHCI_PCI_BAR) { if (resource.ResourceType != RS_TYPE_MEM) { iprintf("OS2AHCI.ADD: BAR #5 must be an MMIO region"); goto add_pci_fail; } /* save this BAR's address as MMIO address */ ad_info->mmio_phys = resource.MEMResource.MemBase; ad_info->mmio_size = resource.MEMResource.MemSize; } /* register [MM]IO region with resource manager */ ret = RMAllocResource(rm_drvh, ad_info->rm_bars + i, &resource); if (ret != RMRC_SUCCESS) { if (ret == RMRC_RES_ALREADY_CLAIMED) { if (iVerbose) iprintf("OS2AHCI.ADD: Device already claimed."); } else { iprintf("OS2AHCI.ADD: couldn't register [MM]IO region (rc = %s)", rmerr(ret)); } goto add_pci_fail; } rc_list->hResource[rc_list->NumResource++] = ad_info->rm_bars[i]; } if (ad_info->mmio_phys == 0) { iprintf("OS2AHCI.ADD: couldn't determine MMIO base address"); goto add_pci_fail; } /**************************************************************************** * Part 3: Fill in the remaining fields in the AD_INFO slot and allocate * memory and GDT selectors for the adapter. Finally, register the adapter * itself with the OS/2 resource manager */ ad_info->pChipName = pci_id->pChipName; ad_info->quirks = pci_id->quirks; ad_info->PciVendor = vendor; ad_info->PciDevice = device; ad_info->BusDevFunc = BusDevFunc; ad_info->irq = irq; ad_info->irq_pin = pin; ad_info->mmio = MapPhysToLin(ad_info->mmio_phys, ad_info->mmio_size); if (!ad_info->mmio) goto add_pci_fail; /* register adapter with resource manager */ memset(&adj, 0x00, sizeof(adj)); adj.pNextAdj = NULL; adj.AdjLength = sizeof(adj); adj.AdjType = ADJ_ADAPTER_NUMBER; adj.Adapter_Number = ad_info_cnt; memset(&adapter, 0x00, sizeof(adapter)); snprintf(tmp, sizeof(tmp), "AHCI_%d Controller", ad_info_cnt); adapter.AdaptDescriptName = tmp; adapter.AdaptFlags = 0; adapter.BaseType = AS_BASE_MSD; adapter.SubType = AS_SUB_IDE; adapter.InterfaceType = AS_INTF_GENERIC; adapter.HostBusType = AS_HOSTBUS_PCI; adapter.HostBusWidth = AS_BUSWIDTH_32BIT; adapter.pAdjunctList = &adj; ret = RMCreateAdapter(rm_drvh, &ad_info->rm_adh, &adapter, NULL, rc_list); if (ret != RMRC_SUCCESS) { iprintf("OS2AHCI.ADD: couldn't register adapter (rc = %s)", rmerr(ret)); goto add_pci_fail; } if (!PciReadConfig(BusDevFunc, PCI_COMMAND, 2, &val)) { val |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); val &= ~PCI_COMMAND_INTX_DISABLE; PciWriteConfig(BusDevFunc, PCI_COMMAND, 2, val); } if (ahci_config_caps(ad_info)) goto add_pci_fail; #ifndef DAZ_NEW_CODE /* fill in DMA scratch buffer addresses in adapter info */ for (i = 0; i < AHCI_MAX_PORTS; i++) { if (!(ad_info->port_map & (1UL << i))) continue; ad_info->ports[i].dma_buf = MemAllocAlign(AHCI_PORT_PRIV_DMA_SZ, 1024); ad_info->ports[i].dma_buf_phys = MemPhysAdr(ad_info->ports[i].dma_buf); } #endif /* Successfully added the adapter and reserved its resources; the adapter * is still under BIOS control so we're not going to do anything else at * this point. */ ad_info_cnt++; return; add_pci_fail: /* something went wrong; try to clean up as far as possible */ for (i = 0; i < sizeof(ad_info->rm_bars) / sizeof(*ad_info->rm_bars); i++) { if (ad_info->rm_bars[i] != 0) RMDeallocResource(rm_drvh, ad_info->rm_bars[i]); } } /****************************************************************************** * Prepare a resource structure for a PCI Base Address Register (BAR). This * basically means the type, address and range of the I/O address space. It * returns the length of the address range as a signed long to allow the caller * to differentiate between error conditions (< 0), unused BARs (0) or valid * bars (> 0). * * NOTE: In order to do this, we need to temporarily write 0xffffffff to * the MMIO base address register (BAR), read back the resulting value * and check the 0 bits from the right end, masking the lower 2 (I/O) or * 4 (MMIO) bits. After doing this, we must restore the original value * set up by the BIOS. * * 31 4 3 2 1 0 * ------------------------------------------------------------------- * base address P T T I * P = prefetchable * T = type (0 = any 32 bit, 1 = <1M, 2 = 64 bit) * I = I/O (1) or memory (0) */ static long bar_resource(USHORT BusDevFunc, RESOURCESTRUCT *resource, int i) { u32 bar_addr = 0; u32 bar_size = 0; /* temporarily write 1s to this BAR to determine the address range */ if (PciReadConfig (BusDevFunc, PCI_BAR(i), sizeof(u32), &bar_addr) || PciWriteConfig(BusDevFunc, PCI_BAR(i), sizeof(u32), ~(0UL)) || PciReadConfig (BusDevFunc, PCI_BAR(i), sizeof(u32), &bar_size) || PciWriteConfig(BusDevFunc, PCI_BAR(i), sizeof(u32), bar_addr) ) { iprintf("OS2AHCI.ADD: couldn't determine [MM]IO size"); if (bar_addr != 0) { PciWriteConfig(BusDevFunc, PCI_BAR(i), sizeof(u32), bar_addr); } return(-1); } /* bar not implemented or device not working properly */ if (bar_size == 0 || bar_size == 0xffffffffUL) return(0); /* prepare resource allocation structure */ memset(resource, 0x00, sizeof(*resource)); if (bar_addr & 1) { bar_size = ~(bar_size & 0xfffffffcUL) + 1; bar_size &= 0xffffUL; /* I/O address space is 16 bits on x86 */ bar_addr &= 0xfffcUL; resource->ResourceType = RS_TYPE_IO; resource->IOResource.BaseIOPort = bar_addr; resource->IOResource.NumIOPorts = bar_size; resource->IOResource.IOFlags = RS_IO_EXCLUSIVE; resource->IOResource.IOAddressLines = 16; } else { bar_size = ~(bar_size & 0xfffffff0UL) + 1; bar_addr &= 0xfffffff0UL; resource->ResourceType = RS_TYPE_MEM; resource->MEMResource.MemBase = bar_addr; resource->MEMResource.MemSize = bar_size; resource->MEMResource.MemFlags = RS_MEM_EXCLUSIVE; } DPRINTF(DBG_INIT, DBG_PREFIX": BAR #%d: type = %s, addr = 0x%08lx, size = %d\n", i, (resource->ResourceType == RS_TYPE_IO) ? "I/O" : "MEM", bar_addr, bar_size); return((long) bar_size); } /****************************************************************************** * return vendor name for PCI vendor ID */ char *vendor_from_id(u16 id) { switch(id) { case PCI_VENDOR_ID_AL: return "Ali"; case PCI_VENDOR_ID_AMD: case PCI_VENDOR_ID_ATI: return "AMD"; case PCI_VENDOR_ID_AT: return "Allied Telesyn"; case PCI_VENDOR_ID_ATT: return "ATT"; case PCI_VENDOR_ID_CMD: return "CMD"; case PCI_VENDOR_ID_CT: return "CT"; case PCI_VENDOR_ID_INTEL: return "Intel"; case PCI_VENDOR_ID_INITIO: return "Initio"; case PCI_VENDOR_ID_JMICRON: return "JMicron"; case PCI_VENDOR_ID_MARVELL: return "Marvell"; case PCI_VENDOR_ID_NVIDIA: return "NVIDIA"; case PCI_VENDOR_ID_PROMISE: return "PROMISE"; case PCI_VENDOR_ID_SI: return "SiS"; case PCI_VENDOR_ID_VIA: return "VIA"; default: break; } return ""; } /****************************************************************************** * Return textual version of a resource manager error */ static char *rmerr(APIRET ret) { switch (ret) { case RMRC_SUCCESS: return("RMRC_SUCCESS"); case RMRC_NOTINITIALIZED: return("RMRC_NOTINITIALIZED"); case RMRC_BAD_DRIVERHANDLE: return("RMRC_BAD_DRIVERHANDLE"); case RMRC_BAD_ADAPTERHANDLE: return("RMRC_BAD_ADAPTERHANDLE"); case RMRC_BAD_DEVICEHANDLE: return("RMRC_BAD_DEVICEHANDLE"); case RMRC_BAD_RESOURCEHANDLE: return("RMRC_BAD_RESOURCEHANDLE"); case RMRC_BAD_LDEVHANDLE: return("RMRC_BAD_LDEVHANDLE"); case RMRC_BAD_SYSNAMEHANDLE: return("RMRC_BAD_SYSNAMEHANDLE"); case RMRC_BAD_DEVHELP: return("RMRC_BAD_DEVHELP"); case RMRC_NULL_POINTER: return("RMRC_NULL_POINTER"); case RMRC_NULL_STRINGS: return("RMRC_NULL_STRINGS"); case RMRC_BAD_VERSION: return("RMRC_BAD_VERSION"); case RMRC_RES_ALREADY_CLAIMED: return("RMRC_RES_ALREADY_CLAIMED"); case RMRC_DEV_ALREADY_CLAIMED: return("RMRC_DEV_ALREADY_CLAIMED"); case RMRC_INVALID_PARM_VALUE: return("RMRC_INVALID_PARM_VALUE"); case RMRC_OUT_OF_MEMORY: return("RMRC_OUT_OF_MEMORY"); case RMRC_SEARCH_FAILED: return("RMRC_SEARCH_FAILED"); case RMRC_BUFFER_TOO_SMALL: return("RMRC_BUFFER_TOO_SMALL"); case RMRC_GENERAL_FAILURE: return("RMRC_GENERAL_FAILURE"); case RMRC_IRQ_ENTRY_ILLEGAL: return("RMRC_IRQ_ENTRY_ILLEGAL"); case RMRC_NOT_IMPLEMENTED: return("RMRC_NOT_IMPLEMENTED"); case RMRC_NOT_INSTALLED: return("RMRC_NOT_INSTALLED"); case RMRC_BAD_DETECTHANDLE: return("RMRC_BAD_DETECTHANDLE"); case RMRC_BAD_RMHANDLE: return("RMRC_BAD_RMHANDLE"); case RMRC_BAD_FLAGS: return("RMRC_BAD_FLAGS"); case RMRC_NO_DETECTED_DATA: return("RMRC_NO_DETECTED_DATA"); default: return("RMRC_UNKOWN"); } }