xref: /illumos-gate/usr/src/uts/common/io/i2c/eeprom/ee100x/ee100x.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  * DDR4 EEPROM Driver
18  *
19  * -------------
20  * Device Design
21  * -------------
22  *
23  * The EE1004 device is found in DDR4 devices and is specified by JC-42.4.
24  * These EEPROMs are 512-byte devices that are split into two 256-byte pages and
25  * have the ability to lock data in 128-byte regions. As was the style of the
26  * time and likely a side effect of device operation, the page select and data
27  * read/write operations are split across two different I2C addresses.
28  *
29  * The JEDEC spec splits the 7-bit address into a 4-bit device class and a
30  * 3-bit device ID. The device ID is used to identify a single DIMM, whose id is
31  * usally set based on pins on the DDR4 socket. So to read from DIMM 0 we would
32  * target address 0x50. From DIMM4, 0x54. From DIMM7, 0x57.
33  *
34  * While these devices have separate addresses for the SPD reads and writes
35  * (along with the temperature sensor) all other addresses are shared between
36  * devices. In particular this includes the page address and the write
37  * protection features. In other words, if you change the active page, it's
38  * going to change it for all devices at that spot in the tree. Similarly if you
39  * issue a write protect command it's for everything that can be reached.
40  *
41  * Now of course, there are only up to 8 device IDs on a bus; however, DDR4
42  * server platforms often supported up to 16 DIMMs, which means muxes are in
43  * play. This means that on a common DDR4 server platform you often have
44  * something that looks like:
45  *
46  *                          +------------+
47  *                          |    I2C     |
48  *                          | controller |
49  *                          +------------+
50  *                                |
51  *                                |
52  *                                v
53  *                            +-------+
54  *             +--------------|  mux  |--------------+
55  *             |              +-------+              |
56  *             |                                     |
57  *    +-----+--+--+-----+                   +-----+--+--+-----+
58  *    |     |     |     |                   |     |     |     |
59  *    v     v     v     v                   v     v     v     v
60  *  +---+ +---+ +---+ +---+               +---+ +---+ +---+ +---+
61  *  | D | | D | | D | | D |               | D | | D | | D | | D |
62  *  | I | | I | | I | | I |               | I | | I | | I | | I |
63  *  | M | | M | | M | | M |               | M | | M | | M | | M |
64  *  | M | | M | | M | | M |               | M | | M | | M | | M |
65  *  |   | |   | |   | |   |               |   | |   | |   | |   |
66  *  | 0 | | 1 | | 2 | | 3 |               | 8 | | 9 | | a | | b |
67  *  +---+ +---+ +---+ +---+               +---+ +---+ +---+ +---+
68  *
69  * So while we said earlier that all devices will change at the same time or be
70  * impacted by I/O to a common address, that isn't quite true. It's technically
71  * all devices that are on the same mux segment.
72  *
73  * -------------------------
74  * Driver Address Management
75  * -------------------------
76  *
77  *
78  * To keep the driver simple, we don't try to ask the question of whether
79  * or not a device is in the same spot of the tree. Similarly, we also don't
80  * actually try to remember what page was last set. We do this because we don't
81  * want to track what device was last active and then this allows someone else
82  * using the bus behind our backs to have made changes. In other words, we will
83  * always set the page for every I/O. While this wastes a bit of bus transaction
84  * time, it's not the end of world. Notably asking what page is set costs the
85  * same as just setting it.
86  *
87  * Each instance of the device ends up with three different I2C clients today:
88  *
89  *  1. A client (and reg handle) for the EEPROM itself.
90  *  2. A client for selecting page 0 (0x36).
91  *  3. A client for selecting page 1 (0x37).
92  *
93  * The client for (1) comes from the device reg[] array. The other two are
94  * shared addresses that we claim at run time. Due to having to change the page,
95  * we end up serializing I/O across all instances of the device with a shared
96  * mutex: ee100x_spa_mutex. Our lock ordering requires that one hold this mutex
97  * prior to beginning any bus transactions. A bus transaction must be made
98  * explicitly and passed from call to call to ensure that we don't have anyone
99  * else interrupt us for a single I/O and potentially change the page semantics
100  * on us.
101  *
102  * While there are are writable portions of the SPD, today the driver does not
103  * support that wanting to ensure that we don't actually damage the device and
104  * make it impossible for the CPU to use the DRAM in question.
105  */
106 
107 #include <sys/modctl.h>
108 #include <sys/conf.h>
109 #include <sys/devops.h>
110 #include <sys/ddi.h>
111 #include <sys/sunddi.h>
112 #include <sys/sysmacros.h>
113 #include <sys/bitext.h>
114 
115 #include <sys/i2c/client.h>
116 #include <eedev.h>
117 
118 /*
119  * These are the I2C addresses that are used to change and query pages per
120  * EE1004. The way that the device indicates what page it's on is by issuing an
121  * ack (page 0) or a nack (page 1) to a request.
122  */
123 #define	EE1004_GET_PAGE_ADDR	0x36
124 #define	EE1004_SET_PAGE0_ADDR	0x36
125 #define	EE1004_SET_PAGE1_ADDR	0x37
126 
127 /*
128  * Organization of an EEPROM1004 device. The device is required to always be a
129  * 512 byte device organized in two banks of 256 bytes.
130  */
131 #define	EE1004_LEN	512
132 #define	EE1004_SEG	256
133 
134 typedef struct ee100x {
135 	dev_info_t *ee_dip;
136 	i2c_client_t *ee_mem;
137 	i2c_reg_hdl_t *ee_mem_hdl;
138 	i2c_client_t *ee_spa0;
139 	i2c_client_t *ee_spa1;
140 	eedev_hdl_t *ee_devhdl;
141 	uint8_t ee_buf[I2C_REQ_MAX];
142 } ee100x_t;
143 
144 /*
145  * Device soft state
146  */
147 static void *ee100x_state;
148 
149 /*
150  * This is a driver-wide mutex that we use to serialize all operations related
151  * to page switching and I/O. Effectively the driver can only support a single
152  * I/O outstanding across the system. See the theory statement for more
153  * information.
154  */
155 static kmutex_t ee100x_spa_mutex;
156 
157 static int
ee100x_read(void * arg,struct uio * uio,uint32_t page,uint32_t pageoff,uint32_t nbytes)158 ee100x_read(void *arg, struct uio *uio, uint32_t page, uint32_t pageoff,
159     uint32_t nbytes)
160 {
161 	int ret = 0;
162 	ee100x_t *ee = arg;
163 	i2c_client_t *client;
164 	i2c_txn_t *txn;
165 	i2c_error_t err;
166 
167 	if (page == 0) {
168 		client = ee->ee_spa0;
169 	} else {
170 		client = ee->ee_spa1;
171 	}
172 
173 	mutex_enter(&ee100x_spa_mutex);
174 	if (i2c_bus_lock(client, 0, &txn) != I2C_CORE_E_OK) {
175 		mutex_exit(&ee100x_spa_mutex);
176 		return (EINTR);
177 	}
178 
179 	if (!smbus_client_write_u8(txn, client, 0, 0, &err)) {
180 		dev_err(ee->ee_dip, CE_WARN, "!failed to select page %u: "
181 		    "0x%x/0x%x", page, err.i2c_error, err.i2c_ctrl);
182 		ret = EIO;
183 		goto done;
184 	}
185 
186 	if (i2c_reg_get(txn, ee->ee_mem_hdl, pageoff, ee->ee_buf, nbytes,
187 	    &err)) {
188 		ret = uiomove(ee->ee_buf, nbytes, UIO_READ, uio);
189 	} else {
190 		dev_err(ee->ee_dip, CE_WARN, "!failed to read %u bytes of NVM "
191 		    "at 0x%x on page %u: 0x%x/0x%x", nbytes, pageoff, page,
192 		    err.i2c_error, err.i2c_ctrl);
193 		ret = EIO;
194 	}
195 
196 done:
197 	i2c_bus_unlock(txn);
198 	mutex_exit(&ee100x_spa_mutex);
199 
200 	return (ret);
201 }
202 
203 static const eedev_ops_t ee100x_eedev_ops = {
204 	.eo_read = ee100x_read
205 };
206 
207 static bool
ee100x_eedev_init(ee100x_t * ee)208 ee100x_eedev_init(ee100x_t *ee)
209 {
210 	int ret;
211 	eedev_reg_t reg;
212 
213 	bzero(&reg, sizeof (reg));
214 	reg.ereg_vers = EEDEV_REG_VERS;
215 	reg.ereg_size = EE1004_LEN;
216 	reg.ereg_seg = EE1004_SEG;
217 	reg.ereg_read_gran = 1;
218 	reg.ereg_ro = true;
219 	reg.ereg_dip = ee->ee_dip;
220 	reg.ereg_driver = ee;
221 	reg.ereg_name = NULL;
222 	reg.ereg_ops = &ee100x_eedev_ops;
223 	reg.ereg_max_read = MIN(i2c_reg_max_read(ee->ee_mem_hdl),
224 	    I2C_REQ_MAX / 2);
225 
226 	if ((ret = eedev_create(&reg, &ee->ee_devhdl)) != 0) {
227 		dev_err(ee->ee_dip, CE_WARN, "failed to create eedev device: "
228 		    "%d", ret);
229 		return (false);
230 	}
231 
232 	return (true);
233 }
234 
235 static void
ee100x_cleanup(ee100x_t * ee)236 ee100x_cleanup(ee100x_t *ee)
237 {
238 	eedev_fini(ee->ee_devhdl);
239 	i2c_client_destroy(ee->ee_spa1);
240 	i2c_client_destroy(ee->ee_spa0);
241 	i2c_reg_handle_destroy(ee->ee_mem_hdl);
242 	i2c_client_destroy(ee->ee_mem);
243 	ddi_soft_state_free(ee100x_state, ddi_get_instance(ee->ee_dip));
244 }
245 
246 static int
ee100x_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)247 ee100x_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
248 {
249 	i2c_errno_t ret;
250 	i2c_addr_t addr;
251 	i2c_reg_acc_attr_t attr;
252 	ee100x_t *ee;
253 
254 	if (cmd == DDI_RESUME) {
255 		return (DDI_SUCCESS);
256 	} else if (cmd != DDI_ATTACH) {
257 		return (DDI_FAILURE);
258 	}
259 
260 	if (ddi_soft_state_zalloc(ee100x_state, ddi_get_instance(dip)) !=
261 	    DDI_SUCCESS) {
262 		dev_err(dip, CE_WARN, "failed to alocate soft state");
263 		return (DDI_FAILURE);
264 	}
265 
266 	ee = ddi_get_soft_state(ee100x_state, ddi_get_instance(dip));
267 	if (ee == NULL) {
268 		dev_err(dip, CE_WARN, "failed to obtain soft state after "
269 		    "alloc");
270 		return (DDI_FAILURE);
271 	}
272 	ee->ee_dip = dip;
273 
274 	if ((ret = i2c_client_init(ee->ee_dip, 0, &ee->ee_mem)) !=
275 	    I2C_CORE_E_OK) {
276 		dev_err(dip, CE_WARN, "failed to initialize memory i2c "
277 		    "client: 0x%x", ret);
278 		goto err;
279 	}
280 
281 	bzero(&attr, sizeof (attr));
282 	attr.i2cacc_version = I2C_REG_ACC_ATTR_V0;
283 	attr.i2cacc_addr_len = 1;
284 	attr.i2cacc_reg_len = 1;
285 	attr.i2cacc_addr_max = UINT8_MAX;
286 
287 	if ((ret = i2c_reg_handle_init(ee->ee_mem, &attr, &ee->ee_mem_hdl)) !=
288 	    I2C_CORE_E_OK) {
289 		dev_err(dip, CE_WARN, "failed to initialize client handle: "
290 		    "0x%x", ret);
291 		goto err;
292 	}
293 
294 	addr.ia_type = I2C_ADDR_7BIT;
295 	addr.ia_addr = EE1004_SET_PAGE0_ADDR;
296 	if ((ret = i2c_client_claim_addr(ee->ee_dip, &addr, I2C_CLAIM_F_SHARED,
297 	    &ee->ee_spa0)) != I2C_CORE_E_OK) {
298 		dev_err(dip, CE_WARN, "failed to claim address 0x%x: 0x%x",
299 		    addr.ia_addr, ret);
300 		goto err;
301 	}
302 
303 	addr.ia_type = I2C_ADDR_7BIT;
304 	addr.ia_addr = EE1004_SET_PAGE1_ADDR;
305 	if ((ret = i2c_client_claim_addr(ee->ee_dip, &addr, I2C_CLAIM_F_SHARED,
306 	    &ee->ee_spa1)) != I2C_CORE_E_OK) {
307 		dev_err(dip, CE_WARN, "failed to claim address 0x%x: 0x%x",
308 		    addr.ia_addr, ret);
309 		goto err;
310 	}
311 
312 	if (!ee100x_eedev_init(ee))
313 		goto err;
314 
315 	return (DDI_SUCCESS);
316 
317 err:
318 	ee100x_cleanup(ee);
319 	return (DDI_FAILURE);
320 }
321 
322 static int
ee100x_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** outp)323 ee100x_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **outp)
324 {
325 	ee100x_t *ee;
326 
327 	switch (cmd) {
328 	case DDI_INFO_DEVT2DEVINFO:
329 		ee = ddi_get_soft_state(ee100x_state, getminor((dev_t)arg));
330 		if (ee == NULL) {
331 			return (DDI_FAILURE);
332 		}
333 		*outp = ee->ee_dip;
334 		break;
335 	case DDI_INFO_DEVT2INSTANCE:
336 		ee = ddi_get_soft_state(ee100x_state, getminor((dev_t)arg));
337 		if (ee == NULL) {
338 			return (DDI_FAILURE);
339 		}
340 
341 		*outp = (void *)(uintptr_t)ddi_get_instance(ee->ee_dip);
342 		break;
343 	default:
344 		return (DDI_FAILURE);
345 	}
346 
347 	return (DDI_SUCCESS);
348 }
349 
350 static int
ee100x_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)351 ee100x_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
352 {
353 	ee100x_t *ee;
354 
355 	if (cmd == DDI_SUSPEND) {
356 		return (DDI_SUCCESS);
357 	} else if (cmd != DDI_DETACH) {
358 		return (DDI_FAILURE);
359 	}
360 
361 	ee = ddi_get_soft_state(ee100x_state, ddi_get_instance(dip));
362 	if (ee == NULL) {
363 		dev_err(dip, CE_WARN, "cannot detach: failed to obtain soft "
364 		    "state");
365 		return (DDI_FAILURE);
366 	}
367 
368 	ee100x_cleanup(ee);
369 	return (DDI_SUCCESS);
370 }
371 
372 static struct dev_ops ee100x_dev_ops = {
373 	.devo_rev = DEVO_REV,
374 	.devo_refcnt = 0,
375 	.devo_getinfo = ee100x_getinfo,
376 	.devo_identify = nulldev,
377 	.devo_probe = nulldev,
378 	.devo_attach = ee100x_attach,
379 	.devo_detach = ee100x_detach,
380 	.devo_reset = nodev,
381 	.devo_quiesce = ddi_quiesce_not_needed
382 };
383 
384 static struct modldrv ee100x_modldrv = {
385 	.drv_modops = &mod_driverops,
386 	.drv_linkinfo = "EE1004 Driver",
387 	.drv_dev_ops = &ee100x_dev_ops
388 };
389 
390 static struct modlinkage ee100x_modlinkage = {
391 	.ml_rev = MODREV_1,
392 	.ml_linkage = { &ee100x_modldrv, NULL }
393 };
394 
395 static void
ee100x_globals_fini(void)396 ee100x_globals_fini(void)
397 {
398 	ddi_soft_state_fini(&ee100x_state);
399 	mutex_destroy(&ee100x_spa_mutex);
400 }
401 
402 static int
ee100x_globals_init(void)403 ee100x_globals_init(void)
404 {
405 	int ret;
406 
407 	if ((ret = ddi_soft_state_init(&ee100x_state, sizeof (ee100x_t), 0)) !=
408 	    0) {
409 		return (ret);
410 	}
411 	mutex_init(&ee100x_spa_mutex, NULL, MUTEX_DRIVER, NULL);
412 
413 	return (0);
414 }
415 
416 int
_init(void)417 _init(void)
418 {
419 	int ret;
420 
421 	if ((ret = ee100x_globals_init()) != 0) {
422 		return (ret);
423 	}
424 
425 	if ((ret = mod_install(&ee100x_modlinkage)) != 0) {
426 		ee100x_globals_fini();
427 	}
428 
429 	return (ret);
430 }
431 
432 int
_info(struct modinfo * modinfop)433 _info(struct modinfo *modinfop)
434 {
435 	return (mod_info(&ee100x_modlinkage, modinfop));
436 }
437 
438 int
_fini(void)439 _fini(void)
440 {
441 	int ret;
442 
443 	if ((ret = mod_remove(&ee100x_modlinkage)) == 0) {
444 		ee100x_globals_fini();
445 	}
446 
447 	return (ret);
448 }
449