const struct slca_entry *slca_get_entry(uint16_t slca_index) { struct HDIF_common_hdr *slca_hdr; int count; slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG); if (!slca_hdr) { prerror("SLCA Invalid\n"); return NULL; } count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY); if (count < 0) { prerror("SLCA: Can't find SLCA array size!\n"); return NULL; } if (slca_index < count) { const struct slca_entry *s_entry; unsigned int entry_sz; s_entry = HDIF_get_iarray_item(slca_hdr, SLCA_IDATA_ARRAY, slca_index, &entry_sz); if (s_entry && entry_sz >= sizeof(*s_entry)) return s_entry; } else prlog(PR_NOTICE, "SLCA: Can't find slca_entry for index %d\n", slca_index); return NULL; }
/* * Get System Attention Indicator SLCA entry */ static const struct slca_entry *slca_get_sai_entry(void) { int count; unsigned int i; struct HDIF_common_hdr *slca_hdr; slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG); if (!slca_hdr) { prerror("SLCA Invalid\n"); return NULL; } count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY); if (count < 0) { prerror("SLCA: Can't find SLCA array size!\n"); return NULL; } for (i = 0; i < count; i++) { const struct slca_entry *s_entry; unsigned int entry_sz; s_entry = HDIF_get_iarray_item(slca_hdr, SLCA_IDATA_ARRAY, i, &entry_sz); if (s_entry && VPD_ID(s_entry->fru_id[0], s_entry->fru_id[1]) == SLCA_SAI_INDICATOR_ID) { prlog(PR_TRACE, "SLCA: SAI index: 0x%x\n", s_entry->my_index); prlog(PR_TRACE, "SLCA: SAI location code: %s\n", s_entry->loc_code); return s_entry; } } return NULL; }
int parse_i2c_devs(const struct HDIF_common_hdr *hdr, int idata_index, struct dt_node *xscom) { struct dt_node *i2cm, *bus, *node; const struct hdat_i2c_type *type; const struct hdat_i2c_info *info; const struct i2c_dev *dev; const char *name, *compat; const struct host_i2c_hdr *ahdr; uint32_t dev_addr; uint32_t version; uint32_t size; uint32_t purpose; int i, count; /* * This code makes a few assumptions about XSCOM addrs, etc * and will need updating for new processors */ assert(proc_gen == proc_gen_p9); /* * Emit an error if we get a newer version. This is an interim measure * until the new version format is finalised. */ ahdr = HDIF_get_idata(hdr, idata_index, &size); if (!ahdr || !size) return -1; /* * Some hostboots don't correctly fill the version field. On these * the offset from the start of the header to the start of the array * is 16 bytes. */ if (be32_to_cpu(ahdr->hdr.offset) == 16) { version = 1; prerror("I2C: HDAT device array has no version! Assuming v1\n"); } else { version = be32_to_cpu(ahdr->version); } if (version == 2) { prlog(PR_INFO, "I2C: v%d found, but not supported. Parsing as v1\n", version); } else if (version > 2) { prerror("I2C: v%d found, but not supported! THIS IS A BUG\n", version); return -1; } count = HDIF_get_iarray_size(hdr, idata_index); for (i = 0; i < count; i++) { dev = HDIF_get_iarray_item(hdr, idata_index, i, &size); /* * XXX: Some broken hostboots populate i2c devs with zeros. * Workaround them for now. */ if (is_zeros(dev, size)) { prerror("I2C: Ignoring broken i2c dev %d\n", i); continue; } /* * On some systems the CFAM I2C master is represented in the * host I2C table as engine 6. There are only 4 (0, 1, 2, 3) * engines accessible to the host via XSCOM so filter out * engines outside this range so we don't create bogus * i2cm@<addr> nodes. */ if (dev->i2cm_engine >= 4 && proc_gen == proc_gen_p9) continue; i2cm = get_i2cm_node(xscom, dev->i2cm_engine); bus = get_bus_node(i2cm, dev->i2cm_port, be16_to_cpu(dev->i2c_bus_freq)); /* * Looks like hostboot gives the address as an 8 bit, left * justified quantity (i.e it includes the R/W bit). So we need * to strip it off to get an address linux can use. */ dev_addr = dev->dev_addr >> 1; purpose = be32_to_cpu(dev->purpose); type = map_type(dev->type); info = get_info(purpose); /* HACK: Hostboot doesn't export the correct type information * for the DIMM SPD EEPROMs. This is a problem because SPD * EEPROMs have a different wire protocol to the atmel,24XXXX * series. The main difference being that SPD EEPROMs have an * 8bit offset rather than a 16bit offset. This means that the * driver will send 2 bytes when doing a random read, * potentially overwriting part of the SPD information. * * Just to make things interested the FSP also gets the device * type wrong. To work around both just set the device-type to * "spd" for anything in the 0x50 to 0x57 range since that's the * SPD eeprom range. * * XXX: Future chips might not use engine 3 for the DIMM buses. */ if (dev->i2cm_engine == 3 && dev_addr >= 0x50 && dev_addr < 0x58) { compat = "spd"; name = "eeprom"; } else if (type) { compat = type->compat; name = type->name; } else { name = "unknown"; compat = NULL; } /* * An i2c device is unknown if either the i2c device list is * outdated or the device is marked as unknown (0xFF) in the * hdat. Log both cases to see what/where/why. */ if (!type || dev->type == 0xFF) { prlog(PR_NOTICE, "HDAT I2C: found e%dp%d - %s@%x dp:%02x (%#x:%s)\n", dev->i2cm_engine, dev->i2cm_port, name, dev_addr, dev->dev_port, purpose, info->label); continue; } prlog(PR_DEBUG, "HDAT I2C: found e%dp%d - %s@%x dp:%02x (%#x:%s)\n", dev->i2cm_engine, dev->i2cm_port, name, dev_addr, dev->dev_port, purpose, info->label); /* * Multi-port device require special handling since we need to * generate the device-specific DT bindings. For now we're just * going to ignore them since these devices are owned by FW * any way. */ if (dev->dev_port != 0xff) continue; node = dt_new_addr(bus, name, dev_addr); if (!node) continue; dt_add_property_cells(node, "reg", dev_addr); dt_add_property_cells(node, "link-id", be32_to_cpu(dev->i2c_link)); if (compat) dt_add_property_string(node, "compatible", compat); if (info->label) dt_add_property_string(node, "label", info->label); if (!info->whitelist) dt_add_property_string(node, "status", "reserved"); /* * Set a default timeout of 2s on the ports with a TPM. This is * to work around a bug with certain TPM firmwares that can * clock stretch for long periods of time and will lock up * until they are power cycled if a STOP condition is sent * during this period. */ if (dev->type == 0x3) dt_add_property_cells(bus, "timeout-ms", 2000); /* XXX: SLCA index? */ } return 0; }