Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 1 | /* |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 2 | * This code gets the card location of the hardware |
| 3 | * Copyright (C) 2001 <Allan H Trautman> <IBM Corp> |
| 4 | * Copyright (C) 2005 Stephen Rothwel, IBM Corp |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the: |
| 18 | * Free Software Foundation, Inc., |
| 19 | * 59 Temple Place, Suite 330, |
| 20 | * Boston, MA 02111-1307 USA |
| 21 | * |
| 22 | * Change Activity: |
| 23 | * Created, Feb 2, 2001 |
| 24 | * Ported to ppc64, August 20, 2001 |
| 25 | * End Change Activity |
| 26 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 27 | #include <linux/init.h> |
| 28 | #include <linux/module.h> |
| 29 | #include <linux/pci.h> |
Stephen Rothwell | 426c1a1 | 2005-10-14 14:51:42 +1000 | [diff] [blame] | 30 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | #include <asm/types.h> |
| 32 | #include <asm/resource.h> |
Stephen Rothwell | 426c1a1 | 2005-10-14 14:51:42 +1000 | [diff] [blame] | 33 | #include <asm/abs_addr.h> |
Stephen Rothwell | bffa8fc | 2005-10-14 15:06:10 +1000 | [diff] [blame] | 34 | #include <asm/pci-bridge.h> |
Kelly Daly | 1ec65d7 | 2005-11-02 13:46:07 +1100 | [diff] [blame] | 35 | #include <asm/iseries/hv_types.h> |
Stephen Rothwell | 426c1a1 | 2005-10-14 14:51:42 +1000 | [diff] [blame] | 36 | |
| 37 | #include "pci.h" |
Stephen Rothwell | c6d2ea9 | 2005-10-14 17:16:17 +1000 | [diff] [blame] | 38 | #include "call_pci.h" |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | |
| 40 | /* |
| 41 | * Size of Bus VPD data |
| 42 | */ |
| 43 | #define BUS_VPDSIZE 1024 |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 44 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | /* |
| 46 | * Bus Vpd Tags |
| 47 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | #define VpdEndOfAreaTag 0x79 |
| 49 | #define VpdIdStringTag 0x82 |
| 50 | #define VpdVendorAreaTag 0x84 |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 51 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | /* |
| 53 | * Mfg Area Tags |
| 54 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | #define VpdFruFrameId 0x4649 // "FI" |
| 56 | #define VpdSlotMapFormat 0x4D46 // "MF" |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 | #define VpdSlotMap 0x534D // "SM" |
| 58 | |
| 59 | /* |
| 60 | * Structures of the areas |
| 61 | */ |
| 62 | struct MfgVpdAreaStruct { |
| 63 | u16 Tag; |
| 64 | u8 TagLength; |
| 65 | u8 AreaData1; |
| 66 | u8 AreaData2; |
| 67 | }; |
| 68 | typedef struct MfgVpdAreaStruct MfgArea; |
| 69 | #define MFG_ENTRY_SIZE 3 |
| 70 | |
| 71 | struct SlotMapStruct { |
| 72 | u8 AgentId; |
| 73 | u8 SecondaryAgentId; |
| 74 | u8 PhbId; |
| 75 | char CardLocation[3]; |
| 76 | char Parms[8]; |
| 77 | char Reserved[2]; |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 78 | }; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 79 | typedef struct SlotMapStruct SlotMap; |
| 80 | #define SLOT_ENTRY_SIZE 16 |
| 81 | |
| 82 | /* |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | * Parse the Slot Area |
| 84 | */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 85 | static void __init iSeries_Parse_SlotArea(SlotMap *MapPtr, int MapLen, |
| 86 | HvAgentId agent, u8 *PhbId, char card[4]) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 87 | { |
| 88 | int SlotMapLen = MapLen; |
| 89 | SlotMap *SlotMapPtr = MapPtr; |
| 90 | |
| 91 | /* |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 92 | * Parse Slot label until we find the one requested |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | */ |
| 94 | while (SlotMapLen > 0) { |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 95 | if (SlotMapPtr->AgentId == agent) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | /* |
| 97 | * If Phb wasn't found, grab the entry first one found. |
| 98 | */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 99 | if (*PhbId == 0xff) |
| 100 | *PhbId = SlotMapPtr->PhbId; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | /* Found it, extract the data. */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 102 | if (SlotMapPtr->PhbId == *PhbId) { |
| 103 | memcpy(card, &SlotMapPtr->CardLocation, 3); |
| 104 | card[3] = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | break; |
| 106 | } |
| 107 | } |
| 108 | /* Point to the next Slot */ |
| 109 | SlotMapPtr = (SlotMap *)((char *)SlotMapPtr + SLOT_ENTRY_SIZE); |
| 110 | SlotMapLen -= SLOT_ENTRY_SIZE; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /* |
| 115 | * Parse the Mfg Area |
| 116 | */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 117 | static void __init iSeries_Parse_MfgArea(u8 *AreaData, int AreaLen, |
| 118 | HvAgentId agent, u8 *PhbId, |
| 119 | u8 *frame, char card[4]) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 120 | { |
| 121 | MfgArea *MfgAreaPtr = (MfgArea *)AreaData; |
| 122 | int MfgAreaLen = AreaLen; |
| 123 | u16 SlotMapFmt = 0; |
| 124 | |
| 125 | /* Parse Mfg Data */ |
| 126 | while (MfgAreaLen > 0) { |
| 127 | int MfgTagLen = MfgAreaPtr->TagLength; |
| 128 | /* Frame ID (FI 4649020310 ) */ |
| 129 | if (MfgAreaPtr->Tag == VpdFruFrameId) /* FI */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 130 | *frame = MfgAreaPtr->AreaData1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 131 | /* Slot Map Format (MF 4D46020004 ) */ |
| 132 | else if (MfgAreaPtr->Tag == VpdSlotMapFormat) /* MF */ |
| 133 | SlotMapFmt = (MfgAreaPtr->AreaData1 * 256) |
| 134 | + MfgAreaPtr->AreaData2; |
| 135 | /* Slot Map (SM 534D90 */ |
| 136 | else if (MfgAreaPtr->Tag == VpdSlotMap) { /* SM */ |
| 137 | SlotMap *SlotMapPtr; |
| 138 | |
| 139 | if (SlotMapFmt == 0x1004) |
| 140 | SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr |
| 141 | + MFG_ENTRY_SIZE + 1); |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 142 | else |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr |
| 144 | + MFG_ENTRY_SIZE); |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 145 | iSeries_Parse_SlotArea(SlotMapPtr, MfgTagLen, |
| 146 | agent, PhbId, card); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | } |
| 148 | /* |
| 149 | * Point to the next Mfg Area |
| 150 | * Use defined size, sizeof give wrong answer |
| 151 | */ |
| 152 | MfgAreaPtr = (MfgArea *)((char *)MfgAreaPtr + MfgTagLen |
| 153 | + MFG_ENTRY_SIZE); |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 154 | MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE); |
| 155 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | /* |
| 159 | * Look for "BUS".. Data is not Null terminated. |
| 160 | * PHBID of 0xFF indicates PHB was not found in VPD Data. |
| 161 | */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 162 | static int __init iSeries_Parse_PhbId(u8 *AreaPtr, int AreaLength) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | { |
| 164 | u8 *PhbPtr = AreaPtr; |
| 165 | int DataLen = AreaLength; |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 166 | char PhbId = 0xFF; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | |
| 168 | while (DataLen > 0) { |
| 169 | if ((*PhbPtr == 'B') && (*(PhbPtr + 1) == 'U') |
| 170 | && (*(PhbPtr + 2) == 'S')) { |
| 171 | PhbPtr += 3; |
| 172 | while (*PhbPtr == ' ') |
| 173 | ++PhbPtr; |
| 174 | PhbId = (*PhbPtr & 0x0F); |
| 175 | break; |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 176 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | ++PhbPtr; |
| 178 | --DataLen; |
| 179 | } |
| 180 | return PhbId; |
| 181 | } |
| 182 | |
| 183 | /* |
| 184 | * Parse out the VPD Areas |
| 185 | */ |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 186 | static void __init iSeries_Parse_Vpd(u8 *VpdData, int VpdDataLen, |
| 187 | HvAgentId agent, u8 *frame, char card[4]) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 188 | { |
| 189 | u8 *TagPtr = VpdData; |
| 190 | int DataLen = VpdDataLen - 3; |
Michael Ellerman | 463c619 | 2006-07-13 17:54:44 +1000 | [diff] [blame] | 191 | u8 PhbId = 0xff; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | |
| 193 | while ((*TagPtr != VpdEndOfAreaTag) && (DataLen > 0)) { |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 194 | int AreaLen = *(TagPtr + 1) + (*(TagPtr + 2) * 256); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | u8 *AreaData = TagPtr + 3; |
| 196 | |
| 197 | if (*TagPtr == VpdIdStringTag) |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 198 | PhbId = iSeries_Parse_PhbId(AreaData, AreaLen); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 199 | else if (*TagPtr == VpdVendorAreaTag) |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 200 | iSeries_Parse_MfgArea(AreaData, AreaLen, |
| 201 | agent, &PhbId, frame, card); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 202 | /* Point to next Area. */ |
| 203 | TagPtr = AreaData + AreaLen; |
| 204 | DataLen -= AreaLen; |
| 205 | } |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 206 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 208 | static int __init iSeries_Get_Location_Code(u16 bus, HvAgentId agent, |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 209 | u8 *frame, char card[4]) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 | { |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 211 | int status = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 | int BusVpdLen = 0; |
Stephen Rothwell | 57ca86d | 2005-06-21 17:15:45 -0700 | [diff] [blame] | 213 | u8 *BusVpdPtr = kmalloc(BUS_VPDSIZE, GFP_KERNEL); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | |
| 215 | if (BusVpdPtr == NULL) { |
| 216 | printk("PCI: Bus VPD Buffer allocation failure.\n"); |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 217 | return 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 218 | } |
Stephen Rothwell | 426c1a1 | 2005-10-14 14:51:42 +1000 | [diff] [blame] | 219 | BusVpdLen = HvCallPci_getBusVpd(bus, iseries_hv_addr(BusVpdPtr), |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | BUS_VPDSIZE); |
| 221 | if (BusVpdLen == 0) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 | printk("PCI: Bus VPD Buffer zero length.\n"); |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 223 | goto out_free; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | } |
| 225 | /* printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); */ |
| 226 | /* Make sure this is what I think it is */ |
| 227 | if (*BusVpdPtr != VpdIdStringTag) { /* 0x82 */ |
| 228 | printk("PCI: Bus VPD Buffer missing starting tag.\n"); |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 229 | goto out_free; |
| 230 | } |
| 231 | iSeries_Parse_Vpd(BusVpdPtr, BusVpdLen, agent, frame, card); |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 232 | status = 1; |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 233 | out_free: |
| 234 | kfree(BusVpdPtr); |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 235 | return status; |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 236 | } |
| 237 | |
| 238 | /* |
| 239 | * Prints the device information. |
| 240 | * - Pass in pci_dev* pointer to the device. |
| 241 | * - Pass in the device count |
| 242 | * |
| 243 | * Format: |
| 244 | * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet |
| 245 | * controller |
| 246 | */ |
| 247 | void __init iSeries_Device_Information(struct pci_dev *PciDev, int count) |
| 248 | { |
Stephen Rothwell | 252e75a | 2005-09-28 14:40:40 +1000 | [diff] [blame] | 249 | struct device_node *DevNode = PciDev->sysdata; |
Stephen Rothwell | bffa8fc | 2005-10-14 15:06:10 +1000 | [diff] [blame] | 250 | struct pci_dn *pdn; |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 251 | u16 bus; |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 252 | u8 frame = 0; |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 253 | char card[4]; |
| 254 | HvSubBusNumber subbus; |
| 255 | HvAgentId agent; |
| 256 | |
| 257 | if (DevNode == NULL) { |
| 258 | printk("%d. PCI: iSeries_Device_Information DevNode is NULL\n", |
| 259 | count); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 260 | return; |
| 261 | } |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 262 | |
Stephen Rothwell | bffa8fc | 2005-10-14 15:06:10 +1000 | [diff] [blame] | 263 | pdn = PCI_DN(DevNode); |
Stephen Rothwell | 20f48cc | 2005-10-14 16:49:58 +1000 | [diff] [blame] | 264 | bus = pdn->busno; |
| 265 | subbus = pdn->bussubno; |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 266 | agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus), |
| 267 | ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); |
Stephen Rothwell | 061c063 | 2005-06-21 17:15:48 -0700 | [diff] [blame] | 268 | |
Michael Ellerman | f357b4c | 2006-07-13 17:54:39 +1000 | [diff] [blame] | 269 | if (iSeries_Get_Location_Code(bus, agent, &frame, card)) { |
| 270 | printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, " |
| 271 | "Card %4s 0x%04X\n", count, bus, |
| 272 | PCI_SLOT(PciDev->devfn), PciDev->vendor, frame, |
| 273 | card, (int)(PciDev->class >> 8)); |
| 274 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 275 | } |