xref: /freebsd-src/sys/dev/hwpmc/hwpmc_cmn600.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
159191f35SAleksandr Rybalko /*-
2753c7fc9SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
359191f35SAleksandr Rybalko  *
459191f35SAleksandr Rybalko  * Copyright (c) 2003-2008 Joseph Koshy
559191f35SAleksandr Rybalko  * Copyright (c) 2007 The FreeBSD Foundation
6d90188efSJohn Baldwin  * Copyright (c) 2021 ARM Ltd
759191f35SAleksandr Rybalko  *
859191f35SAleksandr Rybalko  * Portions of this software were developed by A. Joseph Koshy under
959191f35SAleksandr Rybalko  * sponsorship from the FreeBSD Foundation and Google, Inc.
1059191f35SAleksandr Rybalko  *
1159191f35SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
1259191f35SAleksandr Rybalko  * modification, are permitted provided that the following conditions
1359191f35SAleksandr Rybalko  * are met:
1459191f35SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1559191f35SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1659191f35SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1759191f35SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1859191f35SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1959191f35SAleksandr Rybalko  *
2059191f35SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2159191f35SAleksandr Rybalko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259191f35SAleksandr Rybalko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359191f35SAleksandr Rybalko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2459191f35SAleksandr Rybalko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559191f35SAleksandr Rybalko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659191f35SAleksandr Rybalko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759191f35SAleksandr Rybalko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859191f35SAleksandr Rybalko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959191f35SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059191f35SAleksandr Rybalko  * SUCH DAMAGE.
3159191f35SAleksandr Rybalko  */
3259191f35SAleksandr Rybalko 
3359191f35SAleksandr Rybalko /* Arm CoreLink CMN-600 Coherent Mesh Network PMU Driver */
3459191f35SAleksandr Rybalko 
3559191f35SAleksandr Rybalko #include <sys/param.h>
3659191f35SAleksandr Rybalko #include <sys/lock.h>
3759191f35SAleksandr Rybalko #include <sys/malloc.h>
3859191f35SAleksandr Rybalko #include <sys/module.h>
3959191f35SAleksandr Rybalko #include <sys/mutex.h>
4059191f35SAleksandr Rybalko #include <sys/pmc.h>
4159191f35SAleksandr Rybalko #include <sys/pmckern.h>
4259191f35SAleksandr Rybalko #include <sys/systm.h>
4359191f35SAleksandr Rybalko 
4459191f35SAleksandr Rybalko #include <machine/cmn600_reg.h>
4559191f35SAleksandr Rybalko 
4659191f35SAleksandr Rybalko struct cmn600_descr {
4759191f35SAleksandr Rybalko 	struct pmc_descr pd_descr;  /* "base class" */
4859191f35SAleksandr Rybalko 	void		*pd_rw_arg; /* Argument to use with read/write */
4959191f35SAleksandr Rybalko 	struct pmc	*pd_pmc;
5059191f35SAleksandr Rybalko 	struct pmc_hw	*pd_phw;
5159191f35SAleksandr Rybalko 	uint32_t	 pd_nodeid;
5259191f35SAleksandr Rybalko 	int32_t		 pd_node_type;
5359191f35SAleksandr Rybalko 	int		 pd_local_counter;
5459191f35SAleksandr Rybalko 
5559191f35SAleksandr Rybalko };
5659191f35SAleksandr Rybalko 
5759191f35SAleksandr Rybalko static struct cmn600_descr **cmn600_pmcdesc;
5859191f35SAleksandr Rybalko 
5959191f35SAleksandr Rybalko static struct cmn600_pmc cmn600_pmcs[CMN600_UNIT_MAX];
6059191f35SAleksandr Rybalko static int cmn600_units = 0;
6159191f35SAleksandr Rybalko 
6259191f35SAleksandr Rybalko static inline struct cmn600_descr *
cmn600desc(int ri)6359191f35SAleksandr Rybalko cmn600desc(int ri)
6459191f35SAleksandr Rybalko {
6559191f35SAleksandr Rybalko 
6659191f35SAleksandr Rybalko 	return (cmn600_pmcdesc[ri]);
6759191f35SAleksandr Rybalko }
6859191f35SAleksandr Rybalko 
6959191f35SAleksandr Rybalko static inline int
class_ri2unit(int ri)7059191f35SAleksandr Rybalko class_ri2unit(int ri)
7159191f35SAleksandr Rybalko {
7259191f35SAleksandr Rybalko 
7359191f35SAleksandr Rybalko 	return (ri / CMN600_COUNTERS_N);
7459191f35SAleksandr Rybalko }
7559191f35SAleksandr Rybalko 
7659191f35SAleksandr Rybalko #define	EVENCNTR(x)	(((x) >> POR_DT_PMEVCNT_EVENCNT_SHIFT) << \
7759191f35SAleksandr Rybalko     POR_DTM_PMEVCNT_CNTR_WIDTH)
7859191f35SAleksandr Rybalko #define	ODDCNTR(x)	(((x) >> POR_DT_PMEVCNT_ODDCNT_SHIFT) << \
7959191f35SAleksandr Rybalko     POR_DTM_PMEVCNT_CNTR_WIDTH)
8059191f35SAleksandr Rybalko 
8159191f35SAleksandr Rybalko static uint64_t
cmn600_pmu_readcntr(void * arg,u_int nodeid,u_int xpcntr,u_int dtccntr,u_int width)8259191f35SAleksandr Rybalko cmn600_pmu_readcntr(void *arg, u_int nodeid, u_int xpcntr, u_int dtccntr,
8359191f35SAleksandr Rybalko     u_int width)
8459191f35SAleksandr Rybalko {
8559191f35SAleksandr Rybalko 	uint64_t dtcval, xpval;
8659191f35SAleksandr Rybalko 
8759191f35SAleksandr Rybalko 	KASSERT(xpcntr < 4, ("[cmn600,%d] XP counter number %d is too big."
8859191f35SAleksandr Rybalko 	    " Max: 3", __LINE__, xpcntr));
8959191f35SAleksandr Rybalko 	KASSERT(dtccntr < 8, ("[cmn600,%d] Global counter number %d is too"
9059191f35SAleksandr Rybalko 	    " big. Max: 7", __LINE__, dtccntr));
9159191f35SAleksandr Rybalko 
9259191f35SAleksandr Rybalko 	dtcval = pmu_cmn600_rd8(arg, nodeid, NODE_TYPE_DTC,
9359191f35SAleksandr Rybalko 	    POR_DT_PMEVCNT(dtccntr >> 1));
9459191f35SAleksandr Rybalko 	if (width == 4) {
9559191f35SAleksandr Rybalko 		dtcval = (dtccntr & 1) ? ODDCNTR(dtcval) : EVENCNTR(dtcval);
9659191f35SAleksandr Rybalko 		dtcval &= 0xffffffff0000UL;
9759191f35SAleksandr Rybalko 	} else
9859191f35SAleksandr Rybalko 		dtcval <<= POR_DTM_PMEVCNT_CNTR_WIDTH;
9959191f35SAleksandr Rybalko 
10059191f35SAleksandr Rybalko 	xpval = pmu_cmn600_rd8(arg, nodeid, NODE_TYPE_XP, POR_DTM_PMEVCNT);
10159191f35SAleksandr Rybalko 	xpval >>= xpcntr * POR_DTM_PMEVCNT_CNTR_WIDTH;
10259191f35SAleksandr Rybalko 	xpval &= 0xffffUL;
10359191f35SAleksandr Rybalko 	return (dtcval | xpval);
10459191f35SAleksandr Rybalko }
10559191f35SAleksandr Rybalko 
10659191f35SAleksandr Rybalko static void
cmn600_pmu_writecntr(void * arg,u_int nodeid,u_int xpcntr,u_int dtccntr,u_int width,uint64_t val)10759191f35SAleksandr Rybalko cmn600_pmu_writecntr(void *arg, u_int nodeid, u_int xpcntr, u_int dtccntr,
10859191f35SAleksandr Rybalko     u_int width, uint64_t val)
10959191f35SAleksandr Rybalko {
11059191f35SAleksandr Rybalko 	int shift;
11159191f35SAleksandr Rybalko 
11259191f35SAleksandr Rybalko 	KASSERT(xpcntr < 4, ("[cmn600,%d] XP counter number %d is too big."
11359191f35SAleksandr Rybalko 	    " Max: 3", __LINE__, xpcntr));
11459191f35SAleksandr Rybalko 	KASSERT(dtccntr < 8, ("[cmn600,%d] Global counter number %d is too"
11559191f35SAleksandr Rybalko 	    " big. Max: 7", __LINE__, dtccntr));
11659191f35SAleksandr Rybalko 
11759191f35SAleksandr Rybalko 	if (width == 4) {
11859191f35SAleksandr Rybalko 		shift = (dtccntr & 1) ? POR_DT_PMEVCNT_ODDCNT_SHIFT :
11959191f35SAleksandr Rybalko 		    POR_DT_PMEVCNT_EVENCNT_SHIFT;
12059191f35SAleksandr Rybalko 		pmu_cmn600_md8(arg, nodeid, NODE_TYPE_DTC,
12159191f35SAleksandr Rybalko 		    POR_DT_PMEVCNT(dtccntr >> 1), 0xffffffffUL << shift,
12259191f35SAleksandr Rybalko 		    ((val >> POR_DTM_PMEVCNT_CNTR_WIDTH) & 0xffffffff) << shift);
12359191f35SAleksandr Rybalko 	} else
12459191f35SAleksandr Rybalko 		pmu_cmn600_wr8(arg, nodeid, NODE_TYPE_DTC,
12559191f35SAleksandr Rybalko 		    POR_DT_PMEVCNT(dtccntr & ~0x1), val >>
12659191f35SAleksandr Rybalko 		    POR_DTM_PMEVCNT_CNTR_WIDTH);
12759191f35SAleksandr Rybalko 
12859191f35SAleksandr Rybalko 	shift = xpcntr * POR_DTM_PMEVCNT_CNTR_WIDTH;
12959191f35SAleksandr Rybalko 	val &= 0xffffUL;
13059191f35SAleksandr Rybalko 	pmu_cmn600_md8(arg, nodeid, NODE_TYPE_XP, POR_DTM_PMEVCNT,
13159191f35SAleksandr Rybalko 	    0xffffUL << shift, val << shift);
13259191f35SAleksandr Rybalko }
13359191f35SAleksandr Rybalko 
13459191f35SAleksandr Rybalko #undef	EVENCNTR
13559191f35SAleksandr Rybalko #undef	ODDCNTR
13659191f35SAleksandr Rybalko 
13759191f35SAleksandr Rybalko /*
13859191f35SAleksandr Rybalko  * read a pmc register
13959191f35SAleksandr Rybalko  */
14059191f35SAleksandr Rybalko static int
cmn600_read_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t * v)141*39f92a76SMitchell Horne cmn600_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v)
14259191f35SAleksandr Rybalko {
14359191f35SAleksandr Rybalko 	int counter, local_counter, nodeid;
14459191f35SAleksandr Rybalko 	struct cmn600_descr *desc;
14559191f35SAleksandr Rybalko 	void *arg;
14659191f35SAleksandr Rybalko 
14759191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
14859191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
14959191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
15059191f35SAleksandr Rybalko 	    ri));
15159191f35SAleksandr Rybalko 
15259191f35SAleksandr Rybalko 	counter = ri % CMN600_COUNTERS_N;
15359191f35SAleksandr Rybalko 	desc = cmn600desc(ri);
15459191f35SAleksandr Rybalko 	arg = desc->pd_rw_arg;
15559191f35SAleksandr Rybalko 	nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid;
15659191f35SAleksandr Rybalko 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
15759191f35SAleksandr Rybalko 
15859191f35SAleksandr Rybalko 	*v = cmn600_pmu_readcntr(arg, nodeid, local_counter, counter, 4);
15959191f35SAleksandr Rybalko 	PMCDBG3(MDP, REA, 2, "%s id=%d -> %jd", __func__, ri, *v);
16059191f35SAleksandr Rybalko 
16159191f35SAleksandr Rybalko 	return (0);
16259191f35SAleksandr Rybalko }
16359191f35SAleksandr Rybalko 
16459191f35SAleksandr Rybalko /*
16559191f35SAleksandr Rybalko  * Write a pmc register.
16659191f35SAleksandr Rybalko  */
16759191f35SAleksandr Rybalko static int
cmn600_write_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t v)168*39f92a76SMitchell Horne cmn600_write_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t v)
16959191f35SAleksandr Rybalko {
17059191f35SAleksandr Rybalko 	int counter, local_counter, nodeid;
17159191f35SAleksandr Rybalko 	struct cmn600_descr *desc;
17259191f35SAleksandr Rybalko 	void *arg;
17359191f35SAleksandr Rybalko 
17459191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
17559191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
17659191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
17759191f35SAleksandr Rybalko 	    ri));
17859191f35SAleksandr Rybalko 
17959191f35SAleksandr Rybalko 	counter = ri % CMN600_COUNTERS_N;
18059191f35SAleksandr Rybalko 	desc = cmn600desc(ri);
18159191f35SAleksandr Rybalko 	arg = desc->pd_rw_arg;
18259191f35SAleksandr Rybalko 	nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid;
18359191f35SAleksandr Rybalko 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
18459191f35SAleksandr Rybalko 
18559191f35SAleksandr Rybalko 	KASSERT(pm != NULL,
18659191f35SAleksandr Rybalko 	    ("[cmn600,%d] PMC not owned (cpu%d,pmc%d)", __LINE__,
18759191f35SAleksandr Rybalko 		cpu, ri));
18859191f35SAleksandr Rybalko 
18959191f35SAleksandr Rybalko 	PMCDBG4(MDP, WRI, 1, "%s cpu=%d ri=%d v=%jx", __func__, cpu, ri, v);
19059191f35SAleksandr Rybalko 
19159191f35SAleksandr Rybalko 	cmn600_pmu_writecntr(arg, nodeid, local_counter, counter, 4, v);
19259191f35SAleksandr Rybalko 	return (0);
19359191f35SAleksandr Rybalko }
19459191f35SAleksandr Rybalko 
19559191f35SAleksandr Rybalko /*
19659191f35SAleksandr Rybalko  * configure hardware pmc according to the configuration recorded in
19759191f35SAleksandr Rybalko  * pmc 'pm'.
19859191f35SAleksandr Rybalko  */
19959191f35SAleksandr Rybalko static int
cmn600_config_pmc(int cpu,int ri,struct pmc * pm)20059191f35SAleksandr Rybalko cmn600_config_pmc(int cpu, int ri, struct pmc *pm)
20159191f35SAleksandr Rybalko {
20259191f35SAleksandr Rybalko 	struct pmc_hw *phw;
20359191f35SAleksandr Rybalko 
20459191f35SAleksandr Rybalko 	PMCDBG4(MDP, CFG, 1, "%s cpu=%d ri=%d pm=%p", __func__, cpu, ri, pm);
20559191f35SAleksandr Rybalko 
20659191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
20759191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
20859191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
20959191f35SAleksandr Rybalko 	    ri));
21059191f35SAleksandr Rybalko 
21159191f35SAleksandr Rybalko 	phw = cmn600desc(ri)->pd_phw;
21259191f35SAleksandr Rybalko 
21359191f35SAleksandr Rybalko 	KASSERT(pm == NULL || phw->phw_pmc == NULL,
21459191f35SAleksandr Rybalko 	    ("[cmn600,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
21559191f35SAleksandr Rybalko 		__LINE__, pm, phw->phw_pmc));
21659191f35SAleksandr Rybalko 
21759191f35SAleksandr Rybalko 	phw->phw_pmc = pm;
21859191f35SAleksandr Rybalko 	return (0);
21959191f35SAleksandr Rybalko }
22059191f35SAleksandr Rybalko 
22159191f35SAleksandr Rybalko /*
22259191f35SAleksandr Rybalko  * Retrieve a configured PMC pointer from hardware state.
22359191f35SAleksandr Rybalko  */
22459191f35SAleksandr Rybalko static int
cmn600_get_config(int cpu,int ri,struct pmc ** ppm)22559191f35SAleksandr Rybalko cmn600_get_config(int cpu, int ri, struct pmc **ppm)
22659191f35SAleksandr Rybalko {
22759191f35SAleksandr Rybalko 
22859191f35SAleksandr Rybalko 	*ppm = cmn600desc(ri)->pd_phw->phw_pmc;
22959191f35SAleksandr Rybalko 
23059191f35SAleksandr Rybalko 	return (0);
23159191f35SAleksandr Rybalko }
23259191f35SAleksandr Rybalko 
23359191f35SAleksandr Rybalko #define	CASE_DN_VER_EVT(n, id) case PMC_EV_CMN600_PMU_ ## n: { *event = id; \
23459191f35SAleksandr Rybalko 	return (0); }
23559191f35SAleksandr Rybalko static int
cmn600_map_ev2event(int ev,int rev,int * node_type,uint8_t * event)23659191f35SAleksandr Rybalko cmn600_map_ev2event(int ev, int rev, int *node_type, uint8_t *event)
23759191f35SAleksandr Rybalko {
23859191f35SAleksandr Rybalko 	if (ev < PMC_EV_CMN600_PMU_dn_rxreq_dvmop ||
23959191f35SAleksandr Rybalko 	    ev > PMC_EV_CMN600_PMU_rni_rdb_ord)
24059191f35SAleksandr Rybalko 		return (EINVAL);
24159191f35SAleksandr Rybalko 	if (ev <= PMC_EV_CMN600_PMU_dn_rxreq_trk_full) {
24259191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_DVM;
24359191f35SAleksandr Rybalko 		if (rev < 0x200) {
24459191f35SAleksandr Rybalko 			switch (ev) {
24559191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_dvmop, 1);
24659191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_dvmsync, 2);
24759191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_dvmop_vmid_filtered, 3);
24859191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_retried, 4);
24959191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_trk_occupancy, 5);
25059191f35SAleksandr Rybalko 			}
25159191f35SAleksandr Rybalko 		} else {
25259191f35SAleksandr Rybalko 			switch (ev) {
25359191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_tlbi_dvmop, 0x01);
25459191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_bpi_dvmop, 0x02);
25559191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_pici_dvmop, 0x03);
25659191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_vivi_dvmop, 0x04);
25759191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_dvmsync, 0x05);
25859191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_dvmop_vmid_filtered, 0x06);
25959191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_dvmop_other_filtered, 0x07);
26059191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_retried, 0x08);
26159191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_snp_sent, 0x09);
26259191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_snp_stalled, 0x0a);
26359191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_trk_full, 0x0b);
26459191f35SAleksandr Rybalko 			CASE_DN_VER_EVT(dn_rxreq_trk_occupancy, 0x0c);
26559191f35SAleksandr Rybalko 			}
26659191f35SAleksandr Rybalko 		}
26759191f35SAleksandr Rybalko 		return (EINVAL);
26859191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_hnf_snp_fwded) {
26959191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_HN_F;
27059191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_hnf_cache_miss;
27159191f35SAleksandr Rybalko 		return (0);
27259191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_hni_pcie_serialization) {
27359191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_HN_I;
27459191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_hni_rrt_rd_occ_cnt_ovfl;
27559191f35SAleksandr Rybalko 		return (0);
27659191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_xp_partial_dat_flit) {
27759191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_XP;
27859191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_xp_txflit_valid;
27959191f35SAleksandr Rybalko 		return (0);
28059191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_sbsx_txrsp_stall) {
28159191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_SBSX;
28259191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_sbsx_rd_req;
28359191f35SAleksandr Rybalko 		return (0);
28459191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_rnd_rdb_ord) {
28559191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_RN_D;
28659191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_rnd_s0_rdata_beats;
28759191f35SAleksandr Rybalko 		return (0);
28859191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_rni_rdb_ord) {
28959191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_RN_I;
29059191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_rni_s0_rdata_beats;
29159191f35SAleksandr Rybalko 		return (0);
29259191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_cxha_snphaz_occ) {
29359191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_CXHA;
29459191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_cxha_rddatbyp;
29559191f35SAleksandr Rybalko 		return (0);
29659191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_cxra_ext_dat_stall) {
29759191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_CXRA;
29859191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_cxra_req_trk_occ;
29959191f35SAleksandr Rybalko 		return (0);
30059191f35SAleksandr Rybalko 	} else if (ev <= PMC_EV_CMN600_PMU_cxla_avg_latency_form_tx_tlp) {
30159191f35SAleksandr Rybalko 		*node_type = NODE_TYPE_CXLA;
30259191f35SAleksandr Rybalko 		*event = ev - PMC_EV_CMN600_PMU_cxla_rx_tlp_link0;
30359191f35SAleksandr Rybalko 		return (0);
30459191f35SAleksandr Rybalko 	}
30559191f35SAleksandr Rybalko 	return (EINVAL);
30659191f35SAleksandr Rybalko }
30759191f35SAleksandr Rybalko 
30859191f35SAleksandr Rybalko /*
30959191f35SAleksandr Rybalko  * Check if a given allocation is feasible.
31059191f35SAleksandr Rybalko  */
31159191f35SAleksandr Rybalko 
31259191f35SAleksandr Rybalko static int
cmn600_allocate_pmc(int cpu,int ri,struct pmc * pm,const struct pmc_op_pmcallocate * a)31359191f35SAleksandr Rybalko cmn600_allocate_pmc(int cpu, int ri, struct pmc *pm,
31459191f35SAleksandr Rybalko     const struct pmc_op_pmcallocate *a)
31559191f35SAleksandr Rybalko {
31659191f35SAleksandr Rybalko 	struct cmn600_descr *desc;
31759191f35SAleksandr Rybalko 	const struct pmc_descr *pd;
31859191f35SAleksandr Rybalko 	uint64_t caps __unused;
31959191f35SAleksandr Rybalko 	int local_counter, node_type;
32059191f35SAleksandr Rybalko 	enum pmc_event pe;
32159191f35SAleksandr Rybalko 	void *arg;
32259191f35SAleksandr Rybalko 	uint8_t e;
32359191f35SAleksandr Rybalko 	int err;
32459191f35SAleksandr Rybalko 
32559191f35SAleksandr Rybalko 	(void) cpu;
32659191f35SAleksandr Rybalko 
32759191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
32859191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
32959191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
33059191f35SAleksandr Rybalko 	    ri));
33159191f35SAleksandr Rybalko 
33259191f35SAleksandr Rybalko 	desc = cmn600desc(ri);
33359191f35SAleksandr Rybalko 	arg = desc->pd_rw_arg;
33459191f35SAleksandr Rybalko 	pd = &desc->pd_descr;
33559191f35SAleksandr Rybalko 	if (cmn600_pmcs[class_ri2unit(ri)].domain != pcpu_find(cpu)->pc_domain)
33659191f35SAleksandr Rybalko 		return (EINVAL);
33759191f35SAleksandr Rybalko 
33859191f35SAleksandr Rybalko 	/* check class match */
33959191f35SAleksandr Rybalko 	if (pd->pd_class != a->pm_class)
34059191f35SAleksandr Rybalko 		return (EINVAL);
34159191f35SAleksandr Rybalko 
34259191f35SAleksandr Rybalko 	caps = pm->pm_caps;
34359191f35SAleksandr Rybalko 
34459191f35SAleksandr Rybalko 	PMCDBG3(MDP, ALL, 1, "%s ri=%d caps=0x%x", __func__, ri, caps);
34559191f35SAleksandr Rybalko 
34659191f35SAleksandr Rybalko 	pe = a->pm_ev;
34759191f35SAleksandr Rybalko 	err = cmn600_map_ev2event(pe, pmu_cmn600_rev(arg), &node_type, &e);
34859191f35SAleksandr Rybalko 	if (err != 0)
34959191f35SAleksandr Rybalko 		return (err);
35059191f35SAleksandr Rybalko 	err = pmu_cmn600_alloc_localpmc(arg,
35159191f35SAleksandr Rybalko 	    a->pm_md.pm_cmn600.pma_cmn600_nodeid, node_type, &local_counter);
35259191f35SAleksandr Rybalko 	if (err != 0)
35359191f35SAleksandr Rybalko 		return (err);
35459191f35SAleksandr Rybalko 
35559191f35SAleksandr Rybalko 	pm->pm_md.pm_cmn600.pm_cmn600_config =
35659191f35SAleksandr Rybalko 	    a->pm_md.pm_cmn600.pma_cmn600_config;
35759191f35SAleksandr Rybalko 	pm->pm_md.pm_cmn600.pm_cmn600_occupancy =
35859191f35SAleksandr Rybalko 	    a->pm_md.pm_cmn600.pma_cmn600_occupancy;
35959191f35SAleksandr Rybalko 	desc->pd_nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid =
36059191f35SAleksandr Rybalko 	    a->pm_md.pm_cmn600.pma_cmn600_nodeid;
36159191f35SAleksandr Rybalko 	desc->pd_node_type = pm->pm_md.pm_cmn600.pm_cmn600_node_type =
36259191f35SAleksandr Rybalko 	    node_type;
36359191f35SAleksandr Rybalko 	pm->pm_md.pm_cmn600.pm_cmn600_event = e;
36459191f35SAleksandr Rybalko 	desc->pd_local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter =
36559191f35SAleksandr Rybalko 	    local_counter;
36659191f35SAleksandr Rybalko 
36759191f35SAleksandr Rybalko 	return (0);
36859191f35SAleksandr Rybalko }
36959191f35SAleksandr Rybalko 
37059191f35SAleksandr Rybalko /* Release machine dependent state associated with a PMC. */
37159191f35SAleksandr Rybalko 
37259191f35SAleksandr Rybalko static int
cmn600_release_pmc(int cpu,int ri,struct pmc * pmc)37359191f35SAleksandr Rybalko cmn600_release_pmc(int cpu, int ri, struct pmc *pmc)
37459191f35SAleksandr Rybalko {
37559191f35SAleksandr Rybalko 	struct cmn600_descr *desc;
37659191f35SAleksandr Rybalko 	struct pmc_hw *phw;
377d78bef0eSBjoern A. Zeeb 	struct pmc *pm __diagused;
37859191f35SAleksandr Rybalko 	int err;
37959191f35SAleksandr Rybalko 
38059191f35SAleksandr Rybalko 	(void) pmc;
38159191f35SAleksandr Rybalko 
38259191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
38359191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
38459191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
38559191f35SAleksandr Rybalko 	    ri));
38659191f35SAleksandr Rybalko 
38759191f35SAleksandr Rybalko 	desc = cmn600desc(ri);
38859191f35SAleksandr Rybalko 	phw = desc->pd_phw;
38959191f35SAleksandr Rybalko 	pm  = phw->phw_pmc;
39059191f35SAleksandr Rybalko 	err = pmu_cmn600_free_localpmc(desc->pd_rw_arg, desc->pd_nodeid,
39159191f35SAleksandr Rybalko 	    desc->pd_node_type, desc->pd_local_counter);
39259191f35SAleksandr Rybalko 	if (err != 0)
39359191f35SAleksandr Rybalko 		return (err);
39459191f35SAleksandr Rybalko 
39559191f35SAleksandr Rybalko 	KASSERT(pm == NULL, ("[cmn600,%d] PHW pmc %p non-NULL", __LINE__, pm));
39659191f35SAleksandr Rybalko 
39759191f35SAleksandr Rybalko 	return (0);
39859191f35SAleksandr Rybalko }
39959191f35SAleksandr Rybalko 
40059191f35SAleksandr Rybalko static inline uint64_t
cmn600_encode_source(int node_type,int counter,int port,int sub)40159191f35SAleksandr Rybalko cmn600_encode_source(int node_type, int counter, int port, int sub)
40259191f35SAleksandr Rybalko {
40359191f35SAleksandr Rybalko 
40459191f35SAleksandr Rybalko 	/* Calculate pmevcnt0_input_sel based on list in Table 3-794. */
40559191f35SAleksandr Rybalko 	if (node_type == NODE_TYPE_XP)
40659191f35SAleksandr Rybalko 		return (0x4 | counter);
40759191f35SAleksandr Rybalko 
40859191f35SAleksandr Rybalko 	return (((port + 1) << 4) | (sub << 2) | counter);
40959191f35SAleksandr Rybalko }
41059191f35SAleksandr Rybalko 
41159191f35SAleksandr Rybalko /*
41259191f35SAleksandr Rybalko  * start a PMC.
41359191f35SAleksandr Rybalko  */
41459191f35SAleksandr Rybalko 
41559191f35SAleksandr Rybalko static int
cmn600_start_pmc(int cpu,int ri,struct pmc * pm)416*39f92a76SMitchell Horne cmn600_start_pmc(int cpu, int ri, struct pmc *pm)
41759191f35SAleksandr Rybalko {
41859191f35SAleksandr Rybalko 	int counter, local_counter, node_type, shift;
41959191f35SAleksandr Rybalko 	uint64_t config, occupancy, source, xp_pmucfg;
42059191f35SAleksandr Rybalko 	struct cmn600_descr *desc;
42159191f35SAleksandr Rybalko 	uint8_t event, port, sub;
42259191f35SAleksandr Rybalko 	uint16_t nodeid;
42359191f35SAleksandr Rybalko 	void *arg;
42459191f35SAleksandr Rybalko 
42559191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
42659191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
42759191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
42859191f35SAleksandr Rybalko 	    ri));
42959191f35SAleksandr Rybalko 
43059191f35SAleksandr Rybalko 	counter = ri % CMN600_COUNTERS_N;
43159191f35SAleksandr Rybalko 	desc = cmn600desc(ri);
43259191f35SAleksandr Rybalko 	arg = desc->pd_rw_arg;
43359191f35SAleksandr Rybalko 
43459191f35SAleksandr Rybalko 	PMCDBG3(MDP, STA, 1, "%s cpu=%d ri=%d", __func__, cpu, ri);
43559191f35SAleksandr Rybalko 
43659191f35SAleksandr Rybalko 	config = pm->pm_md.pm_cmn600.pm_cmn600_config;
43759191f35SAleksandr Rybalko 	occupancy = pm->pm_md.pm_cmn600.pm_cmn600_occupancy;
43859191f35SAleksandr Rybalko 	node_type = pm->pm_md.pm_cmn600.pm_cmn600_node_type;
43959191f35SAleksandr Rybalko 	event = pm->pm_md.pm_cmn600.pm_cmn600_event;
44059191f35SAleksandr Rybalko 	nodeid = pm->pm_md.pm_cmn600.pm_cmn600_nodeid;
44159191f35SAleksandr Rybalko 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
44259191f35SAleksandr Rybalko 	port = (nodeid >> 2) & 1;
44359191f35SAleksandr Rybalko 	sub = nodeid & 3;
44459191f35SAleksandr Rybalko 
44559191f35SAleksandr Rybalko 	switch (node_type) {
44659191f35SAleksandr Rybalko 	case NODE_TYPE_DVM:
44759191f35SAleksandr Rybalko 	case NODE_TYPE_HN_F:
44859191f35SAleksandr Rybalko 	case NODE_TYPE_CXHA:
44959191f35SAleksandr Rybalko 	case NODE_TYPE_CXRA:
45059191f35SAleksandr Rybalko 		pmu_cmn600_md8(arg, nodeid, node_type,
45159191f35SAleksandr Rybalko 		    CMN600_COMMON_PMU_EVENT_SEL,
45259191f35SAleksandr Rybalko 		    CMN600_COMMON_PMU_EVENT_SEL_OCC_MASK,
45359191f35SAleksandr Rybalko 		    occupancy << CMN600_COMMON_PMU_EVENT_SEL_OCC_SHIFT);
45459191f35SAleksandr Rybalko 		break;
45559191f35SAleksandr Rybalko 	case NODE_TYPE_XP:
45659191f35SAleksandr Rybalko 		/* Set PC and Interface.*/
45759191f35SAleksandr Rybalko 		event |= config;
45859191f35SAleksandr Rybalko 	}
45959191f35SAleksandr Rybalko 
46059191f35SAleksandr Rybalko 	/*
46159191f35SAleksandr Rybalko 	 * 5.5.1 Set up PMU counters
46259191f35SAleksandr Rybalko 	 * 1. Ensure that the NIDEN input is asserted. HW side. */
46359191f35SAleksandr Rybalko 	/* 2. Select event of target node for one of four outputs. */
46459191f35SAleksandr Rybalko 	pmu_cmn600_md8(arg, nodeid, node_type, CMN600_COMMON_PMU_EVENT_SEL,
46559191f35SAleksandr Rybalko 	    0xff << (local_counter * 8),
46659191f35SAleksandr Rybalko 	    event << (local_counter * 8));
46759191f35SAleksandr Rybalko 
46859191f35SAleksandr Rybalko 	xp_pmucfg = pmu_cmn600_rd8(arg, nodeid, NODE_TYPE_XP,
46959191f35SAleksandr Rybalko 	    POR_DTM_PMU_CONFIG);
47059191f35SAleksandr Rybalko 	/*
47159191f35SAleksandr Rybalko 	 * 3. configure XP to connect one of four target node outputs to local
47259191f35SAleksandr Rybalko 	 * counter.
47359191f35SAleksandr Rybalko 	 */
47459191f35SAleksandr Rybalko 	source = cmn600_encode_source(node_type, local_counter, port, sub);
47559191f35SAleksandr Rybalko 	shift = (local_counter * POR_DTM_PMU_CONFIG_VCNT_INPUT_SEL_WIDTH) +
47659191f35SAleksandr Rybalko 	    POR_DTM_PMU_CONFIG_VCNT_INPUT_SEL_SHIFT;
47759191f35SAleksandr Rybalko 	xp_pmucfg &= ~(0xffUL << shift);
47859191f35SAleksandr Rybalko 	xp_pmucfg |= source << shift;
47959191f35SAleksandr Rybalko 
48059191f35SAleksandr Rybalko 	/* 4. Pair with global counters A, B, C, ..., H. */
48159191f35SAleksandr Rybalko 	shift = (local_counter * 4) + 16;
48259191f35SAleksandr Rybalko 	xp_pmucfg &= ~(0xfUL << shift);
48359191f35SAleksandr Rybalko 	xp_pmucfg |= counter << shift;
48459191f35SAleksandr Rybalko 	/* Enable pairing.*/
48559191f35SAleksandr Rybalko 	xp_pmucfg |= 1 << (local_counter + 4);
48659191f35SAleksandr Rybalko 
48759191f35SAleksandr Rybalko 	/* 5. Combine local counters 0 with 1, 2 with 3 or all four. */
48859191f35SAleksandr Rybalko 	xp_pmucfg &= ~0xeUL;
48959191f35SAleksandr Rybalko 
49059191f35SAleksandr Rybalko 	/* 6. Enable XP's PMU function. */
49159191f35SAleksandr Rybalko 	xp_pmucfg |= POR_DTM_PMU_CONFIG_PMU_EN;
49259191f35SAleksandr Rybalko 	pmu_cmn600_wr8(arg, nodeid, NODE_TYPE_XP, POR_DTM_PMU_CONFIG, xp_pmucfg);
49359191f35SAleksandr Rybalko 	if (node_type == NODE_TYPE_CXLA)
49459191f35SAleksandr Rybalko 		pmu_cmn600_set8(arg, nodeid, NODE_TYPE_CXLA,
49559191f35SAleksandr Rybalko 		    POR_CXG_RA_CFG_CTL, EN_CXLA_PMUCMD_PROP);
49659191f35SAleksandr Rybalko 
49759191f35SAleksandr Rybalko 	/* 7. Enable DTM. */
49859191f35SAleksandr Rybalko 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_XP, POR_DTM_CONTROL,
49959191f35SAleksandr Rybalko 	    POR_DTM_CONTROL_DTM_ENABLE);
50059191f35SAleksandr Rybalko 
50159191f35SAleksandr Rybalko 	/* 8. Reset grouping of global counters. Use 32 bits. */
50259191f35SAleksandr Rybalko 	pmu_cmn600_clr8(arg, nodeid, NODE_TYPE_DTC, POR_DT_PMCR,
50359191f35SAleksandr Rybalko 	    POR_DT_PMCR_CNTCFG_MASK);
50459191f35SAleksandr Rybalko 
50559191f35SAleksandr Rybalko 	/* 9. Enable DTC. */
50659191f35SAleksandr Rybalko 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_DTC, POR_DT_DTC_CTL,
50759191f35SAleksandr Rybalko 	    POR_DT_DTC_CTL_DT_EN);
50859191f35SAleksandr Rybalko 
50959191f35SAleksandr Rybalko 	/* 10. Enable Overflow Interrupt. */
51059191f35SAleksandr Rybalko 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_DTC, POR_DT_PMCR,
51159191f35SAleksandr Rybalko 	    POR_DT_PMCR_OVFL_INTR_EN);
51259191f35SAleksandr Rybalko 
51359191f35SAleksandr Rybalko 	/* 11. Run PMC. */
51459191f35SAleksandr Rybalko 	pmu_cmn600_set8(arg, nodeid, NODE_TYPE_DTC, POR_DT_PMCR,
51559191f35SAleksandr Rybalko 	    POR_DT_PMCR_PMU_EN);
51659191f35SAleksandr Rybalko 
51759191f35SAleksandr Rybalko 	return (0);
51859191f35SAleksandr Rybalko }
51959191f35SAleksandr Rybalko 
52059191f35SAleksandr Rybalko /*
52159191f35SAleksandr Rybalko  * Stop a PMC.
52259191f35SAleksandr Rybalko  */
52359191f35SAleksandr Rybalko 
52459191f35SAleksandr Rybalko static int
cmn600_stop_pmc(int cpu,int ri,struct pmc * pm)525*39f92a76SMitchell Horne cmn600_stop_pmc(int cpu, int ri, struct pmc *pm)
52659191f35SAleksandr Rybalko {
52759191f35SAleksandr Rybalko 	struct cmn600_descr *desc;
52859191f35SAleksandr Rybalko 	int local_counter;
52959191f35SAleksandr Rybalko 	uint64_t val;
53059191f35SAleksandr Rybalko 
53159191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
53259191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU value %d", __LINE__, cpu));
53359191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
53459191f35SAleksandr Rybalko 	    ri));
53559191f35SAleksandr Rybalko 
53659191f35SAleksandr Rybalko 	desc = cmn600desc(ri);
53759191f35SAleksandr Rybalko 
53859191f35SAleksandr Rybalko 	PMCDBG2(MDP, STO, 1, "%s ri=%d", __func__, ri);
53959191f35SAleksandr Rybalko 
54059191f35SAleksandr Rybalko 	/* Disable pairing. */
54159191f35SAleksandr Rybalko 	local_counter = pm->pm_md.pm_cmn600.pm_cmn600_local_counter;
54259191f35SAleksandr Rybalko 	pmu_cmn600_clr8(desc->pd_rw_arg, pm->pm_md.pm_cmn600.pm_cmn600_nodeid,
54359191f35SAleksandr Rybalko 	    NODE_TYPE_XP, POR_DTM_PMU_CONFIG, (1 << (local_counter + 4)));
54459191f35SAleksandr Rybalko 
54559191f35SAleksandr Rybalko 	/* Shutdown XP's DTM function if no paired counters. */
54659191f35SAleksandr Rybalko 	val = pmu_cmn600_rd8(desc->pd_rw_arg,
54759191f35SAleksandr Rybalko 	    pm->pm_md.pm_cmn600.pm_cmn600_nodeid, NODE_TYPE_XP,
54859191f35SAleksandr Rybalko 	    POR_DTM_PMU_CONFIG);
54959191f35SAleksandr Rybalko 	if ((val & 0xf0) == 0)
55059191f35SAleksandr Rybalko 		pmu_cmn600_clr8(desc->pd_rw_arg,
55159191f35SAleksandr Rybalko 		    pm->pm_md.pm_cmn600.pm_cmn600_nodeid, NODE_TYPE_XP,
55259191f35SAleksandr Rybalko 		    POR_DTM_PMU_CONFIG, POR_DTM_CONTROL_DTM_ENABLE);
55359191f35SAleksandr Rybalko 
55459191f35SAleksandr Rybalko 	return (0);
55559191f35SAleksandr Rybalko }
55659191f35SAleksandr Rybalko 
55759191f35SAleksandr Rybalko /*
55859191f35SAleksandr Rybalko  * describe a PMC
55959191f35SAleksandr Rybalko  */
56059191f35SAleksandr Rybalko static int
cmn600_describe(int cpu,int ri,struct pmc_info * pi,struct pmc ** ppmc)56159191f35SAleksandr Rybalko cmn600_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
56259191f35SAleksandr Rybalko {
56331610e34SMitchell Horne 	struct pmc_descr *pd;
56459191f35SAleksandr Rybalko 	struct pmc_hw *phw;
56559191f35SAleksandr Rybalko 
56659191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
56759191f35SAleksandr Rybalko 	    ("[cmn600,%d] illegal CPU %d", __LINE__, cpu));
56859191f35SAleksandr Rybalko 	KASSERT(ri >= 0, ("[cmn600,%d] row-index %d out of range", __LINE__,
56959191f35SAleksandr Rybalko 	    ri));
57059191f35SAleksandr Rybalko 
57159191f35SAleksandr Rybalko 	phw = cmn600desc(ri)->pd_phw;
57231610e34SMitchell Horne 	pd = &cmn600desc(ri)->pd_descr;
57359191f35SAleksandr Rybalko 
57431610e34SMitchell Horne 	strlcpy(pi->pm_name, pd->pd_name, sizeof(pi->pm_name));
57531610e34SMitchell Horne 	pi->pm_class = pd->pd_class;
57659191f35SAleksandr Rybalko 
57759191f35SAleksandr Rybalko 	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
57859191f35SAleksandr Rybalko 		pi->pm_enabled = TRUE;
57959191f35SAleksandr Rybalko 		*ppmc          = phw->phw_pmc;
58059191f35SAleksandr Rybalko 	} else {
58159191f35SAleksandr Rybalko 		pi->pm_enabled = FALSE;
58259191f35SAleksandr Rybalko 		*ppmc          = NULL;
58359191f35SAleksandr Rybalko 	}
58459191f35SAleksandr Rybalko 
58559191f35SAleksandr Rybalko 	return (0);
58659191f35SAleksandr Rybalko }
58759191f35SAleksandr Rybalko 
58859191f35SAleksandr Rybalko /*
58959191f35SAleksandr Rybalko  * processor dependent initialization.
59059191f35SAleksandr Rybalko  */
59159191f35SAleksandr Rybalko 
59259191f35SAleksandr Rybalko static int
cmn600_pcpu_init(struct pmc_mdep * md,int cpu)59359191f35SAleksandr Rybalko cmn600_pcpu_init(struct pmc_mdep *md, int cpu)
59459191f35SAleksandr Rybalko {
59559191f35SAleksandr Rybalko 	int first_ri, n, npmc;
59659191f35SAleksandr Rybalko 	struct pmc_hw  *phw;
59759191f35SAleksandr Rybalko 	struct pmc_cpu *pc;
59859191f35SAleksandr Rybalko 	int mdep_class;
59959191f35SAleksandr Rybalko 
60059191f35SAleksandr Rybalko 	mdep_class = PMC_MDEP_CLASS_INDEX_CMN600;
60159191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
60259191f35SAleksandr Rybalko 	    ("[cmn600,%d] insane cpu number %d", __LINE__, cpu));
60359191f35SAleksandr Rybalko 
60459191f35SAleksandr Rybalko 	PMCDBG1(MDP, INI, 1, "cmn600-init cpu=%d", cpu);
60559191f35SAleksandr Rybalko 
60659191f35SAleksandr Rybalko 	/*
60759191f35SAleksandr Rybalko 	 * Set the content of the hardware descriptors to a known
60859191f35SAleksandr Rybalko 	 * state and initialize pointers in the MI per-cpu descriptor.
60959191f35SAleksandr Rybalko 	 */
61059191f35SAleksandr Rybalko 
61159191f35SAleksandr Rybalko 	pc = pmc_pcpu[cpu];
61259191f35SAleksandr Rybalko 	first_ri = md->pmd_classdep[mdep_class].pcd_ri;
61359191f35SAleksandr Rybalko 	npmc = md->pmd_classdep[mdep_class].pcd_num;
61459191f35SAleksandr Rybalko 
61559191f35SAleksandr Rybalko 	for (n = 0; n < npmc; n++, phw++) {
61659191f35SAleksandr Rybalko 		phw = cmn600desc(n)->pd_phw;
61759191f35SAleksandr Rybalko 		phw->phw_state = PMC_PHW_CPU_TO_STATE(cpu) |
61859191f35SAleksandr Rybalko 		    PMC_PHW_INDEX_TO_STATE(n);
61959191f35SAleksandr Rybalko 		/* Set enabled only if unit present. */
62059191f35SAleksandr Rybalko 		if (cmn600_pmcs[class_ri2unit(n)].arg != NULL)
62159191f35SAleksandr Rybalko 			phw->phw_state |= PMC_PHW_FLAG_IS_ENABLED;
62259191f35SAleksandr Rybalko 		phw->phw_pmc = NULL;
62359191f35SAleksandr Rybalko 		pc->pc_hwpmcs[n + first_ri] = phw;
62459191f35SAleksandr Rybalko 	}
62559191f35SAleksandr Rybalko 	return (0);
62659191f35SAleksandr Rybalko }
62759191f35SAleksandr Rybalko 
62859191f35SAleksandr Rybalko /*
62959191f35SAleksandr Rybalko  * processor dependent cleanup prior to the KLD
63059191f35SAleksandr Rybalko  * being unloaded
63159191f35SAleksandr Rybalko  */
63259191f35SAleksandr Rybalko 
63359191f35SAleksandr Rybalko static int
cmn600_pcpu_fini(struct pmc_mdep * md,int cpu)63459191f35SAleksandr Rybalko cmn600_pcpu_fini(struct pmc_mdep *md, int cpu)
63559191f35SAleksandr Rybalko {
63659191f35SAleksandr Rybalko 
63759191f35SAleksandr Rybalko 	return (0);
63859191f35SAleksandr Rybalko }
63959191f35SAleksandr Rybalko 
64059191f35SAleksandr Rybalko static int
cmn600_pmu_intr(struct trapframe * tf,int unit,int i)64159191f35SAleksandr Rybalko cmn600_pmu_intr(struct trapframe *tf, int unit, int i)
64259191f35SAleksandr Rybalko {
643d78bef0eSBjoern A. Zeeb 	struct pmc_cpu *pc __diagused;
64459191f35SAleksandr Rybalko 	struct pmc_hw *phw;
64559191f35SAleksandr Rybalko 	struct pmc *pm;
64659191f35SAleksandr Rybalko 	int error, cpu, ri;
64759191f35SAleksandr Rybalko 
64859191f35SAleksandr Rybalko 	ri = i + unit * CMN600_COUNTERS_N;
64959191f35SAleksandr Rybalko 	cpu = curcpu;
65059191f35SAleksandr Rybalko 	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
65159191f35SAleksandr Rybalko 	    ("[cmn600,%d] CPU %d out of range", __LINE__, cpu));
65259191f35SAleksandr Rybalko 	pc = pmc_pcpu[cpu];
65359191f35SAleksandr Rybalko 	KASSERT(pc != NULL, ("pc != NULL"));
65459191f35SAleksandr Rybalko 
65559191f35SAleksandr Rybalko 	phw = cmn600desc(ri)->pd_phw;
65659191f35SAleksandr Rybalko 	KASSERT(phw != NULL, ("phw != NULL"));
65759191f35SAleksandr Rybalko 	pm  = phw->phw_pmc;
65859191f35SAleksandr Rybalko 	if (pm == NULL)
65959191f35SAleksandr Rybalko 		return (0);
66059191f35SAleksandr Rybalko 
66159191f35SAleksandr Rybalko 	if (!PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) {
66259191f35SAleksandr Rybalko 		/* Always CPU0. */
66359191f35SAleksandr Rybalko 		pm->pm_pcpu_state[0].pps_overflowcnt += 1;
66459191f35SAleksandr Rybalko 		return (0);
66559191f35SAleksandr Rybalko 	}
66659191f35SAleksandr Rybalko 
66759191f35SAleksandr Rybalko 	if (pm->pm_state != PMC_STATE_RUNNING)
66859191f35SAleksandr Rybalko 		return (0);
66959191f35SAleksandr Rybalko 
67059191f35SAleksandr Rybalko 	error = pmc_process_interrupt(PMC_HR, pm, tf);
67159191f35SAleksandr Rybalko 	if (error)
672*39f92a76SMitchell Horne 		cmn600_stop_pmc(cpu, ri, pm);
67359191f35SAleksandr Rybalko 
67459191f35SAleksandr Rybalko 	/* Reload sampling count */
675*39f92a76SMitchell Horne 	cmn600_write_pmc(cpu, ri, pm, pm->pm_sc.pm_reloadcount);
67659191f35SAleksandr Rybalko 
67759191f35SAleksandr Rybalko 	return (0);
67859191f35SAleksandr Rybalko }
67959191f35SAleksandr Rybalko 
68059191f35SAleksandr Rybalko /*
68159191f35SAleksandr Rybalko  * Initialize ourselves.
68259191f35SAleksandr Rybalko  */
68359191f35SAleksandr Rybalko static int
cmn600_init_pmc_units(void)68405cef747SAndrew Turner cmn600_init_pmc_units(void)
68559191f35SAleksandr Rybalko {
68659191f35SAleksandr Rybalko 	int i;
68759191f35SAleksandr Rybalko 
68859191f35SAleksandr Rybalko 	if (cmn600_units > 0) { /* Already initialized. */
68959191f35SAleksandr Rybalko 		return (0);
69059191f35SAleksandr Rybalko 	}
69159191f35SAleksandr Rybalko 
69259191f35SAleksandr Rybalko 	cmn600_units = cmn600_pmc_nunits();
69359191f35SAleksandr Rybalko 	if (cmn600_units == 0)
69459191f35SAleksandr Rybalko 		return (ENOENT);
69559191f35SAleksandr Rybalko 
69659191f35SAleksandr Rybalko 	for (i = 0; i < cmn600_units; i++) {
69759191f35SAleksandr Rybalko 		if (cmn600_pmc_getunit(i, &cmn600_pmcs[i].arg,
69859191f35SAleksandr Rybalko 		    &cmn600_pmcs[i].domain) != 0)
69959191f35SAleksandr Rybalko 			cmn600_pmcs[i].arg = NULL;
70059191f35SAleksandr Rybalko 	}
70159191f35SAleksandr Rybalko 	return (0);
70259191f35SAleksandr Rybalko }
70359191f35SAleksandr Rybalko 
70459191f35SAleksandr Rybalko int
pmc_cmn600_nclasses(void)70505cef747SAndrew Turner pmc_cmn600_nclasses(void)
70659191f35SAleksandr Rybalko {
70759191f35SAleksandr Rybalko 
70859191f35SAleksandr Rybalko 	if (cmn600_pmc_nunits() > 0)
70959191f35SAleksandr Rybalko 		return (1);
71059191f35SAleksandr Rybalko 	return (0);
71159191f35SAleksandr Rybalko }
71259191f35SAleksandr Rybalko 
71359191f35SAleksandr Rybalko int
pmc_cmn600_initialize(struct pmc_mdep * md)71459191f35SAleksandr Rybalko pmc_cmn600_initialize(struct pmc_mdep *md)
71559191f35SAleksandr Rybalko {
71659191f35SAleksandr Rybalko 	struct pmc_classdep *pcd;
71759191f35SAleksandr Rybalko 	int i, npmc, unit;
71859191f35SAleksandr Rybalko 
71959191f35SAleksandr Rybalko 	cmn600_init_pmc_units();
72059191f35SAleksandr Rybalko 	KASSERT(md != NULL, ("[cmn600,%d] md is NULL", __LINE__));
72159191f35SAleksandr Rybalko 	KASSERT(cmn600_units < CMN600_UNIT_MAX,
72259191f35SAleksandr Rybalko 	    ("[cmn600,%d] cmn600_units too big", __LINE__));
72359191f35SAleksandr Rybalko 
72459191f35SAleksandr Rybalko 	PMCDBG0(MDP,INI,1, "cmn600-initialize");
72559191f35SAleksandr Rybalko 
72659191f35SAleksandr Rybalko 	npmc = CMN600_COUNTERS_N * cmn600_units;
72759191f35SAleksandr Rybalko 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_CMN600];
72859191f35SAleksandr Rybalko 
72959191f35SAleksandr Rybalko 	pcd->pcd_caps		= PMC_CAP_SYSTEM | PMC_CAP_READ |
73059191f35SAleksandr Rybalko 	    PMC_CAP_WRITE | PMC_CAP_QUALIFIER | PMC_CAP_INTERRUPT |
73159191f35SAleksandr Rybalko 	    PMC_CAP_DOMWIDE;
73259191f35SAleksandr Rybalko 	pcd->pcd_class	= PMC_CLASS_CMN600_PMU;
73359191f35SAleksandr Rybalko 	pcd->pcd_num	= npmc;
73459191f35SAleksandr Rybalko 	pcd->pcd_ri	= md->pmd_npmc;
73559191f35SAleksandr Rybalko 	pcd->pcd_width	= 48;
73659191f35SAleksandr Rybalko 
73759191f35SAleksandr Rybalko 	pcd->pcd_allocate_pmc	= cmn600_allocate_pmc;
73859191f35SAleksandr Rybalko 	pcd->pcd_config_pmc	= cmn600_config_pmc;
73959191f35SAleksandr Rybalko 	pcd->pcd_describe	= cmn600_describe;
74059191f35SAleksandr Rybalko 	pcd->pcd_get_config	= cmn600_get_config;
74159191f35SAleksandr Rybalko 	pcd->pcd_get_msr	= NULL;
74259191f35SAleksandr Rybalko 	pcd->pcd_pcpu_fini	= cmn600_pcpu_fini;
74359191f35SAleksandr Rybalko 	pcd->pcd_pcpu_init	= cmn600_pcpu_init;
74459191f35SAleksandr Rybalko 	pcd->pcd_read_pmc	= cmn600_read_pmc;
74559191f35SAleksandr Rybalko 	pcd->pcd_release_pmc	= cmn600_release_pmc;
74659191f35SAleksandr Rybalko 	pcd->pcd_start_pmc	= cmn600_start_pmc;
74759191f35SAleksandr Rybalko 	pcd->pcd_stop_pmc	= cmn600_stop_pmc;
74859191f35SAleksandr Rybalko 	pcd->pcd_write_pmc	= cmn600_write_pmc;
74959191f35SAleksandr Rybalko 
75059191f35SAleksandr Rybalko 	md->pmd_npmc	       += npmc;
75159191f35SAleksandr Rybalko 	cmn600_pmcdesc = malloc(sizeof(struct cmn600_descr *) * npmc *
75259191f35SAleksandr Rybalko 	    CMN600_PMU_DEFAULT_UNITS_N, M_PMC, M_WAITOK|M_ZERO);
75359191f35SAleksandr Rybalko 	for (i = 0; i < npmc; i++) {
75459191f35SAleksandr Rybalko 		cmn600_pmcdesc[i] = malloc(sizeof(struct cmn600_descr), M_PMC,
75559191f35SAleksandr Rybalko 		    M_WAITOK|M_ZERO);
75659191f35SAleksandr Rybalko 
75759191f35SAleksandr Rybalko 		unit = i / CMN600_COUNTERS_N;
75859191f35SAleksandr Rybalko 		KASSERT(unit >= 0, ("unit >= 0"));
75959191f35SAleksandr Rybalko 		KASSERT(cmn600_pmcs[unit].arg != NULL, ("arg != NULL"));
76059191f35SAleksandr Rybalko 
76159191f35SAleksandr Rybalko 		cmn600_pmcdesc[i]->pd_rw_arg = cmn600_pmcs[unit].arg;
76259191f35SAleksandr Rybalko 		cmn600_pmcdesc[i]->pd_descr.pd_class =
76359191f35SAleksandr Rybalko 		    PMC_CLASS_CMN600_PMU;
76459191f35SAleksandr Rybalko 		cmn600_pmcdesc[i]->pd_descr.pd_caps = pcd->pcd_caps;
76559191f35SAleksandr Rybalko 		cmn600_pmcdesc[i]->pd_phw = (struct pmc_hw *)malloc(
76659191f35SAleksandr Rybalko 		    sizeof(struct pmc_hw), M_PMC, M_WAITOK|M_ZERO);
76759191f35SAleksandr Rybalko 		snprintf(cmn600_pmcdesc[i]->pd_descr.pd_name, 63,
76859191f35SAleksandr Rybalko 		    "CMN600_%d", i);
76959191f35SAleksandr Rybalko 		cmn600_pmu_intr_cb(cmn600_pmcs[unit].arg, cmn600_pmu_intr);
77059191f35SAleksandr Rybalko 	}
77159191f35SAleksandr Rybalko 
77259191f35SAleksandr Rybalko 	return (0);
77359191f35SAleksandr Rybalko }
77459191f35SAleksandr Rybalko 
77559191f35SAleksandr Rybalko void
pmc_cmn600_finalize(struct pmc_mdep * md)77659191f35SAleksandr Rybalko pmc_cmn600_finalize(struct pmc_mdep *md)
77759191f35SAleksandr Rybalko {
77859191f35SAleksandr Rybalko 	struct pmc_classdep *pcd;
77959191f35SAleksandr Rybalko 	int i, npmc;
78059191f35SAleksandr Rybalko 
78159191f35SAleksandr Rybalko 	KASSERT(md->pmd_classdep[PMC_MDEP_CLASS_INDEX_CMN600].pcd_class ==
78259191f35SAleksandr Rybalko 	    PMC_CLASS_CMN600_PMU, ("[cmn600,%d] pmc class mismatch",
78359191f35SAleksandr Rybalko 	    __LINE__));
78459191f35SAleksandr Rybalko 
78559191f35SAleksandr Rybalko 	pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_CMN600];
78659191f35SAleksandr Rybalko 
78759191f35SAleksandr Rybalko 	npmc = pcd->pcd_num;
78859191f35SAleksandr Rybalko 	for (i = 0; i < npmc; i++) {
78959191f35SAleksandr Rybalko 		free(cmn600_pmcdesc[i]->pd_phw, M_PMC);
79059191f35SAleksandr Rybalko 		free(cmn600_pmcdesc[i], M_PMC);
79159191f35SAleksandr Rybalko 	}
79259191f35SAleksandr Rybalko 	free(cmn600_pmcdesc, M_PMC);
79359191f35SAleksandr Rybalko 	cmn600_pmcdesc = NULL;
79459191f35SAleksandr Rybalko }
79559191f35SAleksandr Rybalko 
79659191f35SAleksandr Rybalko MODULE_DEPEND(pmc, cmn600, 1, 1, 1);
797