/* * Helper function for adding /ibm,opal/consoles/serial@<xyz> nodes */ struct dt_node *add_opal_console_node(int index, const char *type, uint32_t write_buffer_size) { struct dt_node *con, *consoles; char buffer[32]; consoles = dt_find_by_name(opal_node, "consoles"); if (!consoles) { consoles = dt_new(opal_node, "consoles"); assert(consoles); dt_add_property_cells(consoles, "#address-cells", 1); dt_add_property_cells(consoles, "#size-cells", 0); } con = dt_new_addr(consoles, "serial", index); assert(con); snprintf(buffer, sizeof(buffer), "ibm,opal-console-%s", type); dt_add_property_string(con, "compatible", buffer); dt_add_property_cells(con, "#write-buffer-size", write_buffer_size); dt_add_property_cells(con, "reg", index); dt_add_property_string(con, "device_type", "serial"); return con; }
static void setup_system_flash(struct flash *flash, struct dt_node *node, const char *name, struct ffs_handle *ffs) { char *path; if (!ffs) return; if (system_flash) { /** * @fwts-label SystemFlashMultiple * @fwts-advice OPAL Found multiple system flash. * Since we've already found a system flash we are * going to use that one but this ordering is not * guaranteed so may change in future. */ prlog(PR_WARNING, "FLASH: Attempted to register multiple system " "flash: %s\n", name); return; } prlog(PR_NOTICE, "FLASH: Found system flash: %s id:%i\n", name, flash->id); system_flash = flash; path = dt_get_path(node); dt_add_property_string(dt_chosen, "ibm,system-flash", path); free(path); prlog(PR_INFO, "FLASH: registered system flash device %s\n", name); flash_nvram_probe(flash, ffs); }
void lxvpd_add_slot_properties(struct pci_slot *slot, struct dt_node *np) { struct phb *phb = slot->phb; struct lxvpd_pci_slot *s = slot->data; char loc_code[LOC_CODE_SIZE]; size_t base_loc_code_len, slot_label_len; /* Check if we have platform specific slot */ if (!s || !np) return; /* Check PHB base location code */ if (!phb->base_loc_code) return; /* Check location length is valid */ base_loc_code_len = strlen(phb->base_loc_code); slot_label_len = strlen(s->label); if ((base_loc_code_len + slot_label_len + 1) >= LOC_CODE_SIZE) return; /* Location code */ strcpy(loc_code, phb->base_loc_code); strcat(loc_code, "-"); strcat(loc_code, s->label); dt_add_property(np, "ibm,slot-location-code", loc_code, strlen(loc_code) + 1); dt_add_property_string(np, "ibm,slot-label", s->label); }
/* * SLCA structure contains System Attention Indicator location * code (FRU ID = SA). Add this information to device tree * (under '/ibm,opal/led'). */ void slca_dt_add_sai_node(void) { const struct slca_entry *s_entry; struct dt_node *led_node, *sai_node; s_entry = slca_get_sai_entry(); if (!s_entry) return; /* Create /ibm,opal node, if its not created already */ if (!opal_node) return; /* Crete LED parent node */ led_node = dt_find_by_path(opal_node, DT_PROPERTY_LED_NODE); if (!led_node) return; if (s_entry->loc_code_len == 0 || s_entry->loc_code_len > LOC_CODE_SIZE) return; /* Create SAI node */ sai_node = dt_new(led_node, s_entry->loc_code); assert(sai_node); dt_add_property_string(sai_node, DT_PROPERTY_LED_TYPES, LED_TYPE_ATTENTION); }
static struct dt_node *dt_create_i2c_device(struct dt_node *bus, uint8_t addr, const char *name, const char *compat, const char *label) { struct dt_node *dev; dev = dt_new_addr(bus, name, addr); if (!dev) return NULL; dt_add_property_string(dev, "compatible", compat); dt_add_property_string(dev, "label", label); dt_add_property_cells(dev, "reg", addr); dt_add_property_string(dev, "status", "reserved"); return dev; }
static struct dt_node *create_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, const char *name, uint32_t unit_addr, int okay) { struct dt_node *node; node = dt_new_addr(cpus, name, unit_addr); assert(node); dt_add_property_string(node, "device_type", "cache"); dt_add_property_cells(node, "reg", unit_addr); dt_add_property_string(node, "status", okay ? "okay" : "bad"); dt_add_property(node, "cache-unified", NULL, 0); /* Assume cache associavitity sets is same for L2, L3 and L3.5 */ dt_add_property_cells(node, "d-cache-sets", be32_to_cpu(cache->l2_cache_assoc_sets)); dt_add_property_cells(node, "i-cache-sets", be32_to_cpu(cache->l2_cache_assoc_sets)); return node; }
static bool add_address_range(struct dt_node *root, const struct HDIF_ms_area_id *id, const struct HDIF_ms_area_address_range *arange) { struct dt_node *mem; u64 reg[2]; char *name; u32 chip_id; size_t namesz = sizeof("memory@") + STR_MAX_CHARS(reg[0]); name = (char*)malloc(namesz); prlog(PR_DEBUG, " Range: 0x%016llx..0x%016llx " "on Chip 0x%x mattr: 0x%x\n", (long long)arange->start, (long long)arange->end, pcid_to_chip_id(arange->chip), arange->mirror_attr); /* reg contains start and length */ reg[0] = cleanup_addr(be64_to_cpu(arange->start)); reg[1] = cleanup_addr(be64_to_cpu(arange->end)) - reg[0]; chip_id = pcid_to_chip_id(be32_to_cpu(arange->chip)); if (be16_to_cpu(id->flags) & MS_AREA_SHARED) { /* Only enter shared nodes once. */ mem = find_shared(root, be16_to_cpu(id->share_id), reg[0], reg[1]); if (mem) { append_chip_id(mem, chip_id); return true; } } snprintf(name, namesz, "memory@%llx", (long long)reg[0]); mem = dt_new(root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property_cells(mem, "ibm,chip-id", chip_id); dt_add_property_u64s(mem, "reg", reg[0], reg[1]); if (be16_to_cpu(id->flags) & MS_AREA_SHARED) dt_add_property_cells(mem, DT_PRIVATE "share-id", be16_to_cpu(id->share_id)); free(name); return true; }
static struct dt_node *dt_create_i2c_bus(struct dt_node *i2cm, const char *port_name, uint32_t port_id) { static struct dt_node *port; port = dt_new_addr(i2cm, "i2c-bus", port_id); if (!port) return NULL; dt_add_property_strings(port, "compatible", "ibm,power8-i2c-port", "ibm,opal-i2c"); dt_add_property_string(port, "ibm,port-name", port_name); dt_add_property_cells(port, "reg", port_id); dt_add_property_cells(port, "bus-frequency", 400000); dt_add_property_cells(port, "#address-cells", 1); dt_add_property_cells(port, "#size-cells", 0); return port; }
static void add_mem_node(uint64_t start, uint64_t len) { struct dt_node *mem; u64 reg[2]; char *name; name = (char*)malloc(sizeof("memory@") + STR_MAX_CHARS(reg[0])); assert(name); /* reg contains start and length */ reg[0] = cpu_to_be64(start); reg[1] = cpu_to_be64(len); sprintf(name, "memory@%llx", (long long)start); mem = dt_new(dt_root, name); dt_add_property_string(mem, "device_type", "memory"); dt_add_property(mem, "reg", reg, sizeof(reg)); free(name); }
static struct dt_node *dt_create_i2c_master(struct dt_node *n, uint32_t eng_id) { struct dt_node *i2cm; /* Each master registers set is of length 0x20 */ i2cm = dt_new_addr(n, "i2cm", 0xa0000 + eng_id * 0x20); if (!i2cm) return NULL; dt_add_property_string(i2cm, "compatible", "ibm,power8-i2cm"); dt_add_property_cells(i2cm, "reg", 0xa0000 + eng_id * 0x20, 0x20); dt_add_property_cells(i2cm, "clock-frequency", 50000000); dt_add_property_cells(i2cm, "chip-engine#", eng_id); dt_add_property_cells(i2cm, "#address-cells", 1); dt_add_property_cells(i2cm, "#size-cells", 0); return i2cm; }
void nvram_read_complete(bool success) { struct dt_node *np; /* Read not successful, error out and free the buffer */ if (!success) { free(nvram_image); nvram_size = 0; return; } /* Check and maybe format nvram */ nvram_check(); /* Add nvram node */ np = dt_new(opal_node, "nvram"); dt_add_property_cells(np, "#bytes", nvram_size); dt_add_property_string(np, "compatible", "ibm,opal-nvram"); /* Mark ready */ nvram_ready = true; }
static void add_size_to_ram_area(struct dt_node *ram_node, const struct HDIF_common_hdr *hdr, int indx_vpd) { const void *fruvpd; unsigned int fruvpd_sz; const void *kw; char *str; uint8_t kwsz; fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz); if (!CHECK_SPPTR(fruvpd)) return; /* DIMM Size */ kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SZ", &kwsz); if (!kw) return; str = zalloc(kwsz + 1); memcpy(str, kw, kwsz); dt_add_property_string(ram_node, "size", str); free(str); }
/* This will free ipmi_dev_id structure */ void ipmi_dt_add_bmc_info(void) { char buf[8]; struct dt_node *dt_fw_version; while (bmc_info_waiting) time_wait_ms(5); if (!bmc_info_valid) return; dt_fw_version = dt_find_by_name(dt_root, "ibm,firmware-versions"); if (!dt_fw_version) { free(ipmi_dev_id); return; } memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf), "%x.%02x", ipmi_dev_id->fw_rev1, ipmi_dev_id->fw_rev2); dt_add_property_string(dt_fw_version, "bmc-firmware-version", buf); free(ipmi_dev_id); }
int nx_cfg_rx_fifo(struct dt_node *node, const char *compat, const char *priority, u32 gcid, u32 pid, u32 tid, u64 umac_bar, u64 umac_notify) { u64 cfg; int rc, size; uint64_t fifo; u32 lpid = 0xfff; /* All 1's for 12 bits in UMAC notify match reg */ #define MATCH_ENABLE 1 fifo = (uint64_t) local_alloc(gcid, RX_FIFO_SIZE, RX_FIFO_SIZE); assert(fifo); /* * When configuring the address of the Rx FIFO into the Receive FIFO * BAR, we should _NOT_ shift the address into bits 8:53. Instead we * should copy the address as is and VAS/NX will extract relevant bits. */ /* * Section 5.21 of P9 NX Workbook Version 2.42 shows Receive FIFO BAR * 54:56 represents FIFO size * 000 = 1KB, 8 CRBs * 001 = 2KB, 16 CRBs * 010 = 4KB, 32 CRBs * 011 = 8KB, 64 CRBs * 100 = 16KB, 128 CRBs * 101 = 32KB, 256 CRBs * 110 = 111 reserved */ size = RX_FIFO_SIZE / 1024; cfg = SETFIELD(NX_P9_RX_FIFO_BAR_SIZE, fifo, ilog2(size)); rc = xscom_write(gcid, umac_bar, cfg); if (rc) { prerror("NX%d: ERROR: Setting UMAC FIFO bar failure %d\n", gcid, rc); return rc; } else prlog(PR_DEBUG, "NX%d: Setting UMAC FIFO bar 0x%016lx\n", gcid, (unsigned long)cfg); rc = xscom_read(gcid, umac_notify, &cfg); if (rc) return rc; /* * VAS issues asb_notify with the unique ID to identify the target * co-processor/engine. Logical partition ID (lpid), process ID (pid), * and thread ID (tid) combination is used to define the unique ID * in the system. Export these values in device-tree such that the * driver configure RxFIFO with VAS. Set these values in RxFIFO notify * match register for each engine which compares the ID with each * request. * To define unique indentification, 0xfff (1's for 12 bits), * co-processor type, and counter within coprocessor type are used * for lpid, pid, and tid respectively. */ cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_LPID, cfg, lpid); cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_PID, cfg, pid); cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_TID, cfg, tid); cfg = SETFIELD(NX_P9_RX_FIFO_NOTIFY_MATCH_MATCH_ENABLE, cfg, MATCH_ENABLE); rc = xscom_write(gcid, umac_notify, cfg); if (rc) { prerror("NX%d: ERROR: Setting UMAC notify match failure %d\n", gcid, rc); return rc; } else prlog(PR_DEBUG, "NX%d: Setting UMAC notify match 0x%016lx\n", gcid, (unsigned long)cfg); dt_add_property_string(node, "compatible", compat); dt_add_property_string(node, "priority", priority); dt_add_property_u64(node, "rx-fifo-address", fifo); dt_add_property_cells(node, "rx-fifo-size", RX_FIFO_SIZE); dt_add_property_cells(node, "lpid", lpid); dt_add_property_cells(node, "pid", pid); dt_add_property_cells(node, "tid", tid); return 0; }
struct dt_node * add_core_common(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, const struct sppaca_cpu_timebase *tb, uint32_t int_server, bool okay) { const char *name; struct dt_node *cpu; uint32_t version; uint64_t freq; const uint8_t pa_features[] = { 6, 0, 0xf6, 0x3f, 0xc7, 0x00, 0x80, 0xc0 }; printf(" Cache: I=%u D=%u/%u/%u/%u\n", be32_to_cpu(cache->icache_size_kb), be32_to_cpu(cache->l1_dcache_size_kb), be32_to_cpu(cache->l2_dcache_size_kb), be32_to_cpu(cache->l3_dcache_size_kb), be32_to_cpu(cache->l35_dcache_size_kb)); /* Use the boot CPU PVR to make up a CPU name in the device-tree * since the HDAT doesn't seem to tell.... */ version = mfspr(SPR_PVR); switch(PVR_TYPE(version)) { case PVR_TYPE_P7: name = "PowerPC,POWER7"; break; case PVR_TYPE_P7P: name = "PowerPC,POWER7+"; break; case PVR_TYPE_P8E: case PVR_TYPE_P8: name = "PowerPC,POWER8"; break; default: name = "PowerPC,Unknown"; } cpu = dt_new_addr(cpus, name, int_server); assert(cpu); dt_add_property_string(cpu, "device_type", "cpu"); dt_add_property_string(cpu, "status", okay ? "okay" : "bad"); dt_add_property_cells(cpu, "reg", int_server); dt_add_property_cells(cpu, "cpu-version", version); dt_add_property(cpu, "64-bit", NULL, 0); dt_add_property(cpu, "32-64-bridge", NULL, 0); dt_add_property(cpu, "graphics", NULL, 0); dt_add_property(cpu, "general-purpose", NULL, 0); dt_add_property_cells(cpu, "ibm,processor-segment-sizes", 0x1c, 0x28, 0xffffffff, 0xffffffff); dt_add_property_cells(cpu, "ibm,processor-page-sizes", 0xc, 0x10, 0x18, 0x22); /* Page size encodings appear to be the same for P7 and P8 */ dt_add_property_cells(cpu, "ibm,segment-page-sizes", 0x0c, 0x000, 3, 0x0c, 0x0000, /* 4K seg 4k pages */ 0x10, 0x0007, /* 4K seg 64k pages */ 0x18, 0x0038, /* 4K seg 16M pages */ 0x10, 0x110, 2, 0x10, 0x0001, /* 64K seg 64k pages */ 0x18, 0x0008, /* 64K seg 16M pages */ 0x18, 0x100, 1, 0x18, 0x0000, /* 16M seg 16M pages */ 0x22, 0x120, 1, 0x22, 0x0003); /* 16G seg 16G pages */ dt_add_property(cpu, "ibm,pa-features", pa_features, sizeof(pa_features)); dt_add_property_cells(cpu, "ibm,slb-size", 0x20); dt_add_property_cells(cpu, "ibm,vmx", 0x2); dt_add_property_cells(cpu, "ibm,dfp", 0x2); dt_add_property_cells(cpu, "ibm,purr", 0x1); dt_add_property_cells(cpu, "ibm,spurr", 0x1); /* * Do not create "clock-frequency" if the frequency doesn't * fit in a single cell */ freq = ((uint64_t)be32_to_cpu(tb->actual_clock_speed)) * 1000000ul; if (freq <= 0xfffffffful) dt_add_property_cells(cpu, "clock-frequency", freq); dt_add_property_cells(cpu, "ibm,extended-clock-frequency", hi32(freq), lo32(freq)); /* FIXME: Hardcoding is bad. */ dt_add_property_cells(cpu, "timebase-frequency", 512000000); dt_add_property_cells(cpu, "ibm,extended-timebase-frequency", 0, 512000000); dt_add_property_cells(cpu, "reservation-granule-size", be32_to_cpu(cache->reservation_size)); dt_add_property_cells(cpu, "d-tlb-size", be32_to_cpu(cache->dtlb_entries)); dt_add_property_cells(cpu, "i-tlb-size", be32_to_cpu(cache->itlb_entries)); /* Assume unified TLB */ dt_add_property_cells(cpu, "tlb-size", be32_to_cpu(cache->dtlb_entries)); dt_add_property_cells(cpu, "d-tlb-sets", be32_to_cpu(cache->dtlb_assoc_sets)); dt_add_property_cells(cpu, "i-tlb-sets", be32_to_cpu(cache->itlb_assoc_sets)); dt_add_property_cells(cpu, "tlb-sets", be32_to_cpu(cache->dtlb_assoc_sets)); dt_add_property_cells(cpu, "d-cache-block-size", be32_to_cpu(cache->dcache_block_size)); dt_add_property_cells(cpu, "i-cache-block-size", be32_to_cpu(cache->icache_block_size)); dt_add_property_cells(cpu, "d-cache-size", be32_to_cpu(cache->l1_dcache_size_kb)*1024); dt_add_property_cells(cpu, "i-cache-size", be32_to_cpu(cache->icache_size_kb)*1024); dt_add_property_cells(cpu, "i-cache-sets", be32_to_cpu(cache->icache_assoc_sets)); dt_add_property_cells(cpu, "d-cache-sets", be32_to_cpu(cache->dcache_assoc_sets)); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(cpu, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l1_dcache_line_size != cache->dcache_block_size) dt_add_property_cells(cpu, "d-cache-line-size", be32_to_cpu(cache->l1_dcache_line_size)); return cpu; }
int main(void) { struct dt_node *root, *c1, *c2, *gc1, *gc2, *gc3, *ggc1, *i; const struct dt_property *p; struct dt_property *p2; unsigned int n; root = dt_new_root("root"); assert(!list_top(&root->properties, struct dt_property, list)); c1 = dt_new(root, "c1"); assert(!list_top(&c1->properties, struct dt_property, list)); c2 = dt_new(root, "c2"); assert(!list_top(&c2->properties, struct dt_property, list)); gc1 = dt_new(c1, "gc1"); assert(!list_top(&gc1->properties, struct dt_property, list)); gc2 = dt_new(c1, "gc2"); assert(!list_top(&gc2->properties, struct dt_property, list)); gc3 = dt_new(c1, "gc3"); assert(!list_top(&gc3->properties, struct dt_property, list)); ggc1 = dt_new(gc1, "ggc1"); assert(!list_top(&ggc1->properties, struct dt_property, list)); for (n = 0, i = dt_first(root); i; i = dt_next(root, i), n++) { assert(!list_top(&i->properties, struct dt_property, list)); dt_add_property_cells(i, "visited", 1); } assert(n == 6); for (n = 0, i = dt_first(root); i; i = dt_next(root, i), n++) { p = list_top(&i->properties, struct dt_property, list); assert(strcmp(p->name, "visited") == 0); assert(p->len == sizeof(u32)); assert(fdt32_to_cpu(*(u32 *)p->prop) == 1); } assert(n == 6); dt_add_property_cells(c1, "some-property", 1, 2, 3); p = dt_find_property(c1, "some-property"); assert(p); assert(strcmp(p->name, "some-property") == 0); assert(p->len == sizeof(u32) * 3); assert(fdt32_to_cpu(*(u32 *)p->prop) == 1); assert(fdt32_to_cpu(*((u32 *)p->prop + 1)) == 2); assert(fdt32_to_cpu(*((u32 *)p->prop + 2)) == 3); /* Test freeing a single node */ assert(!list_empty(&gc1->children)); dt_free(ggc1); assert(list_empty(&gc1->children)); /* Test rodata logic. */ assert(!is_rodata("hello")); assert(is_rodata(__rodata_start)); strcpy(__rodata_start, "name"); ggc1 = dt_new(root, __rodata_start); assert(ggc1->name == __rodata_start); /* Test string node. */ dt_add_property_string(ggc1, "somestring", "someval"); assert(dt_has_node_property(ggc1, "somestring", "someval")); assert(!dt_has_node_property(ggc1, "somestrin", "someval")); assert(!dt_has_node_property(ggc1, "somestring", "someva")); assert(!dt_has_node_property(ggc1, "somestring", "somevale")); /* Test resizing property. */ p = p2 = __dt_find_property(c1, "some-property"); assert(p); n = p2->len; while (p2 == p) { n *= 2; dt_resize_property(&p2, n); } assert(dt_find_property(c1, "some-property") == p2); list_check(&c1->properties, "properties after resizing"); dt_del_property(c1, p2); list_check(&c1->properties, "properties after delete"); /* No leaks for valgrind! */ dt_free(root); return 0; }
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; }
struct dt_node * add_core_common(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, const struct sppaca_cpu_timebase *tb, uint32_t int_server, bool okay) { const char *name; struct dt_node *cpu; uint32_t version; uint64_t freq; const uint8_t pa_features_p7[] = { 6, 0, 0xf6, 0x3f, 0xc7, 0x00, 0x80, 0xc0 }; const uint8_t pa_features_p7p[] = { 6, 0, 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xc0 }; const uint8_t pa_features_p8[] = { 24, 0, 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xd0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, }; const uint8_t pa_features_p9n_dd20[] = { 64, 0, 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xd0, 0x80, 0x00, /* 0 .. 7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 .. 15 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 16 .. 23 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 24 .. 31 */ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, /* 32 .. 39 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 40 .. 47 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 .. 55 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 56 .. 63 */ }; const uint8_t pa_features_p9[] = { 64, 0, 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xd0, 0x80, 0x00, /* 0 .. 7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 .. 15 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 16 .. 23 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 24 .. 31 */ 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, /* 32 .. 39 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 40 .. 47 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 .. 55 */ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 56 .. 63 */ }; const uint8_t *pa_features; size_t pa_features_size; prlog(PR_INFO, " Cache: I=%u D=%u/%u/%u/%u\n", be32_to_cpu(cache->icache_size_kb), be32_to_cpu(cache->l1_dcache_size_kb), be32_to_cpu(cache->l2_dcache_size_kb), be32_to_cpu(cache->l3_dcache_size_kb), be32_to_cpu(cache->l35_dcache_size_kb)); /* Use the boot CPU PVR to make up a CPU name in the device-tree * since the HDAT doesn't seem to tell.... */ version = mfspr(SPR_PVR); switch(PVR_TYPE(version)) { case PVR_TYPE_P7: name = "PowerPC,POWER7"; pa_features = pa_features_p7; pa_features_size = sizeof(pa_features_p7); break; case PVR_TYPE_P7P: name = "PowerPC,POWER7+"; pa_features = pa_features_p7p; pa_features_size = sizeof(pa_features_p7p); break; case PVR_TYPE_P8E: case PVR_TYPE_P8: case PVR_TYPE_P8NVL: name = "PowerPC,POWER8"; pa_features = pa_features_p8; pa_features_size = sizeof(pa_features_p8); break; case PVR_TYPE_P9: case PVR_TYPE_P9P: name = "PowerPC,POWER9"; if (is_power9n(version) && (PVR_VERS_MAJ(version) == 2) && (PVR_VERS_MIN(version) == 0)) { /* P9N DD2.0 */ pa_features = pa_features_p9n_dd20; pa_features_size = sizeof(pa_features_p9n_dd20); } else { pa_features = pa_features_p9; pa_features_size = sizeof(pa_features_p9); } break; default: name = "PowerPC,Unknown"; pa_features = NULL; } cpu = dt_new_addr(cpus, name, int_server); assert(cpu); dt_add_property_string(cpu, "device_type", "cpu"); dt_add_property_string(cpu, "status", okay ? "okay" : "bad"); dt_add_property_cells(cpu, "reg", int_server); dt_add_property_cells(cpu, "cpu-version", version); dt_add_property(cpu, "64-bit", NULL, 0); dt_add_property(cpu, "32-64-bridge", NULL, 0); dt_add_property(cpu, "graphics", NULL, 0); dt_add_property(cpu, "general-purpose", NULL, 0); dt_add_property_cells(cpu, "ibm,processor-segment-sizes", 0x1c, 0x28, 0xffffffff, 0xffffffff); dt_add_property_cells(cpu, "ibm,processor-page-sizes", 0xc, 0x10, 0x18, 0x22); if (proc_gen == proc_gen_p9) dt_add_property_cells(cpu, "ibm,processor-radix-AP-encodings", 0x0000000c, 0xa0000010, 0x20000015, 0x4000001e); /* Page size encodings appear to be the same for P7 and P8 */ dt_add_property_cells(cpu, "ibm,segment-page-sizes", 0x0c, 0x000, 3, 0x0c, 0x0000, /* 4K seg 4k pages */ 0x10, 0x0007, /* 4K seg 64k pages */ 0x18, 0x0038, /* 4K seg 16M pages */ 0x10, 0x110, 2, 0x10, 0x0001, /* 64K seg 64k pages */ 0x18, 0x0008, /* 64K seg 16M pages */ 0x18, 0x100, 1, 0x18, 0x0000, /* 16M seg 16M pages */ 0x22, 0x120, 1, 0x22, 0x0003); /* 16G seg 16G pages */ if (pa_features) { dt_add_property(cpu, "ibm,pa-features", pa_features, pa_features_size); } dt_add_property_cells(cpu, "ibm,slb-size", 0x20); dt_add_property_cells(cpu, "ibm,vmx", 0x2); dt_add_property_cells(cpu, "ibm,dfp", 0x2); dt_add_property_cells(cpu, "ibm,purr", 0x1); dt_add_property_cells(cpu, "ibm,spurr", 0x1); /* * Do not create "clock-frequency" if the frequency doesn't * fit in a single cell */ freq = ((uint64_t)be32_to_cpu(tb->actual_clock_speed)) * 1000000ul; if (freq <= 0xfffffffful) dt_add_property_cells(cpu, "clock-frequency", freq); dt_add_property_u64(cpu, "ibm,extended-clock-frequency", freq); /* FIXME: Hardcoding is bad. */ dt_add_property_cells(cpu, "timebase-frequency", 512000000); dt_add_property_cells(cpu, "ibm,extended-timebase-frequency", 0, 512000000); dt_add_property_cells(cpu, "reservation-granule-size", be32_to_cpu(cache->reservation_size)); dt_add_property_cells(cpu, "d-tlb-size", be32_to_cpu(cache->dtlb_entries)); dt_add_property_cells(cpu, "i-tlb-size", be32_to_cpu(cache->itlb_entries)); /* Assume unified TLB */ dt_add_property_cells(cpu, "tlb-size", be32_to_cpu(cache->dtlb_entries)); dt_add_property_cells(cpu, "d-tlb-sets", be32_to_cpu(cache->dtlb_assoc_sets)); dt_add_property_cells(cpu, "i-tlb-sets", be32_to_cpu(cache->itlb_assoc_sets)); dt_add_property_cells(cpu, "tlb-sets", be32_to_cpu(cache->dtlb_assoc_sets)); dt_add_property_cells(cpu, "d-cache-block-size", be32_to_cpu(cache->dcache_block_size)); dt_add_property_cells(cpu, "i-cache-block-size", be32_to_cpu(cache->icache_block_size)); dt_add_property_cells(cpu, "d-cache-size", be32_to_cpu(cache->l1_dcache_size_kb)*1024); dt_add_property_cells(cpu, "i-cache-size", be32_to_cpu(cache->icache_size_kb)*1024); dt_add_property_cells(cpu, "i-cache-sets", be32_to_cpu(cache->icache_assoc_sets)); dt_add_property_cells(cpu, "d-cache-sets", be32_to_cpu(cache->dcache_assoc_sets)); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(cpu, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l1_dcache_line_size != cache->dcache_block_size) dt_add_property_cells(cpu, "d-cache-line-size", be32_to_cpu(cache->l1_dcache_line_size)); return cpu; }