diff options
Diffstat (limited to 'arch/ppc64/kernel/iSeries_VpdInfo.c')
-rw-r--r-- | arch/ppc64/kernel/iSeries_VpdInfo.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/iSeries_VpdInfo.c b/arch/ppc64/kernel/iSeries_VpdInfo.c new file mode 100644 index 000000000000..a6f0ff2d0239 --- /dev/null +++ b/arch/ppc64/kernel/iSeries_VpdInfo.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /************************************************************************/ | ||
2 | /* File iSeries_vpdInfo.c created by Allan Trautman on Fri Feb 2 2001. */ | ||
3 | /************************************************************************/ | ||
4 | /* This code gets the card location of the hardware */ | ||
5 | /* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */ | ||
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: */ | ||
19 | /* Free Software Foundation, Inc., */ | ||
20 | /* 59 Temple Place, Suite 330, */ | ||
21 | /* Boston, MA 02111-1307 USA */ | ||
22 | /************************************************************************/ | ||
23 | /* Change Activity: */ | ||
24 | /* Created, Feb 2, 2001 */ | ||
25 | /* Ported to ppc64, August 20, 2001 */ | ||
26 | /* End Change Activity */ | ||
27 | /************************************************************************/ | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/pci.h> | ||
32 | #include <asm/types.h> | ||
33 | #include <asm/resource.h> | ||
34 | |||
35 | #include <asm/iSeries/HvCallPci.h> | ||
36 | #include <asm/iSeries/HvTypes.h> | ||
37 | #include <asm/iSeries/mf.h> | ||
38 | #include <asm/iSeries/LparData.h> | ||
39 | #include <asm/iSeries/iSeries_pci.h> | ||
40 | #include "pci.h" | ||
41 | |||
42 | /* | ||
43 | * Size of Bus VPD data | ||
44 | */ | ||
45 | #define BUS_VPDSIZE 1024 | ||
46 | /* | ||
47 | * Bus Vpd Tags | ||
48 | */ | ||
49 | #define VpdEndOfDataTag 0x78 | ||
50 | #define VpdEndOfAreaTag 0x79 | ||
51 | #define VpdIdStringTag 0x82 | ||
52 | #define VpdVendorAreaTag 0x84 | ||
53 | /* | ||
54 | * Mfg Area Tags | ||
55 | */ | ||
56 | #define VpdFruFlag 0x4647 // "FG" | ||
57 | #define VpdFruFrameId 0x4649 // "FI" | ||
58 | #define VpdSlotMapFormat 0x4D46 // "MF" | ||
59 | #define VpdAsmPartNumber 0x504E // "PN" | ||
60 | #define VpdFruSerial 0x534E // "SN" | ||
61 | #define VpdSlotMap 0x534D // "SM" | ||
62 | |||
63 | /* | ||
64 | * Structures of the areas | ||
65 | */ | ||
66 | struct MfgVpdAreaStruct { | ||
67 | u16 Tag; | ||
68 | u8 TagLength; | ||
69 | u8 AreaData1; | ||
70 | u8 AreaData2; | ||
71 | }; | ||
72 | typedef struct MfgVpdAreaStruct MfgArea; | ||
73 | #define MFG_ENTRY_SIZE 3 | ||
74 | |||
75 | struct SlotMapStruct { | ||
76 | u8 AgentId; | ||
77 | u8 SecondaryAgentId; | ||
78 | u8 PhbId; | ||
79 | char CardLocation[3]; | ||
80 | char Parms[8]; | ||
81 | char Reserved[2]; | ||
82 | }; | ||
83 | typedef struct SlotMapStruct SlotMap; | ||
84 | #define SLOT_ENTRY_SIZE 16 | ||
85 | |||
86 | /* | ||
87 | * Formats the device information. | ||
88 | * - Pass in pci_dev* pointer to the device. | ||
89 | * - Pass in buffer to place the data. Danger here is the buffer must | ||
90 | * be as big as the client says it is. Should be at least 128 bytes. | ||
91 | * Return will the length of the string data put in the buffer. | ||
92 | * Format: | ||
93 | * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet | ||
94 | * controller | ||
95 | */ | ||
96 | int iSeries_Device_Information(struct pci_dev *PciDev, char *buffer, | ||
97 | int BufferSize) | ||
98 | { | ||
99 | struct iSeries_Device_Node *DevNode = | ||
100 | (struct iSeries_Device_Node *)PciDev->sysdata; | ||
101 | int len; | ||
102 | |||
103 | if (DevNode == NULL) | ||
104 | return sprintf(buffer, | ||
105 | "PCI: iSeries_Device_Information DevNode is NULL"); | ||
106 | |||
107 | if (BufferSize < 128) | ||
108 | return 0; | ||
109 | |||
110 | len = sprintf(buffer, "PCI: Bus%3d, Device%3d, Vendor %04X ", | ||
111 | ISERIES_BUS(DevNode), PCI_SLOT(PciDev->devfn), | ||
112 | PciDev->vendor); | ||
113 | len += sprintf(buffer + len, "Frame%3d, Card %4s ", | ||
114 | DevNode->FrameId, DevNode->CardLocation); | ||
115 | #ifdef CONFIG_PCI | ||
116 | if (pci_class_name(PciDev->class >> 8) == 0) | ||
117 | len += sprintf(buffer + len, "0x%04X ", | ||
118 | (int)(PciDev->class >> 8)); | ||
119 | else | ||
120 | len += sprintf(buffer + len, "%s", | ||
121 | pci_class_name(PciDev->class >> 8)); | ||
122 | #endif | ||
123 | return len; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Parse the Slot Area | ||
128 | */ | ||
129 | void iSeries_Parse_SlotArea(SlotMap *MapPtr, int MapLen, | ||
130 | struct iSeries_Device_Node *DevNode) | ||
131 | { | ||
132 | int SlotMapLen = MapLen; | ||
133 | SlotMap *SlotMapPtr = MapPtr; | ||
134 | |||
135 | /* | ||
136 | * Parse Slot label until we find the one requrested | ||
137 | */ | ||
138 | while (SlotMapLen > 0) { | ||
139 | if (SlotMapPtr->AgentId == DevNode->AgentId ) { | ||
140 | /* | ||
141 | * If Phb wasn't found, grab the entry first one found. | ||
142 | */ | ||
143 | if (DevNode->PhbId == 0xff) | ||
144 | DevNode->PhbId = SlotMapPtr->PhbId; | ||
145 | /* Found it, extract the data. */ | ||
146 | if (SlotMapPtr->PhbId == DevNode->PhbId ) { | ||
147 | memcpy(&DevNode->CardLocation, | ||
148 | &SlotMapPtr->CardLocation, 3); | ||
149 | DevNode->CardLocation[3] = 0; | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | /* Point to the next Slot */ | ||
154 | SlotMapPtr = (SlotMap *)((char *)SlotMapPtr + SLOT_ENTRY_SIZE); | ||
155 | SlotMapLen -= SLOT_ENTRY_SIZE; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Parse the Mfg Area | ||
161 | */ | ||
162 | static void iSeries_Parse_MfgArea(u8 *AreaData, int AreaLen, | ||
163 | struct iSeries_Device_Node *DevNode) | ||
164 | { | ||
165 | MfgArea *MfgAreaPtr = (MfgArea *)AreaData; | ||
166 | int MfgAreaLen = AreaLen; | ||
167 | u16 SlotMapFmt = 0; | ||
168 | |||
169 | /* Parse Mfg Data */ | ||
170 | while (MfgAreaLen > 0) { | ||
171 | int MfgTagLen = MfgAreaPtr->TagLength; | ||
172 | /* Frame ID (FI 4649020310 ) */ | ||
173 | if (MfgAreaPtr->Tag == VpdFruFrameId) /* FI */ | ||
174 | DevNode->FrameId = MfgAreaPtr->AreaData1; | ||
175 | /* Slot Map Format (MF 4D46020004 ) */ | ||
176 | else if (MfgAreaPtr->Tag == VpdSlotMapFormat) /* MF */ | ||
177 | SlotMapFmt = (MfgAreaPtr->AreaData1 * 256) | ||
178 | + MfgAreaPtr->AreaData2; | ||
179 | /* Slot Map (SM 534D90 */ | ||
180 | else if (MfgAreaPtr->Tag == VpdSlotMap) { /* SM */ | ||
181 | SlotMap *SlotMapPtr; | ||
182 | |||
183 | if (SlotMapFmt == 0x1004) | ||
184 | SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr | ||
185 | + MFG_ENTRY_SIZE + 1); | ||
186 | else | ||
187 | SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr | ||
188 | + MFG_ENTRY_SIZE); | ||
189 | iSeries_Parse_SlotArea(SlotMapPtr, MfgTagLen, DevNode); | ||
190 | } | ||
191 | /* | ||
192 | * Point to the next Mfg Area | ||
193 | * Use defined size, sizeof give wrong answer | ||
194 | */ | ||
195 | MfgAreaPtr = (MfgArea *)((char *)MfgAreaPtr + MfgTagLen | ||
196 | + MFG_ENTRY_SIZE); | ||
197 | MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Look for "BUS".. Data is not Null terminated. | ||
203 | * PHBID of 0xFF indicates PHB was not found in VPD Data. | ||
204 | */ | ||
205 | static int iSeries_Parse_PhbId(u8 *AreaPtr, int AreaLength) | ||
206 | { | ||
207 | u8 *PhbPtr = AreaPtr; | ||
208 | int DataLen = AreaLength; | ||
209 | char PhbId = 0xFF; | ||
210 | |||
211 | while (DataLen > 0) { | ||
212 | if ((*PhbPtr == 'B') && (*(PhbPtr + 1) == 'U') | ||
213 | && (*(PhbPtr + 2) == 'S')) { | ||
214 | PhbPtr += 3; | ||
215 | while (*PhbPtr == ' ') | ||
216 | ++PhbPtr; | ||
217 | PhbId = (*PhbPtr & 0x0F); | ||
218 | break; | ||
219 | } | ||
220 | ++PhbPtr; | ||
221 | --DataLen; | ||
222 | } | ||
223 | return PhbId; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * Parse out the VPD Areas | ||
228 | */ | ||
229 | static void iSeries_Parse_Vpd(u8 *VpdData, int VpdDataLen, | ||
230 | struct iSeries_Device_Node *DevNode) | ||
231 | { | ||
232 | u8 *TagPtr = VpdData; | ||
233 | int DataLen = VpdDataLen - 3; | ||
234 | |||
235 | while ((*TagPtr != VpdEndOfAreaTag) && (DataLen > 0)) { | ||
236 | int AreaLen = *(TagPtr + 1) + (*(TagPtr + 2) * 256); | ||
237 | u8 *AreaData = TagPtr + 3; | ||
238 | |||
239 | if (*TagPtr == VpdIdStringTag) | ||
240 | DevNode->PhbId = iSeries_Parse_PhbId(AreaData, AreaLen); | ||
241 | else if (*TagPtr == VpdVendorAreaTag) | ||
242 | iSeries_Parse_MfgArea(AreaData, AreaLen, DevNode); | ||
243 | /* Point to next Area. */ | ||
244 | TagPtr = AreaData + AreaLen; | ||
245 | DataLen -= AreaLen; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | void iSeries_Get_Location_Code(struct iSeries_Device_Node *DevNode) | ||
250 | { | ||
251 | int BusVpdLen = 0; | ||
252 | u8 *BusVpdPtr = (u8 *)kmalloc(BUS_VPDSIZE, GFP_KERNEL); | ||
253 | |||
254 | if (BusVpdPtr == NULL) { | ||
255 | printk("PCI: Bus VPD Buffer allocation failure.\n"); | ||
256 | return; | ||
257 | } | ||
258 | BusVpdLen = HvCallPci_getBusVpd(ISERIES_BUS(DevNode), | ||
259 | ISERIES_HV_ADDR(BusVpdPtr), | ||
260 | BUS_VPDSIZE); | ||
261 | if (BusVpdLen == 0) { | ||
262 | kfree(BusVpdPtr); | ||
263 | printk("PCI: Bus VPD Buffer zero length.\n"); | ||
264 | return; | ||
265 | } | ||
266 | /* printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); */ | ||
267 | /* Make sure this is what I think it is */ | ||
268 | if (*BusVpdPtr != VpdIdStringTag) { /* 0x82 */ | ||
269 | printk("PCI: Bus VPD Buffer missing starting tag.\n"); | ||
270 | kfree(BusVpdPtr); | ||
271 | return; | ||
272 | } | ||
273 | iSeries_Parse_Vpd(BusVpdPtr,BusVpdLen, DevNode); | ||
274 | sprintf(DevNode->Location, "Frame%3d, Card %-4s", DevNode->FrameId, | ||
275 | DevNode->CardLocation); | ||
276 | kfree(BusVpdPtr); | ||
277 | } | ||