xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/clients/seeprom.c (revision 1160694128cd3980cc06abe31af529a887efd310)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/stat.h>
30 #include <sys/modctl.h>
31 #include <sys/open.h>
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/conf.h>
37 #include <sys/file.h>
38 #include <sys/note.h>
39 #include <sys/i2c/misc/i2c_svc.h>
40 #include <sys/i2c/clients/seeprom_impl.h>
41 
42 /*
43  * cb ops
44  */
45 static int seeprom_open(dev_t *, int, int, cred_t *);
46 static int seeprom_close(dev_t, int, int, cred_t *);
47 static int seeprom_read(dev_t, struct uio *, cred_t *);
48 static int seeprom_write(dev_t, struct uio *, cred_t *);
49 static int seeprom_io(dev_t, struct uio *, int);
50 
51 /*
52  * dev ops
53  */
54 static int seeprom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
55 static int seeprom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
56 static int seeprom_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
57 
58 static struct cb_ops seeprom_cbops = {
59 	seeprom_open,			/* open */
60 	seeprom_close,			/* close */
61 	nodev,				/* strategy */
62 	nodev,				/* print */
63 	nodev,				/* dump */
64 	seeprom_read,			/* read */
65 	seeprom_write,			/* write */
66 	nodev,				/* ioctl */
67 	nodev,				/* devmap */
68 	nodev,				/* mmap */
69 	nodev,				/* segmap */
70 	nochpoll,			/* poll */
71 	ddi_prop_op,			/* cb_prop_op */
72 	NULL,				/* streamtab */
73 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
74 	CB_REV,				/* rev */
75 	nodev,				/* int (*cb_aread)() */
76 	nodev				/* int (*cb_awrite)() */
77 };
78 
79 static struct dev_ops seeprom_ops = {
80 	DEVO_REV,
81 	0,
82 	seeprom_info,
83 	nulldev,
84 	nulldev,
85 	seeprom_attach,
86 	seeprom_detach,
87 	nodev,
88 	&seeprom_cbops,
89 	NULL,
90 	nulldev
91 };
92 
93 static struct modldrv seeprom_modldrv = {
94 	&mod_driverops,	 /* type of module - driver */
95 	"I2C serial EEPROM device driver v%I%",
96 	&seeprom_ops,
97 };
98 
99 static struct modlinkage seeprom_modlinkage = {
100 	MODREV_1,
101 	&seeprom_modldrv,
102 	0
103 };
104 
105 /*
106  * globals
107  */
108 
109 static void *seepromsoft_statep;
110 
111 int
112 _init(void)
113 {
114 	int error;
115 
116 	if ((error = ddi_soft_state_init(&seepromsoft_statep,
117 	    sizeof (struct seepromunit), 1)) != 0)
118 		return (error);
119 
120 	if ((error = mod_install(&seeprom_modlinkage)) != 0) {
121 		ddi_soft_state_fini(&seepromsoft_statep);
122 		return (error);
123 	}
124 
125 	return (error);
126 }
127 
128 int
129 _fini(void)
130 {
131 	int error;
132 
133 	error = mod_remove(&seeprom_modlinkage);
134 	if (error == 0) {
135 		ddi_soft_state_fini(&seepromsoft_statep);
136 	}
137 
138 	return (error);
139 }
140 
141 int
142 _info(struct modinfo *modinfop)
143 {
144 	return (mod_info(&seeprom_modlinkage, modinfop));
145 }
146 
147 static int
148 seeprom_do_attach(dev_info_t *dip)
149 {
150 	struct seepromunit *unitp;
151 	int instance;
152 	dev_t	dev;
153 
154 	instance = ddi_get_instance(dip);
155 
156 	if (ddi_soft_state_zalloc(seepromsoft_statep, instance) != 0) {
157 		cmn_err(CE_WARN, "%s_%d: failed to zalloc softstate",
158 			ddi_node_name(dip), instance);
159 
160 		return (DDI_FAILURE);
161 	}
162 
163 	unitp = ddi_get_soft_state(seepromsoft_statep, instance);
164 
165 	unitp->seeprom_dip = dip;
166 
167 	(void) snprintf(unitp->seeprom_name, sizeof (unitp->seeprom_name),
168 			"%s%d", ddi_driver_name(dip), instance);
169 
170 	if (ddi_create_minor_node(dip, ddi_node_name(dip), S_IFCHR,
171 	    instance, SEEPROM_NODE_TYPE, NULL) == DDI_FAILURE) {
172 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed for '%s'",
173 		    unitp->seeprom_name, ddi_node_name(dip));
174 		ddi_soft_state_free(seepromsoft_statep, instance);
175 
176 		return (DDI_FAILURE);
177 	}
178 
179 	if (i2c_client_register(dip, &unitp->seeprom_hdl) != I2C_SUCCESS) {
180 		cmn_err(CE_WARN, "i2c_client_register failed\n");
181 		ddi_remove_minor_node(dip, NULL);
182 		ddi_soft_state_free(seepromsoft_statep, instance);
183 
184 		return (DDI_FAILURE);
185 	}
186 
187 	if (strcmp(ddi_binding_name(dip), "i2c-at34c02") == 0) {
188 		unitp->seeprom_addrsize = AT34C02_ADDRSIZE;
189 		unitp->seeprom_memsize = AT34C02_MEMSIZE;
190 		unitp->seeprom_pagesize = AT34C02_PAGESIZE;
191 		unitp->seeprom_pagemask = AT34C02_PAGEMASK;
192 	} else {
193 		/*
194 		 * Default is i2c-at24c64
195 		 */
196 		unitp->seeprom_addrsize = AT24C64_ADDRSIZE;
197 		unitp->seeprom_memsize = AT24C64_MEMSIZE;
198 		unitp->seeprom_pagesize = AT24C64_PAGESIZE;
199 		unitp->seeprom_pagemask = AT24C64_PAGEMASK;
200 	}
201 	dev = makedevice(DDI_MAJOR_T_UNKNOWN, instance);
202 
203 	(void) ddi_prop_create(dev, dip, DDI_PROP_CANSLEEP, "size",
204 	    (caddr_t)&unitp->seeprom_memsize, sizeof (unitp->seeprom_memsize));
205 
206 	mutex_init(&unitp->seeprom_mutex, NULL, MUTEX_DRIVER, NULL);
207 	cv_init(&unitp->seeprom_cv, NULL, CV_DRIVER, NULL);
208 
209 	return (DDI_SUCCESS);
210 }
211 
212 static int
213 seeprom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
214 {
215 	switch (cmd) {
216 	case DDI_ATTACH:
217 
218 		return (seeprom_do_attach(dip));
219 	case DDI_RESUME:
220 		/*
221 		 * No state to restore.
222 		 */
223 		return (DDI_SUCCESS);
224 	default:
225 
226 		return (DDI_FAILURE);
227 	}
228 }
229 
230 static int
231 seeprom_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
232 {
233 	_NOTE(ARGUNUSED(dip))
234 	struct seepromunit *unitp;
235 
236 	switch (infocmd) {
237 	case DDI_INFO_DEVT2DEVINFO:
238 		unitp = ddi_get_soft_state(seepromsoft_statep,
239 		    getminor((dev_t)arg));
240 		if (unitp == NULL) {
241 
242 			return (DDI_FAILURE);
243 		}
244 		*result = (void *)unitp->seeprom_dip;
245 
246 		return (DDI_SUCCESS);
247 	case DDI_INFO_DEVT2INSTANCE:
248 		*result = (void *)getminor((dev_t)arg);
249 
250 		return (DDI_SUCCESS);
251 	default:
252 		return (DDI_FAILURE);
253 	}
254 
255 }
256 
257 static int
258 seeprom_do_detach(dev_info_t *dip)
259 {
260 	struct seepromunit *unitp;
261 	int instance;
262 
263 	instance = ddi_get_instance(dip);
264 	unitp = ddi_get_soft_state(seepromsoft_statep, instance);
265 	i2c_client_unregister(unitp->seeprom_hdl);
266 	ddi_remove_minor_node(dip, NULL);
267 	mutex_destroy(&unitp->seeprom_mutex);
268 	cv_destroy(&unitp->seeprom_cv);
269 
270 	ddi_soft_state_free(seepromsoft_statep, instance);
271 
272 	return (DDI_SUCCESS);
273 }
274 
275 static int
276 seeprom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
277 {
278 	switch (cmd) {
279 	case DDI_DETACH:
280 
281 		return (seeprom_do_detach(dip));
282 	case DDI_SUSPEND:
283 		/*
284 		 * No state to save.  IO will be blocked by nexus.
285 		 */
286 		return (DDI_SUCCESS);
287 	default:
288 
289 		return (DDI_FAILURE);
290 	}
291 }
292 
293 static int
294 seeprom_open(dev_t *devp, int flags, int otyp, cred_t *credp)
295 {
296 	_NOTE(ARGUNUSED(credp))
297 	struct seepromunit *unitp;
298 	int instance;
299 	int err = 0;
300 
301 	if (otyp != OTYP_CHR) {
302 
303 		return (EINVAL);
304 	}
305 
306 	instance = getminor(*devp);
307 
308 	unitp = (struct seepromunit *)
309 		ddi_get_soft_state(seepromsoft_statep, instance);
310 
311 	if (unitp == NULL) {
312 
313 		return (ENXIO);
314 	}
315 
316 	mutex_enter(&unitp->seeprom_mutex);
317 
318 	if (flags & FEXCL) {
319 		if (unitp->seeprom_oflag != 0) {
320 			err = EBUSY;
321 		} else {
322 			unitp->seeprom_oflag = FEXCL;
323 		}
324 	} else {
325 		if (unitp->seeprom_oflag == FEXCL) {
326 			err = EBUSY;
327 		} else {
328 			unitp->seeprom_oflag = FOPEN;
329 		}
330 	}
331 
332 	mutex_exit(&unitp->seeprom_mutex);
333 
334 	return (err);
335 }
336 
337 static int
338 seeprom_close(dev_t dev, int flags, int otyp, cred_t *credp)
339 {
340 	_NOTE(ARGUNUSED(flags, otyp, credp))
341 	struct seepromunit *unitp;
342 	int instance;
343 
344 	instance = getminor(dev);
345 
346 	unitp = (struct seepromunit *)
347 		ddi_get_soft_state(seepromsoft_statep, instance);
348 
349 	if (unitp == NULL) {
350 
351 		return (ENXIO);
352 	}
353 
354 	mutex_enter(&unitp->seeprom_mutex);
355 
356 	unitp->seeprom_oflag = 0;
357 
358 	mutex_exit(&unitp->seeprom_mutex);
359 
360 	return (DDI_SUCCESS);
361 }
362 
363 static int
364 seeprom_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
365 {
366 	_NOTE(ARGUNUSED(cred_p))
367 	return (seeprom_io(dev, uiop, B_READ));
368 }
369 
370 static int
371 seeprom_write(dev_t dev, struct uio *uiop, cred_t *cred_p)
372 {
373 	_NOTE(ARGUNUSED(cred_p))
374 	return (seeprom_io(dev, uiop, B_WRITE));
375 }
376 
377 static int
378 seeprom_io(dev_t dev, struct uio *uiop, int rw)
379 {
380 	struct seepromunit *unitp;
381 	int instance = getminor(dev);
382 	int	seeprom_addr;
383 	int	bytes_to_rw;
384 	int	err = 0;
385 	int	current_xfer_len;
386 	int	actual_data_xfer;
387 	i2c_transfer_t *i2ctp = NULL;
388 
389 	unitp = (struct seepromunit *)
390 		ddi_get_soft_state(seepromsoft_statep, instance);
391 
392 
393 	if (unitp == NULL) {
394 		return (ENXIO);
395 	}
396 
397 	if (uiop->uio_offset >= unitp->seeprom_memsize) {
398 		/*
399 		 * Exceeded seeprom size.
400 		 */
401 
402 		return (ENXIO);
403 	}
404 
405 	seeprom_addr = uiop->uio_offset;
406 
407 	if (uiop->uio_resid == 0) {
408 		return (0);
409 	}
410 
411 	bytes_to_rw = min(uiop->uio_resid,
412 		unitp->seeprom_memsize - uiop->uio_offset);
413 	/*
414 	 * Serialize access here to prevent a transaction starting
415 	 * until after 20 ms delay if last operation was a write.
416 	 */
417 	mutex_enter(&unitp->seeprom_mutex);
418 	while ((unitp->seeprom_flags & SEEPROM_BUSY) == SEEPROM_BUSY) {
419 		if (cv_wait_sig(&unitp->seeprom_cv,
420 			&unitp->seeprom_mutex) <= 0) {
421 			mutex_exit(&unitp->seeprom_mutex);
422 
423 			return (EINTR);
424 		}
425 	}
426 	unitp->seeprom_flags |= SEEPROM_BUSY;
427 	mutex_exit(&unitp->seeprom_mutex);
428 
429 	while ((bytes_to_rw != 0) && (err == 0)) {
430 		current_xfer_len = min(bytes_to_rw, unitp->seeprom_pagesize -
431 		    (seeprom_addr & unitp->seeprom_pagemask));
432 
433 		if (rw == B_WRITE) {
434 			if (i2ctp == NULL) {
435 				(void) i2c_transfer_alloc(unitp->seeprom_hdl,
436 				    &i2ctp,
437 				    unitp->seeprom_addrsize + current_xfer_len,
438 				    0,
439 				    I2C_SLEEP);
440 
441 				if ((err = uiomove(&i2ctp->i2c_wbuf[
442 				    unitp->seeprom_addrsize],
443 				    current_xfer_len, UIO_WRITE, uiop)) != 0) {
444 					i2c_transfer_free(unitp->seeprom_hdl,
445 					    i2ctp);
446 					break;
447 				}
448 				i2ctp->i2c_version = I2C_XFER_REV;
449 				i2ctp->i2c_flags = I2C_WR;
450 			} else {
451 
452 				/*
453 				 * not all bytes were sent in previous attempt.
454 				 * Adjust the write pointer to the unsent data.
455 				 */
456 				/*LINTED*/
457 				i2ctp->i2c_wbuf += actual_data_xfer;
458 				/*LINTED*/
459 				i2ctp->i2c_wlen -= actual_data_xfer;
460 			}
461 
462 			if (unitp->seeprom_addrsize == 2) {
463 				i2ctp->i2c_wbuf[0] = (seeprom_addr >> 8);
464 				i2ctp->i2c_wbuf[1] = (uchar_t)seeprom_addr;
465 			} else {
466 				i2ctp->i2c_wbuf[0] = (uchar_t)seeprom_addr;
467 			}
468 
469 			if ((err = i2c_transfer(unitp->seeprom_hdl, i2ctp)) !=
470 			    I2C_SUCCESS) {
471 				i2c_transfer_free(unitp->seeprom_hdl, i2ctp);
472 				break;
473 			}
474 
475 			actual_data_xfer = i2ctp->i2c_wlen -
476 			    i2ctp->i2c_w_resid - unitp->seeprom_addrsize;
477 
478 			if (i2ctp->i2c_w_resid == 0) {
479 				i2c_transfer_free(unitp->seeprom_hdl, i2ctp);
480 				i2ctp = NULL;
481 			}
482 			/*
483 			 * 20 ms(20000 Microsec) delay is required before
484 			 * issuing another transaction.  This enforces that
485 			 * wait.
486 			 */
487 			delay(drv_usectohz(20000));
488 		} else {
489 			/*
490 			 * SEEPROM read.  First write out the address to read.
491 			 */
492 			(void) i2c_transfer_alloc(unitp->seeprom_hdl, &i2ctp,
493 			    unitp->seeprom_addrsize, current_xfer_len,
494 			    I2C_SLEEP);
495 			i2ctp->i2c_version = I2C_XFER_REV;
496 
497 			if (unitp->seeprom_addrsize == 2) {
498 				i2ctp->i2c_wbuf[0] = (seeprom_addr >> 8);
499 				i2ctp->i2c_wbuf[1] = (uchar_t)seeprom_addr;
500 			} else {
501 				i2ctp->i2c_wbuf[0] = (uchar_t)seeprom_addr;
502 			}
503 
504 			i2ctp->i2c_flags = I2C_WR_RD;
505 
506 			if ((err = i2c_transfer(unitp->seeprom_hdl, i2ctp)) !=
507 			    I2C_SUCCESS) {
508 				i2c_transfer_free(unitp->seeprom_hdl, i2ctp);
509 				break;
510 			}
511 
512 			actual_data_xfer = i2ctp->i2c_rlen - i2ctp->i2c_r_resid;
513 
514 			err = uiomove(i2ctp->i2c_rbuf, actual_data_xfer,
515 			    UIO_READ, uiop);
516 			i2c_transfer_free(unitp->seeprom_hdl, i2ctp);
517 		}
518 
519 		bytes_to_rw -= actual_data_xfer;
520 		seeprom_addr += actual_data_xfer;
521 	}
522 
523 	mutex_enter(&unitp->seeprom_mutex);
524 	unitp->seeprom_flags = 0;
525 	cv_signal(&unitp->seeprom_cv);
526 	mutex_exit(&unitp->seeprom_mutex);
527 
528 	return (err);
529 }
530