xref: /illumos-gate/usr/src/uts/common/io/mlxcx/mlxcx_sensor.c (revision 16b76d3cb933ff92018a2a75594449010192eacb)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Oxide Computer Company
14  */
15 
16 #include <mlxcx.h>
17 #include <sys/sensors.h>
18 
19 /*
20  * The PRM indicates that the temperature is measured in 1/8th degrees.
21  */
22 #define	MLXCX_TEMP_GRAN	8
23 
24 /*
25  * Read a single temperature sensor entry. The ksensor framework guarantees that
26  * it will only call this once for a given sensor at any time, though multiple
27  * sensors can be in parallel.
28  */
29 static int
30 mlxcx_temperature_read(void *arg, sensor_ioctl_scalar_t *scalar)
31 {
32 	boolean_t ok;
33 	uint16_t tmp;
34 	mlxcx_register_data_t data;
35 	mlxcx_temp_sensor_t *sensor = arg;
36 	mlxcx_t *mlxp = sensor->mlts_mlx;
37 
38 	bzero(&data, sizeof (data));
39 	data.mlrd_mtmp.mlrd_mtmp_sensor_index = to_be16(sensor->mlts_index);
40 	ok = mlxcx_cmd_access_register(mlxp, MLXCX_CMD_ACCESS_REGISTER_READ,
41 	    MLXCX_REG_MTMP, &data);
42 	if (!ok) {
43 		return (EIO);
44 	}
45 
46 	tmp = from_be16(data.mlrd_mtmp.mlrd_mtmp_temperature);
47 	sensor->mlts_value = (int16_t)tmp;
48 	tmp = from_be16(data.mlrd_mtmp.mlrd_mtmp_max_temperature);
49 	sensor->mlts_max_value = (int16_t)tmp;
50 	bcopy(data.mlrd_mtmp.mlrd_mtmp_name, sensor->mlts_name,
51 	    sizeof (sensor->mlts_name));
52 
53 	scalar->sis_unit = SENSOR_UNIT_CELSIUS;
54 	scalar->sis_gran = MLXCX_TEMP_GRAN;
55 	scalar->sis_prec = 0;
56 	scalar->sis_value = (int64_t)sensor->mlts_value;
57 
58 	return (0);
59 }
60 
61 static const ksensor_ops_t mlxcx_temp_ops = {
62 	.kso_kind = ksensor_kind_temperature,
63 	.kso_scalar = mlxcx_temperature_read
64 };
65 
66 void
67 mlxcx_teardown_sensors(mlxcx_t *mlxp)
68 {
69 	if (mlxp->mlx_temp_nsensors == 0)
70 		return;
71 	(void) ksensor_remove(mlxp->mlx_dip, KSENSOR_ALL_IDS);
72 	kmem_free(mlxp->mlx_temp_sensors, sizeof (mlxcx_temp_sensor_t) *
73 	    mlxp->mlx_temp_nsensors);
74 }
75 
76 boolean_t
77 mlxcx_setup_sensors(mlxcx_t *mlxp)
78 {
79 	mlxcx_register_data_t data;
80 	boolean_t ok;
81 
82 	mlxp->mlx_temp_nsensors = 0;
83 	bzero(&data, sizeof (data));
84 	ok = mlxcx_cmd_access_register(mlxp, MLXCX_CMD_ACCESS_REGISTER_READ,
85 	    MLXCX_REG_MTCAP, &data);
86 	if (!ok) {
87 		return (B_FALSE);
88 	}
89 
90 	if (data.mlrd_mtcap.mlrd_mtcap_sensor_count == 0) {
91 		return (B_TRUE);
92 	}
93 
94 	mlxp->mlx_temp_nsensors = data.mlrd_mtcap.mlrd_mtcap_sensor_count;
95 	mlxp->mlx_temp_sensors = kmem_zalloc(sizeof (mlxcx_temp_sensor_t) *
96 	    mlxp->mlx_temp_nsensors, KM_SLEEP);
97 
98 	for (uint8_t i = 0; i < mlxp->mlx_temp_nsensors; i++) {
99 		char buf[32];
100 		int ret;
101 
102 		if (snprintf(buf, sizeof (buf), "temp%u", i) >= sizeof (buf)) {
103 			mlxcx_warn(mlxp, "sensor name %u would overflow "
104 			    "internal buffer");
105 			goto err;
106 		}
107 
108 		mlxp->mlx_temp_sensors[i].mlts_mlx = mlxp;
109 		mlxp->mlx_temp_sensors[i].mlts_index = i;
110 
111 		ret = ksensor_create_scalar_pcidev(mlxp->mlx_dip,
112 		    SENSOR_KIND_TEMPERATURE, &mlxcx_temp_ops,
113 		    &mlxp->mlx_temp_sensors[i], buf,
114 		    &mlxp->mlx_temp_sensors[i].mlts_ksensor);
115 		if (ret != 0) {
116 			mlxcx_warn(mlxp, "failed to create temp sensor %s: %d",
117 			    buf, ret);
118 			goto err;
119 		}
120 	}
121 
122 	return (B_TRUE);
123 err:
124 	mlxcx_teardown_sensors(mlxp);
125 	return (B_FALSE);
126 }
127