diff options
Diffstat (limited to 'arch/powerpc/platforms/iseries/vpdinfo.c')
-rw-r--r-- | arch/powerpc/platforms/iseries/vpdinfo.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/iseries/vpdinfo.c b/arch/powerpc/platforms/iseries/vpdinfo.c new file mode 100644 index 000000000000..9c318849dee7 --- /dev/null +++ b/arch/powerpc/platforms/iseries/vpdinfo.c | |||
@@ -0,0 +1,271 @@ | |||
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/HvTypes.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 | } | ||