Changeset View
Standalone View
sys/dev/hwpmc/hwpmc_power8.c
Show All 37 Lines | |||||||||||||||||||||||||
#include <machine/pmc_mdep.h> | #include <machine/pmc_mdep.h> | ||||||||||||||||||||||||
#include <machine/spr.h> | #include <machine/spr.h> | ||||||||||||||||||||||||
#include <machine/cpu.h> | #include <machine/cpu.h> | ||||||||||||||||||||||||
#include "hwpmc_powerpc.h" | #include "hwpmc_powerpc.h" | ||||||||||||||||||||||||
#define POWER8_MAX_PMCS 6 | #define POWER8_MAX_PMCS 6 | ||||||||||||||||||||||||
#define PM_EVENT_CODE(pe) (pe & 0xffff) | |||||||||||||||||||||||||
#define PM_EVENT_COUNTER(pe) ((pe >> 16) & 0xffff) | |||||||||||||||||||||||||
#define PM_CYC 0x1e | |||||||||||||||||||||||||
#define PM_INST_CMPL 0x02 | |||||||||||||||||||||||||
static struct pmc_ppc_event power8_event_codes[] = { | static struct pmc_ppc_event power8_event_codes[] = { | ||||||||||||||||||||||||
mhorne: I would suggest splitting the removal of these events into a separate change. | |||||||||||||||||||||||||
{PMC_EV_POWER8_INSTR_COMPLETED, | {PMC_EV_POWER8_INSTR_COMPLETED, | ||||||||||||||||||||||||
.pe_flags = PMC_FLAG_PMC5, | .pe_flags = PMC_FLAG_PMC5, | ||||||||||||||||||||||||
.pe_code = 0x00 | .pe_code = 0x00 | ||||||||||||||||||||||||
}, | }, | ||||||||||||||||||||||||
/* | /* | ||||||||||||||||||||||||
* PMC1 can also count cycles, but as PMC6 can only count cycles | * PMC1 can also count cycles, but as PMC6 can only count cycles | ||||||||||||||||||||||||
* it's better to always use it and leave PMC1 free to count | * it's better to always use it and leave PMC1 free to count | ||||||||||||||||||||||||
* other events. | * other events. | ||||||||||||||||||||||||
▲ Show 20 Lines • Show All 215 Lines • ▼ Show 20 Lines | power8_resume_pmc(bool ie) | ||||||||||||||||||||||||
/* Unfreeze counters and re-enable PERF exceptions if requested. */ | /* Unfreeze counters and re-enable PERF exceptions if requested. */ | ||||||||||||||||||||||||
mmcr0 = mfspr(SPR_MMCR0); | mmcr0 = mfspr(SPR_MMCR0); | ||||||||||||||||||||||||
mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE); | mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE); | ||||||||||||||||||||||||
if (ie) | if (ie) | ||||||||||||||||||||||||
mmcr0 |= SPR_MMCR0_PMAE; | mmcr0 |= SPR_MMCR0_PMAE; | ||||||||||||||||||||||||
mtspr(SPR_MMCR0, mmcr0); | mtspr(SPR_MMCR0, mmcr0); | ||||||||||||||||||||||||
} | } | ||||||||||||||||||||||||
static int | |||||||||||||||||||||||||
power8_allocate_pmc(int cpu, int ri, struct pmc *pm, | |||||||||||||||||||||||||
const struct pmc_op_pmcallocate *a) | |||||||||||||||||||||||||
{ | |||||||||||||||||||||||||
uint32_t caps, config, counter, pe; | |||||||||||||||||||||||||
KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), | |||||||||||||||||||||||||
("[powerpc,%d] illegal CPU value %d", __LINE__, cpu)); | |||||||||||||||||||||||||
KASSERT(ri >= 0 && ri < ppc_max_pmcs, | |||||||||||||||||||||||||
("[powerpc,%d] illegal row index %d", __LINE__, ri)); | |||||||||||||||||||||||||
Done Inline Actionsnitpick: long statements should be indented with 4 spaces. luporl: nitpick: long statements should be indented with 4 spaces. | |||||||||||||||||||||||||
pe = a->pm_md.pm_event; | |||||||||||||||||||||||||
counter = PM_EVENT_COUNTER(pe); | |||||||||||||||||||||||||
Done Inline Actions0xFFFF is the maximum width of the event code? A named macro would make this more obvious, and perhaps it's worth returning EINVAL if pm_ev exceeds this value. mhorne: `0xFFFF` is the maximum width of the event code? A named macro would make this more obvious… | |||||||||||||||||||||||||
Done Inline ActionsI tested and the config can receive the entire code to allocate the counter. I didn't add a maximum value check to return EINVAL because it is not a continuous number. leonardo.bianconi_eldorado.org.br: I tested and the config can receive the entire code to allocate the counter.
I have extracted… | |||||||||||||||||||||||||
Done Inline ActionsIf there is no clear upper bound then it is not necessary to add anything. It is only helpful as a defensive check when the width of the event code is defined to be, for example, 16 bits wide. mhorne: If there is no clear upper bound then it is not necessary to add anything. It is only helpful… | |||||||||||||||||||||||||
Done Inline Actions
With pm_md.pm_event being passed from libpmc, config can be obtained from the lower half of it. The counter value should be between 1 to 4 and it must match ri + 1, in order to select the correct PMC for the given config, unless counter is 0, then any PMC may be used. We should also handle 2 special cases: PMC5 and PMC6, that are not programmable, but always count instructions and cycles, respectively. I've seen some event codes in libpmc event database with counter values greater than 4. I don't know how they should be programmed, but I guess it's safe to treat them as invalid for now. The macros in the suggestion above are: #define PM_EVENT_CODE(pe) (pe & 0xffff) #define PM_EVENT_COUNTER(pe) ((pe >> 16) & 0xffff) luporl: With pm_md.pm_event being passed from libpmc, config can be obtained from the lower half of it. | |||||||||||||||||||||||||
config = PM_EVENT_CODE(pe); | |||||||||||||||||||||||||
/* | |||||||||||||||||||||||||
* PMC5 and PMC6 are not programmable and always count instructions | |||||||||||||||||||||||||
* completed and cycles, respectively. | |||||||||||||||||||||||||
* | |||||||||||||||||||||||||
* When counter is 0 any of the 4 programmable PMCs may be used for | |||||||||||||||||||||||||
* the specified event, otherwise it must match ri + 1. | |||||||||||||||||||||||||
*/ | |||||||||||||||||||||||||
if (counter == 0 && config == PM_INST_CMPL) | |||||||||||||||||||||||||
counter = 5; | |||||||||||||||||||||||||
else if (counter == 0 && config == PM_CYC) | |||||||||||||||||||||||||
Done Inline Actionsnitpick: long statements should be indented with 4 spaces. luporl: nitpick: long statements should be indented with 4 spaces. | |||||||||||||||||||||||||
counter = 6; | |||||||||||||||||||||||||
else if (counter > 4) | |||||||||||||||||||||||||
return (EINVAL); | |||||||||||||||||||||||||
if (counter != 0 && counter != ri + 1) | |||||||||||||||||||||||||
return (EINVAL); | |||||||||||||||||||||||||
caps = a->pm_caps; | |||||||||||||||||||||||||
if (caps & PMC_CAP_SYSTEM) | |||||||||||||||||||||||||
config |= POWERPC_PMC_KERNEL_ENABLE; | |||||||||||||||||||||||||
if (caps & PMC_CAP_USER) | |||||||||||||||||||||||||
config |= POWERPC_PMC_USER_ENABLE; | |||||||||||||||||||||||||
if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0) | |||||||||||||||||||||||||
config |= POWERPC_PMC_ENABLE; | |||||||||||||||||||||||||
pm->pm_md.pm_powerpc.pm_powerpc_evsel = config; | |||||||||||||||||||||||||
PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x", | |||||||||||||||||||||||||
cpu, ri, config); | |||||||||||||||||||||||||
return (0); | |||||||||||||||||||||||||
} | |||||||||||||||||||||||||
int | int | ||||||||||||||||||||||||
pmc_power8_initialize(struct pmc_mdep *pmc_mdep) | pmc_power8_initialize(struct pmc_mdep *pmc_mdep) | ||||||||||||||||||||||||
{ | { | ||||||||||||||||||||||||
struct pmc_classdep *pcd; | struct pmc_classdep *pcd; | ||||||||||||||||||||||||
pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8; | pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8; | ||||||||||||||||||||||||
pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC]; | pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC]; | ||||||||||||||||||||||||
pcd->pcd_caps = POWERPC_PMC_CAPS; | pcd->pcd_caps = POWERPC_PMC_CAPS; | ||||||||||||||||||||||||
pcd->pcd_class = PMC_CLASS_POWER8; | pcd->pcd_class = PMC_CLASS_POWER8; | ||||||||||||||||||||||||
pcd->pcd_num = POWER8_MAX_PMCS; | pcd->pcd_num = POWER8_MAX_PMCS; | ||||||||||||||||||||||||
pcd->pcd_ri = pmc_mdep->pmd_npmc; | pcd->pcd_ri = pmc_mdep->pmd_npmc; | ||||||||||||||||||||||||
pcd->pcd_width = 32; | pcd->pcd_width = 32; | ||||||||||||||||||||||||
pcd->pcd_pcpu_init = power8_pcpu_init; | pcd->pcd_pcpu_init = power8_pcpu_init; | ||||||||||||||||||||||||
pcd->pcd_pcpu_fini = power8_pcpu_fini; | pcd->pcd_pcpu_fini = power8_pcpu_fini; | ||||||||||||||||||||||||
pcd->pcd_allocate_pmc = powerpc_allocate_pmc; | pcd->pcd_allocate_pmc = power8_allocate_pmc; | ||||||||||||||||||||||||
Done Inline Actions
mhorne: | |||||||||||||||||||||||||
pcd->pcd_release_pmc = powerpc_release_pmc; | pcd->pcd_release_pmc = powerpc_release_pmc; | ||||||||||||||||||||||||
pcd->pcd_start_pmc = powerpc_start_pmc; | pcd->pcd_start_pmc = powerpc_start_pmc; | ||||||||||||||||||||||||
pcd->pcd_stop_pmc = powerpc_stop_pmc; | pcd->pcd_stop_pmc = powerpc_stop_pmc; | ||||||||||||||||||||||||
pcd->pcd_get_config = powerpc_get_config; | pcd->pcd_get_config = powerpc_get_config; | ||||||||||||||||||||||||
pcd->pcd_config_pmc = powerpc_config_pmc; | pcd->pcd_config_pmc = powerpc_config_pmc; | ||||||||||||||||||||||||
pcd->pcd_describe = powerpc_describe; | pcd->pcd_describe = powerpc_describe; | ||||||||||||||||||||||||
pcd->pcd_read_pmc = powerpc_read_pmc; | pcd->pcd_read_pmc = powerpc_read_pmc; | ||||||||||||||||||||||||
pcd->pcd_write_pmc = powerpc_write_pmc; | pcd->pcd_write_pmc = powerpc_write_pmc; | ||||||||||||||||||||||||
pmc_mdep->pmd_npmc += POWER8_MAX_PMCS; | pmc_mdep->pmd_npmc += POWER8_MAX_PMCS; | ||||||||||||||||||||||||
pmc_mdep->pmd_intr = powerpc_pmc_intr; | pmc_mdep->pmd_intr = powerpc_pmc_intr; | ||||||||||||||||||||||||
ppc_event_codes = power8_event_codes; | |||||||||||||||||||||||||
ppc_event_codes_size = power8_event_codes_size; | ppc_event_codes_size = power8_event_codes_size; | ||||||||||||||||||||||||
Done Inline ActionsDid you mean to add this back? It doesn't appear necessary. mhorne: Did you mean to add this back? It doesn't appear necessary. | |||||||||||||||||||||||||
Done Inline ActionsYes, otherwise I would need to remove the enum events as they are not used anywhere. leonardo.bianconi_eldorado.org.br: Yes, otherwise I would need to remove the enum events as they are not used anywhere. | |||||||||||||||||||||||||
Done Inline ActionsGot it, thanks :) mhorne: Got it, thanks :) | |||||||||||||||||||||||||
ppc_event_first = PMC_EV_POWER8_FIRST; | |||||||||||||||||||||||||
ppc_event_last = PMC_EV_POWER8_LAST; | |||||||||||||||||||||||||
ppc_max_pmcs = POWER8_MAX_PMCS; | ppc_max_pmcs = POWER8_MAX_PMCS; | ||||||||||||||||||||||||
powerpc_set_pmc = power8_set_pmc; | powerpc_set_pmc = power8_set_pmc; | ||||||||||||||||||||||||
powerpc_pmcn_read = powerpc_pmcn_read_default; | powerpc_pmcn_read = powerpc_pmcn_read_default; | ||||||||||||||||||||||||
powerpc_pmcn_write = powerpc_pmcn_write_default; | powerpc_pmcn_write = powerpc_pmcn_write_default; | ||||||||||||||||||||||||
powerpc_resume_pmc = power8_resume_pmc; | powerpc_resume_pmc = power8_resume_pmc; | ||||||||||||||||||||||||
return (0); | return (0); | ||||||||||||||||||||||||
} | } |
I would suggest splitting the removal of these events into a separate change.