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