Merge branch 'master' of /home/tglx/work/mtd/git/linux-2.6.git/
[linux-2.6] / arch / powerpc / platforms / iseries / vpdinfo.c
1 /*
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  */
27 #include <linux/init.h>
28 #include <linux/module.h>
29 #include <linux/pci.h>
30
31 #include <asm/types.h>
32 #include <asm/resource.h>
33 #include <asm/abs_addr.h>
34 #include <asm/pci-bridge.h>
35 #include <asm/iseries/hv_types.h>
36
37 #include "pci.h"
38 #include "call_pci.h"
39
40 /*
41  * Size of Bus VPD data
42  */
43 #define BUS_VPDSIZE      1024
44
45 /*
46  * Bus Vpd Tags
47  */
48 #define  VpdEndOfAreaTag   0x79
49 #define  VpdIdStringTag    0x82
50 #define  VpdVendorAreaTag  0x84
51
52 /*
53  * Mfg Area Tags
54  */
55 #define  VpdFruFrameId    0x4649     // "FI"
56 #define  VpdSlotMapFormat 0x4D46     // "MF"
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];
78 };
79 typedef struct SlotMapStruct SlotMap;
80 #define SLOT_ENTRY_SIZE   16
81
82 /*
83  * Parse the Slot Area
84  */
85 static void __init iSeries_Parse_SlotArea(SlotMap *MapPtr, int MapLen,
86                 HvAgentId agent, u8 *PhbId, char card[4])
87 {
88         int SlotMapLen = MapLen;
89         SlotMap *SlotMapPtr = MapPtr;
90
91         /*
92          * Parse Slot label until we find the one requested
93          */
94         while (SlotMapLen > 0) {
95                 if (SlotMapPtr->AgentId == agent) {
96                         /*
97                          * If Phb wasn't found, grab the entry first one found.
98                          */
99                         if (*PhbId == 0xff)
100                                 *PhbId = SlotMapPtr->PhbId;
101                         /* Found it, extract the data. */
102                         if (SlotMapPtr->PhbId == *PhbId) {
103                                 memcpy(card, &SlotMapPtr->CardLocation, 3);
104                                 card[3]  = 0;
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  */
117 static void __init iSeries_Parse_MfgArea(u8 *AreaData, int AreaLen,
118                 HvAgentId agent, u8 *PhbId,
119                 u8 *frame, char card[4])
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  */
130                         *frame = MfgAreaPtr->AreaData1;
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);
142                         else
143                                 SlotMapPtr = (SlotMap *)((char *)MfgAreaPtr
144                                                 + MFG_ENTRY_SIZE);
145                         iSeries_Parse_SlotArea(SlotMapPtr, MfgTagLen,
146                                         agent, PhbId, card);
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);
154                 MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE);
155         }
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  */
162 static int __init iSeries_Parse_PhbId(u8 *AreaPtr, int AreaLength)
163 {
164         u8 *PhbPtr = AreaPtr;
165         int DataLen = AreaLength;
166         char PhbId = 0xFF;
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;
176                 }
177                 ++PhbPtr;
178                 --DataLen;
179         }
180         return PhbId;
181 }
182
183 /*
184  * Parse out the VPD Areas
185  */
186 static void __init iSeries_Parse_Vpd(u8 *VpdData, int VpdDataLen,
187                 HvAgentId agent, u8 *frame, char card[4])
188 {
189         u8 *TagPtr = VpdData;
190         int DataLen = VpdDataLen - 3;
191         u8 PhbId;
192
193         while ((*TagPtr != VpdEndOfAreaTag) && (DataLen > 0)) {
194                 int AreaLen = *(TagPtr + 1) + (*(TagPtr + 2) * 256);
195                 u8 *AreaData  = TagPtr + 3;
196
197                 if (*TagPtr == VpdIdStringTag)
198                         PhbId = iSeries_Parse_PhbId(AreaData, AreaLen);
199                 else if (*TagPtr == VpdVendorAreaTag)
200                         iSeries_Parse_MfgArea(AreaData, AreaLen,
201                                         agent, &PhbId, frame, card);
202                 /* Point to next Area. */
203                 TagPtr  = AreaData + AreaLen;
204                 DataLen -= AreaLen;
205         }
206 }
207
208 static void __init iSeries_Get_Location_Code(u16 bus, HvAgentId agent,
209                 u8 *frame, char card[4])
210 {
211         int BusVpdLen = 0;
212         u8 *BusVpdPtr = kmalloc(BUS_VPDSIZE, GFP_KERNEL);
213
214         if (BusVpdPtr == NULL) {
215                 printk("PCI: Bus VPD Buffer allocation failure.\n");
216                 return;
217         }
218         BusVpdLen = HvCallPci_getBusVpd(bus, iseries_hv_addr(BusVpdPtr),
219                                         BUS_VPDSIZE);
220         if (BusVpdLen == 0) {
221                 printk("PCI: Bus VPD Buffer zero length.\n");
222                 goto out_free;
223         }
224         /* printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); */
225         /* Make sure this is what I think it is */
226         if (*BusVpdPtr != VpdIdStringTag) {     /* 0x82 */
227                 printk("PCI: Bus VPD Buffer missing starting tag.\n");
228                 goto out_free;
229         }
230         iSeries_Parse_Vpd(BusVpdPtr, BusVpdLen, agent, frame, card);
231 out_free:
232         kfree(BusVpdPtr);
233 }
234
235 /*
236  * Prints the device information.
237  * - Pass in pci_dev* pointer to the device.
238  * - Pass in the device count
239  *
240  * Format:
241  * PCI: Bus  0, Device 26, Vendor 0x12AE  Frame  1, Card  C10  Ethernet
242  * controller
243  */
244 void __init iSeries_Device_Information(struct pci_dev *PciDev, int count)
245 {
246         struct device_node *DevNode = PciDev->sysdata;
247         struct pci_dn *pdn;
248         u16 bus;
249         u8 frame;
250         char card[4];
251         HvSubBusNumber subbus;
252         HvAgentId agent;
253
254         if (DevNode == NULL) {
255                 printk("%d. PCI: iSeries_Device_Information DevNode is NULL\n",
256                                 count);
257                 return;
258         }
259
260         pdn = PCI_DN(DevNode);
261         bus = pdn->busno;
262         subbus = pdn->bussubno;
263         agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus),
264                         ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus));
265         iSeries_Get_Location_Code(bus, agent, &frame, card);
266
267         printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, Card %4s  ",
268                         count, bus, PCI_SLOT(PciDev->devfn), PciDev->vendor,
269                         frame, card);
270         printk("0x%04X\n", (int)(PciDev->class >> 8));
271 }