Commit | Line | Data |
---|---|---|
57ca86d4 | 1 | /* |
57ca86d4 SR |
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 | */ | |
1da177e4 LT |
27 | #include <linux/init.h> |
28 | #include <linux/module.h> | |
29 | #include <linux/pci.h> | |
30 | #include <asm/types.h> | |
31 | #include <asm/resource.h> | |
32 | ||
33 | #include <asm/iSeries/HvCallPci.h> | |
34 | #include <asm/iSeries/HvTypes.h> | |
1da177e4 | 35 | #include <asm/iSeries/iSeries_pci.h> |
1da177e4 LT |
36 | |
37 | /* | |
38 | * Size of Bus VPD data | |
39 | */ | |
40 | #define BUS_VPDSIZE 1024 | |
57ca86d4 | 41 | |
1da177e4 LT |
42 | /* |
43 | * Bus Vpd Tags | |
44 | */ | |
1da177e4 LT |
45 | #define VpdEndOfAreaTag 0x79 |
46 | #define VpdIdStringTag 0x82 | |
47 | #define VpdVendorAreaTag 0x84 | |
57ca86d4 | 48 | |
1da177e4 LT |
49 | /* |
50 | * Mfg Area Tags | |
51 | */ | |
1da177e4 LT |
52 | #define VpdFruFrameId 0x4649 // "FI" |
53 | #define VpdSlotMapFormat 0x4D46 // "MF" | |
1da177e4 LT |
54 | #define VpdSlotMap 0x534D // "SM" |
55 | ||
56 | /* | |
57 | * Structures of the areas | |
58 | */ | |
59 | struct MfgVpdAreaStruct { | |
60 | u16 Tag; | |
61 | u8 TagLength; | |
62 | u8 AreaData1; | |
63 | u8 AreaData2; | |
64 | }; | |
65 | typedef struct MfgVpdAreaStruct MfgArea; | |
66 | #define MFG_ENTRY_SIZE 3 | |
67 | ||
68 | struct SlotMapStruct { | |
69 | u8 AgentId; | |
70 | u8 SecondaryAgentId; | |
71 | u8 PhbId; | |
72 | char CardLocation[3]; | |
73 | char Parms[8]; | |
74 | char Reserved[2]; | |
57ca86d4 | 75 | }; |
1da177e4 LT |
76 | typedef struct SlotMapStruct SlotMap; |
77 | #define SLOT_ENTRY_SIZE 16 | |
78 | ||
1da177e4 LT |
79 | /* |
80 | * Parse the Slot Area | |
81 | */ | |
061c063e SR |
82 | static void __init iSeries_Parse_SlotArea(SlotMap *MapPtr, int MapLen, |
83 | HvAgentId agent, u8 *PhbId, char card[4]) | |
1da177e4 LT |
84 | { |
85 | int SlotMapLen = MapLen; | |
86 | SlotMap *SlotMapPtr = MapPtr; | |
87 | ||
88 | /* | |
57ca86d4 | 89 | * Parse Slot label until we find the one requested |
1da177e4 LT |
90 | */ |
91 | while (SlotMapLen > 0) { | |
061c063e | 92 | if (SlotMapPtr->AgentId == agent) { |
1da177e4 LT |
93 | /* |
94 | * If Phb wasn't found, grab the entry first one found. | |
95 | */ | |
061c063e SR |
96 | if (*PhbId == 0xff) |
97 | *PhbId = SlotMapPtr->PhbId; | |
1da177e4 | 98 | /* Found it, extract the data. */ |
061c063e SR |
99 | if (SlotMapPtr->PhbId == *PhbId) { |
100 | memcpy(card, &SlotMapPtr->CardLocation, 3); | |
101 | card[3] = 0; | |
1da177e4 LT |
102 | break; |
103 | } | |
104 | } | |
105 | /* Point to the next Slot */ | |
106 | SlotMapPtr = (SlotMap *)((char *)SlotMapPtr + SLOT_ENTRY_SIZE); | |
107 | SlotMapLen -= SLOT_ENTRY_SIZE; | |
108 | } | |
109 | } | |
110 | ||
111 | /* | |
112 | * Parse the Mfg Area | |
113 | */ | |
061c063e SR |
114 | static void __init iSeries_Parse_MfgArea(u8 *AreaData, int AreaLen, |
115 | HvAgentId agent, u8 *PhbId, | |
116 | u8 *frame, char card[4]) | |
1da177e4 LT |
117 | { |
118 | MfgArea *MfgAreaPtr = (MfgArea *)AreaData; | |
119 | int MfgAreaLen = AreaLen; | |
120 | u16 SlotMapFmt = 0; | |
121 | ||
122 | /* Parse Mfg Data */ | |
123 | while (MfgAreaLen > 0) { | |
124 | int MfgTagLen = MfgAreaPtr->TagLength; | |
125 | /* Frame ID (FI 4649020310 ) */ | |
126 | if (MfgAreaPtr->Tag == VpdFruFrameId) /* FI */ | |
061c063e | 127 | *frame = MfgAreaPtr->AreaData1; |
1da177e4 LT |
128 | /* Slot Map Format (MF 4D46020004 ) */ |
129 | else if (MfgAreaPtr->Tag == VpdSlotMapFormat) /* MF */ | |
130 | SlotMapFmt = (MfgAreaPtr->AreaData1 * 256) | |
131 | + MfgAreaPtr->AreaData2; | |
132 | /* Slot Map (SM 534D90 */ | |
133 | else if (MfgAreaPtr->Tag == VpdSlotMap) { /* SM */ | |
134 | SlotMap *SlotMapPtr; | |
135 | ||
136 | if (SlotMapFmt == 0x1004) | |
137 | SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr | |
138 | + MFG_ENTRY_SIZE + 1); | |
57ca86d4 | 139 | else |
1da177e4 LT |
140 | SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr |
141 | + MFG_ENTRY_SIZE); | |
061c063e SR |
142 | iSeries_Parse_SlotArea(SlotMapPtr, MfgTagLen, |
143 | agent, PhbId, card); | |
1da177e4 LT |
144 | } |
145 | /* | |
146 | * Point to the next Mfg Area | |
147 | * Use defined size, sizeof give wrong answer | |
148 | */ | |
149 | MfgAreaPtr = (MfgArea *)((char *)MfgAreaPtr + MfgTagLen | |
150 | + MFG_ENTRY_SIZE); | |
57ca86d4 SR |
151 | MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE); |
152 | } | |
1da177e4 LT |
153 | } |
154 | ||
155 | /* | |
156 | * Look for "BUS".. Data is not Null terminated. | |
157 | * PHBID of 0xFF indicates PHB was not found in VPD Data. | |
158 | */ | |
061c063e | 159 | static int __init iSeries_Parse_PhbId(u8 *AreaPtr, int AreaLength) |
1da177e4 LT |
160 | { |
161 | u8 *PhbPtr = AreaPtr; | |
162 | int DataLen = AreaLength; | |
57ca86d4 | 163 | char PhbId = 0xFF; |
1da177e4 LT |
164 | |
165 | while (DataLen > 0) { | |
166 | if ((*PhbPtr == 'B') && (*(PhbPtr + 1) == 'U') | |
167 | && (*(PhbPtr + 2) == 'S')) { | |
168 | PhbPtr += 3; | |
169 | while (*PhbPtr == ' ') | |
170 | ++PhbPtr; | |
171 | PhbId = (*PhbPtr & 0x0F); | |
172 | break; | |
57ca86d4 | 173 | } |
1da177e4 LT |
174 | ++PhbPtr; |
175 | --DataLen; | |
176 | } | |
177 | return PhbId; | |
178 | } | |
179 | ||
180 | /* | |
181 | * Parse out the VPD Areas | |
182 | */ | |
061c063e SR |
183 | static void __init iSeries_Parse_Vpd(u8 *VpdData, int VpdDataLen, |
184 | HvAgentId agent, u8 *frame, char card[4]) | |
1da177e4 LT |
185 | { |
186 | u8 *TagPtr = VpdData; | |
187 | int DataLen = VpdDataLen - 3; | |
061c063e | 188 | u8 PhbId; |
1da177e4 LT |
189 | |
190 | while ((*TagPtr != VpdEndOfAreaTag) && (DataLen > 0)) { | |
57ca86d4 | 191 | int AreaLen = *(TagPtr + 1) + (*(TagPtr + 2) * 256); |
1da177e4 LT |
192 | u8 *AreaData = TagPtr + 3; |
193 | ||
194 | if (*TagPtr == VpdIdStringTag) | |
061c063e | 195 | PhbId = iSeries_Parse_PhbId(AreaData, AreaLen); |
1da177e4 | 196 | else if (*TagPtr == VpdVendorAreaTag) |
061c063e SR |
197 | iSeries_Parse_MfgArea(AreaData, AreaLen, |
198 | agent, &PhbId, frame, card); | |
1da177e4 LT |
199 | /* Point to next Area. */ |
200 | TagPtr = AreaData + AreaLen; | |
201 | DataLen -= AreaLen; | |
202 | } | |
57ca86d4 | 203 | } |
1da177e4 | 204 | |
061c063e SR |
205 | static void __init iSeries_Get_Location_Code(u16 bus, HvAgentId agent, |
206 | u8 *frame, char card[4]) | |
1da177e4 LT |
207 | { |
208 | int BusVpdLen = 0; | |
57ca86d4 | 209 | u8 *BusVpdPtr = kmalloc(BUS_VPDSIZE, GFP_KERNEL); |
1da177e4 LT |
210 | |
211 | if (BusVpdPtr == NULL) { | |
212 | printk("PCI: Bus VPD Buffer allocation failure.\n"); | |
213 | return; | |
214 | } | |
061c063e | 215 | BusVpdLen = HvCallPci_getBusVpd(bus, ISERIES_HV_ADDR(BusVpdPtr), |
1da177e4 LT |
216 | BUS_VPDSIZE); |
217 | if (BusVpdLen == 0) { | |
1da177e4 | 218 | printk("PCI: Bus VPD Buffer zero length.\n"); |
061c063e | 219 | goto out_free; |
1da177e4 LT |
220 | } |
221 | /* printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); */ | |
222 | /* Make sure this is what I think it is */ | |
223 | if (*BusVpdPtr != VpdIdStringTag) { /* 0x82 */ | |
224 | printk("PCI: Bus VPD Buffer missing starting tag.\n"); | |
061c063e | 225 | goto out_free; |
1da177e4 | 226 | } |
061c063e SR |
227 | iSeries_Parse_Vpd(BusVpdPtr, BusVpdLen, agent, frame, card); |
228 | out_free: | |
1da177e4 LT |
229 | kfree(BusVpdPtr); |
230 | } | |
061c063e SR |
231 | |
232 | /* | |
233 | * Prints the device information. | |
234 | * - Pass in pci_dev* pointer to the device. | |
235 | * - Pass in the device count | |
236 | * | |
237 | * Format: | |
238 | * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet | |
239 | * controller | |
240 | */ | |
241 | void __init iSeries_Device_Information(struct pci_dev *PciDev, int count) | |
242 | { | |
252e75a5 | 243 | struct device_node *DevNode = PciDev->sysdata; |
061c063e SR |
244 | u16 bus; |
245 | u8 frame; | |
246 | char card[4]; | |
247 | HvSubBusNumber subbus; | |
248 | HvAgentId agent; | |
249 | ||
250 | if (DevNode == NULL) { | |
251 | printk("%d. PCI: iSeries_Device_Information DevNode is NULL\n", | |
252 | count); | |
253 | return; | |
254 | } | |
255 | ||
256 | bus = ISERIES_BUS(DevNode); | |
257 | subbus = ISERIES_SUBBUS(DevNode); | |
258 | agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus), | |
259 | ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); | |
260 | iSeries_Get_Location_Code(bus, agent, &frame, card); | |
261 | ||
262 | printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, Card %4s ", | |
263 | count, bus, PCI_SLOT(PciDev->devfn), PciDev->vendor, | |
264 | frame, card); | |
982245f0 | 265 | printk("0x%04X\n", (int)(PciDev->class >> 8)); |
061c063e | 266 | } |