diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2007-12-07 16:10:22 +1100 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-12-11 13:46:11 +1100 |
commit | 1dee20262f4b16db73b2f24036bd423e8a7d693d (patch) | |
tree | 5556bf57ef7d71bf22343c48c3f20715df6cb56d /arch/powerpc/platforms/iseries/pci.c | |
parent | 1be9ad650986a87c25686abda97bdea330359708 (diff) |
[POWERPC] iSeries: Merge vpdinfo.c into pci.c
There was only one global function in vpdinfo.c and it was only called
from pci.c, so merge them and make the function static.
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/iseries/pci.c')
-rw-r--r-- | arch/powerpc/platforms/iseries/pci.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c index 5466975bc90..68f248b4c69 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2001 Allan Trautman, IBM Corporation + * Copyright (C) 2005,2007 Stephen Rothwell, IBM Corp * * iSeries specific routines for PCI. * @@ -26,6 +27,7 @@ #include <linux/module.h> #include <linux/pci.h> +#include <asm/types.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/prom.h> @@ -35,6 +37,7 @@ #include <asm/abs_addr.h> #include <asm/firmware.h> +#include <asm/iseries/hv_types.h> #include <asm/iseries/hv_call_xm.h> #include <asm/iseries/mf.h> #include <asm/iseries/iommu.h> @@ -80,6 +83,221 @@ static inline u64 iseries_ds_addr(struct device_node *node) } /* + * Size of Bus VPD data + */ +#define BUS_VPDSIZE 1024 + +/* + * Bus Vpd Tags + */ +#define VPD_END_OF_AREA 0x79 +#define VPD_ID_STRING 0x82 +#define VPD_VENDOR_AREA 0x84 + +/* + * Mfg Area Tags + */ +#define VPD_FRU_FRAME_ID 0x4649 /* "FI" */ +#define VPD_SLOT_MAP_FORMAT 0x4D46 /* "MF" */ +#define VPD_SLOT_MAP 0x534D /* "SM" */ + +/* + * Structures of the areas + */ +struct mfg_vpd_area { + u16 tag; + u8 length; + u8 data1; + u8 data2; +}; +#define MFG_ENTRY_SIZE 3 + +struct slot_map { + u8 agent; + u8 secondary_agent; + u8 phb; + char card_location[3]; + char parms[8]; + char reserved[2]; +}; +#define SLOT_ENTRY_SIZE 16 + +/* + * Parse the Slot Area + */ +static void __init iseries_parse_slot_area(struct slot_map *map, int len, + HvAgentId agent, u8 *phb, char card[4]) +{ + /* + * Parse Slot label until we find the one requested + */ + while (len > 0) { + if (map->agent == agent) { + /* + * If Phb wasn't found, grab the entry first one found. + */ + if (*phb == 0xff) + *phb = map->phb; + /* Found it, extract the data. */ + if (map->phb == *phb) { + memcpy(card, &map->card_location, 3); + card[3] = 0; + break; + } + } + /* Point to the next Slot */ + map = (struct slot_map *)((char *)map + SLOT_ENTRY_SIZE); + len -= SLOT_ENTRY_SIZE; + } +} + +/* + * Parse the Mfg Area + */ +static void __init iseries_parse_mfg_area(struct mfg_vpd_area *area, int len, + HvAgentId agent, u8 *phb, u8 *frame, char card[4]) +{ + u16 slot_map_fmt = 0; + + /* Parse Mfg Data */ + while (len > 0) { + int mfg_tag_len = area->length; + /* Frame ID (FI 4649020310 ) */ + if (area->tag == VPD_FRU_FRAME_ID) + *frame = area->data1; + /* Slot Map Format (MF 4D46020004 ) */ + else if (area->tag == VPD_SLOT_MAP_FORMAT) + slot_map_fmt = (area->data1 * 256) + + area->data2; + /* Slot Map (SM 534D90 */ + else if (area->tag == VPD_SLOT_MAP) { + struct slot_map *slot_map; + + if (slot_map_fmt == 0x1004) + slot_map = (struct slot_map *)((char *)area + + MFG_ENTRY_SIZE + 1); + else + slot_map = (struct slot_map *)((char *)area + + MFG_ENTRY_SIZE); + iseries_parse_slot_area(slot_map, mfg_tag_len, + agent, phb, card); + } + /* + * Point to the next Mfg Area + * Use defined size, sizeof give wrong answer + */ + area = (struct mfg_vpd_area *)((char *)area + mfg_tag_len + + MFG_ENTRY_SIZE); + len -= (mfg_tag_len + MFG_ENTRY_SIZE); + } +} + +/* + * Look for "BUS".. Data is not Null terminated. + * PHBID of 0xFF indicates PHB was not found in VPD Data. + */ +static u8 __init iseries_parse_phbid(u8 *area, int len) +{ + while (len > 0) { + if ((*area == 'B') && (*(area + 1) == 'U') + && (*(area + 2) == 'S')) { + area += 3; + while (*area == ' ') + area++; + return *area & 0x0F; + } + area++; + len--; + } + return 0xff; +} + +/* + * Parse out the VPD Areas + */ +static void __init iseries_parse_vpd(u8 *data, int data_len, + HvAgentId agent, u8 *frame, char card[4]) +{ + u8 phb = 0xff; + + while (data_len > 0) { + int len; + u8 tag = *data; + + if (tag == VPD_END_OF_AREA) + break; + len = *(data + 1) + (*(data + 2) * 256); + data += 3; + data_len -= 3; + if (tag == VPD_ID_STRING) + phb = iseries_parse_phbid(data, len); + else if (tag == VPD_VENDOR_AREA) + iseries_parse_mfg_area((struct mfg_vpd_area *)data, len, + agent, &phb, frame, card); + /* Point to next Area. */ + data += len; + data_len -= len; + } +} + +static int __init iseries_get_location_code(u16 bus, HvAgentId agent, + u8 *frame, char card[4]) +{ + int status = 0; + int bus_vpd_len = 0; + u8 *bus_vpd = kmalloc(BUS_VPDSIZE, GFP_KERNEL); + + if (bus_vpd == NULL) { + printk("PCI: Bus VPD Buffer allocation failure.\n"); + return 0; + } + bus_vpd_len = HvCallPci_getBusVpd(bus, iseries_hv_addr(bus_vpd), + BUS_VPDSIZE); + if (bus_vpd_len == 0) { + printk("PCI: Bus VPD Buffer zero length.\n"); + goto out_free; + } + /* printk("PCI: bus_vpd: %p, %d\n",bus_vpd, bus_vpd_len); */ + /* Make sure this is what I think it is */ + if (*bus_vpd != VPD_ID_STRING) { + printk("PCI: Bus VPD Buffer missing starting tag.\n"); + goto out_free; + } + iseries_parse_vpd(bus_vpd, bus_vpd_len, agent, frame, card); + status = 1; +out_free: + kfree(bus_vpd); + return status; +} + +/* + * Prints the device information. + * - Pass in pci_dev* pointer to the device. + * - Pass in the device count + * + * Format: + * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet + * controller + */ +static void __init iseries_device_information(struct pci_dev *pdev, int count, + u16 bus, HvSubBusNumber subbus) +{ + u8 frame = 0; + char card[4]; + HvAgentId agent; + + agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus), + ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); + + if (iseries_get_location_code(bus, agent, &frame, card)) { + printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, " + "Card %4s 0x%04X\n", count, bus, + PCI_SLOT(pdev->devfn), pdev->vendor, frame, + card, (int)(pdev->class >> 8)); + } +} + +/* * iomm_table_allocate_entry * * Adds pci_dev entry in address translation table |