Skip to content

Commit 8f95faa

Browse files
Madhavan Srinivasanmpe
Madhavan Srinivasan
authored andcommitted
powerpc/powernv: Detect and create IMC device
Code to create platform device for the In-Memory Collection (IMC) counters. Platform devices are created based on the IMC compatibility. New header file created to contain the data structures and macros needed for In-Memory Collection (IMC) counter pmu devices. The device tree for IMC counters starts at the node "imc-counters". This node contains all the IMC PMU nodes and event nodes for these IMC PMUs. Device probe() parses the device to locate three possible IMC device types (Nest/Core/Thread). Function then branch to parse each unit nodes to populate vital information such as device memory sizes, event nodes information, base address for reserve memory access (if any) and so on. Simple bare-minimum shutdown function added which only "stops" the engines. Signed-off-by: Anju T Sudhakar <[email protected]> Signed-off-by: Hemant Kumar <[email protected]> Signed-off-by: Madhavan Srinivasan <[email protected]> [mpe: Fix build with CONFIG_PERF_EVENTS=n] Signed-off-by: Michael Ellerman <[email protected]>
1 parent 28a5db0 commit 8f95faa

File tree

4 files changed

+364
-0
lines changed

4 files changed

+364
-0
lines changed

arch/powerpc/include/asm/imc-pmu.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#ifndef __ASM_POWERPC_IMC_PMU_H
2+
#define __ASM_POWERPC_IMC_PMU_H
3+
4+
/*
5+
* IMC Nest Performance Monitor counter support.
6+
*
7+
* Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation.
8+
* (C) 2017 Anju T Sudhakar, IBM Corporation.
9+
* (C) 2017 Hemant K Shaw, IBM Corporation.
10+
*
11+
* This program is free software; you can redistribute it and/or
12+
* modify it under the terms of the GNU General Public License
13+
* as published by the Free Software Foundation; either version
14+
* 2 of the License, or later version.
15+
*/
16+
17+
#include <linux/perf_event.h>
18+
#include <linux/slab.h>
19+
#include <linux/of.h>
20+
#include <linux/io.h>
21+
#include <asm/opal.h>
22+
23+
/*
24+
* For static allocation of some of the structures.
25+
*/
26+
#define IMC_MAX_PMUS 32
27+
28+
/*
29+
* Compatibility macros for IMC devices
30+
*/
31+
#define IMC_DTB_COMPAT "ibm,opal-in-memory-counters"
32+
#define IMC_DTB_UNIT_COMPAT "ibm,imc-counters"
33+
34+
35+
/*
36+
* LDBAR: Counter address and Enable/Disable macro.
37+
* perf/imc-pmu.c has the LDBAR layout information.
38+
*/
39+
#define THREAD_IMC_LDBAR_MASK 0x0003ffffffffe000ULL
40+
#define THREAD_IMC_ENABLE 0x8000000000000000ULL
41+
42+
/*
43+
* Structure to hold memory address information for imc units.
44+
*/
45+
struct imc_mem_info {
46+
u64 *vbase;
47+
u32 id;
48+
};
49+
50+
/*
51+
* Place holder for nest pmu events and values.
52+
*/
53+
struct imc_events {
54+
u32 value;
55+
char *name;
56+
char *unit;
57+
char *scale;
58+
};
59+
60+
/* Event attribute array index */
61+
#define IMC_FORMAT_ATTR 0
62+
#define IMC_EVENT_ATTR 1
63+
#define IMC_CPUMASK_ATTR 2
64+
#define IMC_NULL_ATTR 3
65+
66+
/* PMU Format attribute macros */
67+
#define IMC_EVENT_OFFSET_MASK 0xffffffffULL
68+
69+
/*
70+
* Device tree parser code detects IMC pmu support and
71+
* registers new IMC pmus. This structure will hold the
72+
* pmu functions, events, counter memory information
73+
* and attrs for each imc pmu and will be referenced at
74+
* the time of pmu registration.
75+
*/
76+
struct imc_pmu {
77+
struct pmu pmu;
78+
struct imc_mem_info *mem_info;
79+
struct imc_events **events;
80+
/*
81+
* Attribute groups for the PMU. Slot 0 used for
82+
* format attribute, slot 1 used for cpusmask attribute,
83+
* slot 2 used for event attribute. Slot 3 keep as
84+
* NULL.
85+
*/
86+
const struct attribute_group *attr_groups[4];
87+
u32 counter_mem_size;
88+
int domain;
89+
/*
90+
* flag to notify whether the memory is mmaped
91+
* or allocated by kernel.
92+
*/
93+
bool imc_counter_mmaped;
94+
};
95+
96+
/*
97+
* Structure to hold id, lock and reference count for the imc events which
98+
* are inited.
99+
*/
100+
struct imc_pmu_ref {
101+
struct mutex lock;
102+
unsigned int id;
103+
int refc;
104+
};
105+
106+
/*
107+
* In-Memory Collection Counters type.
108+
* Data comes from Device tree.
109+
* Three device type are supported.
110+
*/
111+
112+
enum {
113+
IMC_TYPE_THREAD = 0x1,
114+
IMC_TYPE_CORE = 0x4,
115+
IMC_TYPE_CHIP = 0x10,
116+
};
117+
118+
/*
119+
* Domains for IMC PMUs
120+
*/
121+
#define IMC_DOMAIN_NEST 1
122+
#define IMC_DOMAIN_CORE 2
123+
#define IMC_DOMAIN_THREAD 3
124+
125+
extern int init_imc_pmu(struct device_node *parent,
126+
struct imc_pmu *pmu_ptr, int pmu_id);
127+
extern void thread_imc_disable(void);
128+
#endif /* __ASM_POWERPC_IMC_PMU_H */

arch/powerpc/platforms/powernv/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
1212
obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
1313
obj-$(CONFIG_TRACEPOINTS) += opal-tracepoints.o
1414
obj-$(CONFIG_OPAL_PRD) += opal-prd.o
15+
obj-$(CONFIG_PERF_EVENTS) += opal-imc.o
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* OPAL IMC interface detection driver
3+
* Supported on POWERNV platform
4+
*
5+
* Copyright (C) 2017 Madhavan Srinivasan, IBM Corporation.
6+
* (C) 2017 Anju T Sudhakar, IBM Corporation.
7+
* (C) 2017 Hemant K Shaw, IBM Corporation.
8+
*
9+
* This program is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU General Public License
11+
* as published by the Free Software Foundation; either version
12+
* 2 of the License, or later version.
13+
*/
14+
#include <linux/kernel.h>
15+
#include <linux/platform_device.h>
16+
#include <linux/of.h>
17+
#include <linux/of_address.h>
18+
#include <linux/of_platform.h>
19+
#include <linux/crash_dump.h>
20+
#include <asm/opal.h>
21+
#include <asm/io.h>
22+
#include <asm/imc-pmu.h>
23+
#include <asm/cputhreads.h>
24+
25+
/*
26+
* imc_get_mem_addr_nest: Function to get nest counter memory region
27+
* for each chip
28+
*/
29+
static int imc_get_mem_addr_nest(struct device_node *node,
30+
struct imc_pmu *pmu_ptr,
31+
u32 offset)
32+
{
33+
int nr_chips = 0, i;
34+
u64 *base_addr_arr, baddr;
35+
u32 *chipid_arr;
36+
37+
nr_chips = of_property_count_u32_elems(node, "chip-id");
38+
if (nr_chips <= 0)
39+
return -ENODEV;
40+
41+
base_addr_arr = kcalloc(nr_chips, sizeof(u64), GFP_KERNEL);
42+
if (!base_addr_arr)
43+
return -ENOMEM;
44+
45+
chipid_arr = kcalloc(nr_chips, sizeof(u32), GFP_KERNEL);
46+
if (!chipid_arr)
47+
return -ENOMEM;
48+
49+
if (of_property_read_u32_array(node, "chip-id", chipid_arr, nr_chips))
50+
goto error;
51+
52+
if (of_property_read_u64_array(node, "base-addr", base_addr_arr,
53+
nr_chips))
54+
goto error;
55+
56+
pmu_ptr->mem_info = kcalloc(nr_chips, sizeof(struct imc_mem_info),
57+
GFP_KERNEL);
58+
if (!pmu_ptr->mem_info)
59+
goto error;
60+
61+
for (i = 0; i < nr_chips; i++) {
62+
pmu_ptr->mem_info[i].id = chipid_arr[i];
63+
baddr = base_addr_arr[i] + offset;
64+
pmu_ptr->mem_info[i].vbase = phys_to_virt(baddr);
65+
}
66+
67+
pmu_ptr->imc_counter_mmaped = true;
68+
kfree(base_addr_arr);
69+
kfree(chipid_arr);
70+
return 0;
71+
72+
error:
73+
kfree(pmu_ptr->mem_info);
74+
kfree(base_addr_arr);
75+
kfree(chipid_arr);
76+
return -1;
77+
}
78+
79+
/*
80+
* imc_pmu_create : Takes the parent device which is the pmu unit, pmu_index
81+
* and domain as the inputs.
82+
* Allocates memory for the struct imc_pmu, sets up its domain, size and offsets
83+
*/
84+
static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
85+
{
86+
int ret = 0;
87+
struct imc_pmu *pmu_ptr;
88+
u32 offset;
89+
90+
/* memory for pmu */
91+
pmu_ptr = kzalloc(sizeof(struct imc_pmu), GFP_KERNEL);
92+
if (!pmu_ptr)
93+
return -ENOMEM;
94+
95+
/* Set the domain */
96+
pmu_ptr->domain = domain;
97+
98+
ret = of_property_read_u32(parent, "size", &pmu_ptr->counter_mem_size);
99+
if (ret) {
100+
ret = -EINVAL;
101+
goto free_pmu;
102+
}
103+
104+
if (!of_property_read_u32(parent, "offset", &offset)) {
105+
if (imc_get_mem_addr_nest(parent, pmu_ptr, offset)) {
106+
ret = -EINVAL;
107+
goto free_pmu;
108+
}
109+
}
110+
111+
return 0;
112+
113+
free_pmu:
114+
kfree(pmu_ptr);
115+
return ret;
116+
}
117+
118+
static void disable_nest_pmu_counters(void)
119+
{
120+
int nid, cpu;
121+
struct cpumask *l_cpumask;
122+
123+
get_online_cpus();
124+
for_each_online_node(nid) {
125+
l_cpumask = cpumask_of_node(nid);
126+
cpu = cpumask_first(l_cpumask);
127+
opal_imc_counters_stop(OPAL_IMC_COUNTERS_NEST,
128+
get_hard_smp_processor_id(cpu));
129+
}
130+
put_online_cpus();
131+
}
132+
133+
static void disable_core_pmu_counters(void)
134+
{
135+
cpumask_t cores_map;
136+
int cpu, rc;
137+
138+
get_online_cpus();
139+
/* Disable the IMC Core functions */
140+
cores_map = cpu_online_cores_map();
141+
for_each_cpu(cpu, &cores_map) {
142+
rc = opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE,
143+
get_hard_smp_processor_id(cpu));
144+
if (rc)
145+
pr_err("%s: Failed to stop Core (cpu = %d)\n",
146+
__FUNCTION__, cpu);
147+
}
148+
put_online_cpus();
149+
}
150+
151+
static int opal_imc_counters_probe(struct platform_device *pdev)
152+
{
153+
struct device_node *imc_dev = pdev->dev.of_node;
154+
int pmu_count = 0, domain;
155+
u32 type;
156+
157+
/*
158+
* Check whether this is kdump kernel. If yes, force the engines to
159+
* stop and return.
160+
*/
161+
if (is_kdump_kernel()) {
162+
disable_nest_pmu_counters();
163+
disable_core_pmu_counters();
164+
return -ENODEV;
165+
}
166+
167+
for_each_compatible_node(imc_dev, NULL, IMC_DTB_UNIT_COMPAT) {
168+
if (of_property_read_u32(imc_dev, "type", &type)) {
169+
pr_warn("IMC Device without type property\n");
170+
continue;
171+
}
172+
173+
switch (type) {
174+
case IMC_TYPE_CHIP:
175+
domain = IMC_DOMAIN_NEST;
176+
break;
177+
case IMC_TYPE_CORE:
178+
domain =IMC_DOMAIN_CORE;
179+
break;
180+
case IMC_TYPE_THREAD:
181+
domain = IMC_DOMAIN_THREAD;
182+
break;
183+
default:
184+
pr_warn("IMC Unknown Device type \n");
185+
domain = -1;
186+
break;
187+
}
188+
189+
if (!imc_pmu_create(imc_dev, pmu_count, domain))
190+
pmu_count++;
191+
}
192+
193+
return 0;
194+
}
195+
196+
static void opal_imc_counters_shutdown(struct platform_device *pdev)
197+
{
198+
/*
199+
* Function only stops the engines which is bare minimum.
200+
* TODO: Need to handle proper memory cleanup and pmu
201+
* unregister.
202+
*/
203+
disable_nest_pmu_counters();
204+
disable_core_pmu_counters();
205+
}
206+
207+
static const struct of_device_id opal_imc_match[] = {
208+
{ .compatible = IMC_DTB_COMPAT },
209+
{},
210+
};
211+
212+
static struct platform_driver opal_imc_driver = {
213+
.driver = {
214+
.name = "opal-imc-counters",
215+
.of_match_table = opal_imc_match,
216+
},
217+
.probe = opal_imc_counters_probe,
218+
.shutdown = opal_imc_counters_shutdown,
219+
};
220+
221+
builtin_platform_driver(opal_imc_driver);

arch/powerpc/platforms/powernv/opal.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/of.h>
1717
#include <linux/of_fdt.h>
1818
#include <linux/of_platform.h>
19+
#include <linux/of_address.h>
1920
#include <linux/interrupt.h>
2021
#include <linux/notifier.h>
2122
#include <linux/slab.h>
@@ -30,6 +31,7 @@
3031
#include <asm/opal.h>
3132
#include <asm/firmware.h>
3233
#include <asm/mce.h>
34+
#include <asm/imc-pmu.h>
3335

3436
#include "powernv.h"
3537

@@ -720,6 +722,15 @@ static void opal_pdev_init(const char *compatible)
720722
of_platform_device_create(np, NULL, NULL);
721723
}
722724

725+
static void __init opal_imc_init_dev(void)
726+
{
727+
struct device_node *np;
728+
729+
np = of_find_compatible_node(NULL, NULL, IMC_DTB_COMPAT);
730+
if (np)
731+
of_platform_device_create(np, NULL, NULL);
732+
}
733+
723734
static int kopald(void *unused)
724735
{
725736
unsigned long timeout = msecs_to_jiffies(opal_heartbeat) + 1;
@@ -793,6 +804,9 @@ static int __init opal_init(void)
793804
/* Setup a heatbeat thread if requested by OPAL */
794805
opal_init_heartbeat();
795806

807+
/* Detect In-Memory Collection counters and create devices*/
808+
opal_imc_init_dev();
809+
796810
/* Create leds platform devices */
797811
leds = of_find_node_by_path("/ibm,opal/leds");
798812
if (leds) {

0 commit comments

Comments
 (0)