Commit | Line | Data |
---|---|---|
57ca86d4 | 1 | /* |
57ca86d4 SR |
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 | */ | |
1da177e4 LT |
27 | #include <linux/init.h> |
28 | #include <linux/module.h> | |
29 | #include <linux/pci.h> | |
426c1a11 | 30 | |
1da177e4 LT |
31 | #include <asm/types.h> |
32 | #include <asm/resource.h> | |
426c1a11 | 33 | #include <asm/abs_addr.h> |
1ec65d76 | 34 | #include <asm/iseries/hv_types.h> |
426c1a11 SR |
35 | |
36 | #include "pci.h" | |
c6d2ea92 | 37 | #include "call_pci.h" |
1da177e4 LT |
38 | |
39 | /* | |
40 | * Size of Bus VPD data | |
41 | */ | |
42 | #define BUS_VPDSIZE 1024 | |
57ca86d4 | 43 | |
1da177e4 LT |
44 | /* |
45 | * Bus Vpd Tags | |
46 | */ | |
cd9afb34 SR |
47 | #define VPD_END_OF_AREA 0x79 |
48 | #define VPD_ID_STRING 0x82 | |
49 | #define VPD_VENDOR_AREA 0x84 | |
57ca86d4 | 50 | |
1da177e4 LT |
51 | /* |
52 | * Mfg Area Tags | |
53 | */ | |
cd9afb34 SR |
54 | #define VPD_FRU_FRAME_ID 0x4649 /* "FI" */ |
55 | #define VPD_SLOT_MAP_FORMAT 0x4D46 /* "MF" */ | |
56 | #define VPD_SLOT_MAP 0x534D /* "SM" */ | |
1da177e4 LT |
57 | |
58 | /* | |
59 | * Structures of the areas | |
60 | */ | |
cd9afb34 SR |
61 | struct mfg_vpd_area { |
62 | u16 tag; | |
63 | u8 length; | |
64 | u8 data1; | |
65 | u8 data2; | |
1da177e4 | 66 | }; |
1da177e4 LT |
67 | #define MFG_ENTRY_SIZE 3 |
68 | ||
cd9afb34 SR |
69 | struct slot_map { |
70 | u8 agent; | |
71 | u8 secondary_agent; | |
72 | u8 phb; | |
73 | char card_location[3]; | |
74 | char parms[8]; | |
75 | char reserved[2]; | |
57ca86d4 | 76 | }; |
1da177e4 LT |
77 | #define SLOT_ENTRY_SIZE 16 |
78 | ||
1da177e4 LT |
79 | /* |
80 | * Parse the Slot Area | |
81 | */ | |
cd9afb34 SR |
82 | static void __init iseries_parse_slot_area(struct slot_map *map, int len, |
83 | HvAgentId agent, u8 *phb, char card[4]) | |
1da177e4 | 84 | { |
cd9afb34 SR |
85 | int slot_map_len = len; |
86 | struct slot_map *slot_map = map; | |
1da177e4 LT |
87 | |
88 | /* | |
57ca86d4 | 89 | * Parse Slot label until we find the one requested |
1da177e4 | 90 | */ |
cd9afb34 SR |
91 | while (slot_map_len > 0) { |
92 | if (slot_map->agent == agent) { | |
1da177e4 LT |
93 | /* |
94 | * If Phb wasn't found, grab the entry first one found. | |
95 | */ | |
cd9afb34 SR |
96 | if (*phb == 0xff) |
97 | *phb = slot_map->phb; | |
1da177e4 | 98 | /* Found it, extract the data. */ |
cd9afb34 SR |
99 | if (slot_map->phb == *phb) { |
100 | memcpy(card, &slot_map->card_location, 3); | |
061c063e | 101 | card[3] = 0; |
1da177e4 LT |
102 | break; |
103 | } | |
104 | } | |
105 | /* Point to the next Slot */ | |
cd9afb34 SR |
106 | slot_map = (struct slot_map *)((char *)slot_map + SLOT_ENTRY_SIZE); |
107 | slot_map_len -= SLOT_ENTRY_SIZE; | |
1da177e4 LT |
108 | } |
109 | } | |
110 | ||
111 | /* | |
112 | * Parse the Mfg Area | |
113 | */ | |
cd9afb34 SR |
114 | static void __init iseries_parse_mfg_area(u8 *area, int len, |
115 | HvAgentId agent, u8 *phb, | |
061c063e | 116 | u8 *frame, char card[4]) |
1da177e4 | 117 | { |
cd9afb34 SR |
118 | struct mfg_vpd_area *mfg_area = (struct mfg_vpd_area *)area; |
119 | int mfg_area_len = len; | |
120 | u16 slot_map_fmt = 0; | |
1da177e4 LT |
121 | |
122 | /* Parse Mfg Data */ | |
cd9afb34 SR |
123 | while (mfg_area_len > 0) { |
124 | int mfg_tag_len = mfg_area->length; | |
1da177e4 | 125 | /* Frame ID (FI 4649020310 ) */ |
cd9afb34 SR |
126 | if (mfg_area->tag == VPD_FRU_FRAME_ID) |
127 | *frame = mfg_area->data1; | |
1da177e4 | 128 | /* Slot Map Format (MF 4D46020004 ) */ |
cd9afb34 SR |
129 | else if (mfg_area->tag == VPD_SLOT_MAP_FORMAT) |
130 | slot_map_fmt = (mfg_area->data1 * 256) | |
131 | + mfg_area->data2; | |
1da177e4 | 132 | /* Slot Map (SM 534D90 */ |
cd9afb34 SR |
133 | else if (mfg_area->tag == VPD_SLOT_MAP) { |
134 | struct slot_map *slot_map; | |
1da177e4 | 135 | |
cd9afb34 SR |
136 | if (slot_map_fmt == 0x1004) |
137 | slot_map = (struct slot_map *)((char *)mfg_area | |
1da177e4 | 138 | + MFG_ENTRY_SIZE + 1); |
57ca86d4 | 139 | else |
cd9afb34 | 140 | slot_map = (struct slot_map *)((char *)mfg_area |
1da177e4 | 141 | + MFG_ENTRY_SIZE); |
cd9afb34 SR |
142 | iseries_parse_slot_area(slot_map, mfg_tag_len, |
143 | agent, phb, card); | |
1da177e4 LT |
144 | } |
145 | /* | |
146 | * Point to the next Mfg Area | |
147 | * Use defined size, sizeof give wrong answer | |
148 | */ | |
cd9afb34 | 149 | mfg_area = (struct mfg_vpd_area *)((char *)mfg_area + mfg_tag_len |
1da177e4 | 150 | + MFG_ENTRY_SIZE); |
cd9afb34 | 151 | mfg_area_len -= (mfg_tag_len + MFG_ENTRY_SIZE); |
57ca86d4 | 152 | } |
1da177e4 LT |
153 | } |
154 | ||
155 | /* | |
156 | * Look for "BUS".. Data is not Null terminated. | |
157 | * PHBID of 0xFF indicates PHB was not found in VPD Data. | |
158 | */ | |
cd9afb34 | 159 | static int __init iseries_parse_phbid(u8 *area, int len) |
1da177e4 | 160 | { |
cd9afb34 SR |
161 | u8 *phb_ptr = area; |
162 | int data_len = len; | |
163 | char phb = 0xFF; | |
1da177e4 | 164 | |
cd9afb34 SR |
165 | while (data_len > 0) { |
166 | if ((*phb_ptr == 'B') && (*(phb_ptr + 1) == 'U') | |
167 | && (*(phb_ptr + 2) == 'S')) { | |
168 | phb_ptr += 3; | |
169 | while (*phb_ptr == ' ') | |
170 | ++phb_ptr; | |
171 | phb = (*phb_ptr & 0x0F); | |
1da177e4 | 172 | break; |
57ca86d4 | 173 | } |
cd9afb34 SR |
174 | ++phb_ptr; |
175 | --data_len; | |
1da177e4 | 176 | } |
cd9afb34 | 177 | return phb; |
1da177e4 LT |
178 | } |
179 | ||
180 | /* | |
181 | * Parse out the VPD Areas | |
182 | */ | |
cd9afb34 | 183 | static void __init iseries_parse_vpd(u8 *data, int vpd_data_len, |
061c063e | 184 | HvAgentId agent, u8 *frame, char card[4]) |
1da177e4 | 185 | { |
cd9afb34 SR |
186 | u8 *tag_ptr = data; |
187 | int data_len = vpd_data_len - 3; | |
188 | u8 phb = 0xff; | |
1da177e4 | 189 | |
cd9afb34 SR |
190 | while ((*tag_ptr != VPD_END_OF_AREA) && (data_len > 0)) { |
191 | int len = *(tag_ptr + 1) + (*(tag_ptr + 2) * 256); | |
192 | u8 *area = tag_ptr + 3; | |
1da177e4 | 193 | |
cd9afb34 SR |
194 | if (*tag_ptr == VPD_ID_STRING) |
195 | phb = iseries_parse_phbid(area, len); | |
196 | else if (*tag_ptr == VPD_VENDOR_AREA) | |
197 | iseries_parse_mfg_area(area, len, | |
198 | agent, &phb, frame, card); | |
1da177e4 | 199 | /* Point to next Area. */ |
cd9afb34 SR |
200 | tag_ptr = area + len; |
201 | data_len -= len; | |
1da177e4 | 202 | } |
57ca86d4 | 203 | } |
1da177e4 | 204 | |
cd9afb34 | 205 | static int __init iseries_get_location_code(u16 bus, HvAgentId agent, |
061c063e | 206 | u8 *frame, char card[4]) |
1da177e4 | 207 | { |
f357b4cc | 208 | int status = 0; |
cd9afb34 SR |
209 | int bus_vpd_len = 0; |
210 | u8 *bus_vpd = kmalloc(BUS_VPDSIZE, GFP_KERNEL); | |
1da177e4 | 211 | |
cd9afb34 | 212 | if (bus_vpd == NULL) { |
1da177e4 | 213 | printk("PCI: Bus VPD Buffer allocation failure.\n"); |
f357b4cc | 214 | return 0; |
1da177e4 | 215 | } |
cd9afb34 | 216 | bus_vpd_len = HvCallPci_getBusVpd(bus, iseries_hv_addr(bus_vpd), |
1da177e4 | 217 | BUS_VPDSIZE); |
cd9afb34 | 218 | if (bus_vpd_len == 0) { |
1da177e4 | 219 | printk("PCI: Bus VPD Buffer zero length.\n"); |
061c063e | 220 | goto out_free; |
1da177e4 | 221 | } |
cd9afb34 | 222 | /* printk("PCI: bus_vpd: %p, %d\n",bus_vpd, bus_vpd_len); */ |
1da177e4 | 223 | /* Make sure this is what I think it is */ |
cd9afb34 | 224 | if (*bus_vpd != VPD_ID_STRING) { |
1da177e4 | 225 | printk("PCI: Bus VPD Buffer missing starting tag.\n"); |
061c063e | 226 | goto out_free; |
1da177e4 | 227 | } |
cd9afb34 | 228 | iseries_parse_vpd(bus_vpd, bus_vpd_len, agent, frame, card); |
f357b4cc | 229 | status = 1; |
061c063e | 230 | out_free: |
cd9afb34 | 231 | kfree(bus_vpd); |
f357b4cc | 232 | return status; |
1da177e4 | 233 | } |
061c063e SR |
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 | */ | |
cd9afb34 | 244 | void __init iseries_device_information(struct pci_dev *pdev, int count, |
72ece3b8 | 245 | u16 bus, HvSubBusNumber subbus) |
061c063e | 246 | { |
f357b4cc | 247 | u8 frame = 0; |
061c063e | 248 | char card[4]; |
061c063e SR |
249 | HvAgentId agent; |
250 | ||
061c063e SR |
251 | agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus), |
252 | ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); | |
061c063e | 253 | |
cd9afb34 | 254 | if (iseries_get_location_code(bus, agent, &frame, card)) { |
f357b4cc ME |
255 | printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, " |
256 | "Card %4s 0x%04X\n", count, bus, | |
cd9afb34 SR |
257 | PCI_SLOT(pdev->devfn), pdev->vendor, frame, |
258 | card, (int)(pdev->class >> 8)); | |
f357b4cc | 259 | } |
061c063e | 260 | } |