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 * DDR5 SPD5118 Hub Driver.
18 *
19 * The Hub has an integrated temperature sensor and a 1024 KiB EEPROM. This is
20 * based on JESD300-5B.01, Version 1.5.1, May 2023. The device uses the lower
21 * 7-bits of registers to retrieve access to the current page. The upper 7-bits
22 * is used to get access to the current page of NVM data. The current page is
23 * controlled through one of the volatile registers. There is also a second mode
24 * that allows the device to be put into a 2-byte mode where you can access all
25 * of the memory, but we just opt for the traditional paged mode.
26 */
27
28 #include <sys/modctl.h>
29 #include <sys/conf.h>
30 #include <sys/devops.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/bitext.h>
34 #include <sys/sysmacros.h>
35 #include <sys/i2c/client.h>
36 #include <eedev.h>
37
38 /*
39 * Hub Device Registers. This is a subset of the registers that are useful for
40 * us. Note, this uses the same thermal readout mechanism as the spd5118 driver.
41 * See that driver for more information on the temperature logic.
42 */
43 #define HUB_R_TYPE_MSB 0x00
44 #define HUB_R_TYPE_LSB 0x01
45 #define HUB_R_REV 0x02
46 #define HUB_R_VID0 0x03
47 #define HUB_R_VID1 0x04
48 #define HUB_R_CAP 0x05
49 #define HUB_R_CAP_GET_TS_SUP(r) bitx8(r, 1, 1)
50 #define HUB_R_CAP_GET_HUB(r) bitx8(r, 0, 0)
51 #define HUB_R_I2C_CFG 0x0b
52 #define HUB_R_I2C_CFG_GET_MODE(r) bitx8(r, 3, 3)
53 #define HUB_R_I2C_CFG_GET_PAGE(r) bitx8(r, 2, 0)
54 #define HUB_R_I2C_CFG_SET_PAGE(r, v) bitset8(r, 2, 0, v)
55 #define HUB_R_TEMP_LSB 0x31
56 #define HUB_R_TEMP_LSB_GET_TEMP(v) bitx8(v, 7, 2)
57 #define HUB_R_TEMP_MSB 0x32
58 #define HUB_R_TEMP_MSB_GET_TEMP(v) bitx8(v, 3, 0)
59 #define HUB_R_TEMP_MSB_GET_SIGN(v) bitx8(v, 4, 4)
60 #define HUB_R_TEMP_MSB_SHIFT 6
61 #define HUB_R_NVM_BASE 0x80
62 #define HUB_R_REG_MAX UINT8_MAX
63
64 /*
65 * The temperature is measured in units of 0.25 degrees.
66 */
67 #define HUB_TEMP_RES 4
68
69 /*
70 * Attributes of the device's size.
71 */
72 #define HUB_NVM_NPAGES 8
73 #define HUB_NVM_PAGE_SIZE 128
74
75 typedef struct spd5118 {
76 dev_info_t *spd_dip;
77 i2c_client_t *spd_client;
78 i2c_reg_hdl_t *spd_regs;
79 uint8_t spd_vid[2];
80 uint8_t spd_rev;
81 uint8_t spd_cap;
82 eedev_hdl_t *spd_eehdl;
83 id_t spd_ksensor;
84 kmutex_t spd_mutex;
85 uint8_t spd_buf[I2C_REQ_MAX];
86 uint8_t spd_raw[2];
87 int64_t spd_temp;
88 } spd5118_t;
89
90 static const i2c_reg_acc_attr_t spd5118_reg_attr = {
91 .i2cacc_version = I2C_REG_ACC_ATTR_V0,
92 .i2cacc_addr_len = 1,
93 .i2cacc_reg_len = 1,
94 .i2cacc_addr_max = HUB_R_REG_MAX
95 };
96
97 static bool
spd5118_page_change(i2c_txn_t * txn,spd5118_t * spd,uint32_t page)98 spd5118_page_change(i2c_txn_t *txn, spd5118_t *spd, uint32_t page)
99 {
100 uint8_t cfg;
101 i2c_error_t err;
102
103 VERIFY(MUTEX_HELD(&spd->spd_mutex));
104
105 if (!i2c_reg_get(txn, spd->spd_regs, HUB_R_I2C_CFG, &cfg, sizeof (cfg),
106 &err)) {
107 dev_err(spd->spd_dip, CE_WARN, "!failed to read cap register: "
108 "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
109 return (false);
110 }
111
112 if (HUB_R_I2C_CFG_GET_PAGE(cfg) != page) {
113 cfg = HUB_R_I2C_CFG_SET_PAGE(cfg, page);
114 if (!i2c_reg_put(txn, spd->spd_regs, HUB_R_I2C_CFG, &cfg,
115 sizeof (cfg), &err)) {
116 dev_err(spd->spd_dip, CE_WARN, "!failed to write cap "
117 "register: 0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
118 return (false);
119 }
120 }
121
122 return (true);
123 }
124
125 static int
spd5118_temp_read(void * arg,sensor_ioctl_scalar_t * scalar)126 spd5118_temp_read(void *arg, sensor_ioctl_scalar_t *scalar)
127 {
128 int ret;
129 uint8_t val[2];
130 i2c_txn_t *txn;
131 i2c_error_t err;
132 spd5118_t *spd = arg;
133
134 mutex_enter(&spd->spd_mutex);
135 if (i2c_bus_lock(spd->spd_client, 0, &txn) != I2C_CORE_E_OK) {
136 mutex_exit(&spd->spd_mutex);
137 return (EINTR);
138 }
139
140 /*
141 * The hub specification is a bit unclear. It seems to suggest that that
142 * you shouldn't access other registers when you're not on page 0. As
143 * such, we always change back to page 0 out of an abundance of caution.
144 */
145 if (!spd5118_page_change(txn, spd, 0)) {
146 ret = EIO;
147 goto done;
148 }
149
150 if (!i2c_reg_get(txn, spd->spd_regs, HUB_R_TEMP_LSB, val, sizeof (val),
151 &err)) {
152 dev_err(spd->spd_dip, CE_WARN, "!failed to read temp "
153 "registers: 0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
154 return (EIO);
155 }
156
157 bcopy(val, spd->spd_raw, sizeof (val));
158 uint64_t u64 = HUB_R_TEMP_LSB_GET_TEMP(spd->spd_raw[0]) |
159 (HUB_R_TEMP_MSB_GET_TEMP(spd->spd_raw[1]) << HUB_R_TEMP_MSB_SHIFT);
160 if (HUB_R_TEMP_MSB_GET_SIGN(spd->spd_raw[1]) == 1) {
161 u64 |= UINT64_MAX & ~((1 << 10) - 1);
162 }
163 spd->spd_temp = (int64_t)u64;
164 scalar->sis_value = spd->spd_temp;
165
166 /*
167 * The sensor is in units 0.25 Degrees C. According to the Table 65
168 * Temperature Sensor Performance, there are there accuracy ranges:
169 *
170 * TYP 0.5, MAX 1.0 75 <= T~A~ <= 95
171 * TYP 1.0, MAX 2.0 40 <= T~A~ <= 125
172 * TYP 2.0, MAX 3.0 -40 <= T~A~ <= 125
173 */
174 scalar->sis_unit = SENSOR_UNIT_CELSIUS;
175 scalar->sis_gran = HUB_TEMP_RES;
176 int64_t prec_temp = scalar->sis_value / HUB_TEMP_RES;
177 if (75 <= prec_temp && prec_temp <= 95) {
178 scalar->sis_prec = 1 * scalar->sis_gran;
179 } else if (40 <= prec_temp && prec_temp <= 125) {
180 scalar->sis_prec = 2 * scalar->sis_gran;
181 } else {
182 scalar->sis_prec = 3 * scalar->sis_gran;
183 }
184 ret = 0;
185
186 done:
187 i2c_bus_unlock(txn);
188 mutex_exit(&spd->spd_mutex);
189 return (ret);
190 }
191
192 static const ksensor_ops_t spd5118_temp_ops = {
193 .kso_kind = ksensor_kind_temperature,
194 .kso_scalar = spd5118_temp_read
195 };
196
197 static int
spd5118_read(void * arg,struct uio * uio,uint32_t page,uint32_t pageoff,uint32_t nbytes)198 spd5118_read(void *arg, struct uio *uio, uint32_t page, uint32_t pageoff,
199 uint32_t nbytes)
200 {
201 int ret;
202 i2c_txn_t *txn;
203 i2c_error_t err;
204 spd5118_t *spd = arg;
205
206 VERIFY3U(page, <, HUB_NVM_NPAGES);
207 VERIFY3U(pageoff, <, HUB_NVM_PAGE_SIZE);
208
209 mutex_enter(&spd->spd_mutex);
210 if (i2c_bus_lock(spd->spd_client, 0, &txn) != I2C_CORE_E_OK) {
211 mutex_exit(&spd->spd_mutex);
212 return (EINTR);
213 }
214
215 if (!spd5118_page_change(txn, spd, page)) {
216 ret = EIO;
217 goto done;
218 }
219
220 /*
221 * We need to adjust the page offset to get us into the correct part of
222 * the register space.
223 */
224 pageoff += HUB_R_NVM_BASE;
225 if (i2c_reg_get(txn, spd->spd_regs, pageoff, spd->spd_buf, nbytes,
226 &err)) {
227 ret = uiomove(spd->spd_buf, nbytes, UIO_READ, uio);
228 } else {
229 dev_err(spd->spd_dip, CE_WARN, "!failed to read %u bytes of "
230 "NVM at 0x%x on page %u: 0x%x/0x%x", nbytes, pageoff, page,
231 err.i2c_error, err.i2c_ctrl);
232 ret = EIO;
233 }
234
235 done:
236 i2c_bus_unlock(txn);
237 mutex_exit(&spd->spd_mutex);
238 return (ret);
239 }
240
241 static const eedev_ops_t spd5118_eedev_ops = {
242 .eo_read = spd5118_read
243 };
244
245 static bool
spd5118_i2c_init(spd5118_t * spd)246 spd5118_i2c_init(spd5118_t *spd)
247 {
248 i2c_errno_t err;
249
250 if ((err = i2c_client_init(spd->spd_dip, 0, &spd->spd_client)) !=
251 I2C_CORE_E_OK) {
252 dev_err(spd->spd_dip, CE_WARN, "failed to create i2c client: "
253 "0x%x", err);
254 return (false);
255 }
256
257 if ((err = i2c_reg_handle_init(spd->spd_client, &spd5118_reg_attr,
258 &spd->spd_regs)) != I2C_CORE_E_OK) {
259 dev_err(spd->spd_dip, CE_WARN, "failed to create register "
260 "handle: %s (0x%x)", i2c_client_errtostr(spd->spd_client,
261 err), err);
262 return (false);
263 }
264
265 return (true);
266 }
267
268 /*
269 * Read the MSB device type register to make sure that this is an SPD5118
270 * device.
271 */
272 static bool
spd5118_ident(spd5118_t * spd)273 spd5118_ident(spd5118_t *spd)
274 {
275 uint8_t type[2];
276 i2c_error_t err;
277
278 if (!i2c_reg_get(NULL, spd->spd_regs, HUB_R_TYPE_MSB, type,
279 sizeof (type), &err)) {
280 dev_err(spd->spd_dip, CE_WARN, "!failed to read type "
281 "registers: 0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
282 return (false);
283 }
284
285 /*
286 * The hub specification is a bit unclear. It seems to suggest that that
287 * you shouldn't access other registers when you're not on page 0. This
288 * may mean that we can't get the device ID. So if we read a zero ID,
289 * set the page to page 0 and try to read again.
290 */
291 if (type[0] == 0 && type[1] == 0) {
292 mutex_enter(&spd->spd_mutex);
293 if (!spd5118_page_change(NULL, spd, 0)) {
294 mutex_exit(&spd->spd_mutex);
295 return (false);
296 }
297 mutex_exit(&spd->spd_mutex);
298
299 if (!i2c_reg_get(NULL, spd->spd_regs, HUB_R_TYPE_MSB, type,
300 sizeof (type), &err)) {
301 dev_err(spd->spd_dip, CE_WARN, "!failed to read type "
302 "registers: 0x%x/0x%x", err.i2c_error,
303 err.i2c_ctrl);
304 return (false);
305 }
306 }
307
308 if (type[0] != 0x51 || type[1] != 0x18) {
309 dev_err(spd->spd_dip, CE_WARN, "encountered unsupported device "
310 "type: 0x%x/0x%x", type[0], type[1]);
311 return (false);
312 }
313
314 if (!i2c_reg_get(NULL, spd->spd_regs, HUB_R_VID0, spd->spd_vid,
315 sizeof (spd->spd_vid), &err)) {
316 dev_err(spd->spd_dip, CE_WARN, "!failed to read vid registers: "
317 "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
318 return (false);
319 }
320
321 if (!i2c_reg_get(NULL, spd->spd_regs, HUB_R_REV, &spd->spd_rev,
322 sizeof (spd->spd_rev), &err)) {
323 dev_err(spd->spd_dip, CE_WARN, "!failed to read rev register: "
324 "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
325 return (false);
326 }
327
328 if (!i2c_reg_get(NULL, spd->spd_regs, HUB_R_CAP, &spd->spd_cap,
329 sizeof (spd->spd_cap), &err)) {
330 dev_err(spd->spd_dip, CE_WARN, "!failed to read cap register: "
331 "0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
332 return (false);
333 }
334
335 return (true);
336 }
337
338 static bool
spd5118_eedev_init(spd5118_t * spd)339 spd5118_eedev_init(spd5118_t *spd)
340 {
341 int ret;
342 eedev_reg_t reg;
343
344 bzero(®, sizeof (reg));
345 reg.ereg_vers = EEDEV_REG_VERS;
346 reg.ereg_size = HUB_NVM_NPAGES * HUB_NVM_PAGE_SIZE;
347 reg.ereg_seg = HUB_NVM_PAGE_SIZE;
348 reg.ereg_read_gran = 1;
349 reg.ereg_ro = true;
350 reg.ereg_dip = spd->spd_dip;
351 reg.ereg_driver = spd;
352 reg.ereg_name = NULL;
353 reg.ereg_ops = &spd5118_eedev_ops;
354 reg.ereg_max_read = MIN(i2c_reg_max_read(spd->spd_regs),
355 I2C_REQ_MAX / 2);
356
357 if ((ret = eedev_create(®, &spd->spd_eehdl)) != 0) {
358 dev_err(spd->spd_dip, CE_WARN, "failed to create eedev device: "
359 "%d", ret);
360 return (false);
361 }
362
363 return (true);
364 }
365
366 static void
spd5118_cleanup(spd5118_t * spd)367 spd5118_cleanup(spd5118_t *spd)
368 {
369 (void) ksensor_remove(spd->spd_dip, KSENSOR_ALL_IDS);
370 eedev_fini(spd->spd_eehdl);
371 i2c_reg_handle_destroy(spd->spd_regs);
372 i2c_client_destroy(spd->spd_client);
373 mutex_destroy(&spd->spd_mutex);
374 ddi_set_driver_private(spd->spd_dip, NULL);
375 spd->spd_dip = NULL;
376 kmem_free(spd, sizeof (spd5118_t));
377 }
378
379 static int
spd5118_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)380 spd5118_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
381 {
382 int ret;
383 spd5118_t *spd;
384
385 switch (cmd) {
386 case DDI_ATTACH:
387 break;
388 case DDI_RESUME:
389 return (DDI_SUCCESS);
390 default:
391 return (DDI_FAILURE);
392 }
393
394 spd = kmem_zalloc(sizeof (spd5118_t), KM_SLEEP);
395 spd->spd_dip = dip;
396 ddi_set_driver_private(dip, spd);
397 mutex_init(&spd->spd_mutex, NULL, MUTEX_DRIVER, NULL);
398
399 if (!spd5118_i2c_init(spd))
400 goto cleanup;
401
402 if (!spd5118_ident(spd))
403 goto cleanup;
404
405 if (!spd5118_eedev_init(spd))
406 goto cleanup;
407
408 if ((ret = i2c_client_ksensor_create_scalar(spd->spd_client,
409 SENSOR_KIND_TEMPERATURE, &spd5118_temp_ops, spd, "temp",
410 &spd->spd_ksensor)) != 0) {
411 dev_err(spd->spd_dip, CE_WARN, "failed to create ksensor: %d",
412 ret);
413 goto cleanup;
414 }
415
416 return (DDI_SUCCESS);
417
418 cleanup:
419 spd5118_cleanup(spd);
420 return (DDI_FAILURE);
421 }
422
423 static int
spd5118_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)424 spd5118_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
425 {
426 spd5118_t *spd;
427
428 switch (cmd) {
429 case DDI_DETACH:
430 break;
431 case DDI_SUSPEND:
432 return (DDI_SUCCESS);
433 default:
434 return (DDI_FAILURE);
435 }
436
437 spd = ddi_get_driver_private(dip);
438 if (spd == NULL) {
439 dev_err(dip, CE_WARN, "asked to detach, but missing private "
440 "data");
441 return (DDI_FAILURE);
442 }
443 VERIFY3P(spd->spd_dip, ==, dip);
444
445 spd5118_cleanup(spd);
446 return (DDI_SUCCESS);
447 }
448
449 static struct dev_ops spd5118_dev_ops = {
450 .devo_rev = DEVO_REV,
451 .devo_refcnt = 0,
452 .devo_identify = nulldev,
453 .devo_probe = nulldev,
454 .devo_attach = spd5118_attach,
455 .devo_detach = spd5118_detach,
456 .devo_reset = nodev,
457 .devo_quiesce = ddi_quiesce_not_needed
458 };
459
460 static struct modldrv spd5118_modldrv = {
461 .drv_modops = &mod_driverops,
462 .drv_linkinfo = "SPD5118 driver",
463 .drv_dev_ops = &spd5118_dev_ops
464 };
465
466 static struct modlinkage spd5118_modlinkage = {
467 .ml_rev = MODREV_1,
468 .ml_linkage = { &spd5118_modldrv, NULL }
469 };
470
471
472 int
_init(void)473 _init(void)
474 {
475 return (mod_install(&spd5118_modlinkage));
476 }
477
478 int
_info(struct modinfo * modinfop)479 _info(struct modinfo *modinfop)
480 {
481 return (mod_info(&spd5118_modlinkage, modinfop));
482 }
483
484 int
_fini(void)485 _fini(void)
486 {
487 return (mod_remove(&spd5118_modlinkage));
488 }
489