xref: /freebsd/sys/arm64/arm64/cmn600.c (revision 01d463b6)
159191f35SAleksandr Rybalko /*-
2753c7fc9SAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
359191f35SAleksandr Rybalko  *
4d90188efSJohn Baldwin  * Copyright (c) 2021 ARM Ltd
559191f35SAleksandr Rybalko  *
659191f35SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
759191f35SAleksandr Rybalko  * modification, are permitted provided that the following conditions
859191f35SAleksandr Rybalko  * are met:
959191f35SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1059191f35SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1159191f35SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1259191f35SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1359191f35SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1459191f35SAleksandr Rybalko  *
1559191f35SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1659191f35SAleksandr Rybalko  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1759191f35SAleksandr Rybalko  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1859191f35SAleksandr Rybalko  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1959191f35SAleksandr Rybalko  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2059191f35SAleksandr Rybalko  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2159191f35SAleksandr Rybalko  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2259191f35SAleksandr Rybalko  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2359191f35SAleksandr Rybalko  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2459191f35SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2559191f35SAleksandr Rybalko  * SUCH DAMAGE.
2659191f35SAleksandr Rybalko  */
2759191f35SAleksandr Rybalko 
2859191f35SAleksandr Rybalko /* Arm CoreLink CMN-600 Coherent Mesh Network Driver */
2959191f35SAleksandr Rybalko 
3059191f35SAleksandr Rybalko #include <sys/cdefs.h>
3159191f35SAleksandr Rybalko #include "opt_acpi.h"
3259191f35SAleksandr Rybalko 
3359191f35SAleksandr Rybalko #include <sys/param.h>
3459191f35SAleksandr Rybalko #include <sys/bus.h>
3559191f35SAleksandr Rybalko #include <sys/kernel.h>
3659191f35SAleksandr Rybalko #include <sys/malloc.h>
3759191f35SAleksandr Rybalko #include <sys/module.h>
3859191f35SAleksandr Rybalko #include <sys/proc.h>
3959191f35SAleksandr Rybalko #include <sys/rman.h>
4059191f35SAleksandr Rybalko #include <sys/smp.h>
4159191f35SAleksandr Rybalko #include <sys/sysctl.h>
4259191f35SAleksandr Rybalko 
4359191f35SAleksandr Rybalko #include <machine/bus.h>
4459191f35SAleksandr Rybalko #include <machine/cpu.h>
4559191f35SAleksandr Rybalko 
4659191f35SAleksandr Rybalko #include <contrib/dev/acpica/include/acpi.h>
4759191f35SAleksandr Rybalko #include <dev/acpica/acpivar.h>
4859191f35SAleksandr Rybalko 
4959191f35SAleksandr Rybalko #include <machine/cmn600_reg.h>
5059191f35SAleksandr Rybalko 
5159191f35SAleksandr Rybalko #define	RD4(sc, r)		bus_read_4((sc)->sc_res[0], (r))
5259191f35SAleksandr Rybalko #define	RD8(sc, r)		bus_read_8((sc)->sc_res[0], (r))
5359191f35SAleksandr Rybalko #define	WR4(sc, r, v)		bus_write_4((sc)->sc_res[0], (r), (v))
5459191f35SAleksandr Rybalko #define	WR8(sc, r, v)		bus_write_8((sc)->sc_res[0], (r), (v))
5559191f35SAleksandr Rybalko #define	FLD(v, n)		(((v) & n ## _MASK) >> n ## _SHIFT)
5659191f35SAleksandr Rybalko 
5759191f35SAleksandr Rybalko static char *cmn600_ids[] = {
5859191f35SAleksandr Rybalko 	"ARMHC600",
5959191f35SAleksandr Rybalko 	NULL
6059191f35SAleksandr Rybalko };
6159191f35SAleksandr Rybalko 
6259191f35SAleksandr Rybalko static struct resource_spec cmn600_res_spec[] = {
6359191f35SAleksandr Rybalko 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
6459191f35SAleksandr Rybalko 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL },
6559191f35SAleksandr Rybalko 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
6659191f35SAleksandr Rybalko 	{ -1, 0 }
6759191f35SAleksandr Rybalko };
6859191f35SAleksandr Rybalko 
6959191f35SAleksandr Rybalko struct cmn600_node;
7059191f35SAleksandr Rybalko 
7159191f35SAleksandr Rybalko typedef uint64_t (*nd_read_8_t)(struct cmn600_node *, uint32_t);
7259191f35SAleksandr Rybalko typedef uint32_t (*nd_read_4_t)(struct cmn600_node *, uint32_t);
7359191f35SAleksandr Rybalko typedef void (*nd_write_8_t)(struct cmn600_node *, uint32_t, uint64_t);
7459191f35SAleksandr Rybalko typedef void (*nd_write_4_t)(struct cmn600_node *, uint32_t, uint32_t);
7559191f35SAleksandr Rybalko 
7659191f35SAleksandr Rybalko struct cmn600_node {
7759191f35SAleksandr Rybalko 	struct cmn600_softc	*sc;
7859191f35SAleksandr Rybalko 	off_t			 nd_offset;
7959191f35SAleksandr Rybalko 	int			 nd_type;
8059191f35SAleksandr Rybalko 	uint16_t		 nd_id;
8159191f35SAleksandr Rybalko 	uint16_t		 nd_logical_id;
8259191f35SAleksandr Rybalko 	uint8_t			 nd_x, nd_y, nd_port, nd_sub;
8359191f35SAleksandr Rybalko 	uint16_t		 nd_child_count;
8459191f35SAleksandr Rybalko 	uint32_t		 nd_paired;
8559191f35SAleksandr Rybalko 	struct cmn600_node	*nd_parent;
8659191f35SAleksandr Rybalko 	nd_read_8_t		 nd_read8;
8759191f35SAleksandr Rybalko 	nd_read_4_t		 nd_read4;
8859191f35SAleksandr Rybalko 	nd_write_8_t		 nd_write8;
8959191f35SAleksandr Rybalko 	nd_write_4_t		 nd_write4;
9059191f35SAleksandr Rybalko 	struct cmn600_node	**nd_children;
9159191f35SAleksandr Rybalko };
9259191f35SAleksandr Rybalko 
9359191f35SAleksandr Rybalko struct cmn600_softc {
9459191f35SAleksandr Rybalko 	device_t	 sc_dev;
9559191f35SAleksandr Rybalko 	int		 sc_unit;
9659191f35SAleksandr Rybalko 	int		 sc_domain;
9759191f35SAleksandr Rybalko 	int		 sc_longid;
9859191f35SAleksandr Rybalko 	int		 sc_mesh_x;
9959191f35SAleksandr Rybalko 	int		 sc_mesh_y;
10059191f35SAleksandr Rybalko 	struct resource *sc_res[3];
10159191f35SAleksandr Rybalko 	void		*sc_ih;
10259191f35SAleksandr Rybalko 	int		 sc_r2;
10359191f35SAleksandr Rybalko 	int		 sc_rev;
10459191f35SAleksandr Rybalko 	struct cmn600_node *sc_rootnode;
10559191f35SAleksandr Rybalko 	struct cmn600_node *sc_dtcnode;
10659191f35SAleksandr Rybalko 	struct cmn600_node *sc_dvmnode;
10759191f35SAleksandr Rybalko 	struct cmn600_node *sc_xpnodes[64];
10859191f35SAleksandr Rybalko 	int (*sc_pmu_ih)(struct trapframe *tf, int unit, int i);
10959191f35SAleksandr Rybalko };
11059191f35SAleksandr Rybalko 
11159191f35SAleksandr Rybalko static struct cmn600_pmc cmn600_pmcs[CMN600_UNIT_MAX];
11259191f35SAleksandr Rybalko static int cmn600_npmcs = 0;
11359191f35SAleksandr Rybalko 
11459191f35SAleksandr Rybalko static int cmn600_acpi_detach(device_t dev);
11559191f35SAleksandr Rybalko static int cmn600_intr(void *arg);
11659191f35SAleksandr Rybalko 
11759191f35SAleksandr Rybalko static void
cmn600_pmc_register(int unit,void * arg,int domain)11859191f35SAleksandr Rybalko cmn600_pmc_register(int unit, void *arg, int domain)
11959191f35SAleksandr Rybalko {
12059191f35SAleksandr Rybalko 
12159191f35SAleksandr Rybalko 	if (unit >= CMN600_UNIT_MAX) {
12259191f35SAleksandr Rybalko 		/* TODO */
12359191f35SAleksandr Rybalko 		return;
12459191f35SAleksandr Rybalko 	}
12559191f35SAleksandr Rybalko 
12659191f35SAleksandr Rybalko 	cmn600_pmcs[unit].arg = arg;
12759191f35SAleksandr Rybalko 	cmn600_pmcs[unit].domain = domain;
12859191f35SAleksandr Rybalko 	cmn600_npmcs++;
12959191f35SAleksandr Rybalko }
13059191f35SAleksandr Rybalko 
13159191f35SAleksandr Rybalko static void
cmn600_pmc_unregister(int unit)13259191f35SAleksandr Rybalko cmn600_pmc_unregister(int unit)
13359191f35SAleksandr Rybalko {
13459191f35SAleksandr Rybalko 
13559191f35SAleksandr Rybalko 	cmn600_pmcs[unit].arg = NULL;
13659191f35SAleksandr Rybalko 	cmn600_npmcs--;
13759191f35SAleksandr Rybalko }
13859191f35SAleksandr Rybalko 
13959191f35SAleksandr Rybalko int
cmn600_pmc_nunits(void)14059191f35SAleksandr Rybalko cmn600_pmc_nunits(void)
14159191f35SAleksandr Rybalko {
14259191f35SAleksandr Rybalko 
14359191f35SAleksandr Rybalko 	return (cmn600_npmcs);
14459191f35SAleksandr Rybalko }
14559191f35SAleksandr Rybalko 
14659191f35SAleksandr Rybalko int
cmn600_pmc_getunit(int unit,void ** arg,int * domain)14759191f35SAleksandr Rybalko cmn600_pmc_getunit(int unit, void **arg, int *domain)
14859191f35SAleksandr Rybalko {
14959191f35SAleksandr Rybalko 
15059191f35SAleksandr Rybalko 	if (unit >= cmn600_npmcs)
15159191f35SAleksandr Rybalko 		return (EINVAL);
15259191f35SAleksandr Rybalko 	if (cmn600_pmcs[unit].arg == NULL)
15359191f35SAleksandr Rybalko 		return (EINVAL);
15459191f35SAleksandr Rybalko 	*arg = cmn600_pmcs[unit].arg;
15559191f35SAleksandr Rybalko 	*domain = cmn600_pmcs[unit].domain;
15659191f35SAleksandr Rybalko 	return (0);
15759191f35SAleksandr Rybalko }
15859191f35SAleksandr Rybalko 
15959191f35SAleksandr Rybalko int
pmu_cmn600_rev(void * arg)16059191f35SAleksandr Rybalko pmu_cmn600_rev(void *arg)
16159191f35SAleksandr Rybalko {
16259191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
16359191f35SAleksandr Rybalko 
16459191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
16559191f35SAleksandr Rybalko 	switch (sc->sc_rev) {
16659191f35SAleksandr Rybalko 	case 0x0:
16759191f35SAleksandr Rybalko 		return (0x100);
16859191f35SAleksandr Rybalko 	case 0x1:
16959191f35SAleksandr Rybalko 		return (0x101);
17059191f35SAleksandr Rybalko 	case 0x2:
17159191f35SAleksandr Rybalko 		return (0x102);
17259191f35SAleksandr Rybalko 	case 0x3:
17359191f35SAleksandr Rybalko 		return (0x103);
17459191f35SAleksandr Rybalko 	case 0x4:
17559191f35SAleksandr Rybalko 		return (0x200);
17659191f35SAleksandr Rybalko 	case 0x5:
17759191f35SAleksandr Rybalko 		return (0x300);
17859191f35SAleksandr Rybalko 	case 0x6:
17959191f35SAleksandr Rybalko 		return (0x301);
18059191f35SAleksandr Rybalko 	}
18159191f35SAleksandr Rybalko 	return (0x302); /* Unknown revision. */
18259191f35SAleksandr Rybalko }
18359191f35SAleksandr Rybalko 
18459191f35SAleksandr Rybalko static uint64_t
cmn600_node_read8(struct cmn600_node * nd,uint32_t reg)18559191f35SAleksandr Rybalko cmn600_node_read8(struct cmn600_node *nd, uint32_t reg)
18659191f35SAleksandr Rybalko {
18759191f35SAleksandr Rybalko 
18859191f35SAleksandr Rybalko 	return (RD8(nd->sc, nd->nd_offset + reg));
18959191f35SAleksandr Rybalko }
19059191f35SAleksandr Rybalko 
19159191f35SAleksandr Rybalko static void
cmn600_node_write8(struct cmn600_node * nd,uint32_t reg,uint64_t val)19259191f35SAleksandr Rybalko cmn600_node_write8(struct cmn600_node *nd, uint32_t reg, uint64_t val)
19359191f35SAleksandr Rybalko {
19459191f35SAleksandr Rybalko 
19559191f35SAleksandr Rybalko 	WR8(nd->sc, nd->nd_offset + reg, val);
19659191f35SAleksandr Rybalko }
19759191f35SAleksandr Rybalko 
19859191f35SAleksandr Rybalko static uint32_t
cmn600_node_read4(struct cmn600_node * nd,uint32_t reg)19959191f35SAleksandr Rybalko cmn600_node_read4(struct cmn600_node *nd, uint32_t reg)
20059191f35SAleksandr Rybalko {
20159191f35SAleksandr Rybalko 
20259191f35SAleksandr Rybalko 	return (RD4(nd->sc, nd->nd_offset + reg));
20359191f35SAleksandr Rybalko }
20459191f35SAleksandr Rybalko 
20559191f35SAleksandr Rybalko static void
cmn600_node_write4(struct cmn600_node * nd,uint32_t reg,uint32_t val)20659191f35SAleksandr Rybalko cmn600_node_write4(struct cmn600_node *nd, uint32_t reg, uint32_t val)
20759191f35SAleksandr Rybalko {
20859191f35SAleksandr Rybalko 
20959191f35SAleksandr Rybalko 	WR4(nd->sc, nd->nd_offset + reg, val);
21059191f35SAleksandr Rybalko }
21159191f35SAleksandr Rybalko 
21259191f35SAleksandr Rybalko static const char *
cmn600_node_type_str(int type)21359191f35SAleksandr Rybalko cmn600_node_type_str(int type)
21459191f35SAleksandr Rybalko {
21559191f35SAleksandr Rybalko 
21659191f35SAleksandr Rybalko #define	NAME_OF(t, n)	case NODE_TYPE_ ## t: return n
21759191f35SAleksandr Rybalko 	switch (type) {
21859191f35SAleksandr Rybalko 	NAME_OF(INVALID, "<invalid node>");
21959191f35SAleksandr Rybalko 	NAME_OF(DVM, "DVM");
22059191f35SAleksandr Rybalko 	NAME_OF(CFG, "CFG");
22159191f35SAleksandr Rybalko 	NAME_OF(DTC, "DTC");
22259191f35SAleksandr Rybalko 	NAME_OF(HN_I, "HN-I");
22359191f35SAleksandr Rybalko 	NAME_OF(HN_F, "HN-F");
22459191f35SAleksandr Rybalko 	NAME_OF(XP, "XP");
22559191f35SAleksandr Rybalko 	NAME_OF(SBSX, "SBSX");
22659191f35SAleksandr Rybalko 	NAME_OF(RN_I, "RN-I");
22759191f35SAleksandr Rybalko 	NAME_OF(RN_D, "RN-D");
22859191f35SAleksandr Rybalko 	NAME_OF(RN_SAM, "RN-SAM");
22959191f35SAleksandr Rybalko 	NAME_OF(CXRA, "CXRA");
23059191f35SAleksandr Rybalko 	NAME_OF(CXHA, "CXHA");
23159191f35SAleksandr Rybalko 	NAME_OF(CXLA, "CXLA");
23259191f35SAleksandr Rybalko 	default:
23359191f35SAleksandr Rybalko 		return "<unknown node>";
23459191f35SAleksandr Rybalko 	}
23559191f35SAleksandr Rybalko #undef	NAME_OF
23659191f35SAleksandr Rybalko }
23759191f35SAleksandr Rybalko 
23859191f35SAleksandr Rybalko static const char *
cmn600_xpport_dev_type_str(uint8_t type)23959191f35SAleksandr Rybalko cmn600_xpport_dev_type_str(uint8_t type)
24059191f35SAleksandr Rybalko {
24159191f35SAleksandr Rybalko 
24259191f35SAleksandr Rybalko #define	NAME_OF(t, n)	case POR_MXP_PX_INFO_DEV_TYPE_ ## t: return n
24359191f35SAleksandr Rybalko 	switch (type) {
24459191f35SAleksandr Rybalko 	NAME_OF(RN_I, "RN-I");
24559191f35SAleksandr Rybalko 	NAME_OF(RN_D, "RN-D");
24659191f35SAleksandr Rybalko 	NAME_OF(RN_F_CHIB, "RN-F CHIB");
24759191f35SAleksandr Rybalko 	NAME_OF(RN_F_CHIB_ESAM, "RN-F CHIB ESAM");
24859191f35SAleksandr Rybalko 	NAME_OF(RN_F_CHIA, "RN-F CHIA");
24959191f35SAleksandr Rybalko 	NAME_OF(RN_F_CHIA_ESAM, "RN-F CHIA ESAM");
25059191f35SAleksandr Rybalko 	NAME_OF(HN_T, "HN-T");
25159191f35SAleksandr Rybalko 	NAME_OF(HN_I, "HN-I");
25259191f35SAleksandr Rybalko 	NAME_OF(HN_D, "HN-D");
25359191f35SAleksandr Rybalko 	NAME_OF(SN_F, "SN-F");
25459191f35SAleksandr Rybalko 	NAME_OF(SBSX, "SBSX");
25559191f35SAleksandr Rybalko 	NAME_OF(HN_F, "HN-F");
25659191f35SAleksandr Rybalko 	NAME_OF(CXHA, "CXHA");
25759191f35SAleksandr Rybalko 	NAME_OF(CXRA, "CXRA");
25859191f35SAleksandr Rybalko 	NAME_OF(CXRH, "CXRH");
25959191f35SAleksandr Rybalko 	default:
26059191f35SAleksandr Rybalko 		return "<unknown>";
26159191f35SAleksandr Rybalko 	}
26259191f35SAleksandr Rybalko #undef	NAME_OF
26359191f35SAleksandr Rybalko }
26459191f35SAleksandr Rybalko 
26559191f35SAleksandr Rybalko static void
cmn600_dump_node(struct cmn600_node * node,int lvl)26659191f35SAleksandr Rybalko cmn600_dump_node(struct cmn600_node *node, int lvl)
26759191f35SAleksandr Rybalko {
26859191f35SAleksandr Rybalko 	int i;
26959191f35SAleksandr Rybalko 
27059191f35SAleksandr Rybalko 	for (i = 0; i < lvl; i++) printf("    ");
27159191f35SAleksandr Rybalko 	printf("%s [%dx%d:%d:%d] id: 0x%x @0x%lx Logical Id: 0x%x",
27259191f35SAleksandr Rybalko 	    cmn600_node_type_str(node->nd_type), node->nd_x, node->nd_y,
27359191f35SAleksandr Rybalko 	    node->nd_port, node->nd_sub, node->nd_id, node->nd_offset,
27459191f35SAleksandr Rybalko 	    node->nd_logical_id);
27559191f35SAleksandr Rybalko 	if (node->nd_child_count > 0)
27659191f35SAleksandr Rybalko 		printf(", Children: %d", node->nd_child_count);
27759191f35SAleksandr Rybalko 	printf("\n");
27859191f35SAleksandr Rybalko 	if (node->nd_type == NODE_TYPE_XP)
27959191f35SAleksandr Rybalko 		printf("\tPort 0: %s\n\tPort 1: %s\n",
28059191f35SAleksandr Rybalko 		    cmn600_xpport_dev_type_str(node->nd_read4(node,
28159191f35SAleksandr Rybalko 			POR_MXP_P0_INFO) & 0x1f),
28259191f35SAleksandr Rybalko 		    cmn600_xpport_dev_type_str(node->nd_read4(node,
28359191f35SAleksandr Rybalko 			POR_MXP_P1_INFO) & 0x1f));
28459191f35SAleksandr Rybalko }
28559191f35SAleksandr Rybalko 
28659191f35SAleksandr Rybalko static void
cmn600_dump_node_recursive(struct cmn600_node * node,int lvl)28759191f35SAleksandr Rybalko cmn600_dump_node_recursive(struct cmn600_node *node, int lvl)
28859191f35SAleksandr Rybalko {
28959191f35SAleksandr Rybalko 	int i;
29059191f35SAleksandr Rybalko 
29159191f35SAleksandr Rybalko 	cmn600_dump_node(node, lvl);
29259191f35SAleksandr Rybalko 	for (i = 0; i < node->nd_child_count; i++) {
29359191f35SAleksandr Rybalko 		cmn600_dump_node_recursive(node->nd_children[i], lvl + 1);
29459191f35SAleksandr Rybalko 	}
29559191f35SAleksandr Rybalko }
29659191f35SAleksandr Rybalko 
29759191f35SAleksandr Rybalko static void
cmn600_dump_nodes_tree(struct cmn600_softc * sc)29859191f35SAleksandr Rybalko cmn600_dump_nodes_tree(struct cmn600_softc *sc)
29959191f35SAleksandr Rybalko {
30059191f35SAleksandr Rybalko 
30159191f35SAleksandr Rybalko 	device_printf(sc->sc_dev, " nodes:\n");
30259191f35SAleksandr Rybalko 	cmn600_dump_node_recursive(sc->sc_rootnode, 0);
30359191f35SAleksandr Rybalko }
30459191f35SAleksandr Rybalko 
30559191f35SAleksandr Rybalko static int
cmn600_sysctl_dump_nodes(SYSCTL_HANDLER_ARGS)30659191f35SAleksandr Rybalko cmn600_sysctl_dump_nodes(SYSCTL_HANDLER_ARGS)
30759191f35SAleksandr Rybalko {
30859191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
30959191f35SAleksandr Rybalko 	uint32_t val;
31059191f35SAleksandr Rybalko 	int err;
31159191f35SAleksandr Rybalko 
31259191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg1;
31359191f35SAleksandr Rybalko 	val = 0;
31459191f35SAleksandr Rybalko 	err = sysctl_handle_int(oidp, &val, 0, req);
31559191f35SAleksandr Rybalko 
31659191f35SAleksandr Rybalko 	if (err)
31759191f35SAleksandr Rybalko 		return (err);
31859191f35SAleksandr Rybalko 
31959191f35SAleksandr Rybalko 	if (val != 0)
32059191f35SAleksandr Rybalko 		cmn600_dump_nodes_tree(sc);
32159191f35SAleksandr Rybalko 
32259191f35SAleksandr Rybalko 	return (0);
32359191f35SAleksandr Rybalko }
32459191f35SAleksandr Rybalko 
32559191f35SAleksandr Rybalko static struct cmn600_node *
cmn600_create_node(struct cmn600_softc * sc,off_t node_offset,struct cmn600_node * parent,int lvl)32659191f35SAleksandr Rybalko cmn600_create_node(struct cmn600_softc *sc, off_t node_offset,
32759191f35SAleksandr Rybalko     struct cmn600_node *parent, int lvl)
32859191f35SAleksandr Rybalko {
32959191f35SAleksandr Rybalko 	struct cmn600_node *node;
33059191f35SAleksandr Rybalko 	off_t child_offset;
33159191f35SAleksandr Rybalko 	uint64_t val;
33259191f35SAleksandr Rybalko 	int i;
33359191f35SAleksandr Rybalko 
33459191f35SAleksandr Rybalko 	node = malloc(sizeof(struct cmn600_node), M_DEVBUF, M_WAITOK);
33559191f35SAleksandr Rybalko 	if (node == NULL)
33659191f35SAleksandr Rybalko 		return (NULL);
33759191f35SAleksandr Rybalko 
33859191f35SAleksandr Rybalko 	node->sc = sc;
33959191f35SAleksandr Rybalko 	node->nd_offset = node_offset;
34059191f35SAleksandr Rybalko 	node->nd_parent = parent;
34159191f35SAleksandr Rybalko 	node->nd_read4 = cmn600_node_read4;
34259191f35SAleksandr Rybalko 	node->nd_read8 = cmn600_node_read8;
34359191f35SAleksandr Rybalko 	node->nd_write4 = cmn600_node_write4;
34459191f35SAleksandr Rybalko 	node->nd_write8 = cmn600_node_write8;
34559191f35SAleksandr Rybalko 
34659191f35SAleksandr Rybalko 	val = node->nd_read8(node, POR_CFGM_NODE_INFO);
34759191f35SAleksandr Rybalko 	node->nd_type = FLD(val, POR_CFGM_NODE_INFO_NODE_TYPE);
34859191f35SAleksandr Rybalko 	node->nd_id = FLD(val, POR_CFGM_NODE_INFO_NODE_ID);
34959191f35SAleksandr Rybalko 	node->nd_logical_id = FLD(val, POR_CFGM_NODE_INFO_LOGICAL_ID);
35059191f35SAleksandr Rybalko 
35159191f35SAleksandr Rybalko 	val = node->nd_read8(node, POR_CFGM_CHILD_INFO);
35259191f35SAleksandr Rybalko 	node->nd_child_count = FLD(val, POR_CFGM_CHILD_INFO_CHILD_COUNT);
35359191f35SAleksandr Rybalko 	child_offset = FLD(val, POR_CFGM_CHILD_INFO_CHILD_PTR_OFFSET);
35459191f35SAleksandr Rybalko 
35559191f35SAleksandr Rybalko 	if (parent == NULL) {
35601d463b6SLO WEN-CHIEN 		/* Find XP node with Id 8. It has to be last in a row. */
35759191f35SAleksandr Rybalko 		for (i = 0; i < node->nd_child_count; i++) {
35859191f35SAleksandr Rybalko 			val = node->nd_read8(node, child_offset + (i * 8));
35959191f35SAleksandr Rybalko 			val &= POR_CFGM_CHILD_POINTER_BASE_MASK;
36059191f35SAleksandr Rybalko 			val = RD8(sc, val + POR_CFGM_NODE_INFO);
36159191f35SAleksandr Rybalko 
36259191f35SAleksandr Rybalko 			if (FLD(val, POR_CFGM_NODE_INFO_NODE_ID) != 8)
36359191f35SAleksandr Rybalko 				continue;
36459191f35SAleksandr Rybalko 
36559191f35SAleksandr Rybalko 			sc->sc_mesh_x = FLD(val, POR_CFGM_NODE_INFO_LOGICAL_ID);
36659191f35SAleksandr Rybalko 			sc->sc_mesh_y = node->nd_child_count / sc->sc_mesh_x;
36759191f35SAleksandr Rybalko 			if (bootverbose)
36859191f35SAleksandr Rybalko 				printf("Mesh width X/Y %d/%d\n", sc->sc_mesh_x,
36959191f35SAleksandr Rybalko 				    sc->sc_mesh_y);
37059191f35SAleksandr Rybalko 
37159191f35SAleksandr Rybalko 			if ((sc->sc_mesh_x > 4) || (sc->sc_mesh_y > 4))
37259191f35SAleksandr Rybalko 				sc->sc_longid = 1;
37359191f35SAleksandr Rybalko 			break;
37459191f35SAleksandr Rybalko 		}
37559191f35SAleksandr Rybalko 
37659191f35SAleksandr Rybalko 		val = node->nd_read8(node, POR_INFO_GLOBAL);
37759191f35SAleksandr Rybalko 		sc->sc_r2 = (val & POR_INFO_GLOBAL_R2_ENABLE) ? 1 : 0;
37859191f35SAleksandr Rybalko 		val = node->nd_read4(node, POR_CFGM_PERIPH_ID_2_PERIPH_ID_3);
37959191f35SAleksandr Rybalko 		sc->sc_rev = FLD(val, POR_CFGM_PERIPH_ID_2_REV);
38059191f35SAleksandr Rybalko 		if (bootverbose)
38159191f35SAleksandr Rybalko 			printf("  Rev: %d, R2_ENABLE = %s\n", sc->sc_rev,
38259191f35SAleksandr Rybalko 			    sc->sc_r2 ? "true" : "false");
38359191f35SAleksandr Rybalko 	}
38459191f35SAleksandr Rybalko 	node->nd_sub = FLD(node->nd_id, NODE_ID_SUB);
38559191f35SAleksandr Rybalko 	node->nd_port = FLD(node->nd_id, NODE_ID_PORT);
38659191f35SAleksandr Rybalko 	node->nd_paired = 0;
38759191f35SAleksandr Rybalko 	if (sc->sc_longid == 1) {
38859191f35SAleksandr Rybalko 		node->nd_x = FLD(node->nd_id, NODE_ID_X3B);
38959191f35SAleksandr Rybalko 		node->nd_y = FLD(node->nd_id, NODE_ID_Y3B);
39059191f35SAleksandr Rybalko 	} else {
39159191f35SAleksandr Rybalko 		node->nd_x = FLD(node->nd_id, NODE_ID_X2B);
39259191f35SAleksandr Rybalko 		node->nd_y = FLD(node->nd_id, NODE_ID_Y2B);
39359191f35SAleksandr Rybalko 	}
39459191f35SAleksandr Rybalko 
39559191f35SAleksandr Rybalko 	if (bootverbose) {
39659191f35SAleksandr Rybalko 		cmn600_dump_node(node, lvl);
39759191f35SAleksandr Rybalko 	}
39859191f35SAleksandr Rybalko 
39959191f35SAleksandr Rybalko 	node->nd_children = (struct cmn600_node **)mallocarray(
40059191f35SAleksandr Rybalko 	    node->nd_child_count, sizeof(struct cmn600_node *), M_DEVBUF,
40159191f35SAleksandr Rybalko 	    M_WAITOK);
40259191f35SAleksandr Rybalko 	if (node->nd_children == NULL)
40359191f35SAleksandr Rybalko 		goto FAIL;
40459191f35SAleksandr Rybalko 	for (i = 0; i < node->nd_child_count; i++) {
40559191f35SAleksandr Rybalko 		val = node->nd_read8(node, child_offset + (i * 8));
40659191f35SAleksandr Rybalko 		node->nd_children[i] = cmn600_create_node(sc, val &
40759191f35SAleksandr Rybalko 		    POR_CFGM_CHILD_POINTER_BASE_MASK, node, lvl + 1);
40859191f35SAleksandr Rybalko 	}
40959191f35SAleksandr Rybalko 	switch (node->nd_type) {
41059191f35SAleksandr Rybalko 	case NODE_TYPE_DTC:
41159191f35SAleksandr Rybalko 		sc->sc_dtcnode = node;
41259191f35SAleksandr Rybalko 		break;
41359191f35SAleksandr Rybalko 	case NODE_TYPE_DVM:
41459191f35SAleksandr Rybalko 		sc->sc_dvmnode = node;
41559191f35SAleksandr Rybalko 		break;
41659191f35SAleksandr Rybalko 	case NODE_TYPE_XP:
41759191f35SAleksandr Rybalko 		sc->sc_xpnodes[node->nd_id >> NODE_ID_X2B_SHIFT] = node;
41859191f35SAleksandr Rybalko 		break;
41959191f35SAleksandr Rybalko 	default:
42059191f35SAleksandr Rybalko 		break;
42159191f35SAleksandr Rybalko 	}
42259191f35SAleksandr Rybalko 	return (node);
42359191f35SAleksandr Rybalko FAIL:
42459191f35SAleksandr Rybalko 	free(node, M_DEVBUF);
42559191f35SAleksandr Rybalko 	return (NULL);
42659191f35SAleksandr Rybalko }
42759191f35SAleksandr Rybalko 
42859191f35SAleksandr Rybalko static void
cmn600_destroy_node(struct cmn600_node * node)42959191f35SAleksandr Rybalko cmn600_destroy_node(struct cmn600_node *node)
43059191f35SAleksandr Rybalko {
43159191f35SAleksandr Rybalko 	int i;
43259191f35SAleksandr Rybalko 
43359191f35SAleksandr Rybalko 	for (i = 0; i < node->nd_child_count; i++) {
43459191f35SAleksandr Rybalko 		if (node->nd_children[i] == NULL)
43559191f35SAleksandr Rybalko 			continue;
43659191f35SAleksandr Rybalko 		cmn600_destroy_node(node->nd_children[i]);
43759191f35SAleksandr Rybalko 	}
43859191f35SAleksandr Rybalko 	free(node->nd_children, M_DEVBUF);
43959191f35SAleksandr Rybalko 	free(node, M_DEVBUF);
44059191f35SAleksandr Rybalko }
44159191f35SAleksandr Rybalko 
44259191f35SAleksandr Rybalko static int
cmn600_find_node(struct cmn600_softc * sc,int node_id,int type,struct cmn600_node ** node)44359191f35SAleksandr Rybalko cmn600_find_node(struct cmn600_softc *sc, int node_id, int type,
44459191f35SAleksandr Rybalko     struct cmn600_node **node)
44559191f35SAleksandr Rybalko {
44659191f35SAleksandr Rybalko 	struct cmn600_node *xp, *child;
44759191f35SAleksandr Rybalko 	uint8_t xp_xy;
44859191f35SAleksandr Rybalko 	int i;
44959191f35SAleksandr Rybalko 
45059191f35SAleksandr Rybalko 	switch (type) {
45159191f35SAleksandr Rybalko 	case NODE_TYPE_INVALID:
45259191f35SAleksandr Rybalko 		return (ENXIO);
45359191f35SAleksandr Rybalko 	case NODE_TYPE_CFG:
45459191f35SAleksandr Rybalko 		*node = sc->sc_rootnode;
45559191f35SAleksandr Rybalko 		return (0);
45659191f35SAleksandr Rybalko 	case NODE_TYPE_DTC:
45759191f35SAleksandr Rybalko 		*node = sc->sc_dtcnode;
45859191f35SAleksandr Rybalko 		return (0);
45959191f35SAleksandr Rybalko 	case NODE_TYPE_DVM:
46059191f35SAleksandr Rybalko 		*node = sc->sc_dvmnode;
46159191f35SAleksandr Rybalko 		return (0);
46259191f35SAleksandr Rybalko 	default:
46359191f35SAleksandr Rybalko 		break;
46459191f35SAleksandr Rybalko 	}
46559191f35SAleksandr Rybalko 
46659191f35SAleksandr Rybalko 	xp_xy = node_id >> NODE_ID_X2B_SHIFT;
46759191f35SAleksandr Rybalko 	if (xp_xy >= 64)
46859191f35SAleksandr Rybalko 		return (ENXIO);
46959191f35SAleksandr Rybalko 	if (sc->sc_xpnodes[xp_xy] == NULL)
47059191f35SAleksandr Rybalko 		return (ENOENT);
47159191f35SAleksandr Rybalko 
47259191f35SAleksandr Rybalko 	switch (type) {
47359191f35SAleksandr Rybalko 	case NODE_TYPE_XP:
47459191f35SAleksandr Rybalko 		*node = sc->sc_xpnodes[xp_xy];
47559191f35SAleksandr Rybalko 		return (0);
47659191f35SAleksandr Rybalko 	default:
47759191f35SAleksandr Rybalko 		xp = sc->sc_xpnodes[xp_xy];
47859191f35SAleksandr Rybalko 		for (i = 0; i < xp->nd_child_count; i++) {
47959191f35SAleksandr Rybalko 			child = xp->nd_children[i];
48059191f35SAleksandr Rybalko 			if (child->nd_id == node_id && child->nd_type == type) {
48159191f35SAleksandr Rybalko 				*node = child;
48259191f35SAleksandr Rybalko 				return (0);
48359191f35SAleksandr Rybalko 			}
48459191f35SAleksandr Rybalko 		}
48559191f35SAleksandr Rybalko 	}
48659191f35SAleksandr Rybalko 	return (ENOENT);
48759191f35SAleksandr Rybalko }
48859191f35SAleksandr Rybalko 
48959191f35SAleksandr Rybalko int
pmu_cmn600_alloc_localpmc(void * arg,int nodeid,int node_type,int * counter)49059191f35SAleksandr Rybalko pmu_cmn600_alloc_localpmc(void *arg, int nodeid, int node_type, int *counter)
49159191f35SAleksandr Rybalko {
49259191f35SAleksandr Rybalko 	struct cmn600_node *node;
49359191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
49459191f35SAleksandr Rybalko 	uint32_t new, old;
49559191f35SAleksandr Rybalko 	int i, ret;
49659191f35SAleksandr Rybalko 
49759191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
49859191f35SAleksandr Rybalko 	switch (node_type) {
49959191f35SAleksandr Rybalko 	case NODE_TYPE_CXLA:
50059191f35SAleksandr Rybalko 		break;
50159191f35SAleksandr Rybalko 	default:
50259191f35SAleksandr Rybalko 		node_type = NODE_TYPE_XP;
50359191f35SAleksandr Rybalko 		/* Parent XP node has always zero port and device bits. */
50459191f35SAleksandr Rybalko 		nodeid &= ~0x07;
50559191f35SAleksandr Rybalko 	}
50659191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
50759191f35SAleksandr Rybalko 	if (ret != 0)
50859191f35SAleksandr Rybalko 		return (ret);
50959191f35SAleksandr Rybalko 	for (i = 0; i < 4; i++) {
51059191f35SAleksandr Rybalko 		new = old = node->nd_paired;
51159191f35SAleksandr Rybalko 		if (old == 0xf)
51259191f35SAleksandr Rybalko 			return (EBUSY);
51359191f35SAleksandr Rybalko 		if ((old & (1 << i)) != 0)
51459191f35SAleksandr Rybalko 			continue;
51559191f35SAleksandr Rybalko 		new |= 1 << i;
51659191f35SAleksandr Rybalko 		if (atomic_cmpset_32(&node->nd_paired, old, new) != 0)
51759191f35SAleksandr Rybalko 			break;
51859191f35SAleksandr Rybalko 	}
51959191f35SAleksandr Rybalko 	*counter = i;
52059191f35SAleksandr Rybalko 	return (0);
52159191f35SAleksandr Rybalko }
52259191f35SAleksandr Rybalko 
52359191f35SAleksandr Rybalko int
pmu_cmn600_free_localpmc(void * arg,int nodeid,int node_type,int counter)52459191f35SAleksandr Rybalko pmu_cmn600_free_localpmc(void *arg, int nodeid, int node_type, int counter)
52559191f35SAleksandr Rybalko {
52659191f35SAleksandr Rybalko 	struct cmn600_node *node;
52759191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
52859191f35SAleksandr Rybalko 	uint32_t new, old;
52959191f35SAleksandr Rybalko 	int ret;
53059191f35SAleksandr Rybalko 
53159191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
53259191f35SAleksandr Rybalko 	switch (node_type) {
53359191f35SAleksandr Rybalko 	case NODE_TYPE_CXLA:
53459191f35SAleksandr Rybalko 		break;
53559191f35SAleksandr Rybalko 	default:
53659191f35SAleksandr Rybalko 		node_type = NODE_TYPE_XP;
53759191f35SAleksandr Rybalko 	}
53859191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
53959191f35SAleksandr Rybalko 	if (ret != 0)
54059191f35SAleksandr Rybalko 		return (ret);
54159191f35SAleksandr Rybalko 
54259191f35SAleksandr Rybalko 	do {
54359191f35SAleksandr Rybalko 		new = old = node->nd_paired;
54459191f35SAleksandr Rybalko 		new &= ~(1 << counter);
54559191f35SAleksandr Rybalko 	} while (atomic_cmpset_32(&node->nd_paired, old, new) == 0);
54659191f35SAleksandr Rybalko 	return (0);
54759191f35SAleksandr Rybalko }
54859191f35SAleksandr Rybalko 
54959191f35SAleksandr Rybalko uint32_t
pmu_cmn600_rd4(void * arg,int nodeid,int node_type,off_t reg)55059191f35SAleksandr Rybalko pmu_cmn600_rd4(void *arg, int nodeid, int node_type, off_t reg)
55159191f35SAleksandr Rybalko {
55259191f35SAleksandr Rybalko 	struct cmn600_node *node;
55359191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
55459191f35SAleksandr Rybalko 	int ret;
55559191f35SAleksandr Rybalko 
55659191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
55759191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
55859191f35SAleksandr Rybalko 	if (ret != 0)
55959191f35SAleksandr Rybalko 		return (UINT32_MAX);
56059191f35SAleksandr Rybalko 	return (cmn600_node_read4(node, reg));
56159191f35SAleksandr Rybalko }
56259191f35SAleksandr Rybalko 
56359191f35SAleksandr Rybalko int
pmu_cmn600_wr4(void * arg,int nodeid,int node_type,off_t reg,uint32_t val)56459191f35SAleksandr Rybalko pmu_cmn600_wr4(void *arg, int nodeid, int node_type, off_t reg, uint32_t val)
56559191f35SAleksandr Rybalko {
56659191f35SAleksandr Rybalko 	struct cmn600_node *node;
56759191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
56859191f35SAleksandr Rybalko 	int ret;
56959191f35SAleksandr Rybalko 
57059191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
57159191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
57259191f35SAleksandr Rybalko 	if (ret != 0)
57359191f35SAleksandr Rybalko 		return (ret);
57459191f35SAleksandr Rybalko 	cmn600_node_write4(node, reg, val);
57559191f35SAleksandr Rybalko 	return (0);
57659191f35SAleksandr Rybalko }
57759191f35SAleksandr Rybalko 
57859191f35SAleksandr Rybalko uint64_t
pmu_cmn600_rd8(void * arg,int nodeid,int node_type,off_t reg)57959191f35SAleksandr Rybalko pmu_cmn600_rd8(void *arg, int nodeid, int node_type, off_t reg)
58059191f35SAleksandr Rybalko {
58159191f35SAleksandr Rybalko 	struct cmn600_node *node;
58259191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
58359191f35SAleksandr Rybalko 	int ret;
58459191f35SAleksandr Rybalko 
58559191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
58659191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
58759191f35SAleksandr Rybalko 	if (ret != 0)
58859191f35SAleksandr Rybalko 		return (UINT64_MAX);
58959191f35SAleksandr Rybalko 	return (cmn600_node_read8(node, reg));
59059191f35SAleksandr Rybalko }
59159191f35SAleksandr Rybalko 
59259191f35SAleksandr Rybalko int
pmu_cmn600_wr8(void * arg,int nodeid,int node_type,off_t reg,uint64_t val)59359191f35SAleksandr Rybalko pmu_cmn600_wr8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val)
59459191f35SAleksandr Rybalko {
59559191f35SAleksandr Rybalko 	struct cmn600_node *node;
59659191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
59759191f35SAleksandr Rybalko 	int ret;
59859191f35SAleksandr Rybalko 
59959191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
60059191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
60159191f35SAleksandr Rybalko 	if (ret != 0)
60259191f35SAleksandr Rybalko 		return (ret);
60359191f35SAleksandr Rybalko 	cmn600_node_write8(node, reg, val);
60459191f35SAleksandr Rybalko 	return (0);
60559191f35SAleksandr Rybalko }
60659191f35SAleksandr Rybalko 
60759191f35SAleksandr Rybalko int
pmu_cmn600_set8(void * arg,int nodeid,int node_type,off_t reg,uint64_t val)60859191f35SAleksandr Rybalko pmu_cmn600_set8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val)
60959191f35SAleksandr Rybalko {
61059191f35SAleksandr Rybalko 	struct cmn600_node *node;
61159191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
61259191f35SAleksandr Rybalko 	int ret;
61359191f35SAleksandr Rybalko 
61459191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
61559191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
61659191f35SAleksandr Rybalko 	if (ret != 0)
61759191f35SAleksandr Rybalko 		return (ret);
61859191f35SAleksandr Rybalko 	cmn600_node_write8(node, reg, cmn600_node_read8(node, reg) | val);
61959191f35SAleksandr Rybalko 	return (0);
62059191f35SAleksandr Rybalko }
62159191f35SAleksandr Rybalko 
62259191f35SAleksandr Rybalko int
pmu_cmn600_clr8(void * arg,int nodeid,int node_type,off_t reg,uint64_t val)62359191f35SAleksandr Rybalko pmu_cmn600_clr8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val)
62459191f35SAleksandr Rybalko {
62559191f35SAleksandr Rybalko 	struct cmn600_node *node;
62659191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
62759191f35SAleksandr Rybalko 	int ret;
62859191f35SAleksandr Rybalko 
62959191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
63059191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
63159191f35SAleksandr Rybalko 	if (ret != 0)
63259191f35SAleksandr Rybalko 		return (ret);
63359191f35SAleksandr Rybalko 	cmn600_node_write8(node, reg, cmn600_node_read8(node, reg) & ~val);
63459191f35SAleksandr Rybalko 	return (0);
63559191f35SAleksandr Rybalko }
63659191f35SAleksandr Rybalko 
63759191f35SAleksandr Rybalko int
pmu_cmn600_md8(void * arg,int nodeid,int node_type,off_t reg,uint64_t mask,uint64_t val)63859191f35SAleksandr Rybalko pmu_cmn600_md8(void *arg, int nodeid, int node_type, off_t reg, uint64_t mask,
63959191f35SAleksandr Rybalko     uint64_t val)
64059191f35SAleksandr Rybalko {
64159191f35SAleksandr Rybalko 	struct cmn600_node *node;
64259191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
64359191f35SAleksandr Rybalko 	int ret;
64459191f35SAleksandr Rybalko 
64559191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *)arg;
64659191f35SAleksandr Rybalko 	ret = cmn600_find_node(sc, nodeid, node_type, &node);
64759191f35SAleksandr Rybalko 	if (ret != 0)
64859191f35SAleksandr Rybalko 		return (ret);
64959191f35SAleksandr Rybalko 	cmn600_node_write8(node, reg, (cmn600_node_read8(node, reg) & ~mask) |
65059191f35SAleksandr Rybalko 	    val);
65159191f35SAleksandr Rybalko 	return (0);
65259191f35SAleksandr Rybalko }
65359191f35SAleksandr Rybalko 
65459191f35SAleksandr Rybalko static int
cmn600_acpi_probe(device_t dev)65559191f35SAleksandr Rybalko cmn600_acpi_probe(device_t dev)
65659191f35SAleksandr Rybalko {
65759191f35SAleksandr Rybalko 	int err;
65859191f35SAleksandr Rybalko 
65959191f35SAleksandr Rybalko 	err = ACPI_ID_PROBE(device_get_parent(dev), dev, cmn600_ids, NULL);
66059191f35SAleksandr Rybalko 	if (err <= 0)
66159191f35SAleksandr Rybalko 		device_set_desc(dev, "Arm CoreLink CMN-600 Coherent Mesh Network");
66259191f35SAleksandr Rybalko 
66359191f35SAleksandr Rybalko 	return (err);
66459191f35SAleksandr Rybalko }
66559191f35SAleksandr Rybalko 
66659191f35SAleksandr Rybalko static int
cmn600_acpi_attach(device_t dev)66759191f35SAleksandr Rybalko cmn600_acpi_attach(device_t dev)
66859191f35SAleksandr Rybalko {
66959191f35SAleksandr Rybalko 	struct sysctl_ctx_list *ctx;
67059191f35SAleksandr Rybalko 	struct sysctl_oid_list *child;
67159191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
67259191f35SAleksandr Rybalko 	int cpu, domain, i, u;
67359191f35SAleksandr Rybalko 	const char *dname;
67459191f35SAleksandr Rybalko 	rman_res_t count, periph_base, rootnode_base;
67559191f35SAleksandr Rybalko 	struct cmn600_node *node;
67659191f35SAleksandr Rybalko 
67759191f35SAleksandr Rybalko 	dname = device_get_name(dev);
67859191f35SAleksandr Rybalko 	sc = device_get_softc(dev);
67959191f35SAleksandr Rybalko 	sc->sc_dev = dev;
68059191f35SAleksandr Rybalko 	u = device_get_unit(dev);
68159191f35SAleksandr Rybalko 	sc->sc_unit = u;
68259191f35SAleksandr Rybalko 	domain = 0;
68359191f35SAleksandr Rybalko 
68459191f35SAleksandr Rybalko 	if ((resource_int_value(dname, u, "domain", &domain) == 0 ||
68559191f35SAleksandr Rybalko 	    bus_get_domain(dev, &domain) == 0) && domain < MAXMEMDOM) {
68659191f35SAleksandr Rybalko 		sc->sc_domain = domain;
68759191f35SAleksandr Rybalko 	}
68859191f35SAleksandr Rybalko 	if (domain == -1) /* NUMA not supported. Use single domain. */
68959191f35SAleksandr Rybalko 		domain = 0;
69059191f35SAleksandr Rybalko 	sc->sc_domain = domain;
69159191f35SAleksandr Rybalko 	device_printf(dev, "domain=%d\n", sc->sc_domain);
69259191f35SAleksandr Rybalko 
69359191f35SAleksandr Rybalko 	cpu = CPU_FFS(&cpuset_domain[domain]) - 1;
69459191f35SAleksandr Rybalko 
69559191f35SAleksandr Rybalko 	i = bus_alloc_resources(dev, cmn600_res_spec, sc->sc_res);
69659191f35SAleksandr Rybalko 	if (i != 0) {
69759191f35SAleksandr Rybalko 		device_printf(dev, "cannot allocate resources for device (%d)\n",
69859191f35SAleksandr Rybalko 		    i);
69959191f35SAleksandr Rybalko 		return (i);
70059191f35SAleksandr Rybalko 	}
70159191f35SAleksandr Rybalko 
70259191f35SAleksandr Rybalko 	bus_get_resource(dev, cmn600_res_spec[0].type, cmn600_res_spec[0].rid,
70359191f35SAleksandr Rybalko 	    &periph_base, &count);
70459191f35SAleksandr Rybalko 	bus_get_resource(dev, cmn600_res_spec[1].type, cmn600_res_spec[1].rid,
70559191f35SAleksandr Rybalko 	    &rootnode_base, &count);
70659191f35SAleksandr Rybalko 	rootnode_base -= periph_base;
70759191f35SAleksandr Rybalko 	if (bootverbose)
70859191f35SAleksandr Rybalko 		printf("ROOTNODE at %lx x %lx\n", rootnode_base, count);
70959191f35SAleksandr Rybalko 
71059191f35SAleksandr Rybalko 	sc->sc_rootnode = cmn600_create_node(sc, rootnode_base, NULL, 0);
71159191f35SAleksandr Rybalko 	ctx = device_get_sysctl_ctx(sc->sc_dev);
71259191f35SAleksandr Rybalko 
71359191f35SAleksandr Rybalko 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
71459191f35SAleksandr Rybalko 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "dump_nodes", CTLTYPE_INT |
71559191f35SAleksandr Rybalko 	    CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, cmn600_sysctl_dump_nodes,
71659191f35SAleksandr Rybalko 	    "U", "Dump CMN-600 nodes tree");
71759191f35SAleksandr Rybalko 
71859191f35SAleksandr Rybalko 	node = sc->sc_dtcnode;
71959191f35SAleksandr Rybalko 	if (node == NULL)
72059191f35SAleksandr Rybalko 		return (ENXIO);
72159191f35SAleksandr Rybalko 
72259191f35SAleksandr Rybalko 	cmn600_pmc_register(sc->sc_unit, (void *)sc, domain);
72359191f35SAleksandr Rybalko 
72459191f35SAleksandr Rybalko 	node->nd_write8(node, POR_DT_PMCR, 0);
72559191f35SAleksandr Rybalko 	node->nd_write8(node, POR_DT_PMOVSR_CLR, POR_DT_PMOVSR_ALL);
72659191f35SAleksandr Rybalko 	node->nd_write8(node, POR_DT_PMCR, POR_DT_PMCR_OVFL_INTR_EN);
72759191f35SAleksandr Rybalko 	node->nd_write8(node, POR_DT_DTC_CTL, POR_DT_DTC_CTL_DT_EN);
72859191f35SAleksandr Rybalko 
72959191f35SAleksandr Rybalko 	if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE,
73059191f35SAleksandr Rybalko 	    cmn600_intr, NULL, sc, &sc->sc_ih)) {
73159191f35SAleksandr Rybalko 		bus_release_resources(dev, cmn600_res_spec, sc->sc_res);
73259191f35SAleksandr Rybalko 		device_printf(dev, "cannot setup interrupt handler\n");
73359191f35SAleksandr Rybalko 		cmn600_acpi_detach(dev);
73459191f35SAleksandr Rybalko 		return (ENXIO);
73559191f35SAleksandr Rybalko 	}
73659191f35SAleksandr Rybalko 	if (bus_bind_intr(dev, sc->sc_res[2], cpu)) {
73759191f35SAleksandr Rybalko 		bus_teardown_intr(dev, sc->sc_res[2], sc->sc_ih);
73859191f35SAleksandr Rybalko 		bus_release_resources(dev, cmn600_res_spec, sc->sc_res);
73959191f35SAleksandr Rybalko 		device_printf(dev, "cannot setup interrupt handler\n");
74059191f35SAleksandr Rybalko 		cmn600_acpi_detach(dev);
74159191f35SAleksandr Rybalko 		return (ENXIO);
74259191f35SAleksandr Rybalko 	}
74359191f35SAleksandr Rybalko 	return (0);
74459191f35SAleksandr Rybalko }
74559191f35SAleksandr Rybalko 
74659191f35SAleksandr Rybalko static int
cmn600_acpi_detach(device_t dev)74759191f35SAleksandr Rybalko cmn600_acpi_detach(device_t dev)
74859191f35SAleksandr Rybalko {
74959191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
75059191f35SAleksandr Rybalko 	struct cmn600_node *node;
75159191f35SAleksandr Rybalko 
75259191f35SAleksandr Rybalko 	sc = device_get_softc(dev);
75359191f35SAleksandr Rybalko 	if (sc->sc_res[2] != NULL) {
75459191f35SAleksandr Rybalko 		bus_teardown_intr(dev, sc->sc_res[2], sc->sc_ih);
75559191f35SAleksandr Rybalko 	}
75659191f35SAleksandr Rybalko 
75759191f35SAleksandr Rybalko 	node = sc->sc_dtcnode;
75859191f35SAleksandr Rybalko 	node->nd_write4(node, POR_DT_DTC_CTL,
75959191f35SAleksandr Rybalko 	    node->nd_read4(node, POR_DT_DTC_CTL) & ~POR_DT_DTC_CTL_DT_EN);
76059191f35SAleksandr Rybalko 	node->nd_write8(node, POR_DT_PMOVSR_CLR, POR_DT_PMOVSR_ALL);
76159191f35SAleksandr Rybalko 
76259191f35SAleksandr Rybalko 	cmn600_pmc_unregister(sc->sc_unit);
76359191f35SAleksandr Rybalko 	cmn600_destroy_node(sc->sc_rootnode);
76459191f35SAleksandr Rybalko 	bus_release_resources(dev, cmn600_res_spec, sc->sc_res);
76559191f35SAleksandr Rybalko 
76659191f35SAleksandr Rybalko 	return (0);
76759191f35SAleksandr Rybalko }
76859191f35SAleksandr Rybalko 
76959191f35SAleksandr Rybalko int
cmn600_pmu_intr_cb(void * arg,int (* handler)(struct trapframe * tf,int unit,int i))77059191f35SAleksandr Rybalko cmn600_pmu_intr_cb(void *arg, int (*handler)(struct trapframe *tf, int unit,
77159191f35SAleksandr Rybalko     int i))
77259191f35SAleksandr Rybalko {
77359191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
77459191f35SAleksandr Rybalko 
77559191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *) arg;
77659191f35SAleksandr Rybalko 	sc->sc_pmu_ih = handler;
77759191f35SAleksandr Rybalko 	return (0);
77859191f35SAleksandr Rybalko }
77959191f35SAleksandr Rybalko 
78059191f35SAleksandr Rybalko static int
cmn600_intr(void * arg)78159191f35SAleksandr Rybalko cmn600_intr(void *arg)
78259191f35SAleksandr Rybalko {
78359191f35SAleksandr Rybalko 	struct cmn600_node *node;
78459191f35SAleksandr Rybalko 	struct cmn600_softc *sc;
78559191f35SAleksandr Rybalko 	struct trapframe *tf;
78659191f35SAleksandr Rybalko 	uint64_t mask, ready, val;
78759191f35SAleksandr Rybalko 	int i;
78859191f35SAleksandr Rybalko 
78959191f35SAleksandr Rybalko 	tf = PCPU_GET(curthread)->td_intr_frame;
79059191f35SAleksandr Rybalko 	sc = (struct cmn600_softc *) arg;
79159191f35SAleksandr Rybalko 	node = sc->sc_dtcnode;
79259191f35SAleksandr Rybalko 	val = node->nd_read8(node, POR_DT_PMOVSR);
79359191f35SAleksandr Rybalko 	if (val & POR_DT_PMOVSR_CYCLE_COUNTER)
79459191f35SAleksandr Rybalko 		node->nd_write8(node, POR_DT_PMOVSR_CLR,
79559191f35SAleksandr Rybalko 		    POR_DT_PMOVSR_CYCLE_COUNTER);
79659191f35SAleksandr Rybalko 	if (val & POR_DT_PMOVSR_EVENT_COUNTERS) {
79759191f35SAleksandr Rybalko 		for (ready = 0, i = 0; i < 8; i++) {
79859191f35SAleksandr Rybalko 			mask = 1 << i;
79959191f35SAleksandr Rybalko 			if ((val & mask) == 0)
80059191f35SAleksandr Rybalko 				continue;
80159191f35SAleksandr Rybalko 			if (sc->sc_pmu_ih != NULL)
80259191f35SAleksandr Rybalko 				sc->sc_pmu_ih(tf, sc->sc_unit, i);
80359191f35SAleksandr Rybalko 			ready |= mask;
80459191f35SAleksandr Rybalko 
80559191f35SAleksandr Rybalko 		}
80659191f35SAleksandr Rybalko 		node->nd_write8(node, POR_DT_PMOVSR_CLR, ready);
80759191f35SAleksandr Rybalko 	}
80859191f35SAleksandr Rybalko 
80959191f35SAleksandr Rybalko 	return (FILTER_HANDLED);
81059191f35SAleksandr Rybalko }
81159191f35SAleksandr Rybalko 
81259191f35SAleksandr Rybalko static device_method_t cmn600_acpi_methods[] = {
81359191f35SAleksandr Rybalko 	/* Device interface */
81459191f35SAleksandr Rybalko 	DEVMETHOD(device_probe,			cmn600_acpi_probe),
81559191f35SAleksandr Rybalko 	DEVMETHOD(device_attach,		cmn600_acpi_attach),
81659191f35SAleksandr Rybalko 	DEVMETHOD(device_detach,		cmn600_acpi_detach),
81759191f35SAleksandr Rybalko 
81859191f35SAleksandr Rybalko 	/* End */
81959191f35SAleksandr Rybalko 	DEVMETHOD_END
82059191f35SAleksandr Rybalko };
82159191f35SAleksandr Rybalko 
82259191f35SAleksandr Rybalko static driver_t cmn600_acpi_driver = {
82359191f35SAleksandr Rybalko 	"cmn600",
82459191f35SAleksandr Rybalko 	cmn600_acpi_methods,
82559191f35SAleksandr Rybalko 	sizeof(struct cmn600_softc),
82659191f35SAleksandr Rybalko };
82759191f35SAleksandr Rybalko 
8289a777495SJohn Baldwin DRIVER_MODULE(cmn600, acpi, cmn600_acpi_driver, 0, 0);
82959191f35SAleksandr Rybalko MODULE_VERSION(cmn600, 1);
830