/**
* $Id: k10s_kstats.c 755 2012-06-25 06:15:47Z elkner $
*
* * CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* All Rights reserved.
* CDDL HEADER END
*/
/*
* Copyright (c) 2011,2012 by Jens Elkner,
* Otto-von-Guericke Universitaet Magdeburg. All rights reserved.
*/
#include "k10s_kstats.h"
#ifdef SDEBUG
#define K10SDBG cmn_err
#else
#define K10SDBG //
#endif
#define K10S_KSTAT_DATA_NAME_FMT K10S_KSTAT_DATA_NAME_PREFIX"%d"
#define K10S_KSTAT_DATA_NAME_FMT2 K10S_KSTAT_DATA_NAME_PREFIX"%d core %d"
/* needs to be aligned with k10s_kstats.h:k10s_stats_offset_t */
static const kstat_named_t k10s_stats[] = {
{ "Silicon CPU Revision", KSTAT_DATA_CHAR },
{ "Family", KSTAT_DATA_CHAR },
{ "Erratum", KSTAT_DATA_UINT32 },
{ "Thermtrip", KSTAT_DATA_CHAR },
{ "HTC capable", KSTAT_DATA_CHAR },
{ "T Case/Die Max [C/1000]", KSTAT_DATA_INT32 },
{ "T Diode Offset [C/1000]", KSTAT_DATA_INT32 },
{ "HTC", KSTAT_DATA_CHAR },
{ "HTC slew-controlled", KSTAT_DATA_CHAR },
{ "HTC Limit [C/1000]", KSTAT_DATA_INT32 },
{ "HTC Hysteresis [C/1000]", KSTAT_DATA_INT32 },
{ "STC", KSTAT_DATA_CHAR },
{ "STC slew-controlled", KSTAT_DATA_CHAR },
{ "STC Limit [C/1000]", KSTAT_DATA_INT32 },
{ "STC Hysteresis [C/1000]", KSTAT_DATA_INT32 },
};
static int
k10s_data_update(kstat_t *ksp, int rw, int n) {
kstat_named_t *knp;
if (rw == KSTAT_WRITE) {
return (EACCES);
}
k10sensor_t *k10p = ksp->ks_private;
if (k10p == NULL) {
cmn_err(CE_WARN, "k10s_data_update %d - k10p == NULL", n);
return (ENXIO);
}
K10SDBG(CE_WARN, "k10s_data_update %d/%d", n, k10p->v->chipId);
knp = ksp->ks_data;
knp->value.i32 = getControlTemp(k10p->pci_hdl, k10p->v, n);
return 0;
}
static int
k10s_data_update0(kstat_t *ksp, int rw) {
return k10s_data_update(ksp, rw, 0);
}
static int
k10s_data_update1(kstat_t *ksp, int rw) {
return k10s_data_update(ksp, rw, 1);
}
void
k10s_kstat_create(k10sensor_t *k10p) {
int i;
char buf[KSTAT_STRLEN];
kstat_named_t *knp;
uint_t ks_ndata = sizeof(k10s_stats) / sizeof(kstat_named_t);
cpu_vars_t *v = k10p->v;
kstat_t *ksp;
sprintf(buf, K10S_KSTAT_INFO_NAME" %d", v->chipId);
ksp = kstat_create(KMODNAME, v->chipId, buf, K10S_KSTAT_CLASS,
KSTAT_TYPE_NAMED, ks_ndata, 0);
if (ksp == NULL) {
return;
}
K10SDBG(CE_WARN, "kstat created info#%d = %p", v->chipId, ksp);
bcopy(k10s_stats, ksp->ks_data, sizeof(k10s_stats));
if (v->xfamilyModel == 0x30 || v->xfamilyModel == 0x50) {
/* 12h, 14h */
knp = ksp->ks_data;
knp += KS_ATC_ENABLED;
for (i=KS_ATC_ENABLED; i < ks_ndata; i++, knp++) {
strcpy(knp->name + 1, k10s_stats[i].name);
knp->name[0] = 'L';
knp->name[1] = 'H';
}
}
/* init data */
knp = ksp->ks_data;
sprintf((knp++)->value.c, "%s (%02x-%02x)", v->revision,
v->xfamilyModel, v->modelStepping);
strcpy((knp++)->value.c, cat_range_map[v->catIdx].name);
(knp++)->value.ui32, v->erratum;
strcpy((knp++)->value.c,
v->flags & HTC_THERMTRIP ? "enabled" : "n/a");
strcpy((knp++)->value.c,
v->flags & HTC_CAPABLE ? "yes" : "no");
(knp++)->value.i32 = v->t_case_max;
(knp++)->value.i32 = v->diode_offset;
strcpy((knp++)->value.c,
v->limits[HTC].flags & TC_ENABLED ? "enabled" : "disabled");
strcpy((knp++)->value.c,
v->limits[HTC].flags & TC_SLEWED ? "yes" : "no");
(knp++)->value.i32 = v->limits[HTC].tcrit;
(knp++)->value.i32 = v->limits[HTC].threshold;
strcpy((knp++)->value.c,
v->limits[STC].flags & TC_ENABLED ? "enabled" : "disabled");
strcpy((knp++)->value.c,
v->limits[STC].flags & TC_SLEWED ? "yes" : "no");
(knp++)->value.i32 = v->limits[STC].tcrit;
(knp++)->value.i32 = v->limits[STC].threshold;
ksp->ks_private = k10p;
kstat_install(ksp);
k10p->ksp_info = ksp;
if (v->flags & DC_0Fh) {
sprintf(buf, K10S_KSTAT_DATA_NAME_FMT2, v->chipId, 0);
ksp = kstat_create(KMODNAME, v->chipId, buf, K10S_KSTAT_CLASS,
KSTAT_TYPE_NAMED, 1, 0);
knp = ksp->ks_data;
if (ksp != NULL) {
K10SDBG(CE_WARN, "kstat created data0#%d = %p", v->chipId, ksp);
kstat_named_init(knp, K10S_KSTAT_DATA_VALUE_NAME, KSTAT_DATA_INT32);
ksp->ks_private = k10p;
ksp->ks_update = k10s_data_update0;
kstat_install(ksp);
k10p->ksp_data[0] = ksp;
}
sprintf(buf, K10S_KSTAT_DATA_NAME_FMT2, v->chipId, 1);
ksp = kstat_create(KMODNAME, v->chipId, buf, K10S_KSTAT_CLASS,
KSTAT_TYPE_NAMED, 1, 0);
if (ksp != NULL) {
K10SDBG(CE_WARN, "kstat created data1#%d = %p", v->chipId, ksp);
knp = ksp->ks_data;
kstat_named_init(knp, K10S_KSTAT_DATA_VALUE_NAME, KSTAT_DATA_INT32);
ksp->ks_private = k10p;
ksp->ks_update = k10s_data_update1;
kstat_install(ksp);
k10p->ksp_data[1] = ksp;
}
} else if (v->xfamilyModel >= 0x04) {
sprintf(buf, K10S_KSTAT_DATA_NAME_FMT, v->chipId);
ksp = kstat_create(KMODNAME, v->chipId, buf, K10S_KSTAT_CLASS,
KSTAT_TYPE_NAMED, 1, 0);
if (ksp != NULL) {
K10SDBG(CE_WARN, "kstat created data0#%d = %p", v->chipId, ksp);
knp = ksp->ks_data;
kstat_named_init(knp, K10S_KSTAT_DATA_VALUE_NAME, KSTAT_DATA_INT32);
ksp->ks_private = k10p;
ksp->ks_update = k10s_data_update0;
kstat_install(ksp);
k10p->ksp_data[0] = ksp;
}
}
}
void
k10s_kstat_destroy(k10sensor_t *k10p) {
if (k10p->ksp_info != NULL) {
K10SDBG(CE_WARN, "kstat delete info#%d = %p", k10p->v->chipId, k10p->ksp_info);
kstat_delete(k10p->ksp_info);
k10p->ksp_info = NULL;
}
if (k10p->ksp_data[0] != NULL) {
K10SDBG(CE_WARN, "kstat delete data0#%d = %p", k10p->v->chipId, k10p->ksp_data[0]);
kstat_delete(k10p->ksp_data[0]);
k10p->ksp_data[0] = NULL;
}
if (k10p->ksp_data[1] != NULL) {
K10SDBG(CE_WARN, "kstat delete data2#%d = %p", k10p->v->chipId, k10p->ksp_data[1]);
kstat_delete(k10p->ksp_data[1]);
k10p->ksp_data[1] = NULL;
}
}