/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spira.h" #include #include #include #include "hdata.h" 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; } void add_core_attr(struct dt_node *cpu, uint32_t attr) { if (attr & CPU_ATTR_UNIFIED_PL1) dt_add_property(cpu, "cache-unified", NULL, 0); if (attr & CPU_ATTR_SPLIT_TLB) dt_add_property(cpu, "tlb-split", NULL, 0); if (attr & CPU_ATTR_TLBIA) dt_add_property(cpu, "tlbia", NULL, 0); if (attr & CPU_ATTR_PERF_MONITOR) dt_add_property_cells(cpu, "performance-monitor", 0, 1); if (attr & CPU_ATTR_EXTERN_CONT) dt_add_property(cpu, "external-control", NULL, 0); } 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 struct dt_node *l35_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t unit_addr, int okay) { struct dt_node *node; node = create_cache_node(cpus, cache, "l35-cache", unit_addr, okay); dt_add_property_cells(node, "d-cache-size", be32_to_cpu(cache->l35_dcache_size_kb) * 1024); dt_add_property_cells(node, "i-cache-size", be32_to_cpu(cache->l35_dcache_size_kb) * 1024); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(node, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l35_cache_line_size != cache->dcache_block_size) dt_add_property_cells(node, "d-cache-line-size", be32_to_cpu(cache->l35_cache_line_size)); return node; } static struct dt_node *l3_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t unit_addr, int okay) { struct dt_node *node; node = create_cache_node(cpus, cache, "l3-cache", unit_addr, okay); dt_add_property_cells(node, "d-cache-size", be32_to_cpu(cache->l3_dcache_size_kb) * 1024); dt_add_property_cells(node, "i-cache-size", be32_to_cpu(cache->l3_dcache_size_kb) * 1024); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(node, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l3_line_size != cache->dcache_block_size) dt_add_property_cells(node, "d-cache-line-size", be32_to_cpu(cache->l3_line_size)); return node; } static struct dt_node *l2_cache_node(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t unit_addr, int okay) { struct dt_node *node; node = create_cache_node(cpus, cache, "l2-cache", unit_addr, okay); dt_add_property_cells(node, "d-cache-size", be32_to_cpu(cache->l2_dcache_size_kb) * 1024); dt_add_property_cells(node, "i-cache-size", be32_to_cpu(cache->l2_dcache_size_kb) * 1024); if (cache->icache_line_size != cache->icache_block_size) dt_add_property_cells(node, "i-cache-line-size", be32_to_cpu(cache->icache_line_size)); if (cache->l2_line_size != cache->dcache_block_size) dt_add_property_cells(node, "d-cache-line-size", be32_to_cpu(cache->l2_line_size)); return node; } static struct dt_node *find_l2_node(struct dt_node *cpus, u32 unit_addr) { char name[32]; snprintf(name, sizeof(name), "l2-cache@%.08x", unit_addr); return dt_find_by_name(cpus, name); } uint32_t add_core_cache_info(struct dt_node *cpus, const struct sppcia_cpu_cache *cache, uint32_t core_pir, int okay) { struct dt_node *l2_node, *l3_node, *l35_node; uint32_t unit_addr; /* * On P9 the L2 is shared by pairs of SMT=4 cores. We only want * to create a cache node for the first of these so we mask off * the low PIR bits to get the unit address of the shared cache. */ if (proc_gen == proc_gen_p9) { core_pir &= ~0x7; l2_node = find_l2_node(cpus, 0x20 << 24 | core_pir); if (l2_node) return l2_node->phandle; } unit_addr = 0x20 << 24 | core_pir; l2_node = l2_cache_node(cpus, cache, unit_addr, okay); unit_addr = 0x30 << 24 | core_pir; l3_node = l3_cache_node(cpus, cache, unit_addr, okay); /* Represents the next level of cache in the memory hierarchy */ dt_add_property_cells(l2_node, "l2-cache", l3_node->phandle); if (be32_to_cpu(cache->l35_dcache_size_kb)) { unit_addr = 0x35 << 24 | core_pir; l35_node = l35_cache_node(cpus, cache, unit_addr, okay); dt_add_property_cells(l3_node, "l2-cache", l35_node->phandle); } return l2_node->phandle; }