18f95faaaSMadhavan Srinivasan /* 28f95faaaSMadhavan Srinivasan * OPAL IMC interface detection driver 38f95faaaSMadhavan Srinivasan * Supported on POWERNV platform 48f95faaaSMadhavan Srinivasan * 58f95faaaSMadhavan Srinivasan * Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation. 68f95faaaSMadhavan Srinivasan * (C) 2017 Anju T Sudhakar, IBM Corporation. 78f95faaaSMadhavan Srinivasan * (C) 2017 Hemant K Shaw, IBM Corporation. 88f95faaaSMadhavan Srinivasan * 98f95faaaSMadhavan Srinivasan * This program is free software; you can redistribute it and/or 108f95faaaSMadhavan Srinivasan * modify it under the terms of the GNU General Public License 118f95faaaSMadhavan Srinivasan * as published by the Free Software Foundation; either version 128f95faaaSMadhavan Srinivasan * 2 of the License, or later version. 138f95faaaSMadhavan Srinivasan */ 148f95faaaSMadhavan Srinivasan #include <linux/kernel.h> 158f95faaaSMadhavan Srinivasan #include <linux/platform_device.h> 168f95faaaSMadhavan Srinivasan #include <linux/of.h> 178f95faaaSMadhavan Srinivasan #include <linux/of_address.h> 188f95faaaSMadhavan Srinivasan #include <linux/of_platform.h> 198f95faaaSMadhavan Srinivasan #include <linux/crash_dump.h> 208f95faaaSMadhavan Srinivasan #include <asm/opal.h> 218f95faaaSMadhavan Srinivasan #include <asm/io.h> 228f95faaaSMadhavan Srinivasan #include <asm/imc-pmu.h> 238f95faaaSMadhavan Srinivasan #include <asm/cputhreads.h> 248f95faaaSMadhavan Srinivasan 258f95faaaSMadhavan Srinivasan /* 268f95faaaSMadhavan Srinivasan * imc_get_mem_addr_nest: Function to get nest counter memory region 278f95faaaSMadhavan Srinivasan * for each chip 288f95faaaSMadhavan Srinivasan */ 298f95faaaSMadhavan Srinivasan static int imc_get_mem_addr_nest(struct device_node *node, 308f95faaaSMadhavan Srinivasan struct imc_pmu *pmu_ptr, 318f95faaaSMadhavan Srinivasan u32 offset) 328f95faaaSMadhavan Srinivasan { 338f95faaaSMadhavan Srinivasan int nr_chips = 0, i; 348f95faaaSMadhavan Srinivasan u64 *base_addr_arr, baddr; 358f95faaaSMadhavan Srinivasan u32 *chipid_arr; 368f95faaaSMadhavan Srinivasan 378f95faaaSMadhavan Srinivasan nr_chips = of_property_count_u32_elems(node, "chip-id"); 388f95faaaSMadhavan Srinivasan if (nr_chips <= 0) 398f95faaaSMadhavan Srinivasan return -ENODEV; 408f95faaaSMadhavan Srinivasan 418f95faaaSMadhavan Srinivasan base_addr_arr = kcalloc(nr_chips, sizeof(u64), GFP_KERNEL); 428f95faaaSMadhavan Srinivasan if (!base_addr_arr) 438f95faaaSMadhavan Srinivasan return -ENOMEM; 448f95faaaSMadhavan Srinivasan 458f95faaaSMadhavan Srinivasan chipid_arr = kcalloc(nr_chips, sizeof(u32), GFP_KERNEL); 468f95faaaSMadhavan Srinivasan if (!chipid_arr) 478f95faaaSMadhavan Srinivasan return -ENOMEM; 488f95faaaSMadhavan Srinivasan 498f95faaaSMadhavan Srinivasan if (of_property_read_u32_array(node, "chip-id", chipid_arr, nr_chips)) 508f95faaaSMadhavan Srinivasan goto error; 518f95faaaSMadhavan Srinivasan 528f95faaaSMadhavan Srinivasan if (of_property_read_u64_array(node, "base-addr", base_addr_arr, 538f95faaaSMadhavan Srinivasan nr_chips)) 548f95faaaSMadhavan Srinivasan goto error; 558f95faaaSMadhavan Srinivasan 568f95faaaSMadhavan Srinivasan pmu_ptr->mem_info = kcalloc(nr_chips, sizeof(struct imc_mem_info), 578f95faaaSMadhavan Srinivasan GFP_KERNEL); 588f95faaaSMadhavan Srinivasan if (!pmu_ptr->mem_info) 598f95faaaSMadhavan Srinivasan goto error; 608f95faaaSMadhavan Srinivasan 618f95faaaSMadhavan Srinivasan for (i = 0; i < nr_chips; i++) { 628f95faaaSMadhavan Srinivasan pmu_ptr->mem_info[i].id = chipid_arr[i]; 638f95faaaSMadhavan Srinivasan baddr = base_addr_arr[i] + offset; 648f95faaaSMadhavan Srinivasan pmu_ptr->mem_info[i].vbase = phys_to_virt(baddr); 658f95faaaSMadhavan Srinivasan } 668f95faaaSMadhavan Srinivasan 678f95faaaSMadhavan Srinivasan pmu_ptr->imc_counter_mmaped = true; 688f95faaaSMadhavan Srinivasan kfree(base_addr_arr); 698f95faaaSMadhavan Srinivasan kfree(chipid_arr); 708f95faaaSMadhavan Srinivasan return 0; 718f95faaaSMadhavan Srinivasan 728f95faaaSMadhavan Srinivasan error: 738f95faaaSMadhavan Srinivasan kfree(pmu_ptr->mem_info); 748f95faaaSMadhavan Srinivasan kfree(base_addr_arr); 758f95faaaSMadhavan Srinivasan kfree(chipid_arr); 768f95faaaSMadhavan Srinivasan return -1; 778f95faaaSMadhavan Srinivasan } 788f95faaaSMadhavan Srinivasan 798f95faaaSMadhavan Srinivasan /* 808f95faaaSMadhavan Srinivasan * imc_pmu_create : Takes the parent device which is the pmu unit, pmu_index 818f95faaaSMadhavan Srinivasan * and domain as the inputs. 828f95faaaSMadhavan Srinivasan * Allocates memory for the struct imc_pmu, sets up its domain, size and offsets 838f95faaaSMadhavan Srinivasan */ 848f95faaaSMadhavan Srinivasan static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain) 858f95faaaSMadhavan Srinivasan { 868f95faaaSMadhavan Srinivasan int ret = 0; 878f95faaaSMadhavan Srinivasan struct imc_pmu *pmu_ptr; 888f95faaaSMadhavan Srinivasan u32 offset; 898f95faaaSMadhavan Srinivasan 908f95faaaSMadhavan Srinivasan /* memory for pmu */ 918f95faaaSMadhavan Srinivasan pmu_ptr = kzalloc(sizeof(struct imc_pmu), GFP_KERNEL); 928f95faaaSMadhavan Srinivasan if (!pmu_ptr) 938f95faaaSMadhavan Srinivasan return -ENOMEM; 948f95faaaSMadhavan Srinivasan 958f95faaaSMadhavan Srinivasan /* Set the domain */ 968f95faaaSMadhavan Srinivasan pmu_ptr->domain = domain; 978f95faaaSMadhavan Srinivasan 988f95faaaSMadhavan Srinivasan ret = of_property_read_u32(parent, "size", &pmu_ptr->counter_mem_size); 998f95faaaSMadhavan Srinivasan if (ret) { 1008f95faaaSMadhavan Srinivasan ret = -EINVAL; 1018f95faaaSMadhavan Srinivasan goto free_pmu; 1028f95faaaSMadhavan Srinivasan } 1038f95faaaSMadhavan Srinivasan 1048f95faaaSMadhavan Srinivasan if (!of_property_read_u32(parent, "offset", &offset)) { 1058f95faaaSMadhavan Srinivasan if (imc_get_mem_addr_nest(parent, pmu_ptr, offset)) { 1068f95faaaSMadhavan Srinivasan ret = -EINVAL; 1078f95faaaSMadhavan Srinivasan goto free_pmu; 1088f95faaaSMadhavan Srinivasan } 1098f95faaaSMadhavan Srinivasan } 1108f95faaaSMadhavan Srinivasan 1118f95faaaSMadhavan Srinivasan return 0; 1128f95faaaSMadhavan Srinivasan 1138f95faaaSMadhavan Srinivasan free_pmu: 1148f95faaaSMadhavan Srinivasan kfree(pmu_ptr); 1158f95faaaSMadhavan Srinivasan return ret; 1168f95faaaSMadhavan Srinivasan } 1178f95faaaSMadhavan Srinivasan 1188f95faaaSMadhavan Srinivasan static void disable_nest_pmu_counters(void) 1198f95faaaSMadhavan Srinivasan { 1208f95faaaSMadhavan Srinivasan int nid, cpu; 1218f95faaaSMadhavan Srinivasan struct cpumask *l_cpumask; 1228f95faaaSMadhavan Srinivasan 1238f95faaaSMadhavan Srinivasan get_online_cpus(); 1248f95faaaSMadhavan Srinivasan for_each_online_node(nid) { 1258f95faaaSMadhavan Srinivasan l_cpumask = cpumask_of_node(nid); 1268f95faaaSMadhavan Srinivasan cpu = cpumask_first(l_cpumask); 1278f95faaaSMadhavan Srinivasan opal_imc_counters_stop(OPAL_IMC_COUNTERS_NEST, 1288f95faaaSMadhavan Srinivasan get_hard_smp_processor_id(cpu)); 1298f95faaaSMadhavan Srinivasan } 1308f95faaaSMadhavan Srinivasan put_online_cpus(); 1318f95faaaSMadhavan Srinivasan } 1328f95faaaSMadhavan Srinivasan 1338f95faaaSMadhavan Srinivasan static void disable_core_pmu_counters(void) 1348f95faaaSMadhavan Srinivasan { 1358f95faaaSMadhavan Srinivasan cpumask_t cores_map; 1368f95faaaSMadhavan Srinivasan int cpu, rc; 1378f95faaaSMadhavan Srinivasan 1388f95faaaSMadhavan Srinivasan get_online_cpus(); 1398f95faaaSMadhavan Srinivasan /* Disable the IMC Core functions */ 1408f95faaaSMadhavan Srinivasan cores_map = cpu_online_cores_map(); 1418f95faaaSMadhavan Srinivasan for_each_cpu(cpu, &cores_map) { 1428f95faaaSMadhavan Srinivasan rc = opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE, 1438f95faaaSMadhavan Srinivasan get_hard_smp_processor_id(cpu)); 1448f95faaaSMadhavan Srinivasan if (rc) 1458f95faaaSMadhavan Srinivasan pr_err("%s: Failed to stop Core (cpu = %d)\n", 1468f95faaaSMadhavan Srinivasan __FUNCTION__, cpu); 1478f95faaaSMadhavan Srinivasan } 1488f95faaaSMadhavan Srinivasan put_online_cpus(); 1498f95faaaSMadhavan Srinivasan } 1508f95faaaSMadhavan Srinivasan 1518f95faaaSMadhavan Srinivasan static int opal_imc_counters_probe(struct platform_device *pdev) 1528f95faaaSMadhavan Srinivasan { 1538f95faaaSMadhavan Srinivasan struct device_node *imc_dev = pdev->dev.of_node; 1548f95faaaSMadhavan Srinivasan int pmu_count = 0, domain; 1558f95faaaSMadhavan Srinivasan u32 type; 1568f95faaaSMadhavan Srinivasan 1578f95faaaSMadhavan Srinivasan /* 1588f95faaaSMadhavan Srinivasan * Check whether this is kdump kernel. If yes, force the engines to 1598f95faaaSMadhavan Srinivasan * stop and return. 1608f95faaaSMadhavan Srinivasan */ 1618f95faaaSMadhavan Srinivasan if (is_kdump_kernel()) { 1628f95faaaSMadhavan Srinivasan disable_nest_pmu_counters(); 1638f95faaaSMadhavan Srinivasan disable_core_pmu_counters(); 1648f95faaaSMadhavan Srinivasan return -ENODEV; 1658f95faaaSMadhavan Srinivasan } 1668f95faaaSMadhavan Srinivasan 1678f95faaaSMadhavan Srinivasan for_each_compatible_node(imc_dev, NULL, IMC_DTB_UNIT_COMPAT) { 1688f95faaaSMadhavan Srinivasan if (of_property_read_u32(imc_dev, "type", &type)) { 1698f95faaaSMadhavan Srinivasan pr_warn("IMC Device without type property\n"); 1708f95faaaSMadhavan Srinivasan continue; 1718f95faaaSMadhavan Srinivasan } 1728f95faaaSMadhavan Srinivasan 1738f95faaaSMadhavan Srinivasan switch (type) { 1748f95faaaSMadhavan Srinivasan case IMC_TYPE_CHIP: 1758f95faaaSMadhavan Srinivasan domain = IMC_DOMAIN_NEST; 1768f95faaaSMadhavan Srinivasan break; 1778f95faaaSMadhavan Srinivasan case IMC_TYPE_CORE: 1788f95faaaSMadhavan Srinivasan domain =IMC_DOMAIN_CORE; 1798f95faaaSMadhavan Srinivasan break; 1808f95faaaSMadhavan Srinivasan case IMC_TYPE_THREAD: 1818f95faaaSMadhavan Srinivasan domain = IMC_DOMAIN_THREAD; 1828f95faaaSMadhavan Srinivasan break; 1838f95faaaSMadhavan Srinivasan default: 1848f95faaaSMadhavan Srinivasan pr_warn("IMC Unknown Device type \n"); 1858f95faaaSMadhavan Srinivasan domain = -1; 1868f95faaaSMadhavan Srinivasan break; 1878f95faaaSMadhavan Srinivasan } 1888f95faaaSMadhavan Srinivasan 1898f95faaaSMadhavan Srinivasan if (!imc_pmu_create(imc_dev, pmu_count, domain)) 1908f95faaaSMadhavan Srinivasan pmu_count++; 1918f95faaaSMadhavan Srinivasan } 1928f95faaaSMadhavan Srinivasan 1938f95faaaSMadhavan Srinivasan return 0; 1948f95faaaSMadhavan Srinivasan } 1958f95faaaSMadhavan Srinivasan 1968f95faaaSMadhavan Srinivasan static void opal_imc_counters_shutdown(struct platform_device *pdev) 1978f95faaaSMadhavan Srinivasan { 1988f95faaaSMadhavan Srinivasan /* 1998f95faaaSMadhavan Srinivasan * Function only stops the engines which is bare minimum. 2008f95faaaSMadhavan Srinivasan * TODO: Need to handle proper memory cleanup and pmu 2018f95faaaSMadhavan Srinivasan * unregister. 2028f95faaaSMadhavan Srinivasan */ 2038f95faaaSMadhavan Srinivasan disable_nest_pmu_counters(); 2048f95faaaSMadhavan Srinivasan disable_core_pmu_counters(); 2058f95faaaSMadhavan Srinivasan } 2068f95faaaSMadhavan Srinivasan 2078f95faaaSMadhavan Srinivasan static const struct of_device_id opal_imc_match[] = { 2088f95faaaSMadhavan Srinivasan { .compatible = IMC_DTB_COMPAT }, 2098f95faaaSMadhavan Srinivasan {}, 2108f95faaaSMadhavan Srinivasan }; 2118f95faaaSMadhavan Srinivasan 2128f95faaaSMadhavan Srinivasan static struct platform_driver opal_imc_driver = { 2138f95faaaSMadhavan Srinivasan .driver = { 2148f95faaaSMadhavan Srinivasan .name = "opal-imc-counters", 2158f95faaaSMadhavan Srinivasan .of_match_table = opal_imc_match, 2168f95faaaSMadhavan Srinivasan }, 2178f95faaaSMadhavan Srinivasan .probe = opal_imc_counters_probe, 2188f95faaaSMadhavan Srinivasan .shutdown = opal_imc_counters_shutdown, 2198f95faaaSMadhavan Srinivasan }; 2208f95faaaSMadhavan Srinivasan 2218f95faaaSMadhavan Srinivasan builtin_platform_driver(opal_imc_driver); 222