dlg@r5s ~$ kstat 'cpu*'::: dt::: cpu0:0:dt-opp:0 freq: 816 MHz supply: 0.90 VDC cpu0:0:mach:0 impl: ARM part: Cortex-A55 rev: r2p0 cpu1:0:dt-opp:0 freq: 816 MHz supply: 0.90 VDC cpu1:0:mach:0 impl: ARM part: Cortex-A55 rev: r2p0 cpu2:0:dt-opp:0 freq: 816 MHz supply: 0.90 VDC cpu2:0:mach:0 impl: ARM part: Cortex-A55 rev: r2p0 cpu3:0:dt-opp:0 freq: 816 MHz supply: 0.90 VDC cpu3:0:mach:0 impl: ARM part: Cortex-A55 rev: r2p0 dt:0:thermal-zone:0 name: cpu-thermal temperature: 52.50 degC trip-point-node: 0 trip-type: none active-cooling: false dt:0:thermal-zone:1 name: gpu-thermal temperature: 53.12 degC trip-point-node: 0 trip-type: none active-cooling: false Index: arch/arm64/arm64/cpu.c =================================================================== RCS file: /cvs/src/sys/arch/arm64/arm64/cpu.c,v retrieving revision 1.99 diff -u -p -r1.99 cpu.c --- arch/arm64/arm64/cpu.c 24 Oct 2023 13:20:09 -0000 1.99 +++ arch/arm64/arm64/cpu.c 17 Nov 2023 00:41:11 -0000 @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "kstat.h" + #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include @@ -250,6 +253,11 @@ void cpu_psci_init(struct cpu_info *); void cpu_flush_bp_noop(void); void cpu_flush_bp_psci(void); +#if NKSTAT > 0 +void cpu_kstat_attach(struct cpu_info *ci); +void cpu_opp_kstat_attach(struct cpu_info *ci); +#endif + void cpu_identify(struct cpu_info *ci) { @@ -937,6 +945,10 @@ cpu_attach(struct device *parent, struct } cpu_init(); + +#if NKSTAT > 0 + cpu_kstat_attach(ci); +#endif #ifdef MULTIPROCESSOR } #endif @@ -1181,6 +1193,10 @@ cpu_init_secondary(struct cpu_info *ci) spllower(IPL_NONE); +#if NKSTAT > 0 + cpu_kstat_attach(ci); +#endif + sched_toidle(); } @@ -1534,6 +1550,10 @@ cpu_opp_mountroot(struct device *self) if (ot == NULL) continue; +#if NKSTAT > 0 + cpu_opp_kstat_attach(ci); +#endif + /* Skip if this table is shared and we're not the master. */ if (ot->ot_master && ot->ot_master != ci) continue; @@ -1817,3 +1837,153 @@ cpu_psci_init(struct cpu_info *ci) ci->ci_psci_suspend_param = OF_getpropint(node, "arm,psci-suspend-param", 0); } + +#if NKSTAT > 0 + +struct cpu_kstats { + struct kstat_kv ck_impl; + struct kstat_kv ck_part; + struct kstat_kv ck_rev; +}; + +void +cpu_kstat_attach(struct cpu_info *ci) +{ + struct kstat *ks; + struct cpu_kstats *ck; + + uint64_t midr, impl, part; + const char *impl_name = NULL, *part_name = NULL; + const struct cpu_cores *coreselecter = cpu_cores_none; + size_t i; + + ks = kstat_create(ci->ci_dev->dv_xname, 0, "mach", 0, KSTAT_T_KV, 0); + if (ks == NULL) { + printf("%s: unable to create cpu kstats\n", + ci->ci_dev->dv_xname); + /* printf? */ + return; + } + + ck = malloc(sizeof(*ck), M_DEVBUF, M_WAITOK); + + midr = READ_SPECIALREG(midr_el1); + impl = CPU_IMPL(midr); + part = CPU_PART(midr); + + for (i = 0; cpu_implementers[i].name; i++) { + if (impl == cpu_implementers[i].id) { + impl_name = cpu_implementers[i].name; + coreselecter = cpu_implementers[i].corelist; + break; + } + } + + if (impl_name) { + kstat_kv_init(&ck->ck_impl, "impl", KSTAT_KV_T_ISTR); + strlcpy(kstat_kv_istr(&ck->ck_impl), impl_name, + sizeof(kstat_kv_istr(&ck->ck_impl))); + } else + kstat_kv_init(&ck->ck_impl, "impl", KSTAT_KV_T_NULL); + + for (i = 0; coreselecter[i].name; i++) { + if (part == coreselecter[i].id) { + part_name = coreselecter[i].name; + break; + } + } + + if (part_name) { + kstat_kv_init(&ck->ck_part, "part", KSTAT_KV_T_ISTR); + strlcpy(kstat_kv_istr(&ck->ck_part), part_name, + sizeof(kstat_kv_istr(&ck->ck_part))); + } else + kstat_kv_init(&ck->ck_part, "part", KSTAT_KV_T_NULL); + + kstat_kv_init(&ck->ck_rev, "rev", KSTAT_KV_T_ISTR); + snprintf(kstat_kv_istr(&ck->ck_rev), sizeof(kstat_kv_istr(&ck->ck_rev)), + "r%llup%llu", CPU_VAR(midr), CPU_REV(midr)); + + ks->ks_softc = ci; + ks->ks_data = ck; + ks->ks_datalen = sizeof(*ck); + ks->ks_read = kstat_read_nop; + + kstat_install(ks); + + /* XXX should we have a ci->ci_kstat = ks? */ +} + +struct cpu_opp_kstats { + struct kstat_kv coppk_freq; + struct kstat_kv coppk_supply_v; +}; + +int +cpu_opp_kstat_read(struct kstat *ks) +{ + struct cpu_info *ci = ks->ks_softc; + struct cpu_opp_kstats *coppk = ks->ks_data; + + struct opp_table *ot = ci->ci_opp_table; + struct cpu_info *oci = ot->ot_master; + struct timespec now, diff; + + /* rate limit */ + getnanouptime(&now); + timespecsub(&now, &ks->ks_updated, &diff); + if (diff.tv_sec < 1) + return (0); + + if (oci == NULL) + oci = ci; + + kstat_kv_freq(&coppk->coppk_freq) = + clock_get_frequency(oci->ci_node, NULL); + + if (oci->ci_cpu_supply) { + kstat_kv_volts(&coppk->coppk_supply_v) = + regulator_get_voltage(oci->ci_cpu_supply); + } + + ks->ks_updated = now; + + return (0); +} + +void +cpu_opp_kstat_attach(struct cpu_info *ci) +{ + struct kstat *ks; + struct cpu_opp_kstats *coppk; + struct opp_table *ot = ci->ci_opp_table; + struct cpu_info *oci = ot->ot_master; + + if (oci == NULL) + oci = ci; + + ks = kstat_create(ci->ci_dev->dv_xname, 0, "dt-opp", 0, + KSTAT_T_KV, 0); + if (ks == NULL) { + printf("%s: unable to create cpu dt-opp kstats\n", + ci->ci_dev->dv_xname); + return; + } + + coppk = malloc(sizeof(*coppk), M_DEVBUF, M_WAITOK); + + kstat_kv_init(&coppk->coppk_freq, "freq", KSTAT_KV_T_FREQ); + kstat_kv_init(&coppk->coppk_supply_v, "supply", + oci->ci_cpu_supply ? KSTAT_KV_T_VOLTS_DC : KSTAT_KV_T_NULL); + + ks->ks_softc = oci; + ks->ks_data = coppk; + ks->ks_datalen = sizeof(*coppk); + ks->ks_read = cpu_opp_kstat_read; + + kstat_install(ks); + + /* XXX should we have a ci->ci_opp_kstat = ks? */ +} + +#endif /* NKSTAT > 0 */ Index: dev/ofw/ofw_thermal.c =================================================================== RCS file: /cvs/src/sys/dev/ofw/ofw_thermal.c,v retrieving revision 1.7 diff -u -p -r1.7 ofw_thermal.c --- dev/ofw/ofw_thermal.c 31 Dec 2020 11:11:22 -0000 1.7 +++ dev/ofw/ofw_thermal.c 17 Nov 2023 00:41:11 -0000 @@ -15,12 +15,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "kstat.h" + #include #include #include #include #include #include +#include +#include #include @@ -36,6 +40,7 @@ LIST_HEAD(, cooling_device) cooling_devi struct taskq *tztq; struct trippoint { + int tp_node; int32_t tp_temperature; uint32_t tp_hysteresis; int tp_type; @@ -48,6 +53,14 @@ struct trippoint { #define THERMAL_HOT 3 #define THERMAL_CRITICAL 4 +static const char *trip_types[] = { + [THERMAL_NONE] = "none", + [THERMAL_ACTIVE] = "active", + [THERMAL_PASSIVE] = "passive", + [THERMAL_HOT] = "hot", + [THERMAL_CRITICAL] = "critical", +}; + struct cmap { uint32_t *cm_cdev; uint32_t *cm_cdevend; @@ -82,8 +95,16 @@ struct thermal_zone { LIST_HEAD(, cdev) tz_cdevs; int32_t tz_temperature; + + struct rwlock tz_lock; + struct kstat *tz_kstat; }; +#if NKSTAT > 0 +static void thermal_zone_kstat_attach(struct thermal_zone *); +static void thermal_zone_kstat_update(struct thermal_zone *); +#endif /* NKSTAT > 0 */ + LIST_HEAD(, thermal_zone) thermal_zones = LIST_HEAD_INITIALIZER(thermal_zones); @@ -324,6 +345,9 @@ thermal_zone_poll(void *arg) out: tz->tz_temperature = temp; +#if NKSTAT > 0 + thermal_zone_kstat_update(tz); +#endif if (tz->tz_tp && tz->tz_tp->tp_type == THERMAL_PASSIVE) polling_delay = tz->tz_polling_delay_passive; else @@ -331,6 +355,23 @@ out: timeout_add_msec(&tz->tz_poll_to, polling_delay); } +static int +thermal_zone_triptype(const char *prop) +{ + size_t i; + + for (i = 0; i < nitems(trip_types); i++) { + const char *name = trip_types[i]; + if (name == NULL) + continue; + + if (strcmp(name, prop) == 0) + return (i); + } + + return (THERMAL_NONE); +} + void thermal_zone_init(int node) { @@ -351,6 +392,7 @@ thermal_zone_init(int node) tz = malloc(sizeof(struct thermal_zone), M_DEVBUF, M_ZERO | M_WAITOK); tz->tz_node = node; + rw_init(&tz->tz_lock, "tzlk"); OF_getprop(node, "name", &tz->tz_name, sizeof(tz->tz_name)); tz->tz_name[sizeof(tz->tz_name) - 1] = 0; @@ -394,17 +436,11 @@ thermal_zone_init(int node) break; } tp = &tz->tz_trips[i]; + tp->tp_node = node; tp->tp_temperature = temp; tp->tp_hysteresis = OF_getpropint(node, "hysteresis", 0); OF_getprop(node, "type", type, sizeof(type)); - if (strcmp(type, "active") == 0) - tp->tp_type = THERMAL_ACTIVE; - else if (strcmp(type, "passive") == 0) - tp->tp_type = THERMAL_PASSIVE; - else if (strcmp(type, "hot") == 0) - tp->tp_type = THERMAL_HOT; - else if (strcmp(type, "critical") == 0) - tp->tp_type = THERMAL_CRITICAL; + tp->tp_type = thermal_zone_triptype(type); tp->tp_phandle = OF_getpropint(node, "phandle", 0); tp++; } @@ -465,6 +501,10 @@ thermal_zone_init(int node) if (tz->tz_polling_delay > 0) timeout_add_msec(&tz->tz_poll_to, tz->tz_polling_delay); LIST_INSERT_HEAD(&thermal_zones, tz, tz_list); + +#if NKSTAT > 0 + thermal_zone_kstat_attach(tz); +#endif } void @@ -480,3 +520,109 @@ thermal_init(void) for (node = OF_child(node); node != 0; node = OF_peer(node)) thermal_zone_init(node); } + +#if NKSTAT > 0 + +static const char * +thermal_zone_tripname(int type) +{ + if (type >= nitems(trip_types)) + return (NULL); + + return (trip_types[type]); +} + +struct thermal_zone_kstats { + struct kstat_kv tzk_name; /* istr could be short */ + struct kstat_kv tzk_temp; + struct kstat_kv tzk_tp; + struct kstat_kv tzk_tp_type; + struct kstat_kv tzk_cooling; +}; + +static void +thermal_zone_kstat_update(struct thermal_zone *tz) +{ + struct kstat *ks = tz->tz_kstat; + struct thermal_zone_kstats *tzk; + + if (ks == NULL) + return; + + tzk = ks->ks_data; + + rw_enter_write(&tz->tz_lock); + if (tz->tz_temperature == THERMAL_SENSOR_MAX) + tzk->tzk_temp.kv_type = KSTAT_KV_T_NULL; + else { + tzk->tzk_temp.kv_type = KSTAT_KV_T_TEMP; + kstat_kv_temp(&tzk->tzk_temp) = 273150000 + + 1000 * tz->tz_temperature; + } + + if (tz->tz_tp == NULL) { + kstat_kv_u32(&tzk->tzk_tp) = 0; + strlcpy(kstat_kv_istr(&tzk->tzk_tp_type), "none", + sizeof(kstat_kv_istr(&tzk->tzk_tp_type))); + } else { + int triptype = tz->tz_tp->tp_type; + const char *tripname = thermal_zone_tripname(triptype); + + kstat_kv_u32(&tzk->tzk_tp) = tz->tz_tp->tp_node; + + if (tripname == NULL) { + snprintf(kstat_kv_istr(&tzk->tzk_tp_type), + sizeof(kstat_kv_istr(&tzk->tzk_tp_type)), + "%u", triptype); + } else { + strlcpy(kstat_kv_istr(&tzk->tzk_tp_type), tripname, + sizeof(kstat_kv_istr(&tzk->tzk_tp_type))); + } + } + + kstat_kv_bool(&tzk->tzk_cooling) = (tz->tz_cm != NULL); + + getnanouptime(&ks->ks_updated); + rw_exit_write(&tz->tz_lock); +} + +static void +thermal_zone_kstat_attach(struct thermal_zone *tz) +{ + struct kstat *ks; + struct thermal_zone_kstats *tzk; + static unsigned int unit = 0; + + ks = kstat_create("dt", 0, "thermal-zone", unit++, KSTAT_T_KV, 0); + if (ks == NULL) { + printf("unable to create thermal-zone kstats for %s", + tz->tz_name); + return; + } + + tzk = malloc(sizeof(*tzk), M_DEVBUF, M_WAITOK|M_ZERO); + + kstat_kv_init(&tzk->tzk_name, "name", KSTAT_KV_T_ISTR); + strlcpy(kstat_kv_istr(&tzk->tzk_name), tz->tz_name, + sizeof(kstat_kv_istr(&tzk->tzk_name))); + kstat_kv_init(&tzk->tzk_temp, "temperature", KSTAT_KV_T_NULL); + + /* XXX */ + kstat_kv_init(&tzk->tzk_tp, "trip-point-node", KSTAT_KV_T_UINT32); + kstat_kv_init(&tzk->tzk_tp_type, "trip-type", KSTAT_KV_T_ISTR); + strlcpy(kstat_kv_istr(&tzk->tzk_tp_type), "unknown", + sizeof(kstat_kv_istr(&tzk->tzk_tp_type))); + + kstat_kv_init(&tzk->tzk_cooling, "active-cooling", KSTAT_KV_T_BOOL); + kstat_kv_bool(&tzk->tzk_cooling) = 0; + + ks->ks_softc = tz; + ks->ks_data = tzk; + ks->ks_datalen = sizeof(*tzk); + ks->ks_read = kstat_read_nop; + kstat_set_rlock(ks, &tz->tz_lock); + + tz->tz_kstat = ks; + kstat_install(ks); +} +#endif /* NKSTAT > 0 */