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