xref: /illumos-gate/usr/src/uts/common/io/i2c/eeprom/spd511x/spd511x.c (revision 04a1c1a11476a9a84da46c1937024cde61ddb850)
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(&reg, 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(&reg, &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