ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc64 / kernel / iSeries_VpdInfo.c
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_VpdInfo.h>
40 #include <asm/iSeries/iSeries_pci.h>
41 #include "pci.h"
42
43 /*
44  * Size of Bus VPD data
45  */
46 #define BUS_VPDSIZE      1024
47 /*
48  * Bus Vpd Tags
49  */
50 #define  VpdEndOfDataTag   0x78
51 #define  VpdEndOfAreaTag   0x79
52 #define  VpdIdStringTag    0x82
53 #define  VpdVendorAreaTag  0x84
54 /*
55  * Mfg Area Tags
56  */
57 #define  VpdFruFlag       0x4647     // "FG"
58 #define  VpdFruFrameId    0x4649     // "FI"
59 #define  VpdSlotMapFormat 0x4D46     // "MF"
60 #define  VpdAsmPartNumber 0x504E     // "PN"
61 #define  VpdFruSerial     0x534E     // "SN"
62 #define  VpdSlotMap       0x534D     // "SM"
63
64 /*
65  * Structures of the areas
66  */
67 struct MfgVpdAreaStruct {
68         u16 Tag;
69         u8  TagLength;
70         u8  AreaData1;
71         u8  AreaData2;
72 };
73 typedef struct MfgVpdAreaStruct MfgArea;
74 #define MFG_ENTRY_SIZE   3
75
76 struct SlotMapStruct {
77         u8   AgentId;
78         u8   SecondaryAgentId;
79         u8   PhbId;
80         char CardLocation[3];
81         char Parms[8];
82         char Reserved[2];
83 }; 
84 typedef struct SlotMapStruct SlotMap;
85 #define SLOT_ENTRY_SIZE   16
86
87 /*
88  * Bus, Card, Board, FrameId, CardLocation.
89  */
90 LocationData* iSeries_GetLocationData(struct pci_dev *PciDev)
91 {
92         struct iSeries_Device_Node *DevNode =
93                 (struct iSeries_Device_Node *)PciDev->sysdata;
94         LocationData *LocationPtr =
95                 (LocationData *)kmalloc(LOCATION_DATA_SIZE, GFP_KERNEL);
96
97         if (LocationPtr == NULL) {
98                 printk("PCI: LocationData area allocation failed!\n");
99                 return NULL;
100         }
101         memset(LocationPtr, 0, LOCATION_DATA_SIZE);
102         LocationPtr->Bus = ISERIES_BUS(DevNode);
103         LocationPtr->Board = DevNode->Board;
104         LocationPtr->FrameId = DevNode->FrameId;
105         LocationPtr->Card = PCI_SLOT(DevNode->DevFn);
106         strcpy(&LocationPtr->CardLocation[0], &DevNode->CardLocation[0]);
107         return LocationPtr;
108 }
109 EXPORT_SYMBOL(iSeries_GetLocationData);
110
111 /*
112  * Formats the device information.
113  * - Pass in pci_dev* pointer to the device.
114  * - Pass in buffer to place the data.  Danger here is the buffer must
115  *   be as big as the client says it is.   Should be at least 128 bytes.
116  * Return will the length of the string data put in the buffer.
117  * Format:
118  * PCI: Bus  0, Device 26, Vendor 0x12AE  Frame  1, Card  C10  Ethernet
119  * controller
120  */
121 int iSeries_Device_Information(struct pci_dev *PciDev, char *buffer,
122                 int BufferSize)
123 {
124         struct iSeries_Device_Node *DevNode =
125                 (struct iSeries_Device_Node *)PciDev->sysdata;
126         int len;
127
128         if (DevNode == NULL)
129                 return sprintf(buffer,
130                                 "PCI: iSeries_Device_Information DevNode is NULL");
131
132         if (BufferSize < 128)
133                 return 0;
134
135         len = sprintf(buffer, "PCI: Bus%3d, Device%3d, Vendor %04X ",
136                         ISERIES_BUS(DevNode), PCI_SLOT(PciDev->devfn),
137                         PciDev->vendor);
138         len += sprintf(buffer + len, "Frame%3d, Card %4s  ",
139                         DevNode->FrameId, DevNode->CardLocation);
140 #ifdef CONFIG_PCI
141         if (pci_class_name(PciDev->class >> 8) == 0)
142                 len += sprintf(buffer + len, "0x%04X  ",
143                                 (int)(PciDev->class >> 8));
144         else
145                 len += sprintf(buffer + len, "%s",
146                                 pci_class_name(PciDev->class >> 8));
147 #endif
148         return len;
149 }
150
151 /*
152  * Build a character string of the device location, Frame  1, Card  C10
153  */
154 int device_Location(struct pci_dev *PciDev, char *BufPtr)
155 {
156         struct iSeries_Device_Node *DevNode =
157                 (struct iSeries_Device_Node *)PciDev->sysdata;
158         return sprintf(BufPtr, "PCI: Bus%3d, AgentId%3d, Vendor %04X, Location %s",
159                        DevNode->DsaAddr.Dsa.busNumber, DevNode->AgentId,
160                        DevNode->Vendor, DevNode->Location);
161 }
162
163 /*
164  * Parse the Slot Area
165  */
166 void iSeries_Parse_SlotArea(SlotMap *MapPtr, int MapLen,
167                 struct iSeries_Device_Node *DevNode)
168 {
169         int SlotMapLen = MapLen;
170         SlotMap *SlotMapPtr = MapPtr;
171
172         /*
173          * Parse Slot label until we find the one requrested
174          */
175         while (SlotMapLen > 0) {
176                 if (SlotMapPtr->AgentId == DevNode->AgentId ) {
177                         /*
178                          * If Phb wasn't found, grab the entry first one found.
179                          */
180                         if (DevNode->PhbId == 0xff)
181                                 DevNode->PhbId = SlotMapPtr->PhbId; 
182                         /* Found it, extract the data. */
183                         if (SlotMapPtr->PhbId == DevNode->PhbId ) {
184                                 memcpy(&DevNode->CardLocation,
185                                                 &SlotMapPtr->CardLocation, 3);
186                                 DevNode->CardLocation[3]  = 0;
187                                 break;
188                         }
189                 }
190                 /* Point to the next Slot */
191                 SlotMapPtr = (SlotMap *)((char *)SlotMapPtr + SLOT_ENTRY_SIZE);
192                 SlotMapLen -= SLOT_ENTRY_SIZE;
193         }
194 }
195
196 /*
197  * Parse the Mfg Area
198  */
199 static void iSeries_Parse_MfgArea(u8 *AreaData, int AreaLen,
200                 struct iSeries_Device_Node *DevNode)
201 {
202         MfgArea *MfgAreaPtr = (MfgArea *)AreaData;
203         int MfgAreaLen = AreaLen;
204         u16 SlotMapFmt = 0;
205
206         /* Parse Mfg Data */
207         while (MfgAreaLen > 0) {
208                 int MfgTagLen = MfgAreaPtr->TagLength;
209                 /* Frame ID         (FI 4649020310 ) */
210                 if (MfgAreaPtr->Tag == VpdFruFrameId)           /* FI  */
211                         DevNode->FrameId = MfgAreaPtr->AreaData1;
212                 /* Slot Map Format  (MF 4D46020004 ) */
213                 else if (MfgAreaPtr->Tag == VpdSlotMapFormat)   /* MF  */
214                         SlotMapFmt = (MfgAreaPtr->AreaData1 * 256)
215                                 + MfgAreaPtr->AreaData2;
216                 /* Slot Map         (SM 534D90 */
217                 else if (MfgAreaPtr->Tag == VpdSlotMap) {       /* SM  */
218                         SlotMap *SlotMapPtr;
219
220                         if (SlotMapFmt == 0x1004)
221                                 SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr
222                                                 + MFG_ENTRY_SIZE + 1);
223                         else
224                                 SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr
225                                                 + MFG_ENTRY_SIZE);
226                         iSeries_Parse_SlotArea(SlotMapPtr, MfgTagLen, DevNode);
227                 }
228                 /*
229                  * Point to the next Mfg Area
230                  * Use defined size, sizeof give wrong answer
231                  */
232                 MfgAreaPtr = (MfgArea *)((char *)MfgAreaPtr + MfgTagLen
233                                 + MFG_ENTRY_SIZE);
234                 MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE); 
235         }       
236 }
237
238 /*
239  * Look for "BUS".. Data is not Null terminated.
240  * PHBID of 0xFF indicates PHB was not found in VPD Data.
241  */
242 static int iSeries_Parse_PhbId(u8 *AreaPtr, int AreaLength)
243 {
244         u8 *PhbPtr = AreaPtr;
245         int DataLen = AreaLength;
246         char PhbId = 0xFF;                   
247
248         while (DataLen > 0) {
249                 if ((*PhbPtr == 'B') && (*(PhbPtr + 1) == 'U')
250                                 && (*(PhbPtr + 2) == 'S')) {
251                         PhbPtr += 3;
252                         while (*PhbPtr == ' ')
253                                 ++PhbPtr;
254                         PhbId = (*PhbPtr & 0x0F);
255                         break;
256                 }
257                 ++PhbPtr;
258                 --DataLen;
259         }
260         return PhbId;
261 }
262
263 /*
264  * Parse out the VPD Areas
265  */
266 static void iSeries_Parse_Vpd(u8 *VpdData, int VpdDataLen,
267                 struct iSeries_Device_Node *DevNode)
268 {
269         u8 *TagPtr = VpdData;
270         int DataLen = VpdDataLen - 3;
271
272         while ((*TagPtr != VpdEndOfAreaTag) && (DataLen > 0)) {
273                 int AreaLen = *(TagPtr + 1) + (*(TagPtr + 2) * 256);    
274                 u8 *AreaData  = TagPtr + 3;
275
276                 if (*TagPtr == VpdIdStringTag)
277                         DevNode->PhbId = iSeries_Parse_PhbId(AreaData, AreaLen);
278                 else if (*TagPtr == VpdVendorAreaTag)
279                         iSeries_Parse_MfgArea(AreaData, AreaLen, DevNode);
280                 /* Point to next Area. */
281                 TagPtr  = AreaData + AreaLen;
282                 DataLen -= AreaLen;
283         }
284 }    
285
286 void iSeries_Get_Location_Code(struct iSeries_Device_Node *DevNode)
287 {
288         int BusVpdLen = 0;
289         u8 *BusVpdPtr = (u8 *)kmalloc(BUS_VPDSIZE, GFP_KERNEL);
290
291         if (BusVpdPtr == NULL) {
292                 printk("PCI: Bus VPD Buffer allocation failure.\n");
293                 return;
294         }
295         BusVpdLen = HvCallPci_getBusVpd(ISERIES_BUS(DevNode),
296                                         ISERIES_HV_ADDR(BusVpdPtr),
297                                         BUS_VPDSIZE);
298         if (BusVpdLen == 0) {
299                 kfree(BusVpdPtr);
300                 printk("PCI: Bus VPD Buffer zero length.\n");
301                 return;
302         }
303         /* printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); */
304         /* Make sure this is what I think it is */
305         if (*BusVpdPtr != VpdIdStringTag) {     /* 0x82 */
306                 printk("PCI: Bus VPD Buffer missing starting tag.\n");
307                 kfree(BusVpdPtr);
308                 return;
309         }
310         iSeries_Parse_Vpd(BusVpdPtr,BusVpdLen, DevNode);
311         sprintf(DevNode->Location, "Frame%3d, Card %-4s", DevNode->FrameId,
312                         DevNode->CardLocation);
313         kfree(BusVpdPtr);
314 }