iSeries_VpdInfo.c 7.2 KB

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