Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/arm64/cmn600.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
* | |||||
* Copyright (c) 2021 ARM Ltd | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
/* Arm CoreLink CMN-600 Coherent Mesh Network Driver */ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include "opt_acpi.h" | |||||
#include <sys/param.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/rman.h> | |||||
#include <sys/smp.h> | |||||
#include <sys/sysctl.h> | |||||
#include <machine/bus.h> | |||||
#include <machine/cpu.h> | |||||
#include <contrib/dev/acpica/include/acpi.h> | |||||
#include <dev/acpica/acpivar.h> | |||||
#include <machine/cmn600_reg.h> | |||||
#define RD4(sc, r) bus_read_4((sc)->sc_res[0], (r)) | |||||
#define RD8(sc, r) bus_read_8((sc)->sc_res[0], (r)) | |||||
#define WR4(sc, r, v) bus_write_4((sc)->sc_res[0], (r), (v)) | |||||
#define WR8(sc, r, v) bus_write_8((sc)->sc_res[0], (r), (v)) | |||||
#define FLD(v, n) (((v) & n ## _MASK) >> n ## _SHIFT) | |||||
andrew: Is this used? | |||||
Done Inline ActionsRemoved. Thanks! ray: Removed. Thanks! | |||||
static char *cmn600_ids[] = { | |||||
"ARMHC600", | |||||
NULL | |||||
}; | |||||
static struct resource_spec cmn600_res_spec[] = { | |||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, | |||||
{ SYS_RES_MEMORY, 1, RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL }, | |||||
{ SYS_RES_IRQ, 0, RF_ACTIVE }, | |||||
{ -1, 0 } | |||||
Not Done Inline ActionsWhat does this do? mhorne: What does this do? | |||||
Done Inline ActionsFirst include second. Second only used to calculate offset, so no need to map it. ray: First include second. Second only used to calculate offset, so no need to map it.
Thanks | |||||
}; | |||||
Not Done Inline Actionscleanup the RF_SHAREABLE if not needed. mhorne: cleanup the RF_SHAREABLE if not needed. | |||||
Done Inline ActionsDone. Thanks! ray: Done. Thanks! | |||||
struct cmn600_node; | |||||
typedef uint64_t (*nd_read_8_t)(struct cmn600_node *, uint32_t); | |||||
typedef uint32_t (*nd_read_4_t)(struct cmn600_node *, uint32_t); | |||||
typedef void (*nd_write_8_t)(struct cmn600_node *, uint32_t, uint64_t); | |||||
typedef void (*nd_write_4_t)(struct cmn600_node *, uint32_t, uint32_t); | |||||
struct cmn600_node { | |||||
struct cmn600_softc *sc; | |||||
off_t nd_offset; | |||||
int nd_type; | |||||
uint16_t nd_id; | |||||
uint16_t nd_logical_id; | |||||
uint8_t nd_x, nd_y, nd_port, nd_sub; | |||||
uint16_t nd_child_count; | |||||
uint32_t nd_paired; | |||||
struct cmn600_node *nd_parent; | |||||
nd_read_8_t nd_read8; | |||||
nd_read_4_t nd_read4; | |||||
nd_write_8_t nd_write8; | |||||
nd_write_4_t nd_write4; | |||||
struct cmn600_node **nd_children; | |||||
}; | |||||
struct cmn600_softc { | |||||
device_t sc_dev; | |||||
int sc_unit; | |||||
int sc_domain; | |||||
int sc_longid; | |||||
int sc_mesh_x; | |||||
int sc_mesh_y; | |||||
struct resource *sc_res[3]; | |||||
void *sc_ih; | |||||
int sc_r2; | |||||
int sc_rev; | |||||
struct cmn600_node *sc_rootnode; | |||||
struct cmn600_node *sc_dtcnode; | |||||
struct cmn600_node *sc_dvmnode; | |||||
struct cmn600_node *sc_xpnodes[64]; | |||||
int (*sc_pmu_ih)(struct trapframe *tf, int unit, int i); | |||||
}; | |||||
static struct cmn600_pmc cmn600_pmcs[CMN600_UNIT_MAX]; | |||||
static int cmn600_npmcs = 0; | |||||
static int cmn600_acpi_detach(device_t dev); | |||||
static int cmn600_intr(void *arg); | |||||
static void | |||||
cmn600_pmc_register(int unit, void *arg, int domain) | |||||
{ | |||||
if (unit >= CMN600_UNIT_MAX) { | |||||
/* TODO */ | |||||
return; | |||||
} | |||||
cmn600_pmcs[unit].arg = arg; | |||||
cmn600_pmcs[unit].domain = domain; | |||||
cmn600_npmcs++; | |||||
} | |||||
static void | |||||
cmn600_pmc_unregister(int unit) | |||||
{ | |||||
cmn600_pmcs[unit].arg = NULL; | |||||
cmn600_npmcs--; | |||||
} | |||||
int | |||||
cmn600_pmc_nunits(void) | |||||
{ | |||||
return (cmn600_npmcs); | |||||
} | |||||
int | |||||
cmn600_pmc_getunit(int unit, void **arg, int *domain) | |||||
{ | |||||
if (unit >= cmn600_npmcs) | |||||
return (EINVAL); | |||||
if (cmn600_pmcs[unit].arg == NULL) | |||||
return (EINVAL); | |||||
*arg = cmn600_pmcs[unit].arg; | |||||
*domain = cmn600_pmcs[unit].domain; | |||||
return (0); | |||||
} | |||||
int | |||||
pmu_cmn600_rev(void *arg) | |||||
{ | |||||
struct cmn600_softc *sc; | |||||
sc = (struct cmn600_softc *)arg; | |||||
switch (sc->sc_rev) { | |||||
case 0x0: | |||||
return (0x100); | |||||
case 0x1: | |||||
return (0x101); | |||||
case 0x2: | |||||
return (0x102); | |||||
case 0x3: | |||||
return (0x103); | |||||
case 0x4: | |||||
return (0x200); | |||||
case 0x5: | |||||
return (0x300); | |||||
case 0x6: | |||||
return (0x301); | |||||
} | |||||
return (0x302); /* Unknown revision. */ | |||||
} | |||||
static uint64_t | |||||
cmn600_node_read8(struct cmn600_node *nd, uint32_t reg) | |||||
{ | |||||
return (RD8(nd->sc, nd->nd_offset + reg)); | |||||
} | |||||
static void | |||||
cmn600_node_write8(struct cmn600_node *nd, uint32_t reg, uint64_t val) | |||||
{ | |||||
WR8(nd->sc, nd->nd_offset + reg, val); | |||||
} | |||||
static uint32_t | |||||
cmn600_node_read4(struct cmn600_node *nd, uint32_t reg) | |||||
{ | |||||
return (RD4(nd->sc, nd->nd_offset + reg)); | |||||
} | |||||
static void | |||||
cmn600_node_write4(struct cmn600_node *nd, uint32_t reg, uint32_t val) | |||||
{ | |||||
WR4(nd->sc, nd->nd_offset + reg, val); | |||||
} | |||||
static const char * | |||||
cmn600_node_type_str(int type) | |||||
{ | |||||
#define NAME_OF(t, n) case NODE_TYPE_ ## t: return n | |||||
switch (type) { | |||||
NAME_OF(INVALID, "<invalid node>"); | |||||
NAME_OF(DVM, "DVM"); | |||||
NAME_OF(CFG, "CFG"); | |||||
NAME_OF(DTC, "DTC"); | |||||
NAME_OF(HN_I, "HN-I"); | |||||
NAME_OF(HN_F, "HN-F"); | |||||
NAME_OF(XP, "XP"); | |||||
NAME_OF(SBSX, "SBSX"); | |||||
NAME_OF(RN_I, "RN-I"); | |||||
NAME_OF(RN_D, "RN-D"); | |||||
NAME_OF(RN_SAM, "RN-SAM"); | |||||
NAME_OF(CXRA, "CXRA"); | |||||
NAME_OF(CXHA, "CXHA"); | |||||
NAME_OF(CXLA, "CXLA"); | |||||
default: | |||||
return "<unknown node>"; | |||||
} | |||||
#undef NAME_OF | |||||
} | |||||
static const char * | |||||
cmn600_xpport_dev_type_str(uint8_t type) | |||||
{ | |||||
#define NAME_OF(t, n) case POR_MXP_PX_INFO_DEV_TYPE_ ## t: return n | |||||
switch (type) { | |||||
NAME_OF(RN_I, "RN-I"); | |||||
NAME_OF(RN_D, "RN-D"); | |||||
NAME_OF(RN_F_CHIB, "RN-F CHIB"); | |||||
NAME_OF(RN_F_CHIB_ESAM, "RN-F CHIB ESAM"); | |||||
NAME_OF(RN_F_CHIA, "RN-F CHIA"); | |||||
NAME_OF(RN_F_CHIA_ESAM, "RN-F CHIA ESAM"); | |||||
NAME_OF(HN_T, "HN-T"); | |||||
NAME_OF(HN_I, "HN-I"); | |||||
NAME_OF(HN_D, "HN-D"); | |||||
NAME_OF(SN_F, "SN-F"); | |||||
NAME_OF(SBSX, "SBSX"); | |||||
NAME_OF(HN_F, "HN-F"); | |||||
NAME_OF(CXHA, "CXHA"); | |||||
NAME_OF(CXRA, "CXRA"); | |||||
NAME_OF(CXRH, "CXRH"); | |||||
default: | |||||
return "<unknown>"; | |||||
} | |||||
#undef NAME_OF | |||||
} | |||||
static void | |||||
cmn600_dump_node(struct cmn600_node *node, int lvl) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < lvl; i++) printf(" "); | |||||
printf("%s [%dx%d:%d:%d] id: 0x%x @0x%lx Logical Id: 0x%x", | |||||
cmn600_node_type_str(node->nd_type), node->nd_x, node->nd_y, | |||||
node->nd_port, node->nd_sub, node->nd_id, node->nd_offset, | |||||
node->nd_logical_id); | |||||
if (node->nd_child_count > 0) | |||||
printf(", Children: %d", node->nd_child_count); | |||||
printf("\n"); | |||||
if (node->nd_type == NODE_TYPE_XP) | |||||
printf("\tPort 0: %s\n\tPort 1: %s\n", | |||||
cmn600_xpport_dev_type_str(node->nd_read4(node, | |||||
POR_MXP_P0_INFO) & 0x1f), | |||||
cmn600_xpport_dev_type_str(node->nd_read4(node, | |||||
POR_MXP_P1_INFO) & 0x1f)); | |||||
} | |||||
static void | |||||
cmn600_dump_node_recursive(struct cmn600_node *node, int lvl) | |||||
{ | |||||
int i; | |||||
cmn600_dump_node(node, lvl); | |||||
for (i = 0; i < node->nd_child_count; i++) { | |||||
cmn600_dump_node_recursive(node->nd_children[i], lvl + 1); | |||||
} | |||||
} | |||||
static void | |||||
cmn600_dump_nodes_tree(struct cmn600_softc *sc) | |||||
{ | |||||
device_printf(sc->sc_dev, " nodes:\n"); | |||||
cmn600_dump_node_recursive(sc->sc_rootnode, 0); | |||||
} | |||||
static int | |||||
cmn600_sysctl_dump_nodes(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct cmn600_softc *sc; | |||||
uint32_t val; | |||||
int err; | |||||
sc = (struct cmn600_softc *)arg1; | |||||
val = 0; | |||||
err = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (err) | |||||
return (err); | |||||
if (val != 0) | |||||
cmn600_dump_nodes_tree(sc); | |||||
return (0); | |||||
} | |||||
static struct cmn600_node * | |||||
cmn600_create_node(struct cmn600_softc *sc, off_t node_offset, | |||||
struct cmn600_node *parent, int lvl) | |||||
{ | |||||
struct cmn600_node *node; | |||||
off_t child_offset; | |||||
uint64_t val; | |||||
int i; | |||||
node = malloc(sizeof(struct cmn600_node), M_DEVBUF, M_WAITOK); | |||||
if (node == NULL) | |||||
return (NULL); | |||||
Not Done Inline ActionsThere's no need to case the return value of malloc. andrew: There's no need to case the return value of malloc. | |||||
Done Inline ActionsDone. Thanks! ray: Done. Thanks! | |||||
node->sc = sc; | |||||
node->nd_offset = node_offset; | |||||
node->nd_parent = parent; | |||||
node->nd_read4 = cmn600_node_read4; | |||||
node->nd_read8 = cmn600_node_read8; | |||||
node->nd_write4 = cmn600_node_write4; | |||||
node->nd_write8 = cmn600_node_write8; | |||||
val = node->nd_read8(node, POR_CFGM_NODE_INFO); | |||||
node->nd_type = FLD(val, POR_CFGM_NODE_INFO_NODE_TYPE); | |||||
Not Done Inline ActionsWhy are these function pointers? andrew: Why are these function pointers? | |||||
Done Inline ActionsOOP like mode :) ray: OOP like mode :)
At least names are shorter on usage. | |||||
node->nd_id = FLD(val, POR_CFGM_NODE_INFO_NODE_ID); | |||||
node->nd_logical_id = FLD(val, POR_CFGM_NODE_INFO_LOGICAL_ID); | |||||
val = node->nd_read8(node, POR_CFGM_CHILD_INFO); | |||||
node->nd_child_count = FLD(val, POR_CFGM_CHILD_INFO_CHILD_COUNT); | |||||
child_offset = FLD(val, POR_CFGM_CHILD_INFO_CHILD_PTR_OFFSET); | |||||
if (parent == NULL) { | |||||
/* Find XP node with Id 8. It have to be last in a row. */ | |||||
for (i = 0; i < node->nd_child_count; i++) { | |||||
val = node->nd_read8(node, child_offset + (i * 8)); | |||||
val &= POR_CFGM_CHILD_POINTER_BASE_MASK; | |||||
val = RD8(sc, val + POR_CFGM_NODE_INFO); | |||||
if (FLD(val, POR_CFGM_NODE_INFO_NODE_ID) != 8) | |||||
continue; | |||||
sc->sc_mesh_x = FLD(val, POR_CFGM_NODE_INFO_LOGICAL_ID); | |||||
sc->sc_mesh_y = node->nd_child_count / sc->sc_mesh_x; | |||||
if (bootverbose) | |||||
printf("Mesh width X/Y %d/%d\n", sc->sc_mesh_x, | |||||
sc->sc_mesh_y); | |||||
if ((sc->sc_mesh_x > 4) || (sc->sc_mesh_y > 4)) | |||||
sc->sc_longid = 1; | |||||
break; | |||||
} | |||||
val = node->nd_read8(node, POR_INFO_GLOBAL); | |||||
sc->sc_r2 = (val & POR_INFO_GLOBAL_R2_ENABLE) ? 1 : 0; | |||||
val = node->nd_read4(node, POR_CFGM_PERIPH_ID_2_PERIPH_ID_3); | |||||
sc->sc_rev = FLD(val, POR_CFGM_PERIPH_ID_2_REV); | |||||
if (bootverbose) | |||||
printf(" Rev: %d, R2_ENABLE = %s\n", sc->sc_rev, | |||||
sc->sc_r2 ? "true" : "false"); | |||||
} | |||||
node->nd_sub = FLD(node->nd_id, NODE_ID_SUB); | |||||
node->nd_port = FLD(node->nd_id, NODE_ID_PORT); | |||||
node->nd_paired = 0; | |||||
if (sc->sc_longid == 1) { | |||||
node->nd_x = FLD(node->nd_id, NODE_ID_X3B); | |||||
node->nd_y = FLD(node->nd_id, NODE_ID_Y3B); | |||||
} else { | |||||
node->nd_x = FLD(node->nd_id, NODE_ID_X2B); | |||||
node->nd_y = FLD(node->nd_id, NODE_ID_Y2B); | |||||
} | |||||
if (bootverbose) { | |||||
Not Done Inline ActionsWhat are these magic numbers? andrew: What are these magic numbers? | |||||
Done Inline ActionsDone. Thanks! ray: Done. Thanks! | |||||
cmn600_dump_node(node, lvl); | |||||
} | |||||
node->nd_children = (struct cmn600_node **)mallocarray( | |||||
node->nd_child_count, sizeof(struct cmn600_node *), M_DEVBUF, | |||||
M_WAITOK); | |||||
if (node->nd_children == NULL) | |||||
goto FAIL; | |||||
for (i = 0; i < node->nd_child_count; i++) { | |||||
val = node->nd_read8(node, child_offset + (i * 8)); | |||||
node->nd_children[i] = cmn600_create_node(sc, val & | |||||
POR_CFGM_CHILD_POINTER_BASE_MASK, node, lvl + 1); | |||||
} | |||||
switch (node->nd_type) { | |||||
case NODE_TYPE_DTC: | |||||
sc->sc_dtcnode = node; | |||||
break; | |||||
case NODE_TYPE_DVM: | |||||
sc->sc_dvmnode = node; | |||||
break; | |||||
case NODE_TYPE_XP: | |||||
sc->sc_xpnodes[node->nd_id >> NODE_ID_X2B_SHIFT] = node; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return (node); | |||||
FAIL: | |||||
free(node, M_DEVBUF); | |||||
return (NULL); | |||||
} | |||||
static void | |||||
cmn600_destroy_node(struct cmn600_node *node) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < node->nd_child_count; i++) { | |||||
if (node->nd_children[i] == NULL) | |||||
continue; | |||||
cmn600_destroy_node(node->nd_children[i]); | |||||
} | |||||
free(node->nd_children, M_DEVBUF); | |||||
free(node, M_DEVBUF); | |||||
} | |||||
static int | |||||
cmn600_find_node(struct cmn600_softc *sc, int node_id, int type, | |||||
struct cmn600_node **node) | |||||
{ | |||||
struct cmn600_node *xp, *child; | |||||
uint8_t xp_xy; | |||||
int i; | |||||
switch (type) { | |||||
case NODE_TYPE_INVALID: | |||||
return (ENXIO); | |||||
case NODE_TYPE_CFG: | |||||
*node = sc->sc_rootnode; | |||||
return (0); | |||||
case NODE_TYPE_DTC: | |||||
*node = sc->sc_dtcnode; | |||||
return (0); | |||||
case NODE_TYPE_DVM: | |||||
*node = sc->sc_dvmnode; | |||||
return (0); | |||||
default: | |||||
break; | |||||
} | |||||
xp_xy = node_id >> NODE_ID_X2B_SHIFT; | |||||
if (xp_xy >= 64) | |||||
return (ENXIO); | |||||
if (sc->sc_xpnodes[xp_xy] == NULL) | |||||
return (ENOENT); | |||||
switch (type) { | |||||
case NODE_TYPE_XP: | |||||
*node = sc->sc_xpnodes[xp_xy]; | |||||
return (0); | |||||
default: | |||||
xp = sc->sc_xpnodes[xp_xy]; | |||||
for (i = 0; i < xp->nd_child_count; i++) { | |||||
child = xp->nd_children[i]; | |||||
if (child->nd_id == node_id && child->nd_type == type) { | |||||
*node = child; | |||||
return (0); | |||||
} | |||||
} | |||||
} | |||||
return (ENOENT); | |||||
} | |||||
int | |||||
pmu_cmn600_alloc_localpmc(void *arg, int nodeid, int node_type, int *counter) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
uint32_t new, old; | |||||
int i, ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
switch (node_type) { | |||||
case NODE_TYPE_CXLA: | |||||
break; | |||||
default: | |||||
node_type = NODE_TYPE_XP; | |||||
/* Parent XP node has always zero port and device bits. */ | |||||
nodeid &= ~0x07; | |||||
} | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
for (i = 0; i < 4; i++) { | |||||
new = old = node->nd_paired; | |||||
if (old == 0xf) | |||||
return (EBUSY); | |||||
if ((old & (1 << i)) != 0) | |||||
continue; | |||||
new |= 1 << i; | |||||
if (atomic_cmpset_32(&node->nd_paired, old, new) != 0) | |||||
break; | |||||
} | |||||
*counter = i; | |||||
return (0); | |||||
} | |||||
int | |||||
pmu_cmn600_free_localpmc(void *arg, int nodeid, int node_type, int counter) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
uint32_t new, old; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
switch (node_type) { | |||||
case NODE_TYPE_CXLA: | |||||
break; | |||||
default: | |||||
node_type = NODE_TYPE_XP; | |||||
} | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
do { | |||||
new = old = node->nd_paired; | |||||
new &= ~(1 << counter); | |||||
} while (atomic_cmpset_32(&node->nd_paired, old, new) == 0); | |||||
return (0); | |||||
} | |||||
uint32_t | |||||
pmu_cmn600_rd4(void *arg, int nodeid, int node_type, off_t reg) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (UINT32_MAX); | |||||
return (cmn600_node_read4(node, reg)); | |||||
} | |||||
int | |||||
pmu_cmn600_wr4(void *arg, int nodeid, int node_type, off_t reg, uint32_t val) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
cmn600_node_write4(node, reg, val); | |||||
return (0); | |||||
} | |||||
uint64_t | |||||
pmu_cmn600_rd8(void *arg, int nodeid, int node_type, off_t reg) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (UINT64_MAX); | |||||
return (cmn600_node_read8(node, reg)); | |||||
} | |||||
int | |||||
pmu_cmn600_wr8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
cmn600_node_write8(node, reg, val); | |||||
return (0); | |||||
} | |||||
int | |||||
pmu_cmn600_set8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
cmn600_node_write8(node, reg, cmn600_node_read8(node, reg) | val); | |||||
return (0); | |||||
} | |||||
int | |||||
pmu_cmn600_clr8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
cmn600_node_write8(node, reg, cmn600_node_read8(node, reg) & ~val); | |||||
return (0); | |||||
} | |||||
int | |||||
pmu_cmn600_md8(void *arg, int nodeid, int node_type, off_t reg, uint64_t mask, | |||||
uint64_t val) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
int ret; | |||||
sc = (struct cmn600_softc *)arg; | |||||
ret = cmn600_find_node(sc, nodeid, node_type, &node); | |||||
if (ret != 0) | |||||
return (ret); | |||||
cmn600_node_write8(node, reg, (cmn600_node_read8(node, reg) & ~mask) | | |||||
val); | |||||
return (0); | |||||
} | |||||
static int | |||||
cmn600_acpi_probe(device_t dev) | |||||
{ | |||||
int err; | |||||
err = ACPI_ID_PROBE(device_get_parent(dev), dev, cmn600_ids, NULL); | |||||
if (err <= 0) | |||||
device_set_desc(dev, "Arm CoreLink CMN-600 Coherent Mesh Network"); | |||||
return (err); | |||||
} | |||||
static int | |||||
cmn600_acpi_attach(device_t dev) | |||||
{ | |||||
struct sysctl_ctx_list *ctx; | |||||
struct sysctl_oid_list *child; | |||||
struct cmn600_softc *sc; | |||||
int cpu, domain, i, u; | |||||
const char *dname; | |||||
rman_res_t count, periph_base, rootnode_base; | |||||
struct cmn600_node *node; | |||||
dname = device_get_name(dev); | |||||
sc = device_get_softc(dev); | |||||
sc->sc_dev = dev; | |||||
u = device_get_unit(dev); | |||||
sc->sc_unit = u; | |||||
domain = 0; | |||||
if ((resource_int_value(dname, u, "domain", &domain) == 0 || | |||||
bus_get_domain(dev, &domain) == 0) && domain < MAXMEMDOM) { | |||||
sc->sc_domain = domain; | |||||
} | |||||
if (domain == -1) /* NUMA not supported. Use single domain. */ | |||||
domain = 0; | |||||
sc->sc_domain = domain; | |||||
device_printf(dev, "domain=%d\n", sc->sc_domain); | |||||
cpu = CPU_FFS(&cpuset_domain[domain]) - 1; | |||||
i = bus_alloc_resources(dev, cmn600_res_spec, sc->sc_res); | |||||
if (i != 0) { | |||||
device_printf(dev, "cannot allocate resources for device (%d)\n", | |||||
i); | |||||
return (i); | |||||
} | |||||
bus_get_resource(dev, cmn600_res_spec[0].type, cmn600_res_spec[0].rid, | |||||
&periph_base, &count); | |||||
bus_get_resource(dev, cmn600_res_spec[1].type, cmn600_res_spec[1].rid, | |||||
&rootnode_base, &count); | |||||
rootnode_base -= periph_base; | |||||
if (bootverbose) | |||||
printf("ROOTNODE at %lx x %lx\n", rootnode_base, count); | |||||
sc->sc_rootnode = cmn600_create_node(sc, rootnode_base, NULL, 0); | |||||
ctx = device_get_sysctl_ctx(sc->sc_dev); | |||||
child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "dump_nodes", CTLTYPE_INT | | |||||
CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, cmn600_sysctl_dump_nodes, | |||||
"U", "Dump CMN-600 nodes tree"); | |||||
node = sc->sc_dtcnode; | |||||
if (node == NULL) | |||||
return (ENXIO); | |||||
cmn600_pmc_register(sc->sc_unit, (void *)sc, domain); | |||||
node->nd_write8(node, POR_DT_PMCR, 0); | |||||
node->nd_write8(node, POR_DT_PMOVSR_CLR, POR_DT_PMOVSR_ALL); | |||||
node->nd_write8(node, POR_DT_PMCR, POR_DT_PMCR_OVFL_INTR_EN); | |||||
node->nd_write8(node, POR_DT_DTC_CTL, POR_DT_DTC_CTL_DT_EN); | |||||
if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, | |||||
cmn600_intr, NULL, sc, &sc->sc_ih)) { | |||||
bus_release_resources(dev, cmn600_res_spec, sc->sc_res); | |||||
device_printf(dev, "cannot setup interrupt handler\n"); | |||||
cmn600_acpi_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
if (bus_bind_intr(dev, sc->sc_res[2], cpu)) { | |||||
bus_teardown_intr(dev, sc->sc_res[2], sc->sc_ih); | |||||
bus_release_resources(dev, cmn600_res_spec, sc->sc_res); | |||||
device_printf(dev, "cannot setup interrupt handler\n"); | |||||
cmn600_acpi_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
cmn600_acpi_detach(device_t dev) | |||||
{ | |||||
struct cmn600_softc *sc; | |||||
struct cmn600_node *node; | |||||
sc = device_get_softc(dev); | |||||
if (sc->sc_res[2] != NULL) { | |||||
bus_teardown_intr(dev, sc->sc_res[2], sc->sc_ih); | |||||
} | |||||
node = sc->sc_dtcnode; | |||||
node->nd_write4(node, POR_DT_DTC_CTL, | |||||
node->nd_read4(node, POR_DT_DTC_CTL) & ~POR_DT_DTC_CTL_DT_EN); | |||||
node->nd_write8(node, POR_DT_PMOVSR_CLR, POR_DT_PMOVSR_ALL); | |||||
cmn600_pmc_unregister(sc->sc_unit); | |||||
cmn600_destroy_node(sc->sc_rootnode); | |||||
bus_release_resources(dev, cmn600_res_spec, sc->sc_res); | |||||
return (0); | |||||
} | |||||
int | |||||
cmn600_pmu_intr_cb(void *arg, int (*handler)(struct trapframe *tf, int unit, | |||||
int i)) | |||||
{ | |||||
struct cmn600_softc *sc; | |||||
sc = (struct cmn600_softc *) arg; | |||||
sc->sc_pmu_ih = handler; | |||||
return (0); | |||||
} | |||||
static int | |||||
cmn600_intr(void *arg) | |||||
{ | |||||
struct cmn600_node *node; | |||||
struct cmn600_softc *sc; | |||||
struct trapframe *tf; | |||||
uint64_t mask, ready, val; | |||||
int i; | |||||
tf = PCPU_GET(curthread)->td_intr_frame; | |||||
sc = (struct cmn600_softc *) arg; | |||||
node = sc->sc_dtcnode; | |||||
val = node->nd_read8(node, POR_DT_PMOVSR); | |||||
if (val & POR_DT_PMOVSR_CYCLE_COUNTER) | |||||
node->nd_write8(node, POR_DT_PMOVSR_CLR, | |||||
POR_DT_PMOVSR_CYCLE_COUNTER); | |||||
if (val & POR_DT_PMOVSR_EVENT_COUNTERS) { | |||||
for (ready = 0, i = 0; i < 8; i++) { | |||||
mask = 1 << i; | |||||
if ((val & mask) == 0) | |||||
continue; | |||||
if (sc->sc_pmu_ih != NULL) | |||||
sc->sc_pmu_ih(tf, sc->sc_unit, i); | |||||
ready |= mask; | |||||
} | |||||
node->nd_write8(node, POR_DT_PMOVSR_CLR, ready); | |||||
} | |||||
return (FILTER_HANDLED); | |||||
} | |||||
static device_method_t cmn600_acpi_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_probe, cmn600_acpi_probe), | |||||
DEVMETHOD(device_attach, cmn600_acpi_attach), | |||||
DEVMETHOD(device_detach, cmn600_acpi_detach), | |||||
/* End */ | |||||
DEVMETHOD_END | |||||
}; | |||||
static driver_t cmn600_acpi_driver = { | |||||
"cmn600", | |||||
cmn600_acpi_methods, | |||||
sizeof(struct cmn600_softc), | |||||
}; | |||||
static devclass_t cmn600_acpi_devclass; | |||||
DRIVER_MODULE(cmn600, acpi, cmn600_acpi_driver, cmn600_acpi_devclass, 0, 0); | |||||
MODULE_VERSION(cmn600, 1); |
Is this used?