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