]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/power/act8945a.c
Add SAMA5D2 Xplained IAR demo.
[freertos] / FreeRTOS / Demo / CORTEX_A5_SAMA5D2x_Xplained_IAR / AtmelFiles / drivers / power / act8945a.c
1 /* ----------------------------------------------------------------------------\r
2  *         SAM Software Package License\r
3  * ----------------------------------------------------------------------------\r
4  * Copyright (c) 2015, Atmel Corporation\r
5  *\r
6  * All rights reserved.\r
7  *\r
8  * Redistribution and use in source and binary forms, with or without\r
9  * modification, are permitted provided that the following conditions are met:\r
10  *\r
11  * - Redistributions of source code must retain the above copyright notice,\r
12  * this list of conditions and the disclaimer below.\r
13  *\r
14  * Atmel's name may not be used to endorse or promote products derived from\r
15  * this software without specific prior written permission.\r
16  *\r
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  * ----------------------------------------------------------------------------\r
28  */\r
29 \r
30 /*----------------------------------------------------------------------------\r
31  *        Headers\r
32  *----------------------------------------------------------------------------*/\r
33 \r
34 #include "board.h"\r
35 #include "chip.h"\r
36 \r
37 #include "peripherals/pio.h"\r
38 #include "peripherals/pmc.h"\r
39 #include "peripherals/flexcom.h"\r
40 #include "peripherals/twi.h"\r
41 #include "peripherals/twid.h"\r
42 \r
43 #include "power/act8945a.h"\r
44 \r
45 #include "trace.h"\r
46 \r
47 #include <stdio.h>\r
48 #include <stdint.h>\r
49 #include <string.h>\r
50 #include <assert.h>\r
51 \r
52 /*----------------------------------------------------------------------------\r
53  *        Types\r
54  *----------------------------------------------------------------------------*/\r
55 \r
56 #define STATE_VSEL 1 // HW config DDR3L\r
57 \r
58 // SYS @0x00\r
59 union _sys0 {\r
60         struct {\r
61                 uint8_t\r
62                         trst:       1,  // Reset time out 0->260 1->65ms\r
63                         nsysmode:   1,  // response of SYSLEV voltage detector, 1->int 0>shutdown\r
64                         nsyslevmsk: 1,  // 1->unmask int\r
65                         nsysstat:   1,  // 1 if vsys < syslev voltage threshold\r
66                         syslev:     4;  // defines SYSLEV voltage threshold\r
67         } bits;\r
68         uint8_t u8;\r
69 };\r
70 \r
71 // SYS @0x01\r
72 union _sys1 {\r
73         struct {\r
74                 uint8_t\r
75                         scratch: 4, // user area to store system status information\r
76                         ruf4:    1,\r
77                         mstroff: 1, // Set bit to 1 to turn off all regulators\r
78                         ruf67:   2;\r
79         } bits;\r
80         uint8_t u8;\r
81 };\r
82 \r
83 // REG1 @0x20, REG2 @0x30, REG3 @0x40\r
84 union _vset1 {\r
85         struct {\r
86                 uint8_t\r
87                         vset1:  6,\r
88                         ruf_67: 2;\r
89         } bits;\r
90         uint8_t u8;\r
91 };\r
92 \r
93 // REG1 @0x21, REG2 @0x31, REG3 @0x41\r
94 union _vset2 {\r
95         struct {\r
96                 uint8_t\r
97                         vset2:  6,\r
98                         ruf_67: 2;\r
99         } bits;\r
100         uint8_t u8;\r
101 };\r
102 \r
103 // REG1 @0x22, REG2 @0x32, REG3 @0x42\r
104 union _ctrl1 {\r
105         struct {\r
106                 uint8_t\r
107                         ok:      1,\r
108                         nfltmsk: 1,\r
109                         delay:   3,\r
110                         mode:    1,\r
111                         phase:   1,\r
112                         on:      1;\r
113         } bits;\r
114         uint8_t u8;\r
115 };\r
116 \r
117 // REG4 @0x51, REG5 @0x55, REG6 @0x61, REG7 @0x65\r
118 union _ctrl2 {\r
119         struct {\r
120                 uint8_t\r
121                         ok:      1,\r
122                         nfltmsk: 1,\r
123                         delay:   3,\r
124                         lowiq:   1,\r
125                         dis:     1,\r
126                         on:      1;\r
127         } bits;\r
128         uint8_t u8;\r
129 };\r
130 \r
131 union _apch_70 {\r
132         struct {\r
133                 uint8_t\r
134                         ruf_0:  1,\r
135                         ruf_1:  1,\r
136                         ruf_2:  1,\r
137                         ruf_3:  1,\r
138                         ruf_45: 2,\r
139                         ruf_67: 2;\r
140         } bits;\r
141         uint8_t u8;\r
142 };\r
143 \r
144 union _apch_71 {\r
145         struct {\r
146                 uint8_t\r
147                         ovpset:  2,\r
148                         pretimo: 2,\r
149                         tottimo: 2,\r
150                         ruf6:    1,\r
151                         suschg:  1;\r
152         } bits;\r
153         uint8_t u8;\r
154 };\r
155 \r
156 union _apch_78 {\r
157         struct {\r
158                 uint8_t\r
159                         chgdat:   1,\r
160                         indat:    1,\r
161                         tempdat:  1,\r
162                         timrdat:  1,\r
163                         chgstat:  1,\r
164                         instat:   1,\r
165                         tempstat: 1,\r
166                         timrstat: 1;\r
167         } bits;\r
168         uint8_t u8;\r
169 };\r
170 \r
171 union _apch_79 {\r
172         struct {\r
173                 uint8_t\r
174                         chgeocout: 1,\r
175                         indis:     1,\r
176                         tempout:   1,\r
177                         timrpre:   1,\r
178                         chgeocin:  1,\r
179                         incon:     1,\r
180                         tempin:    1,\r
181                         timrtot:   1;\r
182         } bits;\r
183         uint8_t u8;\r
184 };\r
185 \r
186 union _apch_7a {\r
187         struct {\r
188                 uint8_t\r
189                         ruf0:     1,\r
190                         acinstat: 1,\r
191                         ruf32:    2,\r
192                         cstate:   2,\r
193                         ruf76:    2;\r
194         } bits;\r
195         uint8_t u8;\r
196 };\r
197 \r
198 /*----------------------------------------------------------------------------\r
199  *        Constants\r
200  *----------------------------------------------------------------------------*/\r
201 \r
202 /// Slave address\r
203 #define ACT8945A_TWI_ADDRESS 0x5B\r
204 \r
205 #define NUM_REGULATORS 7\r
206 \r
207 #define IADDR_SYS0   0x00\r
208 #define IADDR_SYS1   0x01\r
209 #define IADDR_REG1      0x20\r
210 #define IADDR_REG2      0x30\r
211 #define IADDR_REG3      0x40\r
212 #define IADDR_REG4      0x50\r
213 #define IADDR_REG5      0x54\r
214 #define IADDR_REG6      0x60\r
215 #define IADDR_REG7      0x64\r
216 #define IADDR_APCH_70   0x70\r
217 #define IADDR_APCH_71   0x71\r
218 #define IADDR_APCH_78   0x78\r
219 #define IADDR_APCH_79   0x79\r
220 #define IADDR_APCH_7A   0x7a\r
221 \r
222 static const uint8_t _iaddr_reg[] = {\r
223         IADDR_REG1, IADDR_REG2, IADDR_REG3, IADDR_REG4,\r
224         IADDR_REG5, IADDR_REG6, IADDR_REG7,\r
225 };\r
226 \r
227 static const char* _charging_states[4] = {\r
228         "Suspend/Disable/Fault",\r
229         "End of charge",\r
230         "Fast charge/Top-off",\r
231         "Precondition",\r
232 };\r
233 \r
234 struct _reg\r
235 {\r
236         const char* name;\r
237         uint8_t iaddr;\r
238 };\r
239 \r
240 static const struct _reg _regs[] = {\r
241         { "SYS0   ", IADDR_SYS0 },\r
242         { "SYS1   ", IADDR_SYS1 },\r
243         { "REG1_20", IADDR_REG1 },\r
244         { "REG1_21", IADDR_REG1 + 1 },\r
245         { "REG1_22", IADDR_REG1 + 2 },\r
246         { "REG2_30", IADDR_REG2 },\r
247         { "REG2_31", IADDR_REG2 + 1 },\r
248         { "REG2_32", IADDR_REG2 + 2 },\r
249         { "REG3_40", IADDR_REG3 },\r
250         { "REG3_41", IADDR_REG3 + 1 },\r
251         { "REG3_42", IADDR_REG3 + 2 },\r
252         { "REG4_50", IADDR_REG4 },\r
253         { "REG4_51", IADDR_REG4 + 1 },\r
254         { "REG5_54", IADDR_REG5 },\r
255         { "REG5_55", IADDR_REG5 + 1 },\r
256         { "REG6_60", IADDR_REG6 },\r
257         { "REG6_61", IADDR_REG6 + 1 },\r
258         { "REG7_64", IADDR_REG7 },\r
259         { "REG7_65", IADDR_REG7 + 1 },\r
260         { "APCH_70", IADDR_APCH_70 },\r
261         { "APCH_71", IADDR_APCH_71 },\r
262         { "APCH_78", IADDR_APCH_78 },\r
263         { "APCH_79", IADDR_APCH_79 },\r
264         { "APCH_7A", IADDR_APCH_7A },\r
265 };\r
266 \r
267 static const char *_ovp_setting[4] = {\r
268         "6.6V", "7.0V", "7.5V", "8.0V",\r
269 } ;\r
270 \r
271 /*------------------------------------------------------------------------------\r
272  *         Local functions\r
273  *----------------------------------------------------------------------------*/\r
274 \r
275 static bool _act8945a_read_reg(struct _act8945a* act8945a, uint32_t iaddr,\r
276                 uint8_t* value)\r
277 {\r
278         uint32_t status;\r
279         struct _buffer in = {\r
280                 .data = value,\r
281                 .size = 1\r
282         };\r
283         act8945a->twid->slave_addr = ACT8945A_TWI_ADDRESS;\r
284         act8945a->twid->iaddr = iaddr;\r
285         act8945a->twid->isize = 1;\r
286         status = twid_transfert(act8945a->twid, &in, 0,\r
287                         twid_finish_transfert_callback, 0);\r
288         if (status != TWID_SUCCESS)\r
289                 return false;\r
290         twid_wait_transfert(act8945a->twid);\r
291         return true;\r
292 }\r
293 \r
294 static bool _act8945a_write_reg(struct _act8945a* act8945a, uint32_t iaddr,\r
295                 uint8_t value)\r
296 {\r
297         uint32_t status;\r
298         struct _buffer out = {\r
299                 .data = (uint8_t*)&value,\r
300                 .size = 1\r
301         };\r
302         act8945a->twid->slave_addr = ACT8945A_TWI_ADDRESS;\r
303         act8945a->twid->iaddr = iaddr;\r
304         act8945a->twid->isize = 1;\r
305         status = twid_transfert(act8945a->twid, 0, &out,\r
306                         twid_finish_transfert_callback, 0);\r
307         if (status != TWID_SUCCESS)\r
308                 return false;\r
309         twid_wait_transfert(act8945a->twid);\r
310         return true;\r
311 }\r
312 \r
313 static bool _act8945a_update_cached_registers(struct _act8945a *act8945a)\r
314 {\r
315         return _act8945a_read_reg(act8945a, IADDR_SYS0, &act8945a->sys0) &&\r
316                 _act8945a_read_reg(act8945a, IADDR_APCH_78, &act8945a->apch78) &&\r
317                 _act8945a_read_reg(act8945a, IADDR_APCH_79, &act8945a->apch79) &&\r
318                 _act8945a_read_reg(act8945a, IADDR_APCH_7A, &act8945a->apch7a);\r
319 }\r
320 \r
321 static void _act8945a_irq_handler(uint32_t group, uint32_t status, void* user_arg)\r
322 {\r
323         struct _act8945a *act8945a = (struct _act8945a*)user_arg;\r
324 \r
325         if (status & act8945a->desc.pin_irq.mask) {\r
326                 union _sys0 sys0;\r
327                 union _apch_78 apch78;\r
328                 union _apch_79 apch79;\r
329                 union _apch_7a apch7a;\r
330 \r
331                 // save previous values\r
332                 sys0.u8 = act8945a->sys0;\r
333                 apch78.u8 = act8945a->apch78;\r
334                 apch79.u8 = act8945a->apch79;\r
335                 apch7a.u8 = act8945a->apch7a;\r
336 \r
337                 // update values\r
338                 _act8945a_update_cached_registers(act8945a);\r
339 \r
340                 // show changes\r
341                 if (sys0.u8 != act8945a->sys0) {\r
342                         trace_debug("PMIC IRQ: SYST0 changed\r\n");\r
343                 }\r
344                 if (apch78.u8 != act8945a->apch78) {\r
345                         if (apch78.bits.chgdat == 0x01)\r
346                                 trace_debug("PMIC IRQ: charger state machine, END-OF-CHARGE state\r\n");\r
347                 }\r
348                 if (apch79.u8 != act8945a->apch79) {\r
349                         printf("PMIC IRQ: APCH79 changed\r\n");\r
350                 }\r
351                 if (apch7a.u8 != act8945a->apch7a) {\r
352                         trace_debug("PMIC IRQ: %s\r\n", _charging_states[apch7a.bits.cstate]);\r
353                 }\r
354         }\r
355 }\r
356 \r
357 static void _act8945a_lbo_handler(uint32_t group, uint32_t status, void* user_arg)\r
358 {\r
359         struct _act8945a *act8945a = (struct _act8945a*)user_arg;\r
360 \r
361         if (status & act8945a->desc.pin_lbo.mask) {\r
362                 trace_debug("PMIC LBO: Low Battery Output\r\n");\r
363                 if(     act8945a->lbo_count++ >= 10)\r
364                         pio_disable_it(&act8945a->desc.pin_lbo);\r
365         }\r
366 }\r
367 \r
368 // Enable interrupt on nIRQ pin to MPU\r
369 static void _act8945a_enable_interrupt_handlers(struct _act8945a *act8945a)\r
370 {\r
371         /* Configure PMIC line interrupts. */\r
372         pio_configure_it(&act8945a->desc.pin_irq);\r
373         pio_add_handler_to_group(act8945a->desc.pin_irq.group,\r
374                                  act8945a->desc.pin_irq.mask,\r
375                                  &_act8945a_irq_handler,\r
376                                  act8945a);\r
377         pio_enable_it(&act8945a->desc.pin_irq);\r
378 \r
379         /* Configure LBO line interrupts. */\r
380         act8945a->lbo_count = 0;\r
381         pio_configure_it(&act8945a->desc.pin_lbo);\r
382         pio_add_handler_to_group(act8945a->desc.pin_lbo.group,\r
383                                  act8945a->desc.pin_lbo.mask,\r
384                                  &_act8945a_lbo_handler,\r
385                                  act8945a);\r
386         pio_enable_it(&act8945a->desc.pin_lbo);\r
387 }\r
388 \r
389 static uint16_t _act8945a_convert_voltage_setting(uint8_t setting)\r
390 {\r
391         uint8_t mul20, mul53;\r
392 \r
393         mul20 = (setting & 0x07) >> 0;\r
394         mul53 = (setting & 0x38) >> 3;\r
395 \r
396         if (setting <= 0x17)\r
397                 return (uint16_t)(1000 * (0.6 + (0.2 * mul53) + (0.025 * mul20)));\r
398         else if (setting <= 0x2F)\r
399                 return (uint16_t)(1000 * (1.2 + (0.4 * (mul53 - 3)) + (0.050 * mul20)));\r
400         else\r
401                 return (uint16_t)(1000 * (2.4 + (0.8 * (mul53 - 6)) + (0.1 * mul20)));\r
402 }\r
403 \r
404 /*------------------------------------------------------------------------------\r
405  *         Exported functions\r
406  *----------------------------------------------------------------------------*/\r
407 \r
408 bool act8945a_configure(struct _act8945a *act8945a, struct _twi_desc *twid)\r
409 {\r
410         uint8_t data = 0;\r
411 \r
412         act8945a->twid = twid;\r
413         twid_configure(twid);\r
414 \r
415         pio_configure(&act8945a->desc.pin_chglev, 1);\r
416         pio_configure(&act8945a->desc.pin_irq, 1);\r
417         pio_configure(&act8945a->desc.pin_lbo, 1);\r
418 \r
419         if (!_act8945a_read_reg(act8945a, IADDR_SYS0, &data))\r
420                 return false;\r
421 \r
422         /* Set Charge Level */\r
423         act8945a_set_charge_level(act8945a, ACT8945A_CHARGE_LEVEL_450MA);\r
424 \r
425         /* Set level interrupt */\r
426         act8945a_disable_all_apch_interrupts(act8945a);\r
427         act8945a_configure_apch_interrupt(act8945a, CHARGE_STATE_INTO_EOC_STATE, true);\r
428         act8945a_configure_apch_interrupt(act8945a, CHARGE_STATE_OUT_EOC_STATE, true);\r
429         act8945a_configure_apch_interrupt(act8945a, PRECHARGE_TIME_OUT, true);\r
430         act8945a_configure_apch_interrupt(act8945a, TOTAL_CHARGE_TIME_OUT, true);\r
431         act8945a_enable_system_voltage_level_interrupt(act8945a, true);\r
432 \r
433         /* Update cached register values */\r
434         if (!_act8945a_update_cached_registers(act8945a))\r
435                 return false;\r
436 \r
437         act8945a_enable_regulator_fault_interrupt(act8945a, 1, true);\r
438         act8945a_enable_regulator_fault_interrupt(act8945a, 5, true);\r
439 \r
440         /* Enable interrupts */\r
441         _act8945a_enable_interrupt_handlers(act8945a);\r
442 \r
443         return true;\r
444 }\r
445 \r
446 // Charge Current Selection Input\r
447 // In USB-Mode: CHGLEV = 1 -> I charge 450mA\r
448 //              CHGLEV = 0 -> I charge 100mA\r
449 void act8945a_set_charge_level(struct _act8945a *act8945a,\r
450                 enum _act8945a_charge_level level)\r
451 {\r
452         switch (level) {\r
453         case ACT8945A_CHARGE_LEVEL_100MA:\r
454                 pio_clear(&act8945a->desc.pin_chglev);\r
455                 trace_debug("Charge Level: 100mA\r\n");\r
456                 break;\r
457         case ACT8945A_CHARGE_LEVEL_450MA:\r
458                 pio_set(&act8945a->desc.pin_chglev);\r
459                 trace_debug("Charge Level: 450mA\r\n");\r
460                 break;\r
461         default:\r
462                 trace_warning("Invalid charge level requested: %d\r\n",\r
463                                 (int)level)\r
464                 break;\r
465         }\r
466 }\r
467 \r
468 // Set or Clear an APCH interrupt\r
469 // Set bit to 1 enable interrupt,\r
470 // Clear bit to 0 to disable interrupt\r
471 bool act8945a_configure_apch_interrupt(struct _act8945a *act8945a,\r
472                 enum _act8945a_interrupt interrupt, bool enable)\r
473 {\r
474         bool status;\r
475         union _apch_78 apch78;\r
476         union _apch_79 apch79;\r
477 \r
478         if (!_act8945a_read_reg(act8945a, IADDR_APCH_78, &apch78.u8) ||\r
479                 !_act8945a_read_reg(act8945a, IADDR_APCH_79, &apch79.u8))\r
480                 return false;\r
481 \r
482         switch (interrupt)\r
483         {\r
484         // Interrupt generated any time the input supply is disconnected when\r
485         // INSTAT[] bit is set to 1 and the INDIS[] bit is set to 1.\r
486         case INPUT_VOLTAGE_OUT_VALID_RANGE: // Interrupt\r
487                 apch78.bits.instat = enable ? 1 : 0;\r
488                 apch79.bits.indis = enable ? 1 : 0;\r
489                 break;\r
490 \r
491         // Interrupt generated any time the input supply is connected when\r
492         // INSTAT[] bit is set to 1 and the INCON[] bit is set to 1.\r
493         case INPUT_VOLTAGE_INTO_VALID_RANGE:\r
494                 apch78.bits.instat = enable ? 1 : 0;\r
495                 apch79.bits.incon = enable ? 1 : 0;\r
496                 break;\r
497 \r
498         // Interrupts based upon the status of the battery temperature.\r
499         // Set the TEMPOUT[] bit to 1 and TEMPSTAT[] bit to 1 to generate\r
500         // an interrupt when battery temperature goes out of the valid\r
501         // temperature range.\r
502         case BATTERY_TEMPERATURE_OUT_RANGE:\r
503                 apch78.bits.tempstat = enable ? 1 : 0;\r
504                 apch79.bits.tempout = enable ? 1 : 0;\r
505                 break;\r
506 \r
507         // Interrupts based upon the status of the battery temperature.\r
508         // Set the TEMPIN[] bit to 1 and TEMPSTAT[] bit to 1 to generate\r
509         // an interrupt when battery temperature returns to the valid range.\r
510         case BATTERY_TEMPERATURE_INTO_RANGE:\r
511                 apch78.bits.tempstat = enable ? 1 : 0;\r
512                 apch79.bits.tempin = enable ? 1 : 0;\r
513                 break;\r
514 \r
515         // Interrupt when the charger state machine goes into the\r
516         // END-OF-CHARGE (EOC). Set CHGEOCIN[] bit to 1 and CHGSTAT[] bit\r
517         // to 1 to generate an interrupt when the charger state machine goes\r
518         // into the END-OF-CHARGE (EOC)state.\r
519         case CHARGE_STATE_INTO_EOC_STATE:\r
520                 apch78.bits.chgstat = enable ? 1 : 0;\r
521                 apch79.bits.chgeocin = enable ? 1 : 0;\r
522                 break;\r
523 \r
524         // Interrupt when the charger state machine exit the\r
525         // END-OF-CHARGE (EOC). Set CHGEOCOUT[] bit to 1 and CHGSTAT[] bit\r
526         // to 1 to generate an interrupt when the charger state machine exits\r
527         // the EOC state.\r
528         case CHARGE_STATE_OUT_EOC_STATE:\r
529                 apch78.bits.chgstat = enable ? 1 : 0;\r
530                 apch79.bits.chgeocout = enable ? 1 : 0;\r
531                 break;\r
532 \r
533         // Interrupts based upon the status of the charge timers.\r
534         // Set the TIMRPRE[] bit to 1 and TIMRSTAT[] bit to 1 to generate an\r
535         // interrupt when the Precondition Timer expires.\r
536         case PRECHARGE_TIME_OUT:\r
537                 apch78.bits.timrstat = enable ? 1 : 0;\r
538                 apch79.bits.timrpre = enable ? 1 : 0;\r
539                 break;\r
540 \r
541         // Set the TIMRTOT[] bit to 1 and TIMRSTAT[] bit to 1 to generate an\r
542         // interrupt when the Total-Charge Timer expires.\r
543         case TOTAL_CHARGE_TIME_OUT:\r
544                 apch78.bits.timrstat = enable ? 1 : 0;\r
545                 apch79.bits.timrtot = enable ? 1 : 0;\r
546                 break;\r
547 \r
548         default:\r
549                 trace_warning("Unknown interrupt %d\r\n", interrupt);\r
550                 return false;\r
551         }\r
552 \r
553         // Write configuration to registers\r
554         status = _act8945a_write_reg(act8945a, IADDR_APCH_78, apch78.u8);\r
555         status |= _act8945a_write_reg(act8945a, IADDR_APCH_79, apch79.u8);\r
556         return status;\r
557 \r
558 }\r
559 \r
560 // Disable all interrupt from APCH\r
561 bool act8945a_disable_all_apch_interrupts(struct _act8945a *act8945a)\r
562 {\r
563         return act8945a_configure_apch_interrupt(act8945a, CHARGE_STATE_OUT_EOC_STATE, false) &&\r
564                 act8945a_configure_apch_interrupt(act8945a, INPUT_VOLTAGE_OUT_VALID_RANGE, false) &&\r
565                 act8945a_configure_apch_interrupt(act8945a, BATTERY_TEMPERATURE_OUT_RANGE, false) &&\r
566                 act8945a_configure_apch_interrupt(act8945a, PRECHARGE_TIME_OUT, false) &&\r
567                 act8945a_configure_apch_interrupt(act8945a, CHARGE_STATE_INTO_EOC_STATE, false) &&\r
568                 act8945a_configure_apch_interrupt(act8945a, INPUT_VOLTAGE_INTO_VALID_RANGE, false) &&\r
569                 act8945a_configure_apch_interrupt(act8945a, BATTERY_TEMPERATURE_INTO_RANGE, false) &&\r
570                 act8945a_configure_apch_interrupt(act8945a, TOTAL_CHARGE_TIME_OUT, false);\r
571 }\r
572 \r
573 extern bool act8945a_set_system_voltage_detect_threshold(struct _act8945a *act8945a,\r
574                 uint16_t threshold)\r
575 {\r
576         union _sys0 sys0;\r
577 \r
578         if (threshold < 2300 || threshold > 3800)\r
579                 return false;\r
580         if (!_act8945a_read_reg(act8945a, IADDR_SYS0, &sys0.u8))\r
581                 return false;\r
582         sys0.bits.syslev = (threshold - 2300) / 100;\r
583         return _act8945a_write_reg(act8945a, IADDR_SYS0, sys0.u8);\r
584 }\r
585 \r
586 bool act8945a_enable_system_voltage_level_interrupt(struct _act8945a *act8945a,\r
587                 bool enable)\r
588 {\r
589         union _sys0 sys0;\r
590 \r
591         if (!_act8945a_read_reg(act8945a, IADDR_SYS0, &sys0.u8))\r
592                 return false;\r
593         sys0.bits.nsyslevmsk = enable ? 1 : 0;\r
594 \r
595         sys0.bits.nsysmode = 1; //*************\r
596 \r
597         return _act8945a_write_reg(act8945a, IADDR_SYS0, sys0.u8);\r
598 }\r
599 \r
600 bool act8945a_set_regulator_voltage(struct _act8945a *act8945a,\r
601                 uint8_t reg, uint16_t vout)\r
602 {\r
603         // minimum is 600mV\r
604         if (vout < 600) {\r
605                 trace_warning("Cannot set regulator %d voltage to %dmV, using 600mV instead\r\n", reg, vout);\r
606                 vout = 600;\r
607         }\r
608         // maximum is 3900mV\r
609         if (vout > 3900) {\r
610                 trace_warning("Cannot set regulator %d voltage to %dmV, using 3900mV instead\r\n", reg, vout);\r
611                 vout = 3900;\r
612         }\r
613 \r
614         // can only set voltage for regulators 4 to 7\r
615         if (reg < 4 || reg > 7) {\r
616                 trace_error("Cannot change voltage of regulator %d\r\n", reg);\r
617                 return false;\r
618         };\r
619 \r
620         uint8_t value = 0;\r
621         if (vout < 1200) {\r
622                 value = (vout - 600) / 25;\r
623         } else if (vout < 2400) {\r
624                 value = 0x18 + (vout - 1200) / 50;\r
625         } else if (vout <= 3900) {\r
626                 value = 0x30 + (vout - 2400) / 100;\r
627         }\r
628 \r
629         uint32_t iaddr = _iaddr_reg[reg - 1];\r
630         return _act8945a_write_reg(act8945a, iaddr, value & 0x3f);\r
631 }\r
632 \r
633 bool act8945a_enable_regulator(struct _act8945a *act8945a,\r
634                 uint8_t reg, bool enable)\r
635 {\r
636         if (reg >= 1 && reg <= 3) {\r
637                 union _ctrl1 ctrl1;\r
638                 uint32_t iaddr = _iaddr_reg[reg - 1] + 1;\r
639 \r
640                 if (!_act8945a_read_reg(act8945a, iaddr, &ctrl1.u8))\r
641                         return false;\r
642 \r
643                 ctrl1.bits.on = enable ? 1 : 0;\r
644 \r
645                 if (!_act8945a_write_reg(act8945a, iaddr, ctrl1.u8))\r
646                         return false;\r
647         } else if (reg >= 4 && reg <= 7) {\r
648                 union _ctrl2 ctrl2;\r
649                 uint32_t iaddr = _iaddr_reg[reg - 1] + 1;\r
650 \r
651                 if (!_act8945a_read_reg(act8945a, iaddr, &ctrl2.u8))\r
652                         return false;\r
653 \r
654                 ctrl2.bits.on = enable ? 1 : 0;\r
655 \r
656                 if (!_act8945a_write_reg(act8945a, iaddr, ctrl2.u8))\r
657                         return false;\r
658         } else {\r
659                 trace_error("Invalid regulator number %d\r\n", reg);\r
660                 return false;\r
661         }\r
662 \r
663         return true;\r
664 }\r
665 \r
666 bool act8945a_enable_regulator_fault_interrupt(struct _act8945a *act8945a,\r
667                 uint8_t reg, bool enable)\r
668 {\r
669         if (reg >= 1 && reg <= 3) {\r
670                 union _ctrl1 ctrl1;\r
671                 uint8_t iaddr = (_iaddr_reg[reg-1]) + 2;\r
672 \r
673 \r
674                 if (!_act8945a_read_reg(act8945a, iaddr, &ctrl1.u8))\r
675                         return false;\r
676 \r
677                 ctrl1.bits.nfltmsk = enable ? 1 : 0;\r
678 \r
679                 if (!_act8945a_write_reg(act8945a, iaddr, ctrl1.u8))\r
680                         return false;\r
681         } else if (reg >= 4 && reg <= 7) {\r
682                 union _ctrl2 ctrl2;\r
683                 uint8_t iaddr = (_iaddr_reg[reg-1]) + 1;\r
684 \r
685                 if (!_act8945a_read_reg(act8945a, iaddr, &ctrl2.u8))\r
686                         return false;\r
687 \r
688                 ctrl2.bits.nfltmsk = enable ? 1 : 0;\r
689 \r
690                 if (!_act8945a_write_reg(act8945a, iaddr, ctrl2.u8))\r
691                         return false;\r
692         } else {\r
693                 trace_error("Invalid regulator number %d\r\n", reg);\r
694                 return false;\r
695         }\r
696 \r
697         return true;\r
698 }\r
699 \r
700 extern bool act8945a_get_lbo_pin_state(struct _act8945a *act8945a)\r
701 {\r
702         return pio_get(&act8945a->desc.pin_lbo) ? true : false;\r
703 }\r
704 \r
705 \r
706 \r
707 extern void act8945a_display_voltage_settings(struct _act8945a *act8945a)\r
708 {\r
709         int reg;\r
710 \r
711         trace_info_wp("\r\n-- ACT8945A - Voltage Settings & State --\r\n");\r
712 \r
713         for (reg = 0; reg < NUM_REGULATORS; reg++)\r
714         {\r
715                 uint8_t iadd_reg, setting, ctrl;\r
716                 uint16_t u;\r
717 \r
718                 /* Warning VSEL state */\r
719                 iadd_reg = _iaddr_reg[reg];\r
720                 if( (iadd_reg < IADDR_REG4) && (STATE_VSEL == 1) )\r
721                         iadd_reg ++;\r
722 \r
723                 if (!_act8945a_read_reg(act8945a, iadd_reg, &setting))\r
724                         return;\r
725 \r
726                 if (!_act8945a_read_reg(act8945a, iadd_reg + 1, &ctrl))\r
727                         return;\r
728 \r
729                 u = _act8945a_convert_voltage_setting(setting);\r
730                 trace_info_wp(" - VOUT_%d (0x%02x) = %dmV", reg + 1, ctrl, u);\r
731                 if (reg <= 3) {\r
732                         union _ctrl1 *ctrl1 = (union _ctrl1*)&ctrl;\r
733                         trace_info_wp(" %s", ctrl1->bits.on ? "on" : "off");\r
734                         trace_info_wp(" %s", ctrl1->bits.phase ? "180" : "osc");\r
735                         trace_info_wp(" %s", ctrl1->bits.mode ? "pwm" : "pow-saving");\r
736                         trace_info_wp(" delay:0x%02x", ctrl1->bits.delay);\r
737                         trace_info_wp(" %s", ctrl1->bits.nfltmsk ? "en" : "dis");\r
738                         trace_info_wp(" %s", ctrl1->bits.ok ? "OK" : "<tresh");\r
739                 } else {\r
740                         union _ctrl2 *ctrl2 = (union _ctrl2*)&ctrl;\r
741                         trace_info_wp(" %s", ctrl2->bits.on ? "on": "off");\r
742                         trace_info_wp(" %s", ctrl2->bits.dis ? "off" : "on");\r
743                         trace_info_wp(" %s", ctrl2->bits.lowiq ? "normal" : "low-power");\r
744                         trace_info_wp(" delay:0x%02x", ctrl2->bits.delay);\r
745                         trace_info_wp(" %s", ctrl2->bits.nfltmsk ? "en" : "dis");\r
746                         trace_info_wp(" %s", ctrl2->bits.ok ? "OK" : "<tresh");\r
747                 }\r
748                 trace_info_wp("\r\n");\r
749         }\r
750 \r
751         union _sys0 sys0;\r
752         if (!_act8945a_read_reg(act8945a, IADDR_SYS0, &sys0.u8))\r
753                 return;\r
754         trace_info_wp(" - SYSLEV Failing Treshold (0x%02x) = %dmV\r\n", sys0.u8,\r
755                         2300 + sys0.bits.syslev * 100);\r
756 }\r
757 \r
758 void act8945a_dump_registers(struct _act8945a *act8945a)\r
759 {\r
760         uint8_t reg, data, mask, i;\r
761 \r
762 \r
763         trace_info_wp("\r\n-- ACT8945A - Registers Dump --\r\n");\r
764         for (reg = 0; reg < ARRAY_SIZE(_regs); reg++) {\r
765                 if (!_act8945a_read_reg(act8945a, _regs[reg].iaddr, &data))\r
766                         return;\r
767                 trace_info_wp(" - %s: 0x%02X  b:", _regs[reg].name, data);\r
768                 mask = 0x80;\r
769                 for (i=0; i<8; i++, mask>>=1) {\r
770                         printf ("%x", (data&mask) ? 1 : 0);\r
771                 }\r
772                 trace_info_wp("\r\n");\r
773         }\r
774         trace_info_wp("\r\n");\r
775 }\r
776 \r
777 void act8945a_display_apch_registers(struct _act8945a *act8945a)\r
778 {\r
779         union _apch_71 apch71;\r
780         union _apch_78 apch78;\r
781         union _apch_79 apch79;\r
782         union _apch_7a apch7a;\r
783 \r
784         trace_info_wp("\r\n-- ACT8945A - APCH Registers --\r\n");\r
785 \r
786 //      if (!_act8945a_read_reg(act8945a, IADDR_APCH_70, &data))\r
787 //              return;\r
788 //      trace_info_wp(" - APCH @0x70: 0x%02x (reserved)\r\n", data);\r
789 \r
790         if (!_act8945a_read_reg(act8945a, IADDR_APCH_71, &apch71.u8))\r
791                 return;\r
792         trace_info_wp(" - APCH @0x71: 0x%02x\r\n", apch71.u8);\r
793         trace_info_wp("     Charge Suspend Control Input:          %x\r\n",\r
794                         apch71.bits.suschg);\r
795         trace_info_wp("     Total Charge Time-out Selection:       %x\r\n",\r
796                         apch71.bits.tottimo);\r
797         trace_info_wp("     Precondition Charge Time-out Sel:      %x\r\n",\r
798                         apch71.bits.pretimo);\r
799         trace_info_wp("     Input Over-Volt Prot.Threshold Sel:    %x (%s)\r\n",\r
800                         apch71.bits.ovpset, _ovp_setting[apch71.bits.ovpset]);\r
801 \r
802         if (!_act8945a_read_reg(act8945a, IADDR_APCH_78, &apch78.u8))\r
803                 return;\r
804         trace_info_wp(" - APCH @0x78: 0x%02x\r\n", apch78.u8);\r
805         trace_info_wp("     Charge Time-out Interrupt Status:      %x\r\n",\r
806                         apch78.bits.timrstat);\r
807         trace_info_wp("     Battery Temperature Interrupt Status:  %x\r\n",\r
808                         apch78.bits.tempstat);\r
809         trace_info_wp("     Input Voltage Interrupt Status:        %x\r\n",\r
810                         apch78.bits.instat);\r
811         trace_info_wp("     Charge State Interrupt Status:         %x\r\n",\r
812                         apch78.bits.chgstat);\r
813         trace_info_wp("     Charge Timer Status                    %x\r\n",\r
814                         apch78.bits.timrdat);\r
815         trace_info_wp("     Temperature Status                     %x\r\n",\r
816                         apch78.bits.tempdat);\r
817         trace_info_wp("     Input Voltage Status                   %x\r\n",\r
818                         apch78.bits.indat);\r
819         trace_info_wp("     Charge State Machine Status            %x\r\n",\r
820                         apch78.bits.chgdat);\r
821 \r
822         if (!_act8945a_read_reg(act8945a, IADDR_APCH_79, &apch79.u8))\r
823                 return;\r
824         trace_info_wp(" - APCH @0x79: 0x%02x\r\n", apch79.u8);\r
825         trace_info_wp("     Total Charge Time-out Int Control:     %x\r\n",\r
826                         apch79.bits.timrtot);\r
827         trace_info_wp("     Batt.Temp.Int.Ctrl into valid range:   %x\r\n",\r
828                         apch79.bits.tempin);\r
829         trace_info_wp("     Inp.Voltage Int.Ctrl into valid range: %x\r\n",\r
830                         apch79.bits.incon);\r
831         trace_info_wp("     Charge State Int Ctrl into EOC state:  %x\r\n",\r
832                         apch79.bits.chgeocin);\r
833         trace_info_wp("     Precharge Time-out Int Ctrl:           %x\r\n",\r
834                         apch79.bits.timrpre);\r
835         trace_info_wp("     Batt.Temp.Int.Ctrl. out valid range:   %x\r\n",\r
836                         apch79.bits.tempout);\r
837         trace_info_wp("     Inp.Voltage Int.Ctrl. out valid range: %x\r\n",\r
838                         apch79.bits.indis);\r
839         trace_info_wp("     Charge State Int.Ctrl. out EOC state:  %x\r\n",\r
840                         apch79.bits.chgeocout);\r
841 \r
842         if (!_act8945a_read_reg(act8945a, IADDR_APCH_7A, &apch7a.u8))\r
843                 return;\r
844         trace_info_wp(" - APCH @0x7a: 0x%02x\r\n", apch7a.u8);\r
845         trace_info_wp("     Charge State:                          %x (%s)\r\n",\r
846                         apch7a.bits.cstate, _charging_states[apch7a.bits.cstate]);\r
847         trace_info_wp("     ACIN Status:                           %x\r\n",\r
848                         apch7a.bits.acinstat);\r
849 }\r
850 \r
851 void act8945a_display_system_registers(struct _act8945a *act8945a)\r
852 {\r
853         union _sys0 sys0;\r
854         union _sys1 sys1;\r
855 \r
856         trace_info_wp("\r\n-- ACT8945A - System Registers --\r\n");\r
857 \r
858         if (!_act8945a_read_reg(act8945a, IADDR_SYS0, &sys0.u8))\r
859                 return;\r
860         trace_info_wp(" - SYS0 @0x00: 0x%02x\r\n", sys0.u8);\r
861         trace_info_wp("     Reset Timer Setting:                   %s\r\n",\r
862                         sys0.bits.trst ? "64ms" : "260ms");\r
863         trace_info_wp("     SYSLEV Mode Select:                    %s\r\n",\r
864                         sys0.bits.nsysmode ?"int" : "shutdown");\r
865         trace_info_wp("     System Voltage Level Int.Mask:         %s\r\n",\r
866                         sys0.bits.nsyslevmsk ?"int" : "noint");\r
867         trace_info_wp("     System Voltage Status:                 %s\r\n",\r
868                         sys0.bits.nsysstat ? "vsys<syslev" : "vsys>syslev");\r
869         trace_info_wp("     SYSLEV Failing Treshold value:         %dmV\r\n",\r
870                         2300 + sys0.bits.syslev * 100);\r
871 \r
872         if (!_act8945a_read_reg(act8945a, IADDR_SYS1, &sys1.u8))\r
873                 return;\r
874         trace_info_wp(" - SYS1 @0x01: 0x%02x\r\n", sys1.u8);\r
875         trace_info_wp("     Master Off Ctrl, All regul:            %s\r\n",\r
876                         sys1.bits.mstroff ? "off" : "on");\r
877         trace_info_wp("     Scratchpad Bits, free user:            %x\r\n",\r
878                         sys1.bits.scratch);\r
879 }\r
880 \r
881 void act8945a_display_charge_state(struct _act8945a *act8945a)\r
882 {\r
883         union _apch_7a apch7a;\r
884         if (!_act8945a_read_reg(act8945a, IADDR_APCH_7A, &apch7a.u8)) return;\r
885 \r
886         if (act8945a->apch7a != apch7a.u8) {\r
887                 trace_info_wp(" Charge State: %x (%s)\r\n", apch7a.bits.cstate, _charging_states[apch7a.bits.cstate]);\r
888                 act8945a->apch7a = apch7a.u8;\r
889         }\r
890 }