Commit | Line | Data |
---|---|---|
5814fc35 MW |
1 | /* |
2 | * Copyright (C) ST-Ericsson SA 2010 | |
3 | * | |
4 | * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson. | |
5 | * License Terms: GNU General Public License v2 | |
6 | */ | |
0fbce76e | 7 | /* |
8 | * AB8500 register access | |
9 | * ====================== | |
10 | * | |
11 | * read: | |
12 | * # echo BANK > <debugfs>/ab8500/register-bank | |
13 | * # echo ADDR > <debugfs>/ab8500/register-address | |
14 | * # cat <debugfs>/ab8500/register-value | |
15 | * | |
16 | * write: | |
17 | * # echo BANK > <debugfs>/ab8500/register-bank | |
18 | * # echo ADDR > <debugfs>/ab8500/register-address | |
19 | * # echo VALUE > <debugfs>/ab8500/register-value | |
20 | * | |
21 | * read all registers from a bank: | |
22 | * # echo BANK > <debugfs>/ab8500/register-bank | |
23 | * # cat <debugfs>/ab8500/all-bank-register | |
24 | * | |
25 | * BANK target AB8500 register bank | |
26 | * ADDR target AB8500 register address | |
27 | * VALUE decimal or 0x-prefixed hexadecimal | |
28 | * | |
29 | * | |
30 | * User Space notification on AB8500 IRQ | |
31 | * ===================================== | |
32 | * | |
33 | * Allows user space entity to be notified when target AB8500 IRQ occurs. | |
34 | * When subscribed, a sysfs entry is created in ab8500.i2c platform device. | |
35 | * One can pool this file to get target IRQ occurence information. | |
36 | * | |
37 | * subscribe to an AB8500 IRQ: | |
38 | * # echo IRQ > <debugfs>/ab8500/irq-subscribe | |
39 | * | |
40 | * unsubscribe from an AB8500 IRQ: | |
41 | * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe | |
42 | * | |
43 | * | |
44 | * AB8500 register formated read/write access | |
45 | * ========================================== | |
46 | * | |
47 | * Read: read data, data>>SHIFT, data&=MASK, output data | |
48 | * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE | |
49 | * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data | |
50 | * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98] | |
51 | * | |
52 | * Usage: | |
53 | * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg | |
54 | * | |
55 | * CMD read read access | |
56 | * write write access | |
57 | * | |
58 | * BANK target reg bank | |
59 | * ADDRESS target reg address | |
60 | * VALUE (write) value to be updated | |
61 | * | |
62 | * OPTIONS | |
63 | * -d|-dec (read) output in decimal | |
64 | * -h|-hexa (read) output in 0x-hexa (default) | |
65 | * -l|-w|-b 32bit (default), 16bit or 8bit reg access | |
66 | * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF) | |
67 | * -s|-shift SHIFT bit shift value (read:left, write:right) | |
68 | * -o|-offset OFFSET address offset to add to ADDRESS value | |
69 | * | |
70 | * Warning: bit shift operation is applied to bit-mask. | |
71 | * Warning: bit shift direction depends on read or right command. | |
72 | */ | |
5814fc35 MW |
73 | |
74 | #include <linux/seq_file.h> | |
75 | #include <linux/uaccess.h> | |
76 | #include <linux/fs.h> | |
4e36dd33 | 77 | #include <linux/module.h> |
5814fc35 MW |
78 | #include <linux/debugfs.h> |
79 | #include <linux/platform_device.h> | |
4b8ac082 LJ |
80 | #include <linux/interrupt.h> |
81 | #include <linux/kobject.h> | |
82 | #include <linux/slab.h> | |
5814fc35 MW |
83 | |
84 | #include <linux/mfd/abx500.h> | |
0cd5b6d0 | 85 | #include <linux/mfd/abx500/ab8500.h> |
1478a316 | 86 | #include <linux/mfd/abx500/ab8500-gpadc.h> |
5814fc35 | 87 | |
0fbce76e | 88 | #ifdef CONFIG_DEBUG_FS |
89 | #include <linux/string.h> | |
90 | #include <linux/ctype.h> | |
91 | #endif | |
92 | ||
5814fc35 MW |
93 | static u32 debug_bank; |
94 | static u32 debug_address; | |
95 | ||
4b8ac082 LJ |
96 | static int irq_first; |
97 | static int irq_last; | |
ddba25f1 LW |
98 | static u32 *irq_count; |
99 | static int num_irqs; | |
0b337e70 | 100 | |
ddba25f1 LW |
101 | static struct device_attribute **dev_attr; |
102 | static char **event_name; | |
4b8ac082 | 103 | |
73482346 LJ |
104 | static u8 avg_sample = SAMPLE_16; |
105 | static u8 trig_edge = RISING_EDGE; | |
106 | static u8 conv_type = ADC_SW; | |
107 | static u8 trig_timer; | |
108 | ||
5814fc35 MW |
109 | /** |
110 | * struct ab8500_reg_range | |
111 | * @first: the first address of the range | |
112 | * @last: the last address of the range | |
113 | * @perm: access permissions for the range | |
114 | */ | |
115 | struct ab8500_reg_range { | |
d7b9f322 MW |
116 | u8 first; |
117 | u8 last; | |
118 | u8 perm; | |
5814fc35 MW |
119 | }; |
120 | ||
121 | /** | |
822672a7 | 122 | * struct ab8500_prcmu_ranges |
5814fc35 MW |
123 | * @num_ranges: the number of ranges in the list |
124 | * @bankid: bank identifier | |
125 | * @range: the list of register ranges | |
126 | */ | |
822672a7 | 127 | struct ab8500_prcmu_ranges { |
d7b9f322 MW |
128 | u8 num_ranges; |
129 | u8 bankid; | |
130 | const struct ab8500_reg_range *range; | |
5814fc35 MW |
131 | }; |
132 | ||
0fbce76e | 133 | /* hwreg- "mask" and "shift" entries ressources */ |
134 | struct hwreg_cfg { | |
135 | u32 bank; /* target bank */ | |
136 | u32 addr; /* target address */ | |
137 | uint fmt; /* format */ | |
138 | uint mask; /* read/write mask, applied before any bit shift */ | |
139 | int shift; /* bit shift (read:right shift, write:left shift */ | |
140 | }; | |
141 | /* fmt bit #0: 0=hexa, 1=dec */ | |
142 | #define REG_FMT_DEC(c) ((c)->fmt & 0x1) | |
143 | #define REG_FMT_HEX(c) (!REG_FMT_DEC(c)) | |
144 | ||
145 | static struct hwreg_cfg hwreg_cfg = { | |
146 | .addr = 0, /* default: invalid phys addr */ | |
147 | .fmt = 0, /* default: 32bit access, hex output */ | |
148 | .mask = 0xFFFFFFFF, /* default: no mask */ | |
149 | .shift = 0, /* default: no bit shift */ | |
150 | }; | |
151 | ||
5814fc35 | 152 | #define AB8500_NAME_STRING "ab8500" |
1478a316 | 153 | #define AB8500_ADC_NAME_STRING "gpadc" |
40c064e4 | 154 | #define AB8500_NUM_BANKS 24 |
5814fc35 MW |
155 | |
156 | #define AB8500_REV_REG 0x80 | |
157 | ||
822672a7 | 158 | static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = { |
d7b9f322 MW |
159 | [0x0] = { |
160 | .num_ranges = 0, | |
fad55a86 | 161 | .range = NULL, |
d7b9f322 MW |
162 | }, |
163 | [AB8500_SYS_CTRL1_BLOCK] = { | |
164 | .num_ranges = 3, | |
165 | .range = (struct ab8500_reg_range[]) { | |
166 | { | |
167 | .first = 0x00, | |
168 | .last = 0x02, | |
169 | }, | |
170 | { | |
171 | .first = 0x42, | |
172 | .last = 0x42, | |
173 | }, | |
174 | { | |
175 | .first = 0x80, | |
176 | .last = 0x81, | |
177 | }, | |
178 | }, | |
179 | }, | |
180 | [AB8500_SYS_CTRL2_BLOCK] = { | |
181 | .num_ranges = 4, | |
182 | .range = (struct ab8500_reg_range[]) { | |
183 | { | |
184 | .first = 0x00, | |
185 | .last = 0x0D, | |
186 | }, | |
187 | { | |
188 | .first = 0x0F, | |
189 | .last = 0x17, | |
190 | }, | |
191 | { | |
192 | .first = 0x30, | |
193 | .last = 0x30, | |
194 | }, | |
195 | { | |
196 | .first = 0x32, | |
197 | .last = 0x33, | |
198 | }, | |
199 | }, | |
200 | }, | |
201 | [AB8500_REGU_CTRL1] = { | |
202 | .num_ranges = 3, | |
203 | .range = (struct ab8500_reg_range[]) { | |
204 | { | |
205 | .first = 0x00, | |
206 | .last = 0x00, | |
207 | }, | |
208 | { | |
209 | .first = 0x03, | |
210 | .last = 0x10, | |
211 | }, | |
212 | { | |
213 | .first = 0x80, | |
214 | .last = 0x84, | |
215 | }, | |
216 | }, | |
217 | }, | |
218 | [AB8500_REGU_CTRL2] = { | |
219 | .num_ranges = 5, | |
220 | .range = (struct ab8500_reg_range[]) { | |
221 | { | |
222 | .first = 0x00, | |
223 | .last = 0x15, | |
224 | }, | |
225 | { | |
226 | .first = 0x17, | |
227 | .last = 0x19, | |
228 | }, | |
229 | { | |
230 | .first = 0x1B, | |
231 | .last = 0x1D, | |
232 | }, | |
233 | { | |
234 | .first = 0x1F, | |
235 | .last = 0x22, | |
236 | }, | |
237 | { | |
238 | .first = 0x40, | |
239 | .last = 0x44, | |
240 | }, | |
241 | /* 0x80-0x8B is SIM registers and should | |
242 | * not be accessed from here */ | |
243 | }, | |
244 | }, | |
245 | [AB8500_USB] = { | |
246 | .num_ranges = 2, | |
247 | .range = (struct ab8500_reg_range[]) { | |
248 | { | |
249 | .first = 0x80, | |
250 | .last = 0x83, | |
251 | }, | |
252 | { | |
253 | .first = 0x87, | |
254 | .last = 0x8A, | |
255 | }, | |
256 | }, | |
257 | }, | |
258 | [AB8500_TVOUT] = { | |
259 | .num_ranges = 9, | |
260 | .range = (struct ab8500_reg_range[]) { | |
261 | { | |
262 | .first = 0x00, | |
263 | .last = 0x12, | |
264 | }, | |
265 | { | |
266 | .first = 0x15, | |
267 | .last = 0x17, | |
268 | }, | |
269 | { | |
270 | .first = 0x19, | |
271 | .last = 0x21, | |
272 | }, | |
273 | { | |
274 | .first = 0x27, | |
275 | .last = 0x2C, | |
276 | }, | |
277 | { | |
278 | .first = 0x41, | |
279 | .last = 0x41, | |
280 | }, | |
281 | { | |
282 | .first = 0x45, | |
283 | .last = 0x5B, | |
284 | }, | |
285 | { | |
286 | .first = 0x5D, | |
287 | .last = 0x5D, | |
288 | }, | |
289 | { | |
290 | .first = 0x69, | |
291 | .last = 0x69, | |
292 | }, | |
293 | { | |
294 | .first = 0x80, | |
295 | .last = 0x81, | |
296 | }, | |
297 | }, | |
298 | }, | |
299 | [AB8500_DBI] = { | |
300 | .num_ranges = 0, | |
87fff232 | 301 | .range = NULL, |
d7b9f322 MW |
302 | }, |
303 | [AB8500_ECI_AV_ACC] = { | |
304 | .num_ranges = 1, | |
305 | .range = (struct ab8500_reg_range[]) { | |
306 | { | |
307 | .first = 0x80, | |
308 | .last = 0x82, | |
309 | }, | |
310 | }, | |
311 | }, | |
312 | [0x9] = { | |
313 | .num_ranges = 0, | |
87fff232 | 314 | .range = NULL, |
d7b9f322 MW |
315 | }, |
316 | [AB8500_GPADC] = { | |
317 | .num_ranges = 1, | |
318 | .range = (struct ab8500_reg_range[]) { | |
319 | { | |
320 | .first = 0x00, | |
321 | .last = 0x08, | |
322 | }, | |
323 | }, | |
324 | }, | |
325 | [AB8500_CHARGER] = { | |
40c064e4 | 326 | .num_ranges = 9, |
d7b9f322 MW |
327 | .range = (struct ab8500_reg_range[]) { |
328 | { | |
329 | .first = 0x00, | |
330 | .last = 0x03, | |
331 | }, | |
332 | { | |
333 | .first = 0x05, | |
334 | .last = 0x05, | |
335 | }, | |
336 | { | |
337 | .first = 0x40, | |
338 | .last = 0x40, | |
339 | }, | |
340 | { | |
341 | .first = 0x42, | |
342 | .last = 0x42, | |
343 | }, | |
344 | { | |
345 | .first = 0x44, | |
346 | .last = 0x44, | |
347 | }, | |
348 | { | |
349 | .first = 0x50, | |
350 | .last = 0x55, | |
351 | }, | |
352 | { | |
353 | .first = 0x80, | |
354 | .last = 0x82, | |
355 | }, | |
356 | { | |
357 | .first = 0xC0, | |
358 | .last = 0xC2, | |
359 | }, | |
40c064e4 PL |
360 | { |
361 | .first = 0xf5, | |
362 | .last = 0xf6, | |
363 | }, | |
d7b9f322 MW |
364 | }, |
365 | }, | |
366 | [AB8500_GAS_GAUGE] = { | |
367 | .num_ranges = 3, | |
368 | .range = (struct ab8500_reg_range[]) { | |
369 | { | |
370 | .first = 0x00, | |
371 | .last = 0x00, | |
372 | }, | |
373 | { | |
374 | .first = 0x07, | |
375 | .last = 0x0A, | |
376 | }, | |
377 | { | |
378 | .first = 0x10, | |
379 | .last = 0x14, | |
380 | }, | |
381 | }, | |
382 | }, | |
40c064e4 PL |
383 | [AB8500_DEVELOPMENT] = { |
384 | .num_ranges = 1, | |
385 | .range = (struct ab8500_reg_range[]) { | |
386 | { | |
387 | .first = 0x00, | |
388 | .last = 0x00, | |
389 | }, | |
390 | }, | |
391 | }, | |
392 | [AB8500_DEBUG] = { | |
393 | .num_ranges = 1, | |
394 | .range = (struct ab8500_reg_range[]) { | |
395 | { | |
396 | .first = 0x05, | |
397 | .last = 0x07, | |
398 | }, | |
399 | }, | |
400 | }, | |
d7b9f322 MW |
401 | [AB8500_AUDIO] = { |
402 | .num_ranges = 1, | |
403 | .range = (struct ab8500_reg_range[]) { | |
404 | { | |
405 | .first = 0x00, | |
406 | .last = 0x6F, | |
407 | }, | |
408 | }, | |
409 | }, | |
410 | [AB8500_INTERRUPT] = { | |
411 | .num_ranges = 0, | |
87fff232 | 412 | .range = NULL, |
d7b9f322 MW |
413 | }, |
414 | [AB8500_RTC] = { | |
415 | .num_ranges = 1, | |
416 | .range = (struct ab8500_reg_range[]) { | |
417 | { | |
418 | .first = 0x00, | |
419 | .last = 0x0F, | |
420 | }, | |
421 | }, | |
422 | }, | |
423 | [AB8500_MISC] = { | |
424 | .num_ranges = 8, | |
425 | .range = (struct ab8500_reg_range[]) { | |
426 | { | |
427 | .first = 0x00, | |
428 | .last = 0x05, | |
429 | }, | |
430 | { | |
431 | .first = 0x10, | |
432 | .last = 0x15, | |
433 | }, | |
434 | { | |
435 | .first = 0x20, | |
436 | .last = 0x25, | |
437 | }, | |
438 | { | |
439 | .first = 0x30, | |
440 | .last = 0x35, | |
441 | }, | |
442 | { | |
443 | .first = 0x40, | |
444 | .last = 0x45, | |
445 | }, | |
446 | { | |
447 | .first = 0x50, | |
448 | .last = 0x50, | |
449 | }, | |
450 | { | |
451 | .first = 0x60, | |
452 | .last = 0x67, | |
453 | }, | |
454 | { | |
455 | .first = 0x80, | |
456 | .last = 0x80, | |
457 | }, | |
458 | }, | |
459 | }, | |
460 | [0x11] = { | |
461 | .num_ranges = 0, | |
87fff232 | 462 | .range = NULL, |
d7b9f322 MW |
463 | }, |
464 | [0x12] = { | |
465 | .num_ranges = 0, | |
87fff232 | 466 | .range = NULL, |
d7b9f322 MW |
467 | }, |
468 | [0x13] = { | |
469 | .num_ranges = 0, | |
87fff232 | 470 | .range = NULL, |
d7b9f322 MW |
471 | }, |
472 | [0x14] = { | |
473 | .num_ranges = 0, | |
87fff232 | 474 | .range = NULL, |
d7b9f322 MW |
475 | }, |
476 | [AB8500_OTP_EMUL] = { | |
477 | .num_ranges = 1, | |
478 | .range = (struct ab8500_reg_range[]) { | |
479 | { | |
480 | .first = 0x01, | |
481 | .last = 0x0F, | |
482 | }, | |
483 | }, | |
484 | }, | |
5814fc35 MW |
485 | }; |
486 | ||
4b8ac082 LJ |
487 | static irqreturn_t ab8500_debug_handler(int irq, void *data) |
488 | { | |
489 | char buf[16]; | |
490 | struct kobject *kobj = (struct kobject *)data; | |
0b337e70 | 491 | unsigned int irq_abb = irq - irq_first; |
4b8ac082 | 492 | |
ddba25f1 | 493 | if (irq_abb < num_irqs) |
0b337e70 | 494 | irq_count[irq_abb]++; |
4b8ac082 LJ |
495 | /* |
496 | * This makes it possible to use poll for events (POLLPRI | POLLERR) | |
0b337e70 | 497 | * from userspace on sysfs file named <irq-nr> |
4b8ac082 | 498 | */ |
0b337e70 | 499 | sprintf(buf, "%d", irq); |
4b8ac082 LJ |
500 | sysfs_notify(kobj, NULL, buf); |
501 | ||
502 | return IRQ_HANDLED; | |
503 | } | |
504 | ||
42002c6d MYK |
505 | /* Prints to seq_file or log_buf */ |
506 | static int ab8500_registers_print(struct device *dev, u32 bank, | |
507 | struct seq_file *s) | |
5814fc35 | 508 | { |
d7b9f322 | 509 | unsigned int i; |
d7b9f322 | 510 | |
d7b9f322 MW |
511 | for (i = 0; i < debug_ranges[bank].num_ranges; i++) { |
512 | u32 reg; | |
513 | ||
514 | for (reg = debug_ranges[bank].range[i].first; | |
515 | reg <= debug_ranges[bank].range[i].last; | |
516 | reg++) { | |
517 | u8 value; | |
518 | int err; | |
519 | ||
520 | err = abx500_get_register_interruptible(dev, | |
521 | (u8)bank, (u8)reg, &value); | |
522 | if (err < 0) { | |
523 | dev_err(dev, "ab->read fail %d\n", err); | |
524 | return err; | |
525 | } | |
526 | ||
42002c6d | 527 | if (s) { |
cfc0849c | 528 | err = seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", |
42002c6d MYK |
529 | bank, reg, value); |
530 | if (err < 0) { | |
531 | dev_err(dev, | |
cfc0849c | 532 | "seq_printf overflow bank=0x%02X reg=0x%02X\n", |
42002c6d MYK |
533 | bank, reg); |
534 | /* Error is not returned here since | |
535 | * the output is wanted in any case */ | |
536 | return 0; | |
537 | } | |
538 | } else { | |
cfc0849c MW |
539 | printk(KERN_INFO" [0x%02X/0x%02X]: 0x%02X\n", |
540 | bank, reg, value); | |
d7b9f322 MW |
541 | } |
542 | } | |
543 | } | |
544 | return 0; | |
5814fc35 MW |
545 | } |
546 | ||
42002c6d MYK |
547 | static int ab8500_print_bank_registers(struct seq_file *s, void *p) |
548 | { | |
549 | struct device *dev = s->private; | |
550 | u32 bank = debug_bank; | |
551 | ||
552 | seq_printf(s, AB8500_NAME_STRING " register values:\n"); | |
553 | ||
cfc0849c | 554 | seq_printf(s, " bank 0x%02X:\n", bank); |
42002c6d MYK |
555 | |
556 | ab8500_registers_print(dev, bank, s); | |
557 | return 0; | |
558 | } | |
559 | ||
5814fc35 MW |
560 | static int ab8500_registers_open(struct inode *inode, struct file *file) |
561 | { | |
42002c6d | 562 | return single_open(file, ab8500_print_bank_registers, inode->i_private); |
5814fc35 MW |
563 | } |
564 | ||
565 | static const struct file_operations ab8500_registers_fops = { | |
d7b9f322 MW |
566 | .open = ab8500_registers_open, |
567 | .read = seq_read, | |
568 | .llseek = seq_lseek, | |
569 | .release = single_release, | |
570 | .owner = THIS_MODULE, | |
5814fc35 MW |
571 | }; |
572 | ||
42002c6d MYK |
573 | static int ab8500_print_all_banks(struct seq_file *s, void *p) |
574 | { | |
575 | struct device *dev = s->private; | |
576 | unsigned int i; | |
577 | int err; | |
578 | ||
579 | seq_printf(s, AB8500_NAME_STRING " register values:\n"); | |
580 | ||
581 | for (i = 1; i < AB8500_NUM_BANKS; i++) { | |
cfc0849c | 582 | err = seq_printf(s, " bank 0x%02X:\n", i); |
42002c6d | 583 | if (err < 0) |
cfc0849c | 584 | dev_err(dev, "seq_printf overflow, bank=0x%02X\n", i); |
42002c6d MYK |
585 | |
586 | ab8500_registers_print(dev, i, s); | |
587 | } | |
588 | return 0; | |
589 | } | |
590 | ||
1d843a6c MYK |
591 | /* Dump registers to kernel log */ |
592 | void ab8500_dump_all_banks(struct device *dev) | |
593 | { | |
594 | unsigned int i; | |
595 | ||
596 | printk(KERN_INFO"ab8500 register values:\n"); | |
597 | ||
598 | for (i = 1; i < AB8500_NUM_BANKS; i++) { | |
cfc0849c | 599 | printk(KERN_INFO" bank 0x%02X:\n", i); |
1d843a6c MYK |
600 | ab8500_registers_print(dev, i, NULL); |
601 | } | |
602 | } | |
603 | ||
5ff9090f LJ |
604 | /* Space for 500 registers. */ |
605 | #define DUMP_MAX_REGS 700 | |
606 | struct ab8500_register_dump | |
607 | { | |
608 | u8 bank; | |
609 | u8 reg; | |
610 | u8 value; | |
611 | int ret; | |
612 | } ab8500_complete_register_dump[DUMP_MAX_REGS]; | |
613 | ||
614 | extern int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); | |
615 | ||
616 | /* This shall only be called upon kernel panic! */ | |
617 | void ab8500_dump_all_banks_to_mem(void) | |
618 | { | |
619 | int i, r = 0; | |
620 | u8 bank; | |
621 | ||
622 | pr_info("Saving all ABB registers at \"ab8500_complete_register_dump\" " | |
623 | "for crash analyze.\n"); | |
624 | ||
625 | for (bank = 1; bank < AB8500_NUM_BANKS; bank++) { | |
626 | for (i = 0; i < debug_ranges[bank].num_ranges; i++) { | |
627 | u8 reg; | |
628 | ||
629 | for (reg = debug_ranges[bank].range[i].first; | |
630 | reg <= debug_ranges[bank].range[i].last; | |
631 | reg++) { | |
632 | u8 value; | |
633 | int err; | |
634 | ||
635 | err = prcmu_abb_read(bank, reg, &value, 1); | |
636 | ||
637 | ab8500_complete_register_dump[r].ret = err; | |
638 | ab8500_complete_register_dump[r].bank = bank; | |
639 | ab8500_complete_register_dump[r].reg = reg; | |
640 | ab8500_complete_register_dump[r].value = value; | |
641 | ||
642 | r++; | |
643 | ||
644 | if (r >= DUMP_MAX_REGS) { | |
645 | pr_err("%s: too many register to dump!\n", | |
646 | __func__); | |
647 | return; | |
648 | } | |
649 | } | |
650 | } | |
651 | } | |
652 | } | |
653 | ||
42002c6d MYK |
654 | static int ab8500_all_banks_open(struct inode *inode, struct file *file) |
655 | { | |
656 | struct seq_file *s; | |
657 | int err; | |
658 | ||
659 | err = single_open(file, ab8500_print_all_banks, inode->i_private); | |
660 | if (!err) { | |
661 | /* Default buf size in seq_read is not enough */ | |
662 | s = (struct seq_file *)file->private_data; | |
663 | s->size = (PAGE_SIZE * 2); | |
664 | s->buf = kmalloc(s->size, GFP_KERNEL); | |
665 | if (!s->buf) { | |
666 | single_release(inode, file); | |
667 | err = -ENOMEM; | |
668 | } | |
669 | } | |
670 | return err; | |
671 | } | |
672 | ||
673 | static const struct file_operations ab8500_all_banks_fops = { | |
674 | .open = ab8500_all_banks_open, | |
675 | .read = seq_read, | |
676 | .llseek = seq_lseek, | |
677 | .release = single_release, | |
678 | .owner = THIS_MODULE, | |
679 | }; | |
680 | ||
5814fc35 MW |
681 | static int ab8500_bank_print(struct seq_file *s, void *p) |
682 | { | |
cfc0849c | 683 | return seq_printf(s, "0x%02X\n", debug_bank); |
5814fc35 MW |
684 | } |
685 | ||
686 | static int ab8500_bank_open(struct inode *inode, struct file *file) | |
687 | { | |
d7b9f322 | 688 | return single_open(file, ab8500_bank_print, inode->i_private); |
5814fc35 MW |
689 | } |
690 | ||
691 | static ssize_t ab8500_bank_write(struct file *file, | |
d7b9f322 MW |
692 | const char __user *user_buf, |
693 | size_t count, loff_t *ppos) | |
5814fc35 | 694 | { |
d7b9f322 | 695 | struct device *dev = ((struct seq_file *)(file->private_data))->private; |
d7b9f322 MW |
696 | unsigned long user_bank; |
697 | int err; | |
698 | ||
699 | /* Get userspace string and assure termination */ | |
8504d638 | 700 | err = kstrtoul_from_user(user_buf, count, 0, &user_bank); |
d7b9f322 | 701 | if (err) |
8504d638 | 702 | return err; |
d7b9f322 MW |
703 | |
704 | if (user_bank >= AB8500_NUM_BANKS) { | |
705 | dev_err(dev, "debugfs error input > number of banks\n"); | |
706 | return -EINVAL; | |
707 | } | |
708 | ||
709 | debug_bank = user_bank; | |
710 | ||
8504d638 | 711 | return count; |
5814fc35 MW |
712 | } |
713 | ||
714 | static int ab8500_address_print(struct seq_file *s, void *p) | |
715 | { | |
d7b9f322 | 716 | return seq_printf(s, "0x%02X\n", debug_address); |
5814fc35 MW |
717 | } |
718 | ||
719 | static int ab8500_address_open(struct inode *inode, struct file *file) | |
720 | { | |
d7b9f322 | 721 | return single_open(file, ab8500_address_print, inode->i_private); |
5814fc35 MW |
722 | } |
723 | ||
724 | static ssize_t ab8500_address_write(struct file *file, | |
d7b9f322 MW |
725 | const char __user *user_buf, |
726 | size_t count, loff_t *ppos) | |
5814fc35 | 727 | { |
d7b9f322 | 728 | struct device *dev = ((struct seq_file *)(file->private_data))->private; |
d7b9f322 MW |
729 | unsigned long user_address; |
730 | int err; | |
731 | ||
732 | /* Get userspace string and assure termination */ | |
8504d638 | 733 | err = kstrtoul_from_user(user_buf, count, 0, &user_address); |
d7b9f322 | 734 | if (err) |
8504d638 PH |
735 | return err; |
736 | ||
d7b9f322 MW |
737 | if (user_address > 0xff) { |
738 | dev_err(dev, "debugfs error input > 0xff\n"); | |
739 | return -EINVAL; | |
740 | } | |
741 | debug_address = user_address; | |
8504d638 | 742 | return count; |
5814fc35 MW |
743 | } |
744 | ||
745 | static int ab8500_val_print(struct seq_file *s, void *p) | |
746 | { | |
d7b9f322 MW |
747 | struct device *dev = s->private; |
748 | int ret; | |
749 | u8 regvalue; | |
750 | ||
751 | ret = abx500_get_register_interruptible(dev, | |
752 | (u8)debug_bank, (u8)debug_address, ®value); | |
753 | if (ret < 0) { | |
754 | dev_err(dev, "abx500_get_reg fail %d, %d\n", | |
755 | ret, __LINE__); | |
756 | return -EINVAL; | |
757 | } | |
758 | seq_printf(s, "0x%02X\n", regvalue); | |
759 | ||
760 | return 0; | |
5814fc35 MW |
761 | } |
762 | ||
763 | static int ab8500_val_open(struct inode *inode, struct file *file) | |
764 | { | |
d7b9f322 | 765 | return single_open(file, ab8500_val_print, inode->i_private); |
5814fc35 MW |
766 | } |
767 | ||
768 | static ssize_t ab8500_val_write(struct file *file, | |
d7b9f322 MW |
769 | const char __user *user_buf, |
770 | size_t count, loff_t *ppos) | |
5814fc35 | 771 | { |
d7b9f322 | 772 | struct device *dev = ((struct seq_file *)(file->private_data))->private; |
d7b9f322 MW |
773 | unsigned long user_val; |
774 | int err; | |
775 | ||
776 | /* Get userspace string and assure termination */ | |
8504d638 | 777 | err = kstrtoul_from_user(user_buf, count, 0, &user_val); |
d7b9f322 | 778 | if (err) |
8504d638 PH |
779 | return err; |
780 | ||
d7b9f322 MW |
781 | if (user_val > 0xff) { |
782 | dev_err(dev, "debugfs error input > 0xff\n"); | |
783 | return -EINVAL; | |
784 | } | |
785 | err = abx500_set_register_interruptible(dev, | |
786 | (u8)debug_bank, debug_address, (u8)user_val); | |
787 | if (err < 0) { | |
788 | printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__); | |
789 | return -EINVAL; | |
790 | } | |
791 | ||
8504d638 | 792 | return count; |
5814fc35 MW |
793 | } |
794 | ||
8f0eb43b BJ |
795 | /* |
796 | * Interrupt status | |
797 | */ | |
798 | static u32 num_interrupts[AB8500_MAX_NR_IRQS]; | |
799 | static int num_interrupt_lines; | |
800 | ||
801 | void ab8500_debug_register_interrupt(int line) | |
802 | { | |
803 | if (line < num_interrupt_lines) | |
804 | num_interrupts[line]++; | |
805 | } | |
806 | ||
807 | static int ab8500_interrupts_print(struct seq_file *s, void *p) | |
808 | { | |
809 | int line; | |
810 | ||
811 | seq_printf(s, "irq: number of\n"); | |
812 | ||
813 | for (line = 0; line < num_interrupt_lines; line++) | |
814 | seq_printf(s, "%3i: %6i\n", line, num_interrupts[line]); | |
815 | ||
816 | return 0; | |
817 | } | |
818 | ||
819 | static int ab8500_interrupts_open(struct inode *inode, struct file *file) | |
820 | { | |
821 | return single_open(file, ab8500_interrupts_print, inode->i_private); | |
822 | } | |
823 | ||
0fbce76e | 824 | /* |
825 | * - HWREG DB8500 formated routines | |
826 | */ | |
827 | static int ab8500_hwreg_print(struct seq_file *s, void *d) | |
828 | { | |
829 | struct device *dev = s->private; | |
830 | int ret; | |
831 | u8 regvalue; | |
832 | ||
833 | ret = abx500_get_register_interruptible(dev, | |
834 | (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, ®value); | |
835 | if (ret < 0) { | |
836 | dev_err(dev, "abx500_get_reg fail %d, %d\n", | |
837 | ret, __LINE__); | |
838 | return -EINVAL; | |
839 | } | |
840 | ||
841 | if (hwreg_cfg.shift >= 0) | |
842 | regvalue >>= hwreg_cfg.shift; | |
843 | else | |
844 | regvalue <<= -hwreg_cfg.shift; | |
845 | regvalue &= hwreg_cfg.mask; | |
846 | ||
847 | if (REG_FMT_DEC(&hwreg_cfg)) | |
848 | seq_printf(s, "%d\n", regvalue); | |
849 | else | |
850 | seq_printf(s, "0x%02X\n", regvalue); | |
851 | return 0; | |
852 | } | |
853 | ||
854 | static int ab8500_hwreg_open(struct inode *inode, struct file *file) | |
855 | { | |
856 | return single_open(file, ab8500_hwreg_print, inode->i_private); | |
857 | } | |
858 | ||
1478a316 JB |
859 | static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) |
860 | { | |
861 | int bat_ctrl_raw; | |
862 | int bat_ctrl_convert; | |
863 | struct ab8500_gpadc *gpadc; | |
864 | ||
8908c049 | 865 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
866 | bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL, |
867 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 868 | bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, |
73482346 | 869 | BAT_CTRL, bat_ctrl_raw); |
1478a316 JB |
870 | |
871 | return seq_printf(s, "%d,0x%X\n", | |
872 | bat_ctrl_convert, bat_ctrl_raw); | |
873 | } | |
874 | ||
875 | static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file) | |
876 | { | |
877 | return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private); | |
878 | } | |
879 | ||
880 | static const struct file_operations ab8500_gpadc_bat_ctrl_fops = { | |
881 | .open = ab8500_gpadc_bat_ctrl_open, | |
882 | .read = seq_read, | |
883 | .llseek = seq_lseek, | |
884 | .release = single_release, | |
885 | .owner = THIS_MODULE, | |
886 | }; | |
887 | ||
888 | static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) | |
889 | { | |
890 | int btemp_ball_raw; | |
891 | int btemp_ball_convert; | |
892 | struct ab8500_gpadc *gpadc; | |
893 | ||
8908c049 | 894 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
895 | btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL, |
896 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 897 | btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, |
73482346 | 898 | btemp_ball_raw); |
1478a316 JB |
899 | |
900 | return seq_printf(s, | |
901 | "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw); | |
902 | } | |
903 | ||
904 | static int ab8500_gpadc_btemp_ball_open(struct inode *inode, | |
905 | struct file *file) | |
906 | { | |
907 | return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private); | |
908 | } | |
909 | ||
910 | static const struct file_operations ab8500_gpadc_btemp_ball_fops = { | |
911 | .open = ab8500_gpadc_btemp_ball_open, | |
912 | .read = seq_read, | |
913 | .llseek = seq_lseek, | |
914 | .release = single_release, | |
915 | .owner = THIS_MODULE, | |
916 | }; | |
917 | ||
918 | static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) | |
919 | { | |
920 | int main_charger_v_raw; | |
921 | int main_charger_v_convert; | |
922 | struct ab8500_gpadc *gpadc; | |
923 | ||
8908c049 | 924 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
925 | main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V, |
926 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 927 | main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, |
73482346 | 928 | MAIN_CHARGER_V, main_charger_v_raw); |
1478a316 JB |
929 | |
930 | return seq_printf(s, "%d,0x%X\n", | |
931 | main_charger_v_convert, main_charger_v_raw); | |
932 | } | |
933 | ||
934 | static int ab8500_gpadc_main_charger_v_open(struct inode *inode, | |
935 | struct file *file) | |
936 | { | |
937 | return single_open(file, ab8500_gpadc_main_charger_v_print, | |
938 | inode->i_private); | |
939 | } | |
940 | ||
941 | static const struct file_operations ab8500_gpadc_main_charger_v_fops = { | |
942 | .open = ab8500_gpadc_main_charger_v_open, | |
943 | .read = seq_read, | |
944 | .llseek = seq_lseek, | |
945 | .release = single_release, | |
946 | .owner = THIS_MODULE, | |
947 | }; | |
948 | ||
949 | static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) | |
950 | { | |
951 | int acc_detect1_raw; | |
952 | int acc_detect1_convert; | |
953 | struct ab8500_gpadc *gpadc; | |
954 | ||
8908c049 | 955 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
956 | acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1, |
957 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 958 | acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1, |
73482346 | 959 | acc_detect1_raw); |
1478a316 JB |
960 | |
961 | return seq_printf(s, "%d,0x%X\n", | |
962 | acc_detect1_convert, acc_detect1_raw); | |
963 | } | |
964 | ||
965 | static int ab8500_gpadc_acc_detect1_open(struct inode *inode, | |
966 | struct file *file) | |
967 | { | |
968 | return single_open(file, ab8500_gpadc_acc_detect1_print, | |
969 | inode->i_private); | |
970 | } | |
971 | ||
972 | static const struct file_operations ab8500_gpadc_acc_detect1_fops = { | |
973 | .open = ab8500_gpadc_acc_detect1_open, | |
974 | .read = seq_read, | |
975 | .llseek = seq_lseek, | |
976 | .release = single_release, | |
977 | .owner = THIS_MODULE, | |
978 | }; | |
979 | ||
980 | static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) | |
981 | { | |
982 | int acc_detect2_raw; | |
983 | int acc_detect2_convert; | |
984 | struct ab8500_gpadc *gpadc; | |
985 | ||
8908c049 | 986 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
987 | acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2, |
988 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 989 | acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc, |
73482346 | 990 | ACC_DETECT2, acc_detect2_raw); |
1478a316 JB |
991 | |
992 | return seq_printf(s, "%d,0x%X\n", | |
993 | acc_detect2_convert, acc_detect2_raw); | |
994 | } | |
995 | ||
996 | static int ab8500_gpadc_acc_detect2_open(struct inode *inode, | |
997 | struct file *file) | |
998 | { | |
999 | return single_open(file, ab8500_gpadc_acc_detect2_print, | |
1000 | inode->i_private); | |
1001 | } | |
1002 | ||
1003 | static const struct file_operations ab8500_gpadc_acc_detect2_fops = { | |
1004 | .open = ab8500_gpadc_acc_detect2_open, | |
1005 | .read = seq_read, | |
1006 | .llseek = seq_lseek, | |
1007 | .release = single_release, | |
1008 | .owner = THIS_MODULE, | |
1009 | }; | |
1010 | ||
1011 | static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) | |
1012 | { | |
1013 | int aux1_raw; | |
1014 | int aux1_convert; | |
1015 | struct ab8500_gpadc *gpadc; | |
1016 | ||
8908c049 | 1017 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1018 | aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1, |
1019 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1020 | aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1, |
73482346 | 1021 | aux1_raw); |
1478a316 JB |
1022 | |
1023 | return seq_printf(s, "%d,0x%X\n", | |
1024 | aux1_convert, aux1_raw); | |
1025 | } | |
1026 | ||
1027 | static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file) | |
1028 | { | |
1029 | return single_open(file, ab8500_gpadc_aux1_print, inode->i_private); | |
1030 | } | |
1031 | ||
1032 | static const struct file_operations ab8500_gpadc_aux1_fops = { | |
1033 | .open = ab8500_gpadc_aux1_open, | |
1034 | .read = seq_read, | |
1035 | .llseek = seq_lseek, | |
1036 | .release = single_release, | |
1037 | .owner = THIS_MODULE, | |
1038 | }; | |
1039 | ||
1040 | static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) | |
1041 | { | |
1042 | int aux2_raw; | |
1043 | int aux2_convert; | |
1044 | struct ab8500_gpadc *gpadc; | |
1045 | ||
8908c049 | 1046 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1047 | aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2, |
1048 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1049 | aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2, |
73482346 | 1050 | aux2_raw); |
1478a316 JB |
1051 | |
1052 | return seq_printf(s, "%d,0x%X\n", | |
1053 | aux2_convert, aux2_raw); | |
1054 | } | |
1055 | ||
1056 | static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file) | |
1057 | { | |
1058 | return single_open(file, ab8500_gpadc_aux2_print, inode->i_private); | |
1059 | } | |
1060 | ||
1061 | static const struct file_operations ab8500_gpadc_aux2_fops = { | |
1062 | .open = ab8500_gpadc_aux2_open, | |
1063 | .read = seq_read, | |
1064 | .llseek = seq_lseek, | |
1065 | .release = single_release, | |
1066 | .owner = THIS_MODULE, | |
1067 | }; | |
1068 | ||
1069 | static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) | |
1070 | { | |
1071 | int main_bat_v_raw; | |
1072 | int main_bat_v_convert; | |
1073 | struct ab8500_gpadc *gpadc; | |
1074 | ||
8908c049 | 1075 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1076 | main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V, |
1077 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1078 | main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, |
73482346 | 1079 | main_bat_v_raw); |
1478a316 JB |
1080 | |
1081 | return seq_printf(s, "%d,0x%X\n", | |
1082 | main_bat_v_convert, main_bat_v_raw); | |
1083 | } | |
1084 | ||
1085 | static int ab8500_gpadc_main_bat_v_open(struct inode *inode, | |
1086 | struct file *file) | |
1087 | { | |
1088 | return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private); | |
1089 | } | |
1090 | ||
1091 | static const struct file_operations ab8500_gpadc_main_bat_v_fops = { | |
1092 | .open = ab8500_gpadc_main_bat_v_open, | |
1093 | .read = seq_read, | |
1094 | .llseek = seq_lseek, | |
1095 | .release = single_release, | |
1096 | .owner = THIS_MODULE, | |
1097 | }; | |
1098 | ||
1099 | static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) | |
1100 | { | |
1101 | int vbus_v_raw; | |
1102 | int vbus_v_convert; | |
1103 | struct ab8500_gpadc *gpadc; | |
1104 | ||
8908c049 | 1105 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1106 | vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V, |
1107 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1108 | vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V, |
73482346 | 1109 | vbus_v_raw); |
1478a316 JB |
1110 | |
1111 | return seq_printf(s, "%d,0x%X\n", | |
1112 | vbus_v_convert, vbus_v_raw); | |
1113 | } | |
1114 | ||
1115 | static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file) | |
1116 | { | |
1117 | return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private); | |
1118 | } | |
1119 | ||
1120 | static const struct file_operations ab8500_gpadc_vbus_v_fops = { | |
1121 | .open = ab8500_gpadc_vbus_v_open, | |
1122 | .read = seq_read, | |
1123 | .llseek = seq_lseek, | |
1124 | .release = single_release, | |
1125 | .owner = THIS_MODULE, | |
1126 | }; | |
1127 | ||
1128 | static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) | |
1129 | { | |
1130 | int main_charger_c_raw; | |
1131 | int main_charger_c_convert; | |
1132 | struct ab8500_gpadc *gpadc; | |
1133 | ||
8908c049 | 1134 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1135 | main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C, |
1136 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1137 | main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, |
73482346 | 1138 | MAIN_CHARGER_C, main_charger_c_raw); |
1478a316 JB |
1139 | |
1140 | return seq_printf(s, "%d,0x%X\n", | |
1141 | main_charger_c_convert, main_charger_c_raw); | |
1142 | } | |
1143 | ||
1144 | static int ab8500_gpadc_main_charger_c_open(struct inode *inode, | |
1145 | struct file *file) | |
1146 | { | |
1147 | return single_open(file, ab8500_gpadc_main_charger_c_print, | |
1148 | inode->i_private); | |
1149 | } | |
1150 | ||
1151 | static const struct file_operations ab8500_gpadc_main_charger_c_fops = { | |
1152 | .open = ab8500_gpadc_main_charger_c_open, | |
1153 | .read = seq_read, | |
1154 | .llseek = seq_lseek, | |
1155 | .release = single_release, | |
1156 | .owner = THIS_MODULE, | |
1157 | }; | |
1158 | ||
1159 | static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) | |
1160 | { | |
1161 | int usb_charger_c_raw; | |
1162 | int usb_charger_c_convert; | |
1163 | struct ab8500_gpadc *gpadc; | |
1164 | ||
8908c049 | 1165 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1166 | usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C, |
1167 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1168 | usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, |
73482346 | 1169 | USB_CHARGER_C, usb_charger_c_raw); |
1478a316 JB |
1170 | |
1171 | return seq_printf(s, "%d,0x%X\n", | |
1172 | usb_charger_c_convert, usb_charger_c_raw); | |
1173 | } | |
1174 | ||
1175 | static int ab8500_gpadc_usb_charger_c_open(struct inode *inode, | |
1176 | struct file *file) | |
1177 | { | |
1178 | return single_open(file, ab8500_gpadc_usb_charger_c_print, | |
1179 | inode->i_private); | |
1180 | } | |
1181 | ||
1182 | static const struct file_operations ab8500_gpadc_usb_charger_c_fops = { | |
1183 | .open = ab8500_gpadc_usb_charger_c_open, | |
1184 | .read = seq_read, | |
1185 | .llseek = seq_lseek, | |
1186 | .release = single_release, | |
1187 | .owner = THIS_MODULE, | |
1188 | }; | |
1189 | ||
1190 | static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) | |
1191 | { | |
1192 | int bk_bat_v_raw; | |
1193 | int bk_bat_v_convert; | |
1194 | struct ab8500_gpadc *gpadc; | |
1195 | ||
8908c049 | 1196 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1197 | bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V, |
1198 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1199 | bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, |
73482346 | 1200 | BK_BAT_V, bk_bat_v_raw); |
1478a316 JB |
1201 | |
1202 | return seq_printf(s, "%d,0x%X\n", | |
1203 | bk_bat_v_convert, bk_bat_v_raw); | |
1204 | } | |
1205 | ||
1206 | static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file) | |
1207 | { | |
1208 | return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private); | |
1209 | } | |
1210 | ||
1211 | static const struct file_operations ab8500_gpadc_bk_bat_v_fops = { | |
1212 | .open = ab8500_gpadc_bk_bat_v_open, | |
1213 | .read = seq_read, | |
1214 | .llseek = seq_lseek, | |
1215 | .release = single_release, | |
1216 | .owner = THIS_MODULE, | |
1217 | }; | |
1218 | ||
1219 | static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) | |
1220 | { | |
1221 | int die_temp_raw; | |
1222 | int die_temp_convert; | |
1223 | struct ab8500_gpadc *gpadc; | |
1224 | ||
8908c049 | 1225 | gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
73482346 LJ |
1226 | die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP, |
1227 | avg_sample, trig_edge, trig_timer, conv_type); | |
1478a316 | 1228 | die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP, |
73482346 | 1229 | die_temp_raw); |
1478a316 JB |
1230 | |
1231 | return seq_printf(s, "%d,0x%X\n", | |
1232 | die_temp_convert, die_temp_raw); | |
1233 | } | |
1234 | ||
1235 | static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file) | |
1236 | { | |
1237 | return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private); | |
1238 | } | |
1239 | ||
1240 | static const struct file_operations ab8500_gpadc_die_temp_fops = { | |
1241 | .open = ab8500_gpadc_die_temp_open, | |
1242 | .read = seq_read, | |
1243 | .llseek = seq_lseek, | |
1244 | .release = single_release, | |
1245 | .owner = THIS_MODULE, | |
1246 | }; | |
1247 | ||
73482346 LJ |
1248 | static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) |
1249 | { | |
1250 | return seq_printf(s, "%d\n", avg_sample); | |
1251 | } | |
1252 | ||
1253 | static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file) | |
1254 | { | |
1255 | return single_open(file, ab8500_gpadc_avg_sample_print, | |
1256 | inode->i_private); | |
1257 | } | |
1258 | ||
1259 | static ssize_t ab8500_gpadc_avg_sample_write(struct file *file, | |
1260 | const char __user *user_buf, | |
1261 | size_t count, loff_t *ppos) | |
1262 | { | |
1263 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1264 | char buf[32]; | |
1265 | int buf_size; | |
1266 | unsigned long user_avg_sample; | |
1267 | int err; | |
1268 | ||
1269 | /* Get userspace string and assure termination */ | |
1270 | buf_size = min(count, (sizeof(buf) - 1)); | |
1271 | if (copy_from_user(buf, user_buf, buf_size)) | |
1272 | return -EFAULT; | |
1273 | buf[buf_size] = 0; | |
1274 | ||
1275 | err = strict_strtoul(buf, 0, &user_avg_sample); | |
1276 | if (err) | |
1277 | return -EINVAL; | |
1278 | if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4) | |
1279 | || (user_avg_sample == SAMPLE_8) | |
1280 | || (user_avg_sample == SAMPLE_16)) { | |
1281 | avg_sample = (u8) user_avg_sample; | |
1282 | } else { | |
1283 | dev_err(dev, "debugfs error input: " | |
1284 | "should be egal to 1, 4, 8 or 16\n"); | |
1285 | return -EINVAL; | |
1286 | } | |
1287 | return buf_size; | |
1288 | } | |
1289 | ||
1290 | static const struct file_operations ab8500_gpadc_avg_sample_fops = { | |
1291 | .open = ab8500_gpadc_avg_sample_open, | |
1292 | .read = seq_read, | |
1293 | .write = ab8500_gpadc_avg_sample_write, | |
1294 | .llseek = seq_lseek, | |
1295 | .release = single_release, | |
1296 | .owner = THIS_MODULE, | |
1297 | }; | |
1298 | ||
1299 | static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p) | |
1300 | { | |
1301 | return seq_printf(s, "%d\n", trig_edge); | |
1302 | } | |
1303 | ||
1304 | static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file) | |
1305 | { | |
1306 | return single_open(file, ab8500_gpadc_trig_edge_print, | |
1307 | inode->i_private); | |
1308 | } | |
1309 | ||
1310 | static ssize_t ab8500_gpadc_trig_edge_write(struct file *file, | |
1311 | const char __user *user_buf, | |
1312 | size_t count, loff_t *ppos) | |
1313 | { | |
1314 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1315 | char buf[32]; | |
1316 | int buf_size; | |
1317 | unsigned long user_trig_edge; | |
1318 | int err; | |
1319 | ||
1320 | /* Get userspace string and assure termination */ | |
1321 | buf_size = min(count, (sizeof(buf) - 1)); | |
1322 | if (copy_from_user(buf, user_buf, buf_size)) | |
1323 | return -EFAULT; | |
1324 | buf[buf_size] = 0; | |
1325 | ||
1326 | err = strict_strtoul(buf, 0, &user_trig_edge); | |
1327 | if (err) | |
1328 | return -EINVAL; | |
1329 | if ((user_trig_edge == RISING_EDGE) | |
1330 | || (user_trig_edge == FALLING_EDGE)) { | |
1331 | trig_edge = (u8) user_trig_edge; | |
1332 | } else { | |
1333 | dev_err(dev, "Wrong input:\n" | |
1334 | "Enter 0. Rising edge\n" | |
1335 | "Enter 1. Falling edge\n"); | |
1336 | return -EINVAL; | |
1337 | } | |
1338 | return buf_size; | |
1339 | } | |
1340 | ||
1341 | static const struct file_operations ab8500_gpadc_trig_edge_fops = { | |
1342 | .open = ab8500_gpadc_trig_edge_open, | |
1343 | .read = seq_read, | |
1344 | .write = ab8500_gpadc_trig_edge_write, | |
1345 | .llseek = seq_lseek, | |
1346 | .release = single_release, | |
1347 | .owner = THIS_MODULE, | |
1348 | }; | |
1349 | ||
1350 | static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p) | |
1351 | { | |
1352 | return seq_printf(s, "%d\n", trig_timer); | |
1353 | } | |
1354 | ||
1355 | static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file) | |
1356 | { | |
1357 | return single_open(file, ab8500_gpadc_trig_timer_print, | |
1358 | inode->i_private); | |
1359 | } | |
1360 | ||
1361 | static ssize_t ab8500_gpadc_trig_timer_write(struct file *file, | |
1362 | const char __user *user_buf, | |
1363 | size_t count, loff_t *ppos) | |
1364 | { | |
1365 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1366 | char buf[32]; | |
1367 | int buf_size; | |
1368 | unsigned long user_trig_timer; | |
1369 | int err; | |
1370 | ||
1371 | /* Get userspace string and assure termination */ | |
1372 | buf_size = min(count, (sizeof(buf) - 1)); | |
1373 | if (copy_from_user(buf, user_buf, buf_size)) | |
1374 | return -EFAULT; | |
1375 | buf[buf_size] = 0; | |
1376 | ||
1377 | err = strict_strtoul(buf, 0, &user_trig_timer); | |
1378 | if (err) | |
1379 | return -EINVAL; | |
1380 | if ((user_trig_timer >= 0) && (user_trig_timer <= 255)) { | |
1381 | trig_timer = (u8) user_trig_timer; | |
1382 | } else { | |
1383 | dev_err(dev, "debugfs error input: " | |
1384 | "should be beetween 0 to 255\n"); | |
1385 | return -EINVAL; | |
1386 | } | |
1387 | return buf_size; | |
1388 | } | |
1389 | ||
1390 | static const struct file_operations ab8500_gpadc_trig_timer_fops = { | |
1391 | .open = ab8500_gpadc_trig_timer_open, | |
1392 | .read = seq_read, | |
1393 | .write = ab8500_gpadc_trig_timer_write, | |
1394 | .llseek = seq_lseek, | |
1395 | .release = single_release, | |
1396 | .owner = THIS_MODULE, | |
1397 | }; | |
1398 | ||
1399 | static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p) | |
1400 | { | |
1401 | return seq_printf(s, "%d\n", conv_type); | |
1402 | } | |
1403 | ||
1404 | static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file) | |
1405 | { | |
1406 | return single_open(file, ab8500_gpadc_conv_type_print, | |
1407 | inode->i_private); | |
1408 | } | |
1409 | ||
1410 | static ssize_t ab8500_gpadc_conv_type_write(struct file *file, | |
1411 | const char __user *user_buf, | |
1412 | size_t count, loff_t *ppos) | |
1413 | { | |
1414 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1415 | char buf[32]; | |
1416 | int buf_size; | |
1417 | unsigned long user_conv_type; | |
1418 | int err; | |
1419 | ||
1420 | /* Get userspace string and assure termination */ | |
1421 | buf_size = min(count, (sizeof(buf) - 1)); | |
1422 | if (copy_from_user(buf, user_buf, buf_size)) | |
1423 | return -EFAULT; | |
1424 | buf[buf_size] = 0; | |
1425 | ||
1426 | err = strict_strtoul(buf, 0, &user_conv_type); | |
1427 | if (err) | |
1428 | return -EINVAL; | |
1429 | if ((user_conv_type == ADC_SW) | |
1430 | || (user_conv_type == ADC_HW)) { | |
1431 | conv_type = (u8) user_conv_type; | |
1432 | } else { | |
1433 | dev_err(dev, "Wrong input:\n" | |
1434 | "Enter 0. ADC SW conversion\n" | |
1435 | "Enter 1. ADC HW conversion\n"); | |
1436 | return -EINVAL; | |
1437 | } | |
1438 | return buf_size; | |
1439 | } | |
1440 | ||
1441 | static const struct file_operations ab8500_gpadc_conv_type_fops = { | |
1442 | .open = ab8500_gpadc_conv_type_open, | |
1443 | .read = seq_read, | |
1444 | .write = ab8500_gpadc_conv_type_write, | |
1445 | .llseek = seq_lseek, | |
1446 | .release = single_release, | |
1447 | .owner = THIS_MODULE, | |
1448 | }; | |
1449 | ||
0fbce76e | 1450 | /* |
1451 | * return length of an ASCII numerical value, 0 is string is not a | |
1452 | * numerical value. | |
1453 | * string shall start at value 1st char. | |
1454 | * string can be tailed with \0 or space or newline chars only. | |
1455 | * value can be decimal or hexadecimal (prefixed 0x or 0X). | |
1456 | */ | |
1457 | static int strval_len(char *b) | |
1458 | { | |
1459 | char *s = b; | |
1460 | if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) { | |
1461 | s += 2; | |
1462 | for (; *s && (*s != ' ') && (*s != '\n'); s++) { | |
1463 | if (!isxdigit(*s)) | |
1464 | return 0; | |
1465 | } | |
1466 | } else { | |
1467 | if (*s == '-') | |
1468 | s++; | |
1469 | for (; *s && (*s != ' ') && (*s != '\n'); s++) { | |
1470 | if (!isdigit(*s)) | |
1471 | return 0; | |
1472 | } | |
1473 | } | |
1474 | return (int) (s-b); | |
1475 | } | |
1476 | ||
1477 | /* | |
1478 | * parse hwreg input data. | |
1479 | * update global hwreg_cfg only if input data syntax is ok. | |
1480 | */ | |
1481 | static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg, | |
1482 | struct device *dev) | |
1483 | { | |
1484 | uint write, val = 0; | |
1485 | u8 regvalue; | |
1486 | int ret; | |
1487 | struct hwreg_cfg loc = { | |
1488 | .bank = 0, /* default: invalid phys addr */ | |
1489 | .addr = 0, /* default: invalid phys addr */ | |
1490 | .fmt = 0, /* default: 32bit access, hex output */ | |
1491 | .mask = 0xFFFFFFFF, /* default: no mask */ | |
1492 | .shift = 0, /* default: no bit shift */ | |
1493 | }; | |
1494 | ||
1495 | /* read or write ? */ | |
1496 | if (!strncmp(b, "read ", 5)) { | |
1497 | write = 0; | |
1498 | b += 5; | |
1499 | } else if (!strncmp(b, "write ", 6)) { | |
1500 | write = 1; | |
1501 | b += 6; | |
1502 | } else | |
1503 | return -EINVAL; | |
1504 | ||
1505 | /* OPTIONS -l|-w|-b -s -m -o */ | |
1506 | while ((*b == ' ') || (*b == '-')) { | |
1507 | if (*(b-1) != ' ') { | |
1508 | b++; | |
1509 | continue; | |
1510 | } | |
1511 | if ((!strncmp(b, "-d ", 3)) || | |
1512 | (!strncmp(b, "-dec ", 5))) { | |
1513 | b += (*(b+2) == ' ') ? 3 : 5; | |
1514 | loc.fmt |= (1<<0); | |
1515 | } else if ((!strncmp(b, "-h ", 3)) || | |
1516 | (!strncmp(b, "-hex ", 5))) { | |
1517 | b += (*(b+2) == ' ') ? 3 : 5; | |
1518 | loc.fmt &= ~(1<<0); | |
1519 | } else if ((!strncmp(b, "-m ", 3)) || | |
1520 | (!strncmp(b, "-mask ", 6))) { | |
1521 | b += (*(b+2) == ' ') ? 3 : 6; | |
1522 | if (strval_len(b) == 0) | |
1523 | return -EINVAL; | |
1524 | loc.mask = simple_strtoul(b, &b, 0); | |
1525 | } else if ((!strncmp(b, "-s ", 3)) || | |
1526 | (!strncmp(b, "-shift ", 7))) { | |
1527 | b += (*(b+2) == ' ') ? 3 : 7; | |
1528 | if (strval_len(b) == 0) | |
1529 | return -EINVAL; | |
1530 | loc.shift = simple_strtol(b, &b, 0); | |
1531 | } else { | |
1532 | return -EINVAL; | |
1533 | } | |
1534 | } | |
1535 | /* get arg BANK and ADDRESS */ | |
1536 | if (strval_len(b) == 0) | |
1537 | return -EINVAL; | |
1538 | loc.bank = simple_strtoul(b, &b, 0); | |
1539 | while (*b == ' ') | |
1540 | b++; | |
1541 | if (strval_len(b) == 0) | |
1542 | return -EINVAL; | |
1543 | loc.addr = simple_strtoul(b, &b, 0); | |
1544 | ||
1545 | if (write) { | |
1546 | while (*b == ' ') | |
1547 | b++; | |
1548 | if (strval_len(b) == 0) | |
1549 | return -EINVAL; | |
1550 | val = simple_strtoul(b, &b, 0); | |
1551 | } | |
1552 | ||
1553 | /* args are ok, update target cfg (mainly for read) */ | |
1554 | *cfg = loc; | |
1555 | ||
1556 | #ifdef ABB_HWREG_DEBUG | |
1557 | pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d" | |
1558 | "value=0x%X\n", (write) ? "write" : "read", | |
1559 | REG_FMT_DEC(cfg) ? "decimal" : "hexa", | |
1560 | cfg->addr, cfg->mask, cfg->shift, val); | |
1561 | #endif | |
1562 | ||
1563 | if (!write) | |
1564 | return 0; | |
1565 | ||
1566 | ret = abx500_get_register_interruptible(dev, | |
1567 | (u8)cfg->bank, (u8)cfg->addr, ®value); | |
1568 | if (ret < 0) { | |
1569 | dev_err(dev, "abx500_get_reg fail %d, %d\n", | |
1570 | ret, __LINE__); | |
1571 | return -EINVAL; | |
1572 | } | |
1573 | ||
1574 | if (cfg->shift >= 0) { | |
1575 | regvalue &= ~(cfg->mask << (cfg->shift)); | |
1576 | val = (val & cfg->mask) << (cfg->shift); | |
1577 | } else { | |
1578 | regvalue &= ~(cfg->mask >> (-cfg->shift)); | |
1579 | val = (val & cfg->mask) >> (-cfg->shift); | |
1580 | } | |
1581 | val = val | regvalue; | |
1582 | ||
1583 | ret = abx500_set_register_interruptible(dev, | |
1584 | (u8)cfg->bank, (u8)cfg->addr, (u8)val); | |
1585 | if (ret < 0) { | |
1586 | pr_err("abx500_set_reg failed %d, %d", ret, __LINE__); | |
1587 | return -EINVAL; | |
1588 | } | |
1589 | ||
1590 | return 0; | |
1591 | } | |
1592 | ||
1593 | static ssize_t ab8500_hwreg_write(struct file *file, | |
1594 | const char __user *user_buf, size_t count, loff_t *ppos) | |
1595 | { | |
1596 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1597 | char buf[128]; | |
1598 | int buf_size, ret; | |
1599 | ||
1600 | /* Get userspace string and assure termination */ | |
1601 | buf_size = min(count, (sizeof(buf)-1)); | |
1602 | if (copy_from_user(buf, user_buf, buf_size)) | |
1603 | return -EFAULT; | |
1604 | buf[buf_size] = 0; | |
1605 | ||
1606 | /* get args and process */ | |
1607 | ret = hwreg_common_write(buf, &hwreg_cfg, dev); | |
1608 | return (ret) ? ret : buf_size; | |
1609 | } | |
1610 | ||
1611 | /* | |
1612 | * - irq subscribe/unsubscribe stuff | |
1613 | */ | |
4b8ac082 LJ |
1614 | static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p) |
1615 | { | |
1616 | seq_printf(s, "%d\n", irq_first); | |
1617 | ||
1618 | return 0; | |
1619 | } | |
1620 | ||
1621 | static int ab8500_subscribe_unsubscribe_open(struct inode *inode, | |
1622 | struct file *file) | |
1623 | { | |
1624 | return single_open(file, ab8500_subscribe_unsubscribe_print, | |
1625 | inode->i_private); | |
1626 | } | |
1627 | ||
1628 | /* | |
0b337e70 | 1629 | * Userspace should use poll() on this file. When an event occur |
4b8ac082 LJ |
1630 | * the blocking poll will be released. |
1631 | */ | |
1632 | static ssize_t show_irq(struct device *dev, | |
1633 | struct device_attribute *attr, char *buf) | |
1634 | { | |
0b337e70 MW |
1635 | unsigned long name; |
1636 | unsigned int irq_index; | |
1637 | int err; | |
4b8ac082 | 1638 | |
0b337e70 MW |
1639 | err = strict_strtoul(attr->attr.name, 0, &name); |
1640 | if (err) | |
1641 | return err; | |
1642 | ||
1643 | irq_index = name - irq_first; | |
ddba25f1 | 1644 | if (irq_index >= num_irqs) |
0b337e70 MW |
1645 | return -EINVAL; |
1646 | else | |
1647 | return sprintf(buf, "%u\n", irq_count[irq_index]); | |
1648 | } | |
4b8ac082 LJ |
1649 | |
1650 | static ssize_t ab8500_subscribe_write(struct file *file, | |
1651 | const char __user *user_buf, | |
1652 | size_t count, loff_t *ppos) | |
1653 | { | |
1654 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1655 | char buf[32]; | |
1656 | int buf_size; | |
1657 | unsigned long user_val; | |
1658 | int err; | |
0b337e70 | 1659 | unsigned int irq_index; |
4b8ac082 LJ |
1660 | |
1661 | /* Get userspace string and assure termination */ | |
1662 | buf_size = min(count, (sizeof(buf)-1)); | |
1663 | if (copy_from_user(buf, user_buf, buf_size)) | |
1664 | return -EFAULT; | |
1665 | buf[buf_size] = 0; | |
1666 | ||
1667 | err = strict_strtoul(buf, 0, &user_val); | |
1668 | if (err) | |
1669 | return -EINVAL; | |
1670 | if (user_val < irq_first) { | |
1671 | dev_err(dev, "debugfs error input < %d\n", irq_first); | |
1672 | return -EINVAL; | |
1673 | } | |
1674 | if (user_val > irq_last) { | |
1675 | dev_err(dev, "debugfs error input > %d\n", irq_last); | |
1676 | return -EINVAL; | |
1677 | } | |
1678 | ||
0b337e70 | 1679 | irq_index = user_val - irq_first; |
ddba25f1 | 1680 | if (irq_index >= num_irqs) |
0b337e70 MW |
1681 | return -EINVAL; |
1682 | ||
4b8ac082 | 1683 | /* |
0b337e70 | 1684 | * This will create a sysfs file named <irq-nr> which userspace can |
4b8ac082 LJ |
1685 | * use to select or poll and get the AB8500 events |
1686 | */ | |
0b337e70 MW |
1687 | dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute), |
1688 | GFP_KERNEL); | |
1689 | event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL); | |
1690 | sprintf(event_name[irq_index], "%lu", user_val); | |
1691 | dev_attr[irq_index]->show = show_irq; | |
1692 | dev_attr[irq_index]->store = NULL; | |
1693 | dev_attr[irq_index]->attr.name = event_name[irq_index]; | |
1694 | dev_attr[irq_index]->attr.mode = S_IRUGO; | |
1695 | err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr); | |
4b8ac082 LJ |
1696 | if (err < 0) { |
1697 | printk(KERN_ERR "sysfs_create_file failed %d\n", err); | |
1698 | return err; | |
1699 | } | |
1700 | ||
1701 | err = request_threaded_irq(user_val, NULL, ab8500_debug_handler, | |
1702 | IRQF_SHARED | IRQF_NO_SUSPEND, | |
1703 | "ab8500-debug", &dev->kobj); | |
1704 | if (err < 0) { | |
1705 | printk(KERN_ERR "request_threaded_irq failed %d, %lu\n", | |
1706 | err, user_val); | |
0b337e70 | 1707 | sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); |
4b8ac082 LJ |
1708 | return err; |
1709 | } | |
1710 | ||
1711 | return buf_size; | |
1712 | } | |
1713 | ||
1714 | static ssize_t ab8500_unsubscribe_write(struct file *file, | |
1715 | const char __user *user_buf, | |
1716 | size_t count, loff_t *ppos) | |
1717 | { | |
1718 | struct device *dev = ((struct seq_file *)(file->private_data))->private; | |
1719 | char buf[32]; | |
1720 | int buf_size; | |
1721 | unsigned long user_val; | |
1722 | int err; | |
0b337e70 | 1723 | unsigned int irq_index; |
4b8ac082 LJ |
1724 | |
1725 | /* Get userspace string and assure termination */ | |
1726 | buf_size = min(count, (sizeof(buf)-1)); | |
1727 | if (copy_from_user(buf, user_buf, buf_size)) | |
1728 | return -EFAULT; | |
1729 | buf[buf_size] = 0; | |
1730 | ||
1731 | err = strict_strtoul(buf, 0, &user_val); | |
1732 | if (err) | |
1733 | return -EINVAL; | |
1734 | if (user_val < irq_first) { | |
1735 | dev_err(dev, "debugfs error input < %d\n", irq_first); | |
1736 | return -EINVAL; | |
1737 | } | |
1738 | if (user_val > irq_last) { | |
1739 | dev_err(dev, "debugfs error input > %d\n", irq_last); | |
1740 | return -EINVAL; | |
1741 | } | |
1742 | ||
0b337e70 | 1743 | irq_index = user_val - irq_first; |
ddba25f1 | 1744 | if (irq_index >= num_irqs) |
0b337e70 MW |
1745 | return -EINVAL; |
1746 | ||
1747 | /* Set irq count to 0 when unsubscribe */ | |
1748 | irq_count[irq_index] = 0; | |
1749 | ||
1750 | if (dev_attr[irq_index]) | |
1751 | sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); | |
4b8ac082 | 1752 | |
0b337e70 MW |
1753 | |
1754 | free_irq(user_val, &dev->kobj); | |
1755 | kfree(event_name[irq_index]); | |
1756 | kfree(dev_attr[irq_index]); | |
4b8ac082 LJ |
1757 | |
1758 | return buf_size; | |
1759 | } | |
1760 | ||
0fbce76e | 1761 | /* |
1762 | * - several deubgfs nodes fops | |
1763 | */ | |
1764 | ||
5814fc35 | 1765 | static const struct file_operations ab8500_bank_fops = { |
d7b9f322 MW |
1766 | .open = ab8500_bank_open, |
1767 | .write = ab8500_bank_write, | |
1768 | .read = seq_read, | |
1769 | .llseek = seq_lseek, | |
1770 | .release = single_release, | |
1771 | .owner = THIS_MODULE, | |
5814fc35 MW |
1772 | }; |
1773 | ||
1774 | static const struct file_operations ab8500_address_fops = { | |
d7b9f322 MW |
1775 | .open = ab8500_address_open, |
1776 | .write = ab8500_address_write, | |
1777 | .read = seq_read, | |
1778 | .llseek = seq_lseek, | |
1779 | .release = single_release, | |
1780 | .owner = THIS_MODULE, | |
5814fc35 MW |
1781 | }; |
1782 | ||
1783 | static const struct file_operations ab8500_val_fops = { | |
d7b9f322 MW |
1784 | .open = ab8500_val_open, |
1785 | .write = ab8500_val_write, | |
1786 | .read = seq_read, | |
1787 | .llseek = seq_lseek, | |
1788 | .release = single_release, | |
1789 | .owner = THIS_MODULE, | |
5814fc35 MW |
1790 | }; |
1791 | ||
8f0eb43b BJ |
1792 | static const struct file_operations ab8500_interrupts_fops = { |
1793 | .open = ab8500_interrupts_open, | |
1794 | .read = seq_read, | |
1795 | .llseek = seq_lseek, | |
1796 | .release = single_release, | |
1797 | .owner = THIS_MODULE, | |
1798 | }; | |
1799 | ||
4b8ac082 LJ |
1800 | static const struct file_operations ab8500_subscribe_fops = { |
1801 | .open = ab8500_subscribe_unsubscribe_open, | |
1802 | .write = ab8500_subscribe_write, | |
1803 | .read = seq_read, | |
1804 | .llseek = seq_lseek, | |
1805 | .release = single_release, | |
1806 | .owner = THIS_MODULE, | |
1807 | }; | |
1808 | ||
1809 | static const struct file_operations ab8500_unsubscribe_fops = { | |
1810 | .open = ab8500_subscribe_unsubscribe_open, | |
1811 | .write = ab8500_unsubscribe_write, | |
1812 | .read = seq_read, | |
1813 | .llseek = seq_lseek, | |
1814 | .release = single_release, | |
1815 | .owner = THIS_MODULE, | |
1816 | }; | |
1817 | ||
0fbce76e | 1818 | static const struct file_operations ab8500_hwreg_fops = { |
1819 | .open = ab8500_hwreg_open, | |
1820 | .write = ab8500_hwreg_write, | |
1821 | .read = seq_read, | |
1822 | .llseek = seq_lseek, | |
1823 | .release = single_release, | |
1824 | .owner = THIS_MODULE, | |
1825 | }; | |
1826 | ||
5814fc35 | 1827 | static struct dentry *ab8500_dir; |
1478a316 | 1828 | static struct dentry *ab8500_gpadc_dir; |
5814fc35 | 1829 | |
f791be49 | 1830 | static int ab8500_debug_probe(struct platform_device *plf) |
5814fc35 | 1831 | { |
0fbce76e | 1832 | struct dentry *file; |
ddba25f1 LW |
1833 | int ret = -ENOMEM; |
1834 | struct ab8500 *ab8500; | |
d7b9f322 MW |
1835 | debug_bank = AB8500_MISC; |
1836 | debug_address = AB8500_REV_REG & 0x00FF; | |
5814fc35 | 1837 | |
ddba25f1 LW |
1838 | ab8500 = dev_get_drvdata(plf->dev.parent); |
1839 | num_irqs = ab8500->mask_size; | |
1840 | ||
70bad04f | 1841 | irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL); |
ddba25f1 LW |
1842 | if (!irq_count) |
1843 | return -ENOMEM; | |
1844 | ||
1845 | dev_attr = kzalloc(sizeof(*dev_attr)*num_irqs,GFP_KERNEL); | |
1846 | if (!dev_attr) | |
1847 | goto out_freeirq_count; | |
1848 | ||
1849 | event_name = kzalloc(sizeof(*event_name)*num_irqs, GFP_KERNEL); | |
1850 | if (!event_name) | |
1851 | goto out_freedev_attr; | |
1852 | ||
4b8ac082 LJ |
1853 | irq_first = platform_get_irq_byname(plf, "IRQ_FIRST"); |
1854 | if (irq_first < 0) { | |
1855 | dev_err(&plf->dev, "First irq not found, err %d\n", | |
1478a316 | 1856 | irq_first); |
ddba25f1 LW |
1857 | ret = irq_first; |
1858 | goto out_freeevent_name; | |
4b8ac082 LJ |
1859 | } |
1860 | ||
1861 | irq_last = platform_get_irq_byname(plf, "IRQ_LAST"); | |
1862 | if (irq_last < 0) { | |
1863 | dev_err(&plf->dev, "Last irq not found, err %d\n", | |
1478a316 | 1864 | irq_last); |
ddba25f1 LW |
1865 | ret = irq_last; |
1866 | goto out_freeevent_name; | |
4b8ac082 LJ |
1867 | } |
1868 | ||
d7b9f322 MW |
1869 | ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); |
1870 | if (!ab8500_dir) | |
0fbce76e | 1871 | goto err; |
5814fc35 | 1872 | |
1478a316 JB |
1873 | ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING, |
1874 | ab8500_dir); | |
1875 | if (!ab8500_gpadc_dir) | |
1876 | goto err; | |
1877 | ||
1878 | file = debugfs_create_file("all-bank-registers", S_IRUGO, | |
1879 | ab8500_dir, &plf->dev, &ab8500_registers_fops); | |
1880 | if (!file) | |
1881 | goto err; | |
1882 | ||
42002c6d MYK |
1883 | file = debugfs_create_file("all-banks", S_IRUGO, |
1884 | ab8500_dir, &plf->dev, &ab8500_all_banks_fops); | |
1885 | if (!file) | |
1886 | goto err; | |
1887 | ||
1478a316 JB |
1888 | file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR), |
1889 | ab8500_dir, &plf->dev, &ab8500_bank_fops); | |
1890 | if (!file) | |
1891 | goto err; | |
1892 | ||
1893 | file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR), | |
1894 | ab8500_dir, &plf->dev, &ab8500_address_fops); | |
1895 | if (!file) | |
1896 | goto err; | |
1897 | ||
1898 | file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR), | |
1899 | ab8500_dir, &plf->dev, &ab8500_val_fops); | |
1900 | if (!file) | |
1901 | goto err; | |
1902 | ||
1903 | file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR), | |
1904 | ab8500_dir, &plf->dev, &ab8500_subscribe_fops); | |
1905 | if (!file) | |
1906 | goto err; | |
1907 | ||
8f0eb43b BJ |
1908 | if (is_ab8500(ab8500)) |
1909 | num_interrupt_lines = AB8500_NR_IRQS; | |
1910 | else if (is_ab8505(ab8500)) | |
1911 | num_interrupt_lines = AB8505_NR_IRQS; | |
1912 | else if (is_ab9540(ab8500)) | |
1913 | num_interrupt_lines = AB9540_NR_IRQS; | |
1914 | ||
1915 | file = debugfs_create_file("interrupts", (S_IRUGO), | |
1916 | ab8500_dir, &plf->dev, &ab8500_interrupts_fops); | |
1917 | if (!file) | |
1918 | goto err; | |
1919 | ||
1478a316 JB |
1920 | file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR), |
1921 | ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops); | |
1922 | if (!file) | |
1923 | goto err; | |
1924 | ||
1925 | file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR), | |
1926 | ab8500_dir, &plf->dev, &ab8500_hwreg_fops); | |
1927 | if (!file) | |
1928 | goto err; | |
1929 | ||
1930 | file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR), | |
1931 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops); | |
1932 | if (!file) | |
1933 | goto err; | |
1934 | ||
1935 | file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR), | |
1936 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops); | |
1937 | if (!file) | |
1938 | goto err; | |
1939 | ||
1940 | file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR), | |
1941 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops); | |
1942 | if (!file) | |
1943 | goto err; | |
1944 | ||
1945 | file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR), | |
1946 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops); | |
1947 | if (!file) | |
1948 | goto err; | |
1949 | ||
1950 | file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR), | |
1951 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops); | |
1952 | if (!file) | |
1953 | goto err; | |
1954 | ||
1955 | file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR), | |
1956 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops); | |
1957 | if (!file) | |
1958 | goto err; | |
1959 | ||
1960 | file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR), | |
1961 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops); | |
0fbce76e | 1962 | if (!file) |
1963 | goto err; | |
5814fc35 | 1964 | |
1478a316 JB |
1965 | file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR), |
1966 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops); | |
0fbce76e | 1967 | if (!file) |
1968 | goto err; | |
5814fc35 | 1969 | |
1478a316 JB |
1970 | file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR), |
1971 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops); | |
0fbce76e | 1972 | if (!file) |
1973 | goto err; | |
5814fc35 | 1974 | |
1478a316 JB |
1975 | file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR), |
1976 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops); | |
0fbce76e | 1977 | if (!file) |
1978 | goto err; | |
1979 | ||
1478a316 JB |
1980 | file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR), |
1981 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops); | |
0fbce76e | 1982 | if (!file) |
1983 | goto err; | |
1984 | ||
1478a316 JB |
1985 | file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR), |
1986 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops); | |
0fbce76e | 1987 | if (!file) |
1988 | goto err; | |
1989 | ||
1478a316 JB |
1990 | file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR), |
1991 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops); | |
0fbce76e | 1992 | if (!file) |
1993 | goto err; | |
73482346 LJ |
1994 | |
1995 | file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUGO), | |
1996 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops); | |
1997 | if (!file) | |
1998 | goto err; | |
1999 | ||
2000 | file = debugfs_create_file("trig_edge", (S_IRUGO | S_IWUGO), | |
2001 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_edge_fops); | |
2002 | if (!file) | |
2003 | goto err; | |
2004 | ||
2005 | file = debugfs_create_file("trig_timer", (S_IRUGO | S_IWUGO), | |
2006 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_timer_fops); | |
2007 | if (!file) | |
2008 | goto err; | |
2009 | ||
2010 | file = debugfs_create_file("conv_type", (S_IRUGO | S_IWUGO), | |
2011 | ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_conv_type_fops); | |
2012 | if (!file) | |
2013 | goto err; | |
4b8ac082 | 2014 | |
d7b9f322 | 2015 | return 0; |
5814fc35 | 2016 | |
0fbce76e | 2017 | err: |
2018 | if (ab8500_dir) | |
2019 | debugfs_remove_recursive(ab8500_dir); | |
d7b9f322 | 2020 | dev_err(&plf->dev, "failed to create debugfs entries.\n"); |
ddba25f1 LW |
2021 | out_freeevent_name: |
2022 | kfree(event_name); | |
2023 | out_freedev_attr: | |
2024 | kfree(dev_attr); | |
2025 | out_freeirq_count: | |
2026 | kfree(irq_count); | |
2027 | ||
2028 | return ret; | |
5814fc35 MW |
2029 | } |
2030 | ||
4740f73f | 2031 | static int ab8500_debug_remove(struct platform_device *plf) |
5814fc35 | 2032 | { |
0fbce76e | 2033 | debugfs_remove_recursive(ab8500_dir); |
ddba25f1 LW |
2034 | kfree(event_name); |
2035 | kfree(dev_attr); | |
2036 | kfree(irq_count); | |
2037 | ||
d7b9f322 | 2038 | return 0; |
5814fc35 MW |
2039 | } |
2040 | ||
2041 | static struct platform_driver ab8500_debug_driver = { | |
d7b9f322 MW |
2042 | .driver = { |
2043 | .name = "ab8500-debug", | |
2044 | .owner = THIS_MODULE, | |
2045 | }, | |
2046 | .probe = ab8500_debug_probe, | |
84449216 | 2047 | .remove = ab8500_debug_remove |
5814fc35 MW |
2048 | }; |
2049 | ||
2050 | static int __init ab8500_debug_init(void) | |
2051 | { | |
d7b9f322 | 2052 | return platform_driver_register(&ab8500_debug_driver); |
5814fc35 MW |
2053 | } |
2054 | ||
2055 | static void __exit ab8500_debug_exit(void) | |
2056 | { | |
d7b9f322 | 2057 | platform_driver_unregister(&ab8500_debug_driver); |
5814fc35 MW |
2058 | } |
2059 | subsys_initcall(ab8500_debug_init); | |
2060 | module_exit(ab8500_debug_exit); | |
2061 | ||
2062 | MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com"); | |
2063 | MODULE_DESCRIPTION("AB8500 DEBUG"); | |
2064 | MODULE_LICENSE("GPL v2"); |