summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/mlxcx
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/mlxcx')
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx.c15
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx.h23
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_cmd.c13
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_gld.c27
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_intr.c101
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_reg.h28
-rw-r--r--usr/src/uts/common/io/mlxcx/mlxcx_sensor.c126
7 files changed, 320 insertions, 13 deletions
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx.c b/usr/src/uts/common/io/mlxcx/mlxcx.c
index dbad9be958..90964d2fd1 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx.c
@@ -1066,6 +1066,11 @@ mlxcx_teardown(mlxcx_t *mlxp)
mlxcx_intr_disable(mlxp);
}
+ if (mlxp->mlx_attach & MLXCX_ATTACH_SENSORS) {
+ mlxcx_teardown_sensors(mlxp);
+ mlxp->mlx_attach &= ~MLXCX_ATTACH_SENSORS;
+ }
+
if (mlxp->mlx_attach & MLXCX_ATTACH_CHKTIMERS) {
mlxcx_teardown_checktimers(mlxp);
mlxp->mlx_attach &= ~MLXCX_ATTACH_CHKTIMERS;
@@ -1800,7 +1805,7 @@ mlxcx_setup_ports(mlxcx_t *mlxp)
p->mlx_port_event.mla_mlx = mlxp;
p->mlx_port_event.mla_port = p;
mutex_init(&p->mlx_port_event.mla_mtx, NULL,
- MUTEX_DRIVER, DDI_INTR_PRI(mlxp->mlx_intr_pri));
+ MUTEX_DRIVER, DDI_INTR_PRI(mlxp->mlx_async_intr_pri));
p->mlp_init |= MLXCX_PORT_INIT;
mutex_init(&p->mlp_mtx, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(mlxp->mlx_intr_pri));
@@ -2716,7 +2721,7 @@ mlxcx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
for (i = 0; i <= MLXCX_FUNC_ID_MAX; i++) {
mlxp->mlx_npages_req[i].mla_mlx = mlxp;
mutex_init(&mlxp->mlx_npages_req[i].mla_mtx, NULL,
- MUTEX_DRIVER, DDI_INTR_PRI(mlxp->mlx_intr_pri));
+ MUTEX_DRIVER, DDI_INTR_PRI(mlxp->mlx_async_intr_pri));
}
mlxp->mlx_attach |= MLXCX_ATTACH_ASYNC_TQ;
@@ -2869,6 +2874,11 @@ mlxcx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
}
mlxp->mlx_attach |= MLXCX_ATTACH_CHKTIMERS;
+ if (!mlxcx_setup_sensors(mlxp)) {
+ goto err;
+ }
+ mlxp->mlx_attach |= MLXCX_ATTACH_SENSORS;
+
/*
* Finally, tell MAC that we exist!
*/
@@ -2913,7 +2923,6 @@ static struct dev_ops mlxcx_dev_ops = {
.devo_attach = mlxcx_attach,
.devo_detach = mlxcx_detach,
.devo_reset = nodev,
- .devo_power = ddi_power,
.devo_quiesce = ddi_quiesce_not_supported,
.devo_cb_ops = &mlxcx_cb_ops
};
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx.h b/usr/src/uts/common/io/mlxcx/mlxcx.h
index 77d36447c6..e28fe89806 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx.h
+++ b/usr/src/uts/common/io/mlxcx/mlxcx.h
@@ -1009,6 +1009,15 @@ typedef struct {
uint64_t mldp_wq_check_interval_sec;
} mlxcx_drv_props_t;
+typedef struct {
+ mlxcx_t *mlts_mlx;
+ uint8_t mlts_index;
+ id_t mlts_ksensor;
+ int16_t mlts_value;
+ int16_t mlts_max_value;
+ uint8_t mlts_name[MLXCX_MTMP_NAMELEN];
+} mlxcx_temp_sensor_t;
+
typedef enum {
MLXCX_ATTACH_FM = 1 << 0,
MLXCX_ATTACH_PCI_CONFIG = 1 << 1,
@@ -1028,6 +1037,7 @@ typedef enum {
MLXCX_ATTACH_CAPS = 1 << 15,
MLXCX_ATTACH_CHKTIMERS = 1 << 16,
MLXCX_ATTACH_ASYNC_TQ = 1 << 17,
+ MLXCX_ATTACH_SENSORS = 1 << 18
} mlxcx_attach_progress_t;
struct mlxcx {
@@ -1082,6 +1092,7 @@ struct mlxcx {
* Interrupts
*/
uint_t mlx_intr_pri;
+ uint_t mlx_async_intr_pri;
uint_t mlx_intr_type; /* always MSI-X */
int mlx_intr_count;
size_t mlx_intr_size; /* allocation size */
@@ -1171,6 +1182,12 @@ struct mlxcx {
ddi_periodic_t mlx_eq_checktimer;
ddi_periodic_t mlx_cq_checktimer;
ddi_periodic_t mlx_wq_checktimer;
+
+ /*
+ * Sensors
+ */
+ uint8_t mlx_temp_nsensors;
+ mlxcx_temp_sensor_t *mlx_temp_sensors;
};
/*
@@ -1446,6 +1463,12 @@ extern const char *mlxcx_port_status_string(mlxcx_port_status_t);
extern const char *mlxcx_event_name(mlxcx_event_t);
+/*
+ * Sensor Functions
+ */
+extern boolean_t mlxcx_setup_sensors(mlxcx_t *);
+extern void mlxcx_teardown_sensors(mlxcx_t *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_cmd.c b/usr/src/uts/common/io/mlxcx/mlxcx_cmd.c
index c8eb1335ea..32c40ec3ea 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx_cmd.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_cmd.c
@@ -667,7 +667,8 @@ static void
mlxcx_cmd_init(mlxcx_t *mlxp, mlxcx_cmd_t *cmd)
{
bzero(cmd, sizeof (*cmd));
- mutex_init(&cmd->mlcmd_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&cmd->mlcmd_lock, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(mlxp->mlx_async_intr_pri));
cv_init(&cmd->mlcmd_cv, NULL, CV_DRIVER, NULL);
cmd->mlcmd_token = id_alloc(mlxp->mlx_cmd.mcmd_tokens);
cmd->mlcmd_poll = mlxp->mlx_cmd.mcmd_polled;
@@ -1687,6 +1688,10 @@ mlxcx_reg_name(mlxcx_register_id_t rid)
return ("PPCNT");
case MLXCX_REG_PPLM:
return ("PPLM");
+ case MLXCX_REG_MTCAP:
+ return ("MTCAP");
+ case MLXCX_REG_MTMP:
+ return ("MTMP");
default:
return ("???");
}
@@ -1736,6 +1741,12 @@ mlxcx_cmd_access_register(mlxcx_t *mlxp, mlxcx_cmd_reg_opmod_t opmod,
case MLXCX_REG_PPLM:
dsize = sizeof (mlxcx_reg_pplm_t);
break;
+ case MLXCX_REG_MTCAP:
+ dsize = sizeof (mlxcx_reg_mtcap_t);
+ break;
+ case MLXCX_REG_MTMP:
+ dsize = sizeof (mlxcx_reg_mtmp_t);
+ break;
default:
dsize = 0;
VERIFY(0);
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_gld.c b/usr/src/uts/common/io/mlxcx/mlxcx_gld.c
index 89645bb2b1..941eb0f9e7 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx_gld.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_gld.c
@@ -809,19 +809,32 @@ mlxcx_mac_ring_stop(mac_ring_driver_t rh)
if (wq->mlwq_state & MLXCX_WQ_BUFFERS) {
+ list_t cq_buffers;
+
+ /*
+ * Take the buffers away from the CQ. If the CQ is being
+ * processed and the WQ has been stopped, a completion
+ * which does not match to a buffer will be ignored.
+ */
+ list_create(&cq_buffers, sizeof (mlxcx_buffer_t),
+ offsetof(mlxcx_buffer_t, mlb_cq_entry));
+
+ list_move_tail(&cq_buffers, &cq->mlcq_buffers);
+
+ mutex_enter(&cq->mlcq_bufbmtx);
+ list_move_tail(&cq_buffers, &cq->mlcq_buffers_b);
+ mutex_exit(&cq->mlcq_bufbmtx);
+
+ cq->mlcq_bufcnt = 0;
+
mutex_exit(&wq->mlwq_mtx);
mutex_exit(&cq->mlcq_mtx);
/* Return any outstanding buffers to the free pool. */
- while ((buf = list_remove_head(&cq->mlcq_buffers)) != NULL) {
+ while ((buf = list_remove_head(&cq_buffers)) != NULL) {
mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
}
- mutex_enter(&cq->mlcq_bufbmtx);
- while ((buf = list_remove_head(&cq->mlcq_buffers_b)) != NULL) {
- mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
- }
- mutex_exit(&cq->mlcq_bufbmtx);
- cq->mlcq_bufcnt = 0;
+ list_destroy(&cq_buffers);
s = wq->mlwq_bufs;
mutex_enter(&s->mlbs_mtx);
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_intr.c b/usr/src/uts/common/io/mlxcx/mlxcx_intr.c
index f79c148d20..53ea4d683e 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx_intr.c
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_intr.c
@@ -12,6 +12,7 @@
/*
* Copyright (c) 2020, the University of Queensland
* Copyright 2020 RackTop Systems, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
/*
@@ -922,6 +923,20 @@ lookagain:
if (added)
goto lookagain;
+ /*
+ * This check could go just after the lookagain
+ * label, but it is a hot code path so we don't
+ * want to unnecessarily grab a lock and check
+ * a flag for a relatively rare event (the ring
+ * being stopped).
+ */
+ mutex_enter(&wq->mlwq_mtx);
+ if ((wq->mlwq_state & MLXCX_WQ_STARTED) == 0) {
+ mutex_exit(&wq->mlwq_mtx);
+ goto nextcq;
+ }
+ mutex_exit(&wq->mlwq_mtx);
+
buf = list_head(&mlcq->mlcq_buffers);
mlxcx_warn(mlxp, "got completion on CQ %x but "
"no buffer matching wqe found: %x (first "
@@ -1165,6 +1180,7 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
ret = ddi_intr_get_supported_types(dip, &types);
if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp, "Failed to get supported interrupt types");
return (B_FALSE);
}
@@ -1176,15 +1192,21 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
ret = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_MSIX, &nintrs);
if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp, "Failed to get number of interrupts");
return (B_FALSE);
}
if (nintrs < 2) {
- mlxcx_warn(mlxp, "%d MSI-X interrupts available, but mlxcx "
+ mlxcx_warn(mlxp, "%d MSI-X interrupts supported, but mlxcx "
"requires 2", nintrs);
return (B_FALSE);
}
ret = ddi_intr_get_navail(dip, DDI_INTR_TYPE_MSIX, &navail);
+ if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp,
+ "Failed to get number of available interrupts");
+ return (B_FALSE);
+ }
if (navail < 2) {
mlxcx_warn(mlxp, "%d MSI-X interrupts available, but mlxcx "
"requires 2", navail);
@@ -1203,10 +1225,14 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
ret = ddi_intr_alloc(dip, mlxp->mlx_intr_handles, DDI_INTR_TYPE_MSIX,
0, navail, &mlxp->mlx_intr_count, DDI_INTR_ALLOC_NORMAL);
if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp, "Failed to allocate %d interrupts", navail);
mlxcx_intr_teardown(mlxp);
return (B_FALSE);
}
if (mlxp->mlx_intr_count < mlxp->mlx_intr_cq0 + 1) {
+ mlxcx_warn(mlxp, "%d MSI-X interrupts allocated, but mlxcx "
+ "requires %d", mlxp->mlx_intr_count,
+ mlxp->mlx_intr_cq0 + 1);
mlxcx_intr_teardown(mlxp);
return (B_FALSE);
}
@@ -1214,10 +1240,29 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
ret = ddi_intr_get_pri(mlxp->mlx_intr_handles[0], &mlxp->mlx_intr_pri);
if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp, "Failed to get interrupt priority");
mlxcx_intr_teardown(mlxp);
return (B_FALSE);
}
+ /*
+ * Set the interrupt priority for the asynchronous handler higher
+ * than the ring handlers. Some operations which issue commands,
+ * and thus rely on the async interrupt handler for posting
+ * completion, do so with a CQ mutex held. The CQ mutex is also
+ * acquired during ring processing, so if the ring processing vector
+ * happens to be assigned to the same CPU as the async vector
+ * it can hold off the async interrupt thread and lead to a deadlock.
+ * By assigning a higher priority to the async vector, it will
+ * always be dispatched.
+ */
+ mlxp->mlx_async_intr_pri = mlxp->mlx_intr_pri;
+ if (mlxp->mlx_async_intr_pri < LOCK_LEVEL) {
+ mlxp->mlx_async_intr_pri++;
+ } else {
+ mlxp->mlx_intr_pri--;
+ }
+
mlxp->mlx_eqs_size = mlxp->mlx_intr_count *
sizeof (mlxcx_event_queue_t);
mlxp->mlx_eqs = kmem_zalloc(mlxp->mlx_eqs_size, KM_SLEEP);
@@ -1227,8 +1272,11 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
* mutex and avl tree to be init'ed - so do it now.
*/
for (i = 0; i < mlxp->mlx_intr_count; ++i) {
+ uint_t pri = (i == 0) ? mlxp->mlx_async_intr_pri :
+ mlxp->mlx_intr_pri;
+
mutex_init(&mlxp->mlx_eqs[i].mleq_mtx, NULL, MUTEX_DRIVER,
- DDI_INTR_PRI(mlxp->mlx_intr_pri));
+ DDI_INTR_PRI(pri));
cv_init(&mlxp->mlx_eqs[i].mleq_cv, NULL, CV_DRIVER, NULL);
if (i < mlxp->mlx_intr_cq0)
@@ -1239,9 +1287,38 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
offsetof(mlxcx_completion_queue_t, mlcq_eq_entry));
}
+ while (mlxp->mlx_async_intr_pri > DDI_INTR_PRI_MIN) {
+ ret = ddi_intr_set_pri(mlxp->mlx_intr_handles[0],
+ mlxp->mlx_async_intr_pri);
+ if (ret == DDI_SUCCESS)
+ break;
+ mlxcx_note(mlxp,
+ "!Failed to set interrupt priority to %u for "
+ "async interrupt vector", mlxp->mlx_async_intr_pri);
+ /*
+ * If it was not possible to set the IPL for the async
+ * interrupt to the desired value, then try a lower priority.
+ * Some PSMs can only accommodate a limited number of vectors
+ * at eatch priority level (or group of priority levels). Since
+ * the async priority must be set higher than the ring
+ * handlers, lower both. The ring handler priority is set
+ * below.
+ */
+ mlxp->mlx_async_intr_pri--;
+ mlxp->mlx_intr_pri--;
+ }
+
+ if (mlxp->mlx_async_intr_pri == DDI_INTR_PRI_MIN) {
+ mlxcx_warn(mlxp, "Failed to find an interrupt priority for "
+ "async interrupt vector");
+ mlxcx_intr_teardown(mlxp);
+ return (B_FALSE);
+ }
+
ret = ddi_intr_add_handler(mlxp->mlx_intr_handles[0], mlxcx_intr_async,
(caddr_t)mlxp, (caddr_t)&mlxp->mlx_eqs[0]);
if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp, "Failed to add async interrupt handler");
mlxcx_intr_teardown(mlxp);
return (B_FALSE);
}
@@ -1268,9 +1345,29 @@ mlxcx_intr_setup(mlxcx_t *mlxp)
eqt = MLXCX_EQ_TYPE_RX;
}
+ while (mlxp->mlx_intr_pri >= DDI_INTR_PRI_MIN) {
+ ret = ddi_intr_set_pri(mlxp->mlx_intr_handles[i],
+ mlxp->mlx_intr_pri);
+ if (ret == DDI_SUCCESS)
+ break;
+ mlxcx_note(mlxp, "!Failed to set interrupt priority to "
+ "%u for interrupt vector %d",
+ mlxp->mlx_intr_pri, i);
+ mlxp->mlx_intr_pri--;
+ }
+ if (mlxp->mlx_intr_pri < DDI_INTR_PRI_MIN) {
+ mlxcx_warn(mlxp,
+ "Failed to find an interrupt priority for "
+ "interrupt vector %d", i);
+ mlxcx_intr_teardown(mlxp);
+ return (B_FALSE);
+ }
+
ret = ddi_intr_add_handler(mlxp->mlx_intr_handles[i],
mlxcx_intr_n, (caddr_t)mlxp, (caddr_t)&mlxp->mlx_eqs[i]);
if (ret != DDI_SUCCESS) {
+ mlxcx_warn(mlxp, "Failed to add interrupt handler %d",
+ i);
mlxcx_intr_teardown(mlxp);
return (B_FALSE);
}
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_reg.h b/usr/src/uts/common/io/mlxcx/mlxcx_reg.h
index 1987ae06ea..4b92de92b8 100644
--- a/usr/src/uts/common/io/mlxcx/mlxcx_reg.h
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_reg.h
@@ -2530,6 +2530,30 @@ typedef struct {
uint16be_t mlrd_pplm_fec_override_admin_fdr10;
} mlxcx_reg_pplm_t;
+typedef struct {
+ uint8_t mlrd_mtcap_rsvd[3];
+ uint8_t mlrd_mtcap_sensor_count;
+ uint8_t mlrd_mtcap_rsvd1[4];
+ uint64be_t mlrd_mtcap_sensor_map;
+} mlxcx_reg_mtcap_t;
+
+#define MLXCX_MTMP_NAMELEN 8
+
+typedef struct {
+ uint8_t mlrd_mtmp_rsvd[2];
+ uint16be_t mlrd_mtmp_sensor_index;
+ uint8_t mlrd_mtmp_rsvd1[2];
+ uint16be_t mlrd_mtmp_temperature;
+ bits16_t mlrd_mtmp_max_flags;
+ uint16be_t mlrd_mtmp_max_temperature;
+ bits16_t mlrd_mtmp_tee;
+ uint16be_t mlrd_mtmp_temp_thresh_hi;
+ uint8_t mlrd_mtmp_rsvd2[2];
+ uint16be_t mlrd_mtmp_temp_thresh_lo;
+ uint8_t mlrd_mtmp_rsvd3[4];
+ uint8_t mlrd_mtmp_name[MLXCX_MTMP_NAMELEN];
+} mlxcx_reg_mtmp_t;
+
typedef enum {
MLXCX_REG_PMTU = 0x5003,
MLXCX_REG_PTYS = 0x5004,
@@ -2540,6 +2564,8 @@ typedef enum {
MLXCX_REG_MCIA = 0x9014,
MLXCX_REG_PPCNT = 0x5008,
MLXCX_REG_PPLM = 0x5023,
+ MLXCX_REG_MTCAP = 0x9009,
+ MLXCX_REG_MTMP = 0x900A
} mlxcx_register_id_t;
typedef union {
@@ -2551,6 +2577,8 @@ typedef union {
mlxcx_reg_mcia_t mlrd_mcia;
mlxcx_reg_ppcnt_t mlrd_ppcnt;
mlxcx_reg_pplm_t mlrd_pplm;
+ mlxcx_reg_mtcap_t mlrd_mtcap;
+ mlxcx_reg_mtmp_t mlrd_mtmp;
} mlxcx_register_data_t;
typedef enum {
diff --git a/usr/src/uts/common/io/mlxcx/mlxcx_sensor.c b/usr/src/uts/common/io/mlxcx/mlxcx_sensor.c
new file mode 100644
index 0000000000..6d2c7d0778
--- /dev/null
+++ b/usr/src/uts/common/io/mlxcx/mlxcx_sensor.c
@@ -0,0 +1,126 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Oxide Computer Company
+ */
+
+#include <mlxcx.h>
+#include <sys/sensors.h>
+
+/*
+ * The PRM indicates that the temperature is measured in 1/8th degrees.
+ */
+#define MLXCX_TEMP_GRAN 8
+
+/*
+ * Read a single temperature sensor entry. The ksensor framework guarantees that
+ * it will only call this once for a given sensor at any time, though multiple
+ * sensors can be in parallel.
+ */
+static int
+mlxcx_temperature_read(void *arg, sensor_ioctl_scalar_t *scalar)
+{
+ boolean_t ok;
+ uint16_t tmp;
+ mlxcx_register_data_t data;
+ mlxcx_temp_sensor_t *sensor = arg;
+ mlxcx_t *mlxp = sensor->mlts_mlx;
+
+ bzero(&data, sizeof (data));
+ data.mlrd_mtmp.mlrd_mtmp_sensor_index = to_be16(sensor->mlts_index);
+ ok = mlxcx_cmd_access_register(mlxp, MLXCX_CMD_ACCESS_REGISTER_READ,
+ MLXCX_REG_MTMP, &data);
+ if (!ok) {
+ return (EIO);
+ }
+
+ tmp = from_be16(data.mlrd_mtmp.mlrd_mtmp_temperature);
+ sensor->mlts_value = (int16_t)tmp;
+ tmp = from_be16(data.mlrd_mtmp.mlrd_mtmp_max_temperature);
+ sensor->mlts_max_value = (int16_t)tmp;
+ bcopy(data.mlrd_mtmp.mlrd_mtmp_name, sensor->mlts_name,
+ sizeof (sensor->mlts_name));
+
+ scalar->sis_unit = SENSOR_UNIT_CELSIUS;
+ scalar->sis_gran = MLXCX_TEMP_GRAN;
+ scalar->sis_prec = 0;
+ scalar->sis_value = (int64_t)sensor->mlts_value;
+
+ return (0);
+}
+
+static const ksensor_ops_t mlxcx_temp_ops = {
+ .kso_kind = ksensor_kind_temperature,
+ .kso_scalar = mlxcx_temperature_read
+};
+
+void
+mlxcx_teardown_sensors(mlxcx_t *mlxp)
+{
+ if (mlxp->mlx_temp_nsensors == 0)
+ return;
+ (void) ksensor_remove(mlxp->mlx_dip, KSENSOR_ALL_IDS);
+ kmem_free(mlxp->mlx_temp_sensors, sizeof (mlxcx_temp_sensor_t) *
+ mlxp->mlx_temp_nsensors);
+}
+
+boolean_t
+mlxcx_setup_sensors(mlxcx_t *mlxp)
+{
+ mlxcx_register_data_t data;
+ boolean_t ok;
+
+ mlxp->mlx_temp_nsensors = 0;
+ bzero(&data, sizeof (data));
+ ok = mlxcx_cmd_access_register(mlxp, MLXCX_CMD_ACCESS_REGISTER_READ,
+ MLXCX_REG_MTCAP, &data);
+ if (!ok) {
+ return (B_FALSE);
+ }
+
+ if (data.mlrd_mtcap.mlrd_mtcap_sensor_count == 0) {
+ return (B_TRUE);
+ }
+
+ mlxp->mlx_temp_nsensors = data.mlrd_mtcap.mlrd_mtcap_sensor_count;
+ mlxp->mlx_temp_sensors = kmem_zalloc(sizeof (mlxcx_temp_sensor_t) *
+ mlxp->mlx_temp_nsensors, KM_SLEEP);
+
+ for (uint8_t i = 0; i < mlxp->mlx_temp_nsensors; i++) {
+ char buf[32];
+ int ret;
+
+ if (snprintf(buf, sizeof (buf), "temp%u", i) >= sizeof (buf)) {
+ mlxcx_warn(mlxp, "sensor name %u would overflow "
+ "internal buffer");
+ goto err;
+ }
+
+ mlxp->mlx_temp_sensors[i].mlts_mlx = mlxp;
+ mlxp->mlx_temp_sensors[i].mlts_index = i;
+
+ ret = ksensor_create_scalar_pcidev(mlxp->mlx_dip,
+ SENSOR_KIND_TEMPERATURE, &mlxcx_temp_ops,
+ &mlxp->mlx_temp_sensors[i], buf,
+ &mlxp->mlx_temp_sensors[i].mlts_ksensor);
+ if (ret != 0) {
+ mlxcx_warn(mlxp, "failed to create temp sensor %s: %d",
+ buf, ret);
+ goto err;
+ }
+ }
+
+ return (B_TRUE);
+err:
+ mlxcx_teardown_sensors(mlxp);
+ return (B_FALSE);
+}