xref: /minix3/minix/drivers/eeprom/cat24c256/cat24c256.c (revision 3f82ac6a4e188419336747098d0d6616cd2f3d3d)
1 #include <minix/blockdriver.h>
2 #include <minix/com.h>
3 #include <minix/drivers.h>
4 #include <minix/ds.h>
5 #include <minix/i2c.h>
6 #include <minix/i2cdriver.h>
7 #include <minix/log.h>
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 
12 /* constants */
13 #define NR_DEVS 1		/* number of devices this driver handles */
14 #define EEPROM_DEV 0		/* index of eeprom device */
15 
16 /* When passing data over a grant one needs to pass
17  * a buffer to sys_safecopy copybuff is used for that*/
18 #define COPYBUF_SIZE 0x1000	/* 4k buff */
19 static unsigned char copybuf[COPYBUF_SIZE];
20 
21 static i2c_addr_t valid_addrs[9] = {
22 	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x00
23 };
24 
25 /* libblockdriver callbacks */
26 static int cat24c256_blk_open(devminor_t minor, int access);
27 static int cat24c256_blk_close(devminor_t minor);
28 static ssize_t cat24c256_blk_transfer(devminor_t minor, int do_write,
29     u64_t pos, endpoint_t endpt, iovec_t * iov, unsigned int count, int flags);
30 static int cat24c256_blk_ioctl(devminor_t minor, unsigned long request,
31     endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt);
32 static struct device *cat24c256_blk_part(devminor_t minor);
33 static void cat24c256_blk_other(message * m, int ipc_status);
34 
35 /* Entry points into the device dependent code of block drivers. */
36 struct blockdriver cat24c256_tab = {
37 	.bdr_type = BLOCKDRIVER_TYPE_OTHER,
38 	.bdr_open = cat24c256_blk_open,
39 	.bdr_close = cat24c256_blk_close,
40 	.bdr_transfer = cat24c256_blk_transfer,
41 	.bdr_ioctl = cat24c256_blk_ioctl,	/* always returns ENOTTY */
42 	.bdr_part = cat24c256_blk_part,
43 	.bdr_other = cat24c256_blk_other	/* for notify events from DS */
44 };
45 
46 static int cat24c256_read128(uint16_t memaddr, void *buf, size_t buflen, int flags);
47 static int cat24c256_read(uint16_t memaddr, void *buf, size_t buflen, int flags);
48 static int cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen, int flags);
49 static int cat24c256_write(uint16_t memaddr, void *buf, size_t buflen, int flags);
50 
51 /* globals */
52 
53 /* counts the number of times a device file is open */
54 static int openct[NR_DEVS];
55 
56 /* base and size of each device */
57 static struct device geom[NR_DEVS];
58 
59 /* the bus that this device is on (counting starting at 1) */
60 static uint32_t bus;
61 
62 /* slave address of the device */
63 static i2c_addr_t address;
64 
65 /* endpoint for the driver for the bus itself. */
66 static endpoint_t bus_endpoint;
67 
68 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
69 static struct log log = {
70 	.name = "cat24c256",
71 	.log_level = LEVEL_INFO,
72 	.log_func = default_log
73 };
74 
75 static int
cat24c256_blk_open(devminor_t minor,int access)76 cat24c256_blk_open(devminor_t minor, int access)
77 {
78 	log_trace(&log, "cat24c256_blk_open(%d,%d)\n", minor, access);
79 	if (cat24c256_blk_part(minor) == NULL) {
80 		return ENXIO;
81 	}
82 
83 	openct[minor]++;
84 
85 	return OK;
86 }
87 
88 static int
cat24c256_blk_close(devminor_t minor)89 cat24c256_blk_close(devminor_t minor)
90 {
91 	log_trace(&log, "cat24c256_blk_close(%d)\n", minor);
92 	if (cat24c256_blk_part(minor) == NULL) {
93 		return ENXIO;
94 	}
95 
96 	if (openct[minor] < 1) {
97 		log_warn(&log, "closing unopened device %d\n", minor);
98 		return EINVAL;
99 	}
100 	openct[minor]--;
101 
102 	return OK;
103 }
104 
105 static ssize_t
cat24c256_blk_transfer(devminor_t minor,int do_write,u64_t pos64,endpoint_t endpt,iovec_t * iov,unsigned int nr_req,int flags)106 cat24c256_blk_transfer(devminor_t minor, int do_write, u64_t pos64,
107     endpoint_t endpt, iovec_t * iov, unsigned int nr_req, int flags)
108 {
109 	/* Read or write one the driver's block devices. */
110 	unsigned int count;
111 	struct device *dv;
112 	u64_t dv_size;
113 	int r;
114 	u64_t position;
115 	cp_grant_id_t grant;
116 	ssize_t total = 0;
117 	vir_bytes offset;
118 
119 	log_trace(&log, "cat24c256_blk_transfer()\n");
120 
121 	/* Get minor device information. */
122 	dv = cat24c256_blk_part(minor);
123 	if (dv == NULL) {
124 		return ENXIO;
125 	}
126 
127 	if (nr_req > NR_IOREQS) {
128 		return EINVAL;
129 	}
130 
131 	dv_size = dv->dv_size;
132 	if (pos64 > dv_size) {
133 		return OK;	/* Beyond EOF */
134 	}
135 	position = pos64;
136 	offset = 0;
137 
138 	while (nr_req > 0) {
139 
140 		/* How much to transfer and where to / from. */
141 		count = iov->iov_size;
142 		grant = (cp_grant_id_t) iov->iov_addr;
143 
144 		/* check for EOF */
145 		if (position >= dv_size) {
146 			return total;
147 		}
148 
149 		/* don't go past the end of the device */
150 		if (position + count > dv_size) {
151 			count = dv_size - position;
152 		}
153 
154 		/* don't overflow copybuf */
155 		if (count > COPYBUF_SIZE) {
156 			count = COPYBUF_SIZE;
157 		}
158 
159 		log_trace(&log, "transfering 0x%x bytes\n", count);
160 
161 		if (do_write) {
162 			r = sys_safecopyfrom(endpt, grant, (vir_bytes) offset,
163 			    (vir_bytes) copybuf, count);
164 			if (r != OK) {
165 				log_warn(&log, "safecopyfrom failed\n");
166 				return EINVAL;
167 			}
168 
169 			r = cat24c256_write(position, copybuf, count, flags);
170 			if (r != OK) {
171 				log_warn(&log, "write failed (r=%d)\n", r);
172 				return r;
173 			}
174 		} else {
175 			r = cat24c256_read(position, copybuf, count, flags);
176 			if (r != OK) {
177 				log_warn(&log, "read failed (r=%d)\n", r);
178 				return r;
179 			}
180 
181 			r = sys_safecopyto(endpt, grant, (vir_bytes) offset,
182 			    (vir_bytes) copybuf, count);
183 			if (r != OK) {
184 				log_warn(&log, "safecopyto failed\n");
185 				return EINVAL;
186 			}
187 		}
188 
189 		/* Book the number of bytes transferred. */
190 		position += count;
191 		total += count;
192 		offset += count;
193 
194 		/* only go on to the next iov when this one is full */
195 		if ((iov->iov_size -= count) == 0) {
196 			iov++;
197 			nr_req--;
198 			offset = 0;
199 		}
200 	}
201 
202 	return total;
203 }
204 
205 static int
cat24c256_blk_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,endpoint_t UNUSED (user_endpt))206 cat24c256_blk_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
207     cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
208 {
209 	log_trace(&log, "cat24c256_blk_ioctl(%d)\n", minor);
210 	/* no supported ioctls for this device */
211 	return ENOTTY;
212 }
213 
214 static struct device *
cat24c256_blk_part(devminor_t minor)215 cat24c256_blk_part(devminor_t minor)
216 {
217 	log_trace(&log, "cat24c256_blk_part(%d)\n", minor);
218 
219 	if (minor < 0 || minor >= NR_DEVS) {
220 		return NULL;
221 	}
222 
223 	return &geom[minor];
224 }
225 
226 static void
cat24c256_blk_other(message * m,int ipc_status)227 cat24c256_blk_other(message * m, int ipc_status)
228 {
229 	log_trace(&log, "cat24c256_blk_other(0x%x)\n", m->m_type);
230 
231 	if (is_ipc_notify(ipc_status)) {
232 		if (m->m_source == DS_PROC_NR) {
233 			log_debug(&log,
234 			    "bus driver changed state, update endpoint\n");
235 			i2cdriver_handle_bus_update(&bus_endpoint, bus,
236 			    address);
237 		}
238 	} else
239 		log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
240 }
241 
242 /* The lower level i2c interface can only read/write 128 bytes at a time.
243  * One might want to do more I/O than that at once w/EEPROM, so there is
244  * cat24c256_read() and cat24c256_read128(). cat24c256_read128() does the
245  * actual reading in chunks up to 128 bytes. cat24c256_read() splits
246  * the request up into chunks and repeatedly calls cat24c256_read128()
247  * until all of the requested EEPROM locations have been read.
248  */
249 
250 static int
cat24c256_read128(uint16_t memaddr,void * buf,size_t buflen,int flags)251 cat24c256_read128(uint16_t memaddr, void *buf, size_t buflen, int flags)
252 {
253 	int r;
254 	minix_i2c_ioctl_exec_t ioctl_exec;
255 
256 	if (buflen > I2C_EXEC_MAX_BUFLEN || buf == NULL
257 	    || (((uint16_t) (memaddr + buflen)) < memaddr)) {
258 		log_warn(&log,
259 		    "buflen exceeded max or buf == NULL or would overflow\n");
260 		return -1;
261 	}
262 
263 	memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
264 
265 	ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
266 	ioctl_exec.iie_addr = address;
267 
268 	/* set the memory address to read from */
269 	if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) {
270 		/* reading within the current page */
271 		ioctl_exec.iie_cmd[0] = (memaddr & 0xff);
272 		ioctl_exec.iie_cmdlen = 1;
273 	} else {
274 		ioctl_exec.iie_cmd[0] = ((memaddr >> 8) & 0xff);
275 		ioctl_exec.iie_cmd[1] = (memaddr & 0xff);
276 		ioctl_exec.iie_cmdlen = 2;
277 	}
278 
279 	ioctl_exec.iie_buflen = buflen;
280 
281 	r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
282 	if (r != OK) {
283 		return r;
284 	}
285 
286 	/* call was good, copy results to caller's buffer */
287 	memcpy(buf, ioctl_exec.iie_buf, buflen);
288 
289 	log_debug(&log, "Read %d bytes from 0x%x 0x%x OK\n", buflen,
290 	    ioctl_exec.iie_cmd[0], ioctl_exec.iie_cmd[1]);
291 
292 	return OK;
293 }
294 
295 int
cat24c256_read(uint16_t memaddr,void * buf,size_t buflen,int flags)296 cat24c256_read(uint16_t memaddr, void *buf, size_t buflen, int flags)
297 {
298 	int r;
299 	uint16_t i;
300 
301 	if (buf == NULL || ((memaddr + buflen) < memaddr)) {
302 		log_warn(&log, "buf == NULL or would overflow\n");
303 		return -1;
304 	}
305 
306 	for (i = 0; i < buflen; i += 128) {
307 
308 		r = cat24c256_read128(memaddr + i, buf + i,
309 		    ((buflen - i) < 128) ? (buflen - i) : 128, flags);
310 		if (r != OK) {
311 			return r;
312 		}
313 
314 		log_trace(&log, "read %d bytes starting at 0x%x\n",
315 		    ((buflen - i) < 128) ? (buflen - i) : 128, memaddr + i);
316 	}
317 
318 	return OK;
319 }
320 
321 static int
cat24c256_write16(uint16_t memaddr,void * buf,size_t buflen,int flags)322 cat24c256_write16(uint16_t memaddr, void *buf, size_t buflen, int flags)
323 {
324 	int r;
325 	int addrlen;
326 	minix_i2c_ioctl_exec_t ioctl_exec;
327 
328 	if (buflen > (I2C_EXEC_MAX_BUFLEN - 2) || buf == NULL
329 	    || (((uint16_t) (memaddr + buflen + 2)) < memaddr)) {
330 		log_warn(&log,
331 		    "buflen exceeded max or buf == NULL or would overflow\n");
332 		return -1;
333 	}
334 
335 	memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
336 
337 	ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP;
338 	ioctl_exec.iie_addr = address;
339 	ioctl_exec.iie_cmdlen = 0;
340 
341 	/* set the memory address to write to */
342 	if ((BDEV_NOPAGE & flags) == BDEV_NOPAGE) {
343 		/* writing within the current page */
344 		ioctl_exec.iie_buf[0] = (memaddr & 0xff);	/* address */
345 		addrlen = 1;
346 	} else {
347 		ioctl_exec.iie_buf[0] = ((memaddr >> 8) & 0xff);/* page */
348 		ioctl_exec.iie_buf[1] = (memaddr & 0xff);	/* address */
349 		addrlen = 2;
350 	}
351 	memcpy(ioctl_exec.iie_buf + addrlen, buf, buflen);
352 	ioctl_exec.iie_buflen = buflen + addrlen;
353 
354 	r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
355 	if (r != OK) {
356 		return r;
357 	}
358 
359 	log_debug(&log, "Wrote %d bytes to 0x%x 0x%x OK - First = 0x%x\n",
360 	    buflen, ioctl_exec.iie_buf[0], ioctl_exec.iie_buf[1],
361 	    ioctl_exec.iie_buf[2]);
362 
363 	return OK;
364 }
365 
366 int
cat24c256_write(uint16_t memaddr,void * buf,size_t buflen,int flags)367 cat24c256_write(uint16_t memaddr, void *buf, size_t buflen, int flags)
368 {
369 	int r;
370 	uint16_t i;
371 
372 	if (buf == NULL || ((memaddr + buflen) < memaddr)) {
373 		log_warn(&log, "buf == NULL or would overflow\n");
374 		return -1;
375 	}
376 
377 	for (i = 0; i < buflen; i += 16) {
378 
379 		r = cat24c256_write16(memaddr + i, buf + i,
380 		    ((buflen - i) < 16) ? (buflen - i) : 16, flags);
381 		if (r != OK) {
382 			return r;
383 		}
384 
385 		log_trace(&log, "wrote %d bytes starting at 0x%x\n",
386 		    ((buflen - i) < 16) ? (buflen - i) : 16, memaddr + i);
387 	}
388 
389 	return OK;
390 }
391 
392 static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))393 sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
394 {
395 	ds_publish_u32("bus", bus, DSF_OVERWRITE);
396 	ds_publish_u32("address", address, DSF_OVERWRITE);
397 	return OK;
398 }
399 
400 static int
lu_state_restore(void)401 lu_state_restore(void)
402 {
403 	/* Restore the state. */
404 	u32_t value;
405 
406 	ds_retrieve_u32("bus", &value);
407 	ds_delete_u32("bus");
408 	bus = (int) value;
409 
410 	ds_retrieve_u32("address", &value);
411 	ds_delete_u32("address");
412 	address = (int) value;
413 
414 	return OK;
415 }
416 
417 static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))418 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
419 {
420 	int r;
421 
422 	if (type == SEF_INIT_LU) {
423 		/* Restore the state. */
424 		lu_state_restore();
425 	}
426 
427 	geom[EEPROM_DEV].dv_base = ((u64_t) (0));
428 	geom[EEPROM_DEV].dv_size = ((u64_t) (32768));
429 
430 	/* look-up the endpoint for the bus driver */
431 	bus_endpoint = i2cdriver_bus_endpoint(bus);
432 	if (bus_endpoint == 0) {
433 		log_warn(&log, "Couldn't find bus driver.\n");
434 		return EXIT_FAILURE;
435 	}
436 
437 	/* claim the EEPROM device */
438 	r = i2cdriver_reserve_device(bus_endpoint, address);
439 	if (r != OK) {
440 		log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
441 		    address, r);
442 		return EXIT_FAILURE;
443 	}
444 
445 	if (type != SEF_INIT_LU) {
446 
447 		/* sign up for updates about the i2c bus going down/up */
448 		r = i2cdriver_subscribe_bus_updates(bus);
449 		if (r != OK) {
450 			log_warn(&log, "Couldn't subscribe to bus updates\n");
451 			return EXIT_FAILURE;
452 		}
453 
454 		i2cdriver_announce(bus);
455 		blockdriver_announce(type);
456 		log_debug(&log, "announced\n");
457 	}
458 
459 	return OK;
460 }
461 
462 static void
sef_local_startup(void)463 sef_local_startup(void)
464 {
465 	/*
466 	 * Register init callbacks. Use the same function for all event types
467 	 */
468 	sef_setcb_init_fresh(sef_cb_init);
469 	sef_setcb_init_lu(sef_cb_init);
470 	sef_setcb_init_restart(sef_cb_init);
471 
472 	/*
473 	 * Register live update callbacks.
474 	 */
475 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
476 
477 	/* Let SEF perform startup. */
478 	sef_startup();
479 }
480 
481 int
main(int argc,char * argv[])482 main(int argc, char *argv[])
483 {
484 	int r;
485 
486 	env_setargs(argc, argv);
487 	r = i2cdriver_env_parse(&bus, &address, valid_addrs);
488 	if (r < 0) {
489 		log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
490 		log_warn(&log, "Example -args 'bus=3 address=0x54'\n");
491 		return EXIT_FAILURE;
492 	} else if (r > 0) {
493 		log_warn(&log,
494 		    "Invalid slave address for device, expecting 0x50-0x57\n");
495 		return EXIT_FAILURE;
496 	}
497 
498 	sef_local_startup();
499 
500 	log_debug(&log, "Startup Complete\n");
501 	blockdriver_task(&cat24c256_tab);
502 	log_debug(&log, "Shutting down\n");
503 
504 	return OK;
505 }
506