1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Silicon Labs C2 port core Linux support |
4 | * |
5 | * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it> |
6 | * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/device.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/err.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/ctype.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/idr.h> |
18 | #include <linux/sched.h> |
19 | #include <linux/slab.h> |
20 | |
21 | #include <linux/c2port.h> |
22 | |
23 | #define DRIVER_NAME "c2port" |
24 | #define DRIVER_VERSION "0.51.0" |
25 | |
26 | static DEFINE_SPINLOCK(c2port_idr_lock); |
27 | static DEFINE_IDR(c2port_idr); |
28 | |
29 | /* |
30 | * Local variables |
31 | */ |
32 | |
33 | static struct class *c2port_class; |
34 | |
35 | /* |
36 | * C2 registers & commands defines |
37 | */ |
38 | |
39 | /* C2 registers */ |
40 | #define C2PORT_DEVICEID 0x00 |
41 | #define C2PORT_REVID 0x01 |
42 | #define C2PORT_FPCTL 0x02 |
43 | #define C2PORT_FPDAT 0xB4 |
44 | |
45 | /* C2 interface commands */ |
46 | #define C2PORT_GET_VERSION 0x01 |
47 | #define C2PORT_DEVICE_ERASE 0x03 |
48 | #define C2PORT_BLOCK_READ 0x06 |
49 | #define C2PORT_BLOCK_WRITE 0x07 |
50 | #define C2PORT_PAGE_ERASE 0x08 |
51 | |
52 | /* C2 status return codes */ |
53 | #define C2PORT_INVALID_COMMAND 0x00 |
54 | #define C2PORT_COMMAND_FAILED 0x02 |
55 | #define C2PORT_COMMAND_OK 0x0d |
56 | |
57 | /* |
58 | * C2 port low level signal managements |
59 | */ |
60 | |
61 | static void c2port_reset(struct c2port_device *dev) |
62 | { |
63 | struct c2port_ops *ops = dev->ops; |
64 | |
65 | /* To reset the device we have to keep clock line low for at least |
66 | * 20us. |
67 | */ |
68 | local_irq_disable(); |
69 | ops->c2ck_set(dev, 0); |
70 | udelay(25); |
71 | ops->c2ck_set(dev, 1); |
72 | local_irq_enable(); |
73 | |
74 | udelay(1); |
75 | } |
76 | |
77 | static void c2port_strobe_ck(struct c2port_device *dev) |
78 | { |
79 | struct c2port_ops *ops = dev->ops; |
80 | |
81 | /* During hi-low-hi transition we disable local IRQs to avoid |
82 | * interructions since C2 port specification says that it must be |
83 | * shorter than 5us, otherwise the microcontroller may consider |
84 | * it as a reset signal! |
85 | */ |
86 | local_irq_disable(); |
87 | ops->c2ck_set(dev, 0); |
88 | udelay(1); |
89 | ops->c2ck_set(dev, 1); |
90 | local_irq_enable(); |
91 | |
92 | udelay(1); |
93 | } |
94 | |
95 | /* |
96 | * C2 port basic functions |
97 | */ |
98 | |
99 | static void c2port_write_ar(struct c2port_device *dev, u8 addr) |
100 | { |
101 | struct c2port_ops *ops = dev->ops; |
102 | int i; |
103 | |
104 | /* START field */ |
105 | c2port_strobe_ck(dev); |
106 | |
107 | /* INS field (11b, LSB first) */ |
108 | ops->c2d_dir(dev, 0); |
109 | ops->c2d_set(dev, 1); |
110 | c2port_strobe_ck(dev); |
111 | ops->c2d_set(dev, 1); |
112 | c2port_strobe_ck(dev); |
113 | |
114 | /* ADDRESS field */ |
115 | for (i = 0; i < 8; i++) { |
116 | ops->c2d_set(dev, addr & 0x01); |
117 | c2port_strobe_ck(dev); |
118 | |
119 | addr >>= 1; |
120 | } |
121 | |
122 | /* STOP field */ |
123 | ops->c2d_dir(dev, 1); |
124 | c2port_strobe_ck(dev); |
125 | } |
126 | |
127 | static int c2port_read_ar(struct c2port_device *dev, u8 *addr) |
128 | { |
129 | struct c2port_ops *ops = dev->ops; |
130 | int i; |
131 | |
132 | /* START field */ |
133 | c2port_strobe_ck(dev); |
134 | |
135 | /* INS field (10b, LSB first) */ |
136 | ops->c2d_dir(dev, 0); |
137 | ops->c2d_set(dev, 0); |
138 | c2port_strobe_ck(dev); |
139 | ops->c2d_set(dev, 1); |
140 | c2port_strobe_ck(dev); |
141 | |
142 | /* ADDRESS field */ |
143 | ops->c2d_dir(dev, 1); |
144 | *addr = 0; |
145 | for (i = 0; i < 8; i++) { |
146 | *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */ |
147 | |
148 | c2port_strobe_ck(dev); |
149 | if (ops->c2d_get(dev)) |
150 | *addr |= 0x80; |
151 | } |
152 | |
153 | /* STOP field */ |
154 | c2port_strobe_ck(dev); |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static int c2port_write_dr(struct c2port_device *dev, u8 data) |
160 | { |
161 | struct c2port_ops *ops = dev->ops; |
162 | int timeout, i; |
163 | |
164 | /* START field */ |
165 | c2port_strobe_ck(dev); |
166 | |
167 | /* INS field (01b, LSB first) */ |
168 | ops->c2d_dir(dev, 0); |
169 | ops->c2d_set(dev, 1); |
170 | c2port_strobe_ck(dev); |
171 | ops->c2d_set(dev, 0); |
172 | c2port_strobe_ck(dev); |
173 | |
174 | /* LENGTH field (00b, LSB first -> 1 byte) */ |
175 | ops->c2d_set(dev, 0); |
176 | c2port_strobe_ck(dev); |
177 | ops->c2d_set(dev, 0); |
178 | c2port_strobe_ck(dev); |
179 | |
180 | /* DATA field */ |
181 | for (i = 0; i < 8; i++) { |
182 | ops->c2d_set(dev, data & 0x01); |
183 | c2port_strobe_ck(dev); |
184 | |
185 | data >>= 1; |
186 | } |
187 | |
188 | /* WAIT field */ |
189 | ops->c2d_dir(dev, 1); |
190 | timeout = 20; |
191 | do { |
192 | c2port_strobe_ck(dev); |
193 | if (ops->c2d_get(dev)) |
194 | break; |
195 | |
196 | udelay(1); |
197 | } while (--timeout > 0); |
198 | if (timeout == 0) |
199 | return -EIO; |
200 | |
201 | /* STOP field */ |
202 | c2port_strobe_ck(dev); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static int c2port_read_dr(struct c2port_device *dev, u8 *data) |
208 | { |
209 | struct c2port_ops *ops = dev->ops; |
210 | int timeout, i; |
211 | |
212 | /* START field */ |
213 | c2port_strobe_ck(dev); |
214 | |
215 | /* INS field (00b, LSB first) */ |
216 | ops->c2d_dir(dev, 0); |
217 | ops->c2d_set(dev, 0); |
218 | c2port_strobe_ck(dev); |
219 | ops->c2d_set(dev, 0); |
220 | c2port_strobe_ck(dev); |
221 | |
222 | /* LENGTH field (00b, LSB first -> 1 byte) */ |
223 | ops->c2d_set(dev, 0); |
224 | c2port_strobe_ck(dev); |
225 | ops->c2d_set(dev, 0); |
226 | c2port_strobe_ck(dev); |
227 | |
228 | /* WAIT field */ |
229 | ops->c2d_dir(dev, 1); |
230 | timeout = 20; |
231 | do { |
232 | c2port_strobe_ck(dev); |
233 | if (ops->c2d_get(dev)) |
234 | break; |
235 | |
236 | udelay(1); |
237 | } while (--timeout > 0); |
238 | if (timeout == 0) |
239 | return -EIO; |
240 | |
241 | /* DATA field */ |
242 | *data = 0; |
243 | for (i = 0; i < 8; i++) { |
244 | *data >>= 1; /* shift in 8-bit DATA field LSB first */ |
245 | |
246 | c2port_strobe_ck(dev); |
247 | if (ops->c2d_get(dev)) |
248 | *data |= 0x80; |
249 | } |
250 | |
251 | /* STOP field */ |
252 | c2port_strobe_ck(dev); |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | static int c2port_poll_in_busy(struct c2port_device *dev) |
258 | { |
259 | u8 addr; |
260 | int ret, timeout = 20; |
261 | |
262 | do { |
263 | ret = (c2port_read_ar(dev, addr: &addr)); |
264 | if (ret < 0) |
265 | return -EIO; |
266 | |
267 | if (!(addr & 0x02)) |
268 | break; |
269 | |
270 | udelay(1); |
271 | } while (--timeout > 0); |
272 | if (timeout == 0) |
273 | return -EIO; |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | static int c2port_poll_out_ready(struct c2port_device *dev) |
279 | { |
280 | u8 addr; |
281 | int ret, timeout = 10000; /* erase flash needs long time... */ |
282 | |
283 | do { |
284 | ret = (c2port_read_ar(dev, addr: &addr)); |
285 | if (ret < 0) |
286 | return -EIO; |
287 | |
288 | if (addr & 0x01) |
289 | break; |
290 | |
291 | udelay(1); |
292 | } while (--timeout > 0); |
293 | if (timeout == 0) |
294 | return -EIO; |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | /* |
300 | * sysfs methods |
301 | */ |
302 | |
303 | static ssize_t c2port_show_name(struct device *dev, |
304 | struct device_attribute *attr, char *buf) |
305 | { |
306 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
307 | |
308 | return sprintf(buf, fmt: "%s\n" , c2dev->name); |
309 | } |
310 | static DEVICE_ATTR(name, 0444, c2port_show_name, NULL); |
311 | |
312 | static ssize_t c2port_show_flash_blocks_num(struct device *dev, |
313 | struct device_attribute *attr, char *buf) |
314 | { |
315 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
316 | struct c2port_ops *ops = c2dev->ops; |
317 | |
318 | return sprintf(buf, fmt: "%d\n" , ops->blocks_num); |
319 | } |
320 | static DEVICE_ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL); |
321 | |
322 | static ssize_t c2port_show_flash_block_size(struct device *dev, |
323 | struct device_attribute *attr, char *buf) |
324 | { |
325 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
326 | struct c2port_ops *ops = c2dev->ops; |
327 | |
328 | return sprintf(buf, fmt: "%d\n" , ops->block_size); |
329 | } |
330 | static DEVICE_ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL); |
331 | |
332 | static ssize_t c2port_show_flash_size(struct device *dev, |
333 | struct device_attribute *attr, char *buf) |
334 | { |
335 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
336 | struct c2port_ops *ops = c2dev->ops; |
337 | |
338 | return sprintf(buf, fmt: "%d\n" , ops->blocks_num * ops->block_size); |
339 | } |
340 | static DEVICE_ATTR(flash_size, 0444, c2port_show_flash_size, NULL); |
341 | |
342 | static ssize_t access_show(struct device *dev, struct device_attribute *attr, |
343 | char *buf) |
344 | { |
345 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
346 | |
347 | return sprintf(buf, fmt: "%d\n" , c2dev->access); |
348 | } |
349 | |
350 | static ssize_t access_store(struct device *dev, struct device_attribute *attr, |
351 | const char *buf, size_t count) |
352 | { |
353 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
354 | struct c2port_ops *ops = c2dev->ops; |
355 | int status, ret; |
356 | |
357 | ret = sscanf(buf, "%d" , &status); |
358 | if (ret != 1) |
359 | return -EINVAL; |
360 | |
361 | mutex_lock(&c2dev->mutex); |
362 | |
363 | c2dev->access = !!status; |
364 | |
365 | /* If access is "on" clock should be HIGH _before_ setting the line |
366 | * as output and data line should be set as INPUT anyway */ |
367 | if (c2dev->access) |
368 | ops->c2ck_set(c2dev, 1); |
369 | ops->access(c2dev, c2dev->access); |
370 | if (c2dev->access) |
371 | ops->c2d_dir(c2dev, 1); |
372 | |
373 | mutex_unlock(lock: &c2dev->mutex); |
374 | |
375 | return count; |
376 | } |
377 | static DEVICE_ATTR_RW(access); |
378 | |
379 | static ssize_t c2port_store_reset(struct device *dev, |
380 | struct device_attribute *attr, |
381 | const char *buf, size_t count) |
382 | { |
383 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
384 | |
385 | /* Check the device access status */ |
386 | if (!c2dev->access) |
387 | return -EBUSY; |
388 | |
389 | mutex_lock(&c2dev->mutex); |
390 | |
391 | c2port_reset(dev: c2dev); |
392 | c2dev->flash_access = 0; |
393 | |
394 | mutex_unlock(lock: &c2dev->mutex); |
395 | |
396 | return count; |
397 | } |
398 | static DEVICE_ATTR(reset, 0200, NULL, c2port_store_reset); |
399 | |
400 | static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf) |
401 | { |
402 | u8 data; |
403 | int ret; |
404 | |
405 | /* Select DEVICEID register for C2 data register accesses */ |
406 | c2port_write_ar(dev, C2PORT_DEVICEID); |
407 | |
408 | /* Read and return the device ID register */ |
409 | ret = c2port_read_dr(dev, data: &data); |
410 | if (ret < 0) |
411 | return ret; |
412 | |
413 | return sprintf(buf, fmt: "%d\n" , data); |
414 | } |
415 | |
416 | static ssize_t c2port_show_dev_id(struct device *dev, |
417 | struct device_attribute *attr, char *buf) |
418 | { |
419 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
420 | ssize_t ret; |
421 | |
422 | /* Check the device access status */ |
423 | if (!c2dev->access) |
424 | return -EBUSY; |
425 | |
426 | mutex_lock(&c2dev->mutex); |
427 | ret = __c2port_show_dev_id(dev: c2dev, buf); |
428 | mutex_unlock(lock: &c2dev->mutex); |
429 | |
430 | if (ret < 0) |
431 | dev_err(dev, "cannot read from %s\n" , c2dev->name); |
432 | |
433 | return ret; |
434 | } |
435 | static DEVICE_ATTR(dev_id, 0444, c2port_show_dev_id, NULL); |
436 | |
437 | static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf) |
438 | { |
439 | u8 data; |
440 | int ret; |
441 | |
442 | /* Select REVID register for C2 data register accesses */ |
443 | c2port_write_ar(dev, C2PORT_REVID); |
444 | |
445 | /* Read and return the revision ID register */ |
446 | ret = c2port_read_dr(dev, data: &data); |
447 | if (ret < 0) |
448 | return ret; |
449 | |
450 | return sprintf(buf, fmt: "%d\n" , data); |
451 | } |
452 | |
453 | static ssize_t c2port_show_rev_id(struct device *dev, |
454 | struct device_attribute *attr, char *buf) |
455 | { |
456 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
457 | ssize_t ret; |
458 | |
459 | /* Check the device access status */ |
460 | if (!c2dev->access) |
461 | return -EBUSY; |
462 | |
463 | mutex_lock(&c2dev->mutex); |
464 | ret = __c2port_show_rev_id(dev: c2dev, buf); |
465 | mutex_unlock(lock: &c2dev->mutex); |
466 | |
467 | if (ret < 0) |
468 | dev_err(c2dev->dev, "cannot read from %s\n" , c2dev->name); |
469 | |
470 | return ret; |
471 | } |
472 | static DEVICE_ATTR(rev_id, 0444, c2port_show_rev_id, NULL); |
473 | |
474 | static ssize_t c2port_show_flash_access(struct device *dev, |
475 | struct device_attribute *attr, char *buf) |
476 | { |
477 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
478 | |
479 | return sprintf(buf, fmt: "%d\n" , c2dev->flash_access); |
480 | } |
481 | |
482 | static ssize_t __c2port_store_flash_access(struct c2port_device *dev, |
483 | int status) |
484 | { |
485 | int ret; |
486 | |
487 | /* Check the device access status */ |
488 | if (!dev->access) |
489 | return -EBUSY; |
490 | |
491 | dev->flash_access = !!status; |
492 | |
493 | /* If flash_access is off we have nothing to do... */ |
494 | if (dev->flash_access == 0) |
495 | return 0; |
496 | |
497 | /* Target the C2 flash programming control register for C2 data |
498 | * register access */ |
499 | c2port_write_ar(dev, C2PORT_FPCTL); |
500 | |
501 | /* Write the first keycode to enable C2 Flash programming */ |
502 | ret = c2port_write_dr(dev, data: 0x02); |
503 | if (ret < 0) |
504 | return ret; |
505 | |
506 | /* Write the second keycode to enable C2 Flash programming */ |
507 | ret = c2port_write_dr(dev, data: 0x01); |
508 | if (ret < 0) |
509 | return ret; |
510 | |
511 | /* Delay for at least 20ms to ensure the target is ready for |
512 | * C2 flash programming */ |
513 | mdelay(25); |
514 | |
515 | return 0; |
516 | } |
517 | |
518 | static ssize_t c2port_store_flash_access(struct device *dev, |
519 | struct device_attribute *attr, |
520 | const char *buf, size_t count) |
521 | { |
522 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
523 | int status; |
524 | ssize_t ret; |
525 | |
526 | ret = sscanf(buf, "%d" , &status); |
527 | if (ret != 1) |
528 | return -EINVAL; |
529 | |
530 | mutex_lock(&c2dev->mutex); |
531 | ret = __c2port_store_flash_access(dev: c2dev, status); |
532 | mutex_unlock(lock: &c2dev->mutex); |
533 | |
534 | if (ret < 0) { |
535 | dev_err(c2dev->dev, "cannot enable %s flash programming\n" , |
536 | c2dev->name); |
537 | return ret; |
538 | } |
539 | |
540 | return count; |
541 | } |
542 | static DEVICE_ATTR(flash_access, 0644, c2port_show_flash_access, |
543 | c2port_store_flash_access); |
544 | |
545 | static ssize_t __c2port_write_flash_erase(struct c2port_device *dev) |
546 | { |
547 | u8 status; |
548 | int ret; |
549 | |
550 | /* Target the C2 flash programming data register for C2 data register |
551 | * access. |
552 | */ |
553 | c2port_write_ar(dev, C2PORT_FPDAT); |
554 | |
555 | /* Send device erase command */ |
556 | c2port_write_dr(dev, C2PORT_DEVICE_ERASE); |
557 | |
558 | /* Wait for input acknowledge */ |
559 | ret = c2port_poll_in_busy(dev); |
560 | if (ret < 0) |
561 | return ret; |
562 | |
563 | /* Should check status before starting FLASH access sequence */ |
564 | |
565 | /* Wait for status information */ |
566 | ret = c2port_poll_out_ready(dev); |
567 | if (ret < 0) |
568 | return ret; |
569 | |
570 | /* Read flash programming interface status */ |
571 | ret = c2port_read_dr(dev, data: &status); |
572 | if (ret < 0) |
573 | return ret; |
574 | if (status != C2PORT_COMMAND_OK) |
575 | return -EBUSY; |
576 | |
577 | /* Send a three-byte arming sequence to enable the device erase. |
578 | * If the sequence is not received correctly, the command will be |
579 | * ignored. |
580 | * Sequence is: 0xde, 0xad, 0xa5. |
581 | */ |
582 | c2port_write_dr(dev, data: 0xde); |
583 | ret = c2port_poll_in_busy(dev); |
584 | if (ret < 0) |
585 | return ret; |
586 | c2port_write_dr(dev, data: 0xad); |
587 | ret = c2port_poll_in_busy(dev); |
588 | if (ret < 0) |
589 | return ret; |
590 | c2port_write_dr(dev, data: 0xa5); |
591 | ret = c2port_poll_in_busy(dev); |
592 | if (ret < 0) |
593 | return ret; |
594 | |
595 | ret = c2port_poll_out_ready(dev); |
596 | if (ret < 0) |
597 | return ret; |
598 | |
599 | return 0; |
600 | } |
601 | |
602 | static ssize_t c2port_store_flash_erase(struct device *dev, |
603 | struct device_attribute *attr, |
604 | const char *buf, size_t count) |
605 | { |
606 | struct c2port_device *c2dev = dev_get_drvdata(dev); |
607 | int ret; |
608 | |
609 | /* Check the device and flash access status */ |
610 | if (!c2dev->access || !c2dev->flash_access) |
611 | return -EBUSY; |
612 | |
613 | mutex_lock(&c2dev->mutex); |
614 | ret = __c2port_write_flash_erase(dev: c2dev); |
615 | mutex_unlock(lock: &c2dev->mutex); |
616 | |
617 | if (ret < 0) { |
618 | dev_err(c2dev->dev, "cannot erase %s flash\n" , c2dev->name); |
619 | return ret; |
620 | } |
621 | |
622 | return count; |
623 | } |
624 | static DEVICE_ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase); |
625 | |
626 | static ssize_t __c2port_read_flash_data(struct c2port_device *dev, |
627 | char *buffer, loff_t offset, size_t count) |
628 | { |
629 | struct c2port_ops *ops = dev->ops; |
630 | u8 status, nread = 128; |
631 | int i, ret; |
632 | |
633 | /* Check for flash end */ |
634 | if (offset >= ops->block_size * ops->blocks_num) |
635 | return 0; |
636 | |
637 | if (ops->block_size * ops->blocks_num - offset < nread) |
638 | nread = ops->block_size * ops->blocks_num - offset; |
639 | if (count < nread) |
640 | nread = count; |
641 | if (nread == 0) |
642 | return nread; |
643 | |
644 | /* Target the C2 flash programming data register for C2 data register |
645 | * access */ |
646 | c2port_write_ar(dev, C2PORT_FPDAT); |
647 | |
648 | /* Send flash block read command */ |
649 | c2port_write_dr(dev, C2PORT_BLOCK_READ); |
650 | |
651 | /* Wait for input acknowledge */ |
652 | ret = c2port_poll_in_busy(dev); |
653 | if (ret < 0) |
654 | return ret; |
655 | |
656 | /* Should check status before starting FLASH access sequence */ |
657 | |
658 | /* Wait for status information */ |
659 | ret = c2port_poll_out_ready(dev); |
660 | if (ret < 0) |
661 | return ret; |
662 | |
663 | /* Read flash programming interface status */ |
664 | ret = c2port_read_dr(dev, data: &status); |
665 | if (ret < 0) |
666 | return ret; |
667 | if (status != C2PORT_COMMAND_OK) |
668 | return -EBUSY; |
669 | |
670 | /* Send address high byte */ |
671 | c2port_write_dr(dev, data: offset >> 8); |
672 | ret = c2port_poll_in_busy(dev); |
673 | if (ret < 0) |
674 | return ret; |
675 | |
676 | /* Send address low byte */ |
677 | c2port_write_dr(dev, data: offset & 0x00ff); |
678 | ret = c2port_poll_in_busy(dev); |
679 | if (ret < 0) |
680 | return ret; |
681 | |
682 | /* Send address block size */ |
683 | c2port_write_dr(dev, data: nread); |
684 | ret = c2port_poll_in_busy(dev); |
685 | if (ret < 0) |
686 | return ret; |
687 | |
688 | /* Should check status before reading FLASH block */ |
689 | |
690 | /* Wait for status information */ |
691 | ret = c2port_poll_out_ready(dev); |
692 | if (ret < 0) |
693 | return ret; |
694 | |
695 | /* Read flash programming interface status */ |
696 | ret = c2port_read_dr(dev, data: &status); |
697 | if (ret < 0) |
698 | return ret; |
699 | if (status != C2PORT_COMMAND_OK) |
700 | return -EBUSY; |
701 | |
702 | /* Read flash block */ |
703 | for (i = 0; i < nread; i++) { |
704 | ret = c2port_poll_out_ready(dev); |
705 | if (ret < 0) |
706 | return ret; |
707 | |
708 | ret = c2port_read_dr(dev, data: buffer+i); |
709 | if (ret < 0) |
710 | return ret; |
711 | } |
712 | |
713 | return nread; |
714 | } |
715 | |
716 | static ssize_t c2port_read_flash_data(struct file *filp, struct kobject *kobj, |
717 | struct bin_attribute *attr, |
718 | char *buffer, loff_t offset, size_t count) |
719 | { |
720 | struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); |
721 | ssize_t ret; |
722 | |
723 | /* Check the device and flash access status */ |
724 | if (!c2dev->access || !c2dev->flash_access) |
725 | return -EBUSY; |
726 | |
727 | mutex_lock(&c2dev->mutex); |
728 | ret = __c2port_read_flash_data(dev: c2dev, buffer, offset, count); |
729 | mutex_unlock(lock: &c2dev->mutex); |
730 | |
731 | if (ret < 0) |
732 | dev_err(c2dev->dev, "cannot read %s flash\n" , c2dev->name); |
733 | |
734 | return ret; |
735 | } |
736 | |
737 | static ssize_t __c2port_write_flash_data(struct c2port_device *dev, |
738 | char *buffer, loff_t offset, size_t count) |
739 | { |
740 | struct c2port_ops *ops = dev->ops; |
741 | u8 status, nwrite = 128; |
742 | int i, ret; |
743 | |
744 | if (nwrite > count) |
745 | nwrite = count; |
746 | if (ops->block_size * ops->blocks_num - offset < nwrite) |
747 | nwrite = ops->block_size * ops->blocks_num - offset; |
748 | |
749 | /* Check for flash end */ |
750 | if (offset >= ops->block_size * ops->blocks_num) |
751 | return -EINVAL; |
752 | |
753 | /* Target the C2 flash programming data register for C2 data register |
754 | * access */ |
755 | c2port_write_ar(dev, C2PORT_FPDAT); |
756 | |
757 | /* Send flash block write command */ |
758 | c2port_write_dr(dev, C2PORT_BLOCK_WRITE); |
759 | |
760 | /* Wait for input acknowledge */ |
761 | ret = c2port_poll_in_busy(dev); |
762 | if (ret < 0) |
763 | return ret; |
764 | |
765 | /* Should check status before starting FLASH access sequence */ |
766 | |
767 | /* Wait for status information */ |
768 | ret = c2port_poll_out_ready(dev); |
769 | if (ret < 0) |
770 | return ret; |
771 | |
772 | /* Read flash programming interface status */ |
773 | ret = c2port_read_dr(dev, data: &status); |
774 | if (ret < 0) |
775 | return ret; |
776 | if (status != C2PORT_COMMAND_OK) |
777 | return -EBUSY; |
778 | |
779 | /* Send address high byte */ |
780 | c2port_write_dr(dev, data: offset >> 8); |
781 | ret = c2port_poll_in_busy(dev); |
782 | if (ret < 0) |
783 | return ret; |
784 | |
785 | /* Send address low byte */ |
786 | c2port_write_dr(dev, data: offset & 0x00ff); |
787 | ret = c2port_poll_in_busy(dev); |
788 | if (ret < 0) |
789 | return ret; |
790 | |
791 | /* Send address block size */ |
792 | c2port_write_dr(dev, data: nwrite); |
793 | ret = c2port_poll_in_busy(dev); |
794 | if (ret < 0) |
795 | return ret; |
796 | |
797 | /* Should check status before writing FLASH block */ |
798 | |
799 | /* Wait for status information */ |
800 | ret = c2port_poll_out_ready(dev); |
801 | if (ret < 0) |
802 | return ret; |
803 | |
804 | /* Read flash programming interface status */ |
805 | ret = c2port_read_dr(dev, data: &status); |
806 | if (ret < 0) |
807 | return ret; |
808 | if (status != C2PORT_COMMAND_OK) |
809 | return -EBUSY; |
810 | |
811 | /* Write flash block */ |
812 | for (i = 0; i < nwrite; i++) { |
813 | ret = c2port_write_dr(dev, data: *(buffer+i)); |
814 | if (ret < 0) |
815 | return ret; |
816 | |
817 | ret = c2port_poll_in_busy(dev); |
818 | if (ret < 0) |
819 | return ret; |
820 | |
821 | } |
822 | |
823 | /* Wait for last flash write to complete */ |
824 | ret = c2port_poll_out_ready(dev); |
825 | if (ret < 0) |
826 | return ret; |
827 | |
828 | return nwrite; |
829 | } |
830 | |
831 | static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj, |
832 | struct bin_attribute *attr, |
833 | char *buffer, loff_t offset, size_t count) |
834 | { |
835 | struct c2port_device *c2dev = dev_get_drvdata(kobj_to_dev(kobj)); |
836 | int ret; |
837 | |
838 | /* Check the device access status */ |
839 | if (!c2dev->access || !c2dev->flash_access) |
840 | return -EBUSY; |
841 | |
842 | mutex_lock(&c2dev->mutex); |
843 | ret = __c2port_write_flash_data(dev: c2dev, buffer, offset, count); |
844 | mutex_unlock(lock: &c2dev->mutex); |
845 | |
846 | if (ret < 0) |
847 | dev_err(c2dev->dev, "cannot write %s flash\n" , c2dev->name); |
848 | |
849 | return ret; |
850 | } |
851 | /* size is computed at run-time */ |
852 | static BIN_ATTR(flash_data, 0644, c2port_read_flash_data, |
853 | c2port_write_flash_data, 0); |
854 | |
855 | /* |
856 | * Class attributes |
857 | */ |
858 | static struct attribute *c2port_attrs[] = { |
859 | &dev_attr_name.attr, |
860 | &dev_attr_flash_blocks_num.attr, |
861 | &dev_attr_flash_block_size.attr, |
862 | &dev_attr_flash_size.attr, |
863 | &dev_attr_access.attr, |
864 | &dev_attr_reset.attr, |
865 | &dev_attr_dev_id.attr, |
866 | &dev_attr_rev_id.attr, |
867 | &dev_attr_flash_access.attr, |
868 | &dev_attr_flash_erase.attr, |
869 | NULL, |
870 | }; |
871 | |
872 | static struct bin_attribute *c2port_bin_attrs[] = { |
873 | &bin_attr_flash_data, |
874 | NULL, |
875 | }; |
876 | |
877 | static const struct attribute_group c2port_group = { |
878 | .attrs = c2port_attrs, |
879 | .bin_attrs = c2port_bin_attrs, |
880 | }; |
881 | |
882 | static const struct attribute_group *c2port_groups[] = { |
883 | &c2port_group, |
884 | NULL, |
885 | }; |
886 | |
887 | /* |
888 | * Exported functions |
889 | */ |
890 | |
891 | struct c2port_device *c2port_device_register(char *name, |
892 | struct c2port_ops *ops, void *devdata) |
893 | { |
894 | struct c2port_device *c2dev; |
895 | int ret; |
896 | |
897 | if (unlikely(!ops) || unlikely(!ops->access) || \ |
898 | unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \ |
899 | unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set)) |
900 | return ERR_PTR(error: -EINVAL); |
901 | |
902 | c2dev = kzalloc(size: sizeof(struct c2port_device), GFP_KERNEL); |
903 | if (unlikely(!c2dev)) |
904 | return ERR_PTR(error: -ENOMEM); |
905 | |
906 | idr_preload(GFP_KERNEL); |
907 | spin_lock_irq(lock: &c2port_idr_lock); |
908 | ret = idr_alloc(&c2port_idr, ptr: c2dev, start: 0, end: 0, GFP_NOWAIT); |
909 | spin_unlock_irq(lock: &c2port_idr_lock); |
910 | idr_preload_end(); |
911 | |
912 | if (ret < 0) |
913 | goto error_idr_alloc; |
914 | c2dev->id = ret; |
915 | |
916 | bin_attr_flash_data.size = ops->blocks_num * ops->block_size; |
917 | |
918 | c2dev->dev = device_create(cls: c2port_class, NULL, devt: 0, drvdata: c2dev, |
919 | fmt: "c2port%d" , c2dev->id); |
920 | if (IS_ERR(ptr: c2dev->dev)) { |
921 | ret = PTR_ERR(ptr: c2dev->dev); |
922 | goto error_device_create; |
923 | } |
924 | dev_set_drvdata(dev: c2dev->dev, data: c2dev); |
925 | |
926 | strscpy(c2dev->name, name, sizeof(c2dev->name)); |
927 | c2dev->ops = ops; |
928 | mutex_init(&c2dev->mutex); |
929 | |
930 | /* By default C2 port access is off */ |
931 | c2dev->access = c2dev->flash_access = 0; |
932 | ops->access(c2dev, 0); |
933 | |
934 | dev_info(c2dev->dev, "C2 port %s added\n" , name); |
935 | dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes " |
936 | "(%d bytes total)\n" , |
937 | name, ops->blocks_num, ops->block_size, |
938 | ops->blocks_num * ops->block_size); |
939 | |
940 | return c2dev; |
941 | |
942 | error_device_create: |
943 | spin_lock_irq(lock: &c2port_idr_lock); |
944 | idr_remove(&c2port_idr, id: c2dev->id); |
945 | spin_unlock_irq(lock: &c2port_idr_lock); |
946 | |
947 | error_idr_alloc: |
948 | kfree(objp: c2dev); |
949 | |
950 | return ERR_PTR(error: ret); |
951 | } |
952 | EXPORT_SYMBOL(c2port_device_register); |
953 | |
954 | void c2port_device_unregister(struct c2port_device *c2dev) |
955 | { |
956 | if (!c2dev) |
957 | return; |
958 | |
959 | dev_info(c2dev->dev, "C2 port %s removed\n" , c2dev->name); |
960 | |
961 | spin_lock_irq(lock: &c2port_idr_lock); |
962 | idr_remove(&c2port_idr, id: c2dev->id); |
963 | spin_unlock_irq(lock: &c2port_idr_lock); |
964 | |
965 | device_destroy(cls: c2port_class, devt: c2dev->id); |
966 | |
967 | kfree(objp: c2dev); |
968 | } |
969 | EXPORT_SYMBOL(c2port_device_unregister); |
970 | |
971 | /* |
972 | * Module stuff |
973 | */ |
974 | |
975 | static int __init c2port_init(void) |
976 | { |
977 | printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION |
978 | " - (C) 2007 Rodolfo Giometti\n" ); |
979 | |
980 | c2port_class = class_create(name: "c2port" ); |
981 | if (IS_ERR(ptr: c2port_class)) { |
982 | printk(KERN_ERR "c2port: failed to allocate class\n" ); |
983 | return PTR_ERR(ptr: c2port_class); |
984 | } |
985 | c2port_class->dev_groups = c2port_groups; |
986 | |
987 | return 0; |
988 | } |
989 | |
990 | static void __exit c2port_exit(void) |
991 | { |
992 | class_destroy(cls: c2port_class); |
993 | } |
994 | |
995 | module_init(c2port_init); |
996 | module_exit(c2port_exit); |
997 | |
998 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>" ); |
999 | MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION); |
1000 | MODULE_LICENSE("GPL" ); |
1001 | |