xref: /illumos-gate/usr/src/uts/common/io/i2c/sensor/ts511x/ts511x.c (revision 2042f022d202609e92f891e343803ac28419cace)
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 2025 Oxide Computer Company
14  */
15 
16 /*
17  * JEDEC DDR5 TS511x and TS521x temperature sensor driver.
18  *
19  * DDR5 DIMMs may have some number of temperature sensor drivers present, which
20  * generally implement the TS511x or TS521x specification. This driver is based
21  * on JESD302-1A, revision 2.0 (August 2023).
22  */
23 
24 #include <sys/modctl.h>
25 #include <sys/conf.h>
26 #include <sys/devops.h>
27 #include <sys/ddi.h>
28 #include <sys/sunddi.h>
29 #include <sys/bitext.h>
30 #include <sys/debug.h>
31 #include <sys/i2c/client.h>
32 #include <sys/sensors.h>
33 
34 /*
35  * The following are a subset of the device registers.
36  */
37 #define	TS_R_TYPE_MSB	0
38 #define	TS_R_TYPE_511X	0x51
39 #define	TS_R_TYPE_521X	0x52
40 #define	TS_R_TYPE_LSB	1
41 #define	TS_R_TYPE_LSB_GRADE_A	0x11
42 #define	TS_R_TYPE_LSB_GRADE_B	0x10
43 #define	TS_R_REV	2
44 #define	TS_R_REV_GET_MAJ(v)	bitx8(v, 5, 4)
45 #define	TS_R_REV_GET_MIN(v)	bitx8(v, 3, 1)
46 #define	TS_R_VID0	3
47 #define	TS_R_VID1	4
48 
49 /*
50  * All two byte thermal registers are in units of 0.25 C. These are signed
51  * values. The low bit currently has bits [1:0] as reserved. Similarly the high
52  * register has bits [7:5] reserved and [4] is the sign bit. The macros for
53  * getting these values are defined in the temp register.
54  */
55 #define	TS_R_HIGH_LIMIT_LSB	0x1c
56 #define	TS_R_HIGH_LIMIT_MSB	0x1d
57 #define	TS_R_LOW_LIMIT_LSB	0x1e
58 #define	TS_R_LOW_LIMIT_MSB	0x1f
59 #define	TS_R_HIGH_CRIT_LSB	0x20
60 #define	TS_R_HIGH_CRIT_MSB	0x21
61 #define	TS_R_LOW_CRIT_LSB	0x22
62 #define	TS_R_LOW_CRIT_MSB	0x23
63 #define	TS_R_TEMP_LSB		0x31
64 #define	TS_R_TEMP_LSB_GET_TEMP(v)	bitx8(v, 7, 2)
65 #define	TS_R_TEMP_MSB		0x32
66 #define	TS_R_TEMP_MSB_GET_TEMP(v)	bitx8(v, 3, 0)
67 #define	TS_R_TEMP_MSB_GET_SIGN(v)	bitx8(v, 4, 4)
68 #define	TS_R_TEMP_MSB_SHIFT	6
69 /*
70  * The serial number is only present on TS521x devices.
71  */
72 #define	TS_R_SN0	0x50
73 #define	TS_R_SN1	0x51
74 #define	TS_R_SN2	0x52
75 #define	TS_R_SN3	0x53
76 #define	TS_R_SN4	0x54
77 #define	TS_R_MAX	0xFF
78 
79 /*
80  * The temperature is measured in units of 0.25 degrees.
81  */
82 #define	TS_TEMP_RES	4
83 
84 typedef enum ts511x_type {
85 	TS_TYPE_511X,
86 	TS_TYPE_521X
87 } ts511x_type_t;
88 
89 typedef struct ts511x {
90 	dev_info_t *ts_dip;
91 	i2c_client_t *ts_client;
92 	i2c_reg_hdl_t *ts_regs;
93 	ts511x_type_t ts_type;
94 	id_t ts_ksensor;
95 	uint8_t ts_vid[2];
96 	uint8_t ts_rev;
97 	uint8_t ts_sn[5];
98 	kmutex_t ts_mutex;
99 	uint8_t ts_raw[2];
100 	int64_t ts_temp;
101 } ts511x_t;
102 
103 static const i2c_reg_acc_attr_t ts511x_reg_attr = {
104 	.i2cacc_version = I2C_REG_ACC_ATTR_V0,
105 	.i2cacc_addr_len = 1,
106 	.i2cacc_reg_len = 1,
107 	.i2cacc_addr_max = TS_R_MAX
108 };
109 
110 static int
ts511x_temp_read(void * arg,sensor_ioctl_scalar_t * scalar)111 ts511x_temp_read(void *arg, sensor_ioctl_scalar_t *scalar)
112 {
113 	ts511x_t *ts = arg;
114 	uint8_t val[2];
115 	i2c_error_t err;
116 
117 	if (!i2c_reg_get(NULL, ts->ts_regs, TS_R_TEMP_LSB, val, sizeof (val),
118 	    &err)) {
119 		dev_err(ts->ts_dip, CE_WARN, "!failed to read temp registers: "
120 		    "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
121 		return (EIO);
122 	}
123 
124 	mutex_enter(&ts->ts_mutex);
125 	bcopy(val, ts->ts_raw, sizeof (val));
126 	uint64_t u64 = TS_R_TEMP_LSB_GET_TEMP(ts->ts_raw[0]) |
127 	    (TS_R_TEMP_MSB_GET_TEMP(ts->ts_raw[1]) << TS_R_TEMP_MSB_SHIFT);
128 	if (TS_R_TEMP_MSB_GET_SIGN(ts->ts_raw[1]) == 1) {
129 		u64 |= UINT64_MAX & ~((1 << 10) - 1);
130 	}
131 	ts->ts_temp = (int64_t)u64;
132 	scalar->sis_value = ts->ts_temp;
133 
134 	/*
135 	 * The sensor is in units 0.25 Degrees C. According to the Table 65
136 	 * Temperature Sensor Performance, there are three accuracy ranges:
137 	 *
138 	 *  TYP 0.5, MAX 1.0	 75 <= T~A~ <= 95
139 	 *  TYP 1.0, MAX 2.0	 40 <= T~A~ <= 125
140 	 *  TYP 2.0, MAX 3.0	-40 <= T~A~ <= 125
141 	 */
142 	scalar->sis_unit = SENSOR_UNIT_CELSIUS;
143 	scalar->sis_gran = TS_TEMP_RES;
144 	int64_t prec_temp = scalar->sis_value / TS_TEMP_RES;
145 	if (75 <= prec_temp && prec_temp <= 95) {
146 		scalar->sis_prec = 1 * scalar->sis_gran;
147 	} else if (40 <= prec_temp && prec_temp <= 125) {
148 		scalar->sis_prec = 2 * scalar->sis_gran;
149 	} else {
150 		scalar->sis_prec = 3 * scalar->sis_gran;
151 	}
152 	mutex_exit(&ts->ts_mutex);
153 
154 	return (0);
155 }
156 
157 static const ksensor_ops_t ts511x_temp_ops = {
158 	.kso_kind = ksensor_kind_temperature,
159 	.kso_scalar = ts511x_temp_read
160 };
161 
162 static bool
ts511x_i2c_init(ts511x_t * ts)163 ts511x_i2c_init(ts511x_t *ts)
164 {
165 	i2c_errno_t err;
166 
167 	if ((err = i2c_client_init(ts->ts_dip, 0, &ts->ts_client)) !=
168 	    I2C_CORE_E_OK) {
169 		dev_err(ts->ts_dip, CE_WARN, "failed to create i2c client: "
170 		    "0x%x", err);
171 		return (false);
172 	}
173 
174 	if ((err = i2c_reg_handle_init(ts->ts_client, &ts511x_reg_attr,
175 	    &ts->ts_regs)) != I2C_CORE_E_OK) {
176 		dev_err(ts->ts_dip, CE_WARN, "failed to create register "
177 		    "handle: %s (0x%x)", i2c_client_errtostr(ts->ts_client,
178 		    err), err);
179 		return (false);
180 	}
181 
182 	return (true);
183 }
184 
185 /*
186  * Read the MSB device type register to make sure we know what kind of device
187  * this is. Once we do, snapshot a bit of additional information about the
188  * device such as the revision, JEDEC ID, and serial number if it has one.
189  */
190 static bool
ts511x_ident(ts511x_t * ts)191 ts511x_ident(ts511x_t *ts)
192 {
193 	uint8_t type;
194 	i2c_error_t err;
195 
196 	if (!i2c_reg_get(NULL, ts->ts_regs, TS_R_TYPE_MSB, &type, sizeof (type),
197 	    &err)) {
198 		dev_err(ts->ts_dip, CE_WARN, "!failed to read type register: "
199 		    "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
200 		return (false);
201 	}
202 
203 	switch (type) {
204 	case TS_R_TYPE_511X:
205 		ts->ts_type = TS_TYPE_511X;
206 		break;
207 	case TS_R_TYPE_521X:
208 		ts->ts_type = TS_TYPE_521X;
209 		break;
210 	default:
211 		dev_err(ts->ts_dip, CE_WARN, "encountered unsupported device "
212 		    "type: 0x%x", type);
213 		return (false);
214 	}
215 
216 	if (!i2c_reg_get(NULL, ts->ts_regs, TS_R_VID0, ts->ts_vid,
217 	    sizeof (ts->ts_vid), &err)) {
218 		dev_err(ts->ts_dip, CE_WARN, "!failed to read vid registers: "
219 		    "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
220 		return (false);
221 	}
222 
223 	if (!i2c_reg_get(NULL, ts->ts_regs, TS_R_REV, &ts->ts_rev,
224 	    sizeof (ts->ts_rev), &err)) {
225 		dev_err(ts->ts_dip, CE_WARN, "!failed to read rev register: "
226 		    "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
227 		return (false);
228 	}
229 
230 	if (ts->ts_type != TS_TYPE_521X) {
231 		return (true);
232 	}
233 
234 	if (!i2c_reg_get(NULL, ts->ts_regs, TS_R_REV, ts->ts_sn,
235 	    sizeof (ts->ts_sn), &err)) {
236 		dev_err(ts->ts_dip, CE_WARN, "!failed to read sn registers: "
237 		    "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
238 		return (false);
239 	}
240 
241 	return (true);
242 }
243 
244 static void
ts511x_cleanup(ts511x_t * ts)245 ts511x_cleanup(ts511x_t *ts)
246 {
247 	(void) ksensor_remove(ts->ts_dip, KSENSOR_ALL_IDS);
248 	i2c_reg_handle_destroy(ts->ts_regs);
249 	i2c_client_destroy(ts->ts_client);
250 	mutex_destroy(&ts->ts_mutex);
251 	ddi_set_driver_private(ts->ts_dip, NULL);
252 	ts->ts_dip = NULL;
253 	kmem_free(ts, sizeof (ts511x_t));
254 }
255 
256 static int
ts511x_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)257 ts511x_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
258 {
259 	int ret;
260 	ts511x_t *ts;
261 
262 	switch (cmd) {
263 	case DDI_ATTACH:
264 		break;
265 	case DDI_RESUME:
266 		return (DDI_SUCCESS);
267 	default:
268 		return (DDI_FAILURE);
269 	}
270 
271 	ts = kmem_zalloc(sizeof (ts511x_t), KM_SLEEP);
272 	ts->ts_dip = dip;
273 	ddi_set_driver_private(dip, ts);
274 	mutex_init(&ts->ts_mutex, NULL, MUTEX_DRIVER, NULL);
275 
276 	if (!ts511x_i2c_init(ts))
277 		goto cleanup;
278 
279 	if (!ts511x_ident(ts))
280 		goto cleanup;
281 
282 	if ((ret = i2c_client_ksensor_create_scalar(ts->ts_client,
283 	    SENSOR_KIND_TEMPERATURE, &ts511x_temp_ops, ts, "temp",
284 	    &ts->ts_ksensor)) != 0) {
285 		dev_err(ts->ts_dip, CE_WARN, "failed to create ksensor: %d",
286 		    ret);
287 		goto cleanup;
288 	}
289 
290 	return (DDI_SUCCESS);
291 
292 cleanup:
293 	ts511x_cleanup(ts);
294 	return (DDI_FAILURE);
295 }
296 
297 static int
ts511x_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)298 ts511x_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
299 {
300 	ts511x_t *ts;
301 
302 	switch (cmd) {
303 	case DDI_DETACH:
304 		break;
305 	case DDI_SUSPEND:
306 		return (DDI_SUCCESS);
307 	default:
308 		return (DDI_FAILURE);
309 	}
310 
311 	ts = ddi_get_driver_private(dip);
312 	if (ts == NULL) {
313 		dev_err(dip, CE_WARN, "asked to detach, but missing private "
314 		    "data");
315 		return (DDI_FAILURE);
316 	}
317 	VERIFY3P(ts->ts_dip, ==, dip);
318 
319 	ts511x_cleanup(ts);
320 	return (DDI_SUCCESS);
321 }
322 
323 static struct dev_ops ts511x_dev_ops = {
324 	.devo_rev = DEVO_REV,
325 	.devo_refcnt = 0,
326 	.devo_identify = nulldev,
327 	.devo_probe = nulldev,
328 	.devo_attach = ts511x_attach,
329 	.devo_detach = ts511x_detach,
330 	.devo_reset = nodev,
331 	.devo_quiesce = ddi_quiesce_not_needed
332 };
333 
334 static struct modldrv ts511x_modldrv = {
335 	.drv_modops = &mod_driverops,
336 	.drv_linkinfo = "TS511X/TS521X driver",
337 	.drv_dev_ops = &ts511x_dev_ops
338 };
339 
340 static struct modlinkage ts511x_modlinkage = {
341 	.ml_rev = MODREV_1,
342 	.ml_linkage = { &ts511x_modldrv, NULL }
343 };
344 
345 
346 int
_init(void)347 _init(void)
348 {
349 	return (mod_install(&ts511x_modlinkage));
350 }
351 
352 int
_info(struct modinfo * modinfop)353 _info(struct modinfo *modinfop)
354 {
355 	return (mod_info(&ts511x_modlinkage, modinfop));
356 }
357 
358 int
_fini(void)359 _fini(void)
360 {
361 	return (mod_remove(&ts511x_modlinkage));
362 }
363