]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hwpmc/hwpmc_power8.c
zfs: merge openzfs/zfs@043c6ee3b
[FreeBSD/FreeBSD.git] / sys / dev / hwpmc / hwpmc_power8.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 Justin Hibbits
5  * Copyright (c) 2020 Leandro Lupori
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 #include <sys/param.h>
32 #include <sys/pmc.h>
33 #include <sys/pmckern.h>
34 #include <sys/systm.h>
35
36 #include <machine/pmc_mdep.h>
37 #include <machine/spr.h>
38 #include <machine/cpu.h>
39
40 #include "hwpmc_powerpc.h"
41
42 #define POWER8_MAX_PMCS         6
43
44 #define PM_EVENT_CODE(pe)       (pe & 0xffff)
45 #define PM_EVENT_COUNTER(pe)    ((pe >> 16) & 0xffff)
46
47 #define PM_CYC                  0x1e
48 #define PM_INST_CMPL            0x02
49
50 static void
51 power8_set_pmc(int cpu, int ri, int config)
52 {
53         register_t mmcr;
54
55         /* Select event */
56         switch (ri) {
57         case 0:
58         case 1:
59         case 2:
60         case 3:
61                 mmcr = mfspr(SPR_MMCR1);
62                 mmcr &= ~SPR_MMCR1_P8_PMCNSEL_MASK(ri);
63                 mmcr |= SPR_MMCR1_P8_PMCNSEL(ri, config & ~POWERPC_PMC_ENABLE);
64                 mtspr(SPR_MMCR1, mmcr);
65                 break;
66         }
67
68         /*
69          * By default, freeze counter in all states.
70          * If counter is being started, unfreeze it in selected states.
71          */
72         mmcr = mfspr(SPR_MMCR2) | SPR_MMCR2_FCNHSP(ri);
73         if (config != PMCN_NONE) {
74                 if (config & POWERPC_PMC_USER_ENABLE)
75                         mmcr &= ~(SPR_MMCR2_FCNP0(ri) |
76                             SPR_MMCR2_FCNP1(ri));
77                 if (config & POWERPC_PMC_KERNEL_ENABLE)
78                         mmcr &= ~(SPR_MMCR2_FCNH(ri) |
79                             SPR_MMCR2_FCNS(ri));
80         }
81         mtspr(SPR_MMCR2, mmcr);
82 }
83
84 static int
85 power8_pcpu_init(struct pmc_mdep *md, int cpu)
86 {
87         register_t mmcr0;
88         int i;
89
90         powerpc_pcpu_init(md, cpu);
91
92         /* Freeze all counters before modifying PMC registers */
93         mmcr0 = mfspr(SPR_MMCR0) | SPR_MMCR0_FC;
94         mtspr(SPR_MMCR0, mmcr0);
95
96         /*
97          * Now setup MMCR0:
98          *  - PMAO=0: clear alerts
99          *  - FCPC=0, FCP=0: don't freeze counters in problem state
100          *  - FCECE: Freeze Counters on Enabled Condition or Event
101          *  - PMC1CE/PMCNCE: PMC1/N Condition Enable
102          */
103         mmcr0 &= ~(SPR_MMCR0_PMAO | SPR_MMCR0_FCPC | SPR_MMCR0_FCP);
104         mmcr0 |= SPR_MMCR0_FCECE | SPR_MMCR0_PMC1CE | SPR_MMCR0_PMCNCE;
105         mtspr(SPR_MMCR0, mmcr0);
106
107         /* Clear all PMCs to prevent enabled condition interrupts */
108         for (i = 0; i < POWER8_MAX_PMCS; i++)
109                 powerpc_pmcn_write(i, 0);
110
111         /* Disable events in PMCs 1-4 */
112         mtspr(SPR_MMCR1, mfspr(SPR_MMCR1) & ~SPR_MMCR1_P8_PMCSEL_ALL);
113
114         /* Freeze each counter, in all states */
115         mtspr(SPR_MMCR2, mfspr(SPR_MMCR2) |
116             SPR_MMCR2_FCNHSP(0) | SPR_MMCR2_FCNHSP(1) | SPR_MMCR2_FCNHSP(2) |
117             SPR_MMCR2_FCNHSP(3) | SPR_MMCR2_FCNHSP(4) | SPR_MMCR2_FCNHSP(5));
118
119         /* Enable interrupts, unset global freeze */
120         mmcr0 &= ~SPR_MMCR0_FC;
121         mmcr0 |= SPR_MMCR0_PMAE;
122         mtspr(SPR_MMCR0, mmcr0);
123         return (0);
124 }
125
126 static int
127 power8_pcpu_fini(struct pmc_mdep *md, int cpu)
128 {
129         register_t mmcr0;
130
131         /* Freeze counters, disable interrupts */
132         mmcr0 = mfspr(SPR_MMCR0);
133         mmcr0 &= ~SPR_MMCR0_PMAE;
134         mmcr0 |= SPR_MMCR0_FC;
135         mtspr(SPR_MMCR0, mmcr0);
136
137         return (powerpc_pcpu_fini(md, cpu));
138 }
139
140 static void
141 power8_resume_pmc(bool ie)
142 {
143         register_t mmcr0;
144
145         /* Unfreeze counters and re-enable PERF exceptions if requested. */
146         mmcr0 = mfspr(SPR_MMCR0);
147         mmcr0 &= ~(SPR_MMCR0_FC | SPR_MMCR0_PMAO | SPR_MMCR0_PMAE);
148         if (ie)
149                 mmcr0 |= SPR_MMCR0_PMAE;
150         mtspr(SPR_MMCR0, mmcr0);
151 }
152
153 static int
154 power8_allocate_pmc(int cpu, int ri, struct pmc *pm,
155         const struct pmc_op_pmcallocate *a)
156 {
157         uint32_t caps, config, counter, pe;
158
159         KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
160             ("[powerpc,%d] illegal CPU value %d", __LINE__, cpu));
161         KASSERT(ri >= 0 && ri < ppc_max_pmcs,
162             ("[powerpc,%d] illegal row index %d", __LINE__, ri));
163
164         pe = a->pm_md.pm_event;
165         counter = PM_EVENT_COUNTER(pe);
166         config = PM_EVENT_CODE(pe);
167
168         if (a->pm_class != PMC_CLASS_POWER8)
169                 return (EINVAL);
170
171         if ((a->pm_flags & PMC_F_EV_PMU) == 0)
172                 return (EINVAL);
173
174         /*
175          * PMC5 and PMC6 are not programmable and always count instructions
176          * completed and cycles, respectively.
177          *
178          * When counter is 0 any of the 4 programmable PMCs may be used for
179          * the specified event, otherwise it must match ri + 1.
180          */
181         if (counter == 0 && config == PM_INST_CMPL)
182                 counter = 5;
183         else if (counter == 0 && config == PM_CYC)
184                 counter = 6;
185         else if (counter > 4)
186                 return (EINVAL);
187
188         if (counter != 0 && counter != ri + 1)
189                 return (EINVAL);
190
191         caps = a->pm_caps;
192
193         if (caps & PMC_CAP_SYSTEM)
194                 config |= POWERPC_PMC_KERNEL_ENABLE;
195         if (caps & PMC_CAP_USER)
196                 config |= POWERPC_PMC_USER_ENABLE;
197         if ((caps & (PMC_CAP_USER | PMC_CAP_SYSTEM)) == 0)
198                 config |= POWERPC_PMC_ENABLE;
199
200         pm->pm_md.pm_powerpc.pm_powerpc_evsel = config;
201
202         PMCDBG3(MDP,ALL,1,"powerpc-allocate cpu=%d ri=%d -> config=0x%x",
203             cpu, ri, config);
204         return (0);
205 }
206
207 int
208 pmc_power8_initialize(struct pmc_mdep *pmc_mdep)
209 {
210         struct pmc_classdep *pcd;
211
212         pmc_mdep->pmd_cputype = PMC_CPU_PPC_POWER8;
213
214         pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_POWERPC];
215         pcd->pcd_caps  = POWERPC_PMC_CAPS;
216         pcd->pcd_class = PMC_CLASS_POWER8;
217         pcd->pcd_num   = POWER8_MAX_PMCS;
218         pcd->pcd_ri    = pmc_mdep->pmd_npmc;
219         pcd->pcd_width = 32;
220
221         pcd->pcd_pcpu_init      = power8_pcpu_init;
222         pcd->pcd_pcpu_fini      = power8_pcpu_fini;
223         pcd->pcd_allocate_pmc   = power8_allocate_pmc;
224         pcd->pcd_release_pmc    = powerpc_release_pmc;
225         pcd->pcd_start_pmc      = powerpc_start_pmc;
226         pcd->pcd_stop_pmc       = powerpc_stop_pmc;
227         pcd->pcd_get_config     = powerpc_get_config;
228         pcd->pcd_config_pmc     = powerpc_config_pmc;
229         pcd->pcd_describe       = powerpc_describe;
230         pcd->pcd_read_pmc       = powerpc_read_pmc;
231         pcd->pcd_write_pmc      = powerpc_write_pmc;
232
233         pmc_mdep->pmd_npmc     += POWER8_MAX_PMCS;
234         pmc_mdep->pmd_intr      = powerpc_pmc_intr;
235
236         ppc_max_pmcs = POWER8_MAX_PMCS;
237
238         powerpc_set_pmc = power8_set_pmc;
239         powerpc_pmcn_read = powerpc_pmcn_read_default;
240         powerpc_pmcn_write = powerpc_pmcn_write_default;
241         powerpc_resume_pmc = power8_resume_pmc;
242
243         return (0);
244 }