summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c97
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h21
-rw-r--r--firmware/target/arm/iriver/h10/power-h10.c4
-rw-r--r--firmware/target/arm/olympus/mrobe-100/power-mr100.c4
-rwxr-xr-xfirmware/target/arm/philips/hdd1630/power-hdd1630.c4
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c17
-rw-r--r--firmware/target/arm/tatung/tpj1022/power-tpj1022.c4
-rw-r--r--firmware/target/sh/archos/recorder/power-recorder.c12
-rw-r--r--firmware/target/sh/archos/recorder/powermgmt-recorder.c437
-rw-r--r--firmware/target/sh/archos/recorder/powermgmt-target.h101
10 files changed, 594 insertions, 107 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
index fa9c7b045f..b4a6c61fbb 100644
--- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
@@ -168,7 +168,7 @@ int battery_adc_temp(void)
168/* All code has a preference for the main charger being connected over 168/* All code has a preference for the main charger being connected over
169 * USB. USB is considered in the algorithm only if it is the sole source. */ 169 * USB. USB is considered in the algorithm only if it is the sole source. */
170static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ 170static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */
171static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */ 171static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */
172static int charger_total_timer = 0; /* Total allowed charging time */ 172static int charger_total_timer = 0; /* Total allowed charging time */
173static int icharger_ave = 0; /* Filtered charging current */ 173static int icharger_ave = 0; /* Filtered charging current */
174static bool charger_close = false; /* Shutdown notification */ 174static bool charger_close = false; /* Shutdown notification */
@@ -181,7 +181,7 @@ static int autorecharge_counter = 0 ; /* Battery < threshold debounce */
181static int chgcurr_timer = 0; /* Countdown to CHGCURR error */ 181static int chgcurr_timer = 0; /* Countdown to CHGCURR error */
182#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */ 182#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */
183#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */ 183#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */
184#define CHGCURR_TIMEOUT (2*2) /* 2s debounce */ 184#define CHGCURR_TIMEOUT (4*2) /* 4s debounce */
185 185
186/* Temperature monitoring */ 186/* Temperature monitoring */
187static enum 187static enum
@@ -217,7 +217,7 @@ static bool charger_current_filter_step(void)
217/* Return true if the main charger is connected. */ 217/* Return true if the main charger is connected. */
218static bool main_charger_connected(void) 218static bool main_charger_connected(void)
219{ 219{
220 return (power_status & 220 return (last_inputs &
221 POWER_INPUT_MAIN_CHARGER & 221 POWER_INPUT_MAIN_CHARGER &
222 POWER_INPUT_CHARGER) != 0; 222 POWER_INPUT_CHARGER) != 0;
223} 223}
@@ -233,16 +233,14 @@ static unsigned int auto_recharge_voltage(void)
233 return BATT_USB_VAUTO_RECHARGE; 233 return BATT_USB_VAUTO_RECHARGE;
234} 234}
235 235
236#ifndef NO_LOW_BATTERY_SHUTDOWN
237/* Return greater of supply (BP) or filtered battery voltage. */ 236/* Return greater of supply (BP) or filtered battery voltage. */
238static unsigned int input_millivolts(void) 237unsigned int input_millivolts(void)
239{ 238{
240 unsigned int app_millivolts = application_supply_adc_voltage(); 239 unsigned int app_millivolts = application_supply_adc_voltage();
241 unsigned int bat_millivolts = battery_voltage(); 240 unsigned int bat_millivolts = battery_voltage();
242 241
243 return MAX(app_millivolts, bat_millivolts); 242 return MAX(app_millivolts, bat_millivolts);
244} 243}
245#endif
246 244
247/* Get smoothed readings for initializing filtered data. */ 245/* Get smoothed readings for initializing filtered data. */
248static int stat_battery_reading(int type) 246static int stat_battery_reading(int type)
@@ -292,7 +290,7 @@ static bool update_filtered_battery_voltage(void)
292 290
293 if (millivolts != INT_MIN) 291 if (millivolts != INT_MIN)
294 { 292 {
295 set_filtered_battery_voltage(millivolts); 293 reset_battery_filter(millivolts);
296 return true; 294 return true;
297 } 295 }
298 296
@@ -357,13 +355,13 @@ static bool adjust_charger_current(void)
357 int usb_select; 355 int usb_select;
358 uint32_t i; 356 uint32_t i;
359 357
360 usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB) 358 usb_select = ((last_inputs & POWER_INPUT) == POWER_INPUT_USB)
361 ? 1 : 0; 359 ? 1 : 0;
362 360
363 if (charge_state == DISCHARGING && usb_select == 1) 361 if (charge_state == DISCHARGING && usb_select == 1)
364 { 362 {
365 /* USB-only, DISCHARGING, = maintaining battery */ 363 /* USB-only, DISCHARGING, = maintaining battery */
366 int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1; 364 int select = (last_inputs & POWER_INPUT_CHARGER) ? 0 : 1;
367 charger_setting = charger_bits[CHARGING+1][select]; 365 charger_setting = charger_bits[CHARGING+1][select];
368 } 366 }
369 else 367 else
@@ -458,7 +456,7 @@ static bool charging_ok(void)
458 if (ok) 456 if (ok)
459 { 457 {
460 /* Is the battery even connected? */ 458 /* Is the battery even connected? */
461 ok = (power_status & POWER_INPUT_BATTERY) != 0; 459 ok = (last_inputs & POWER_INPUT_BATTERY) != 0;
462 } 460 }
463 461
464 if (ok) 462 if (ok)
@@ -591,20 +589,6 @@ void powermgmt_init_target(void)
591#endif 589#endif
592} 590}
593 591
594/* Returns CHARGING or DISCHARGING since that's all we really do. */
595int powermgmt_filter_charge_state(void)
596{
597 switch(charge_state)
598 {
599 case TRICKLE:
600 case TOPOFF:
601 case CHARGING:
602 return CHARGING;
603 default:
604 return DISCHARGING;
605 }
606}
607
608/* Returns true if the unit is charging the batteries. */ 592/* Returns true if the unit is charging the batteries. */
609bool charging_state(void) 593bool charging_state(void)
610{ 594{
@@ -625,24 +609,6 @@ int battery_charge_current(void)
625 return icharger_ave / ICHARGER_AVE_SAMPLES; 609 return icharger_ave / ICHARGER_AVE_SAMPLES;
626} 610}
627 611
628bool query_force_shutdown(void)
629{
630#ifndef NO_LOW_BATTERY_SHUTDOWN
631 return input_millivolts() < battery_level_shutoff[0];
632#else
633 return false;
634#endif
635}
636
637bool battery_level_safe(void)
638{
639#ifndef NO_LOW_BATTERY_SHUTDOWN
640 return input_millivolts() > battery_level_dangerous[0];
641#else
642 return true;
643#endif
644}
645
646static void charger_plugged(void) 612static void charger_plugged(void)
647{ 613{
648 adc_enable_channel(ADC_BATTERY_TEMP, true); 614 adc_enable_channel(ADC_BATTERY_TEMP, true);
@@ -662,7 +628,7 @@ static void charger_unplugged(void)
662 } 628 }
663 629
664 /* Might need to reevaluate these bits in charger_none. */ 630 /* Might need to reevaluate these bits in charger_none. */
665 power_status &= ~(POWER_INPUT | POWER_INPUT_CHARGER); 631 last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
666 temp_state = TEMP_STATE_NORMAL; 632 temp_state = TEMP_STATE_NORMAL;
667 autorecharge_counter = 0; 633 autorecharge_counter = 0;
668 chgcurr_timer = 0; 634 chgcurr_timer = 0;
@@ -672,15 +638,11 @@ static void charger_unplugged(void)
672 638
673static void charger_none(void) 639static void charger_none(void)
674{ 640{
675 unsigned int pwr = power_input_status(); 641 unsigned int pwr = power_thread_inputs;
676 642
677 if (power_status != pwr) 643 if (last_inputs != pwr)
678 { 644 {
679 /* If battery switch state changed, reset filter. */ 645 last_inputs = pwr;
680 if ((power_status ^ pwr) & POWER_INPUT_BATTERY)
681 update_filtered_battery_voltage();
682
683 power_status = pwr;
684 646
685 if (charge_state == CHARGE_STATE_DISABLED) 647 if (charge_state == CHARGE_STATE_DISABLED)
686 return; 648 return;
@@ -696,7 +658,7 @@ static void charger_none(void)
696 else 658 else
697 { 659 {
698 charger_unplugged(); 660 charger_unplugged();
699 power_status = pwr; /* Restore status */ 661 last_inputs = pwr; /* Restore status */
700 } 662 }
701 } 663 }
702 else if (charger_setting != 0) 664 else if (charger_setting != 0)
@@ -716,17 +678,13 @@ static void charger_none(void)
716 678
717static void charger_control(void) 679static void charger_control(void)
718{ 680{
719 unsigned int pwr = power_input_status(); 681 unsigned int pwr = power_thread_inputs;
720 682
721 if (power_status != pwr) 683 if (last_inputs != pwr)
722 { 684 {
723 unsigned int changed = power_status ^ pwr; 685 unsigned int changed = last_inputs ^ pwr;
724 686
725 power_status = pwr; 687 last_inputs = pwr;
726
727 /* If battery switch state changed, reset filter. */
728 if (changed & POWER_INPUT_BATTERY)
729 update_filtered_battery_voltage();
730 688
731 if (charger_setting != 0) 689 if (charger_setting != 0)
732 charger_setting = CHARGER_ADJUST; 690 charger_setting = CHARGER_ADJUST;
@@ -771,12 +729,11 @@ static void charger_control(void)
771 { 729 {
772 /* Battery voltage may have dropped and a charge cycle should 730 /* Battery voltage may have dropped and a charge cycle should
773 * start again. Debounced. */ 731 * start again. Debounced. */
774 if (autorecharge_counter < 0) 732 if (autorecharge_counter < 0 &&
733 battery_adc_voltage() < BATT_FULL_VOLTAGE)
775 { 734 {
776 /* Try starting a cycle now regardless of battery level to 735 /* Try starting a cycle now if battery isn't already topped
777 * allow user to ensure the battery is topped off. It 736 * off to allow user to ensure the battery is full. */
778 * will soon turn off if already full. */
779 autorecharge_counter = 0;
780 } 737 }
781 else if (battery_voltage() > auto_recharge_voltage()) 738 else if (battery_voltage() > auto_recharge_voltage())
782 { 739 {
@@ -791,6 +748,8 @@ static void charger_control(void)
791 break; 748 break;
792 } 749 }
793 750
751 autorecharge_counter = 0;
752
794 charging_set_thread_priority(true); 753 charging_set_thread_priority(true);
795 754
796 if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE) 755 if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
@@ -858,10 +817,12 @@ static void charger_control(void)
858} 817}
859 818
860/* Main charging algorithm - called from powermgmt.c */ 819/* Main charging algorithm - called from powermgmt.c */
861void charging_algorithm_small_step(void) 820void charging_algorithm_step(void)
862{ 821{
822#ifdef IMX31_ALLOW_CHARGING
863 if (service_wdt) 823 if (service_wdt)
864 watchdog_service(); 824 watchdog_service();
825#endif
865 826
866 /* Switch by input state */ 827 /* Switch by input state */
867 switch (charger_input_state) 828 switch (charger_input_state)
@@ -909,12 +870,6 @@ void charging_algorithm_small_step(void)
909 } 870 }
910} 871}
911 872
912void charging_algorithm_big_step(void)
913{
914 /* Sleep for one minute */
915 power_thread_sleep(HZ*60);
916}
917
918/* Disable the charger and prepare for poweroff - called off-thread so we 873/* Disable the charger and prepare for poweroff - called off-thread so we
919 * signal the charging thread to prepare to quit. */ 874 * signal the charging thread to prepare to quit. */
920void charging_algorithm_close(void) 875void charging_algorithm_close(void)
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
index 8ad4af8d18..1b55a4ed24 100644
--- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
@@ -53,6 +53,7 @@
53#define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */ 53#define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */
54#define BATT_VSLOW_CHARGE 3500 /* Lower-current charge mode below 54#define BATT_VSLOW_CHARGE 3500 /* Lower-current charge mode below
55 * this level */ 55 * this level */
56#define BATT_FULL_VOLTAGE 4161 /* Battery already topped */
56#define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */ 57#define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */
57#define BATT_USB_VAUTO_RECHARGE 4000 /* When to cycle with USB only */ 58#define BATT_USB_VAUTO_RECHARGE 4000 /* When to cycle with USB only */
58#define BATT_USB_VSTOP 4140 /* When to "stop" when USB only */ 59#define BATT_USB_VSTOP 4140 /* When to "stop" when USB only */
@@ -92,23 +93,13 @@
92#define BATT_AVE_SAMPLES 32 93#define BATT_AVE_SAMPLES 32
93#define ICHARGER_AVE_SAMPLES 32 94#define ICHARGER_AVE_SAMPLES 32
94 95
96void powermgmt_init_target(void);
97void charging_algorithm_step(void);
98void charging_algorithm_close(void);
99
95/* Provide filtered charge current */ 100/* Provide filtered charge current */
96int battery_charge_current(void); 101int battery_charge_current(void);
97 102
98#ifndef SIMULATOR 103#define CURRENT_MAX_CHG battery_charge_current()
99/* Indicate various functions that require implementation at the target level.
100 * This is because the battery could be low or the battery switch is off but
101 * with the main charger attached which implies safe power for anything. The
102 * true battery reading is always reported for voltage readings and not the
103 * value at the application supply. */
104#define TARGET_QUERY_FORCE_SHUTDOWN
105
106/* For this the application supply is read out if the charger is attached or
107 * the battery read if not (completely hardware selected at BP). */
108#define TARGET_BATTERY_LEVEL_SAFE
109
110/* The state should be adjusted to CHARGING or DISCHARGING */
111#define TARGET_POWERMGMT_FILTER_CHARGE_STATE
112#endif /* SIMULATOR */
113 104
114#endif /* POWERMGMT_TARGET_H */ 105#endif /* POWERMGMT_TARGET_H */
diff --git a/firmware/target/arm/iriver/h10/power-h10.c b/firmware/target/arm/iriver/h10/power-h10.c
index deca3258e2..1a1f6afbfb 100644
--- a/firmware/target/arm/iriver/h10/power-h10.c
+++ b/firmware/target/arm/iriver/h10/power-h10.c
@@ -33,10 +33,6 @@
33#include "logf.h" 33#include "logf.h"
34#include "usb.h" 34#include "usb.h"
35 35
36#if CONFIG_CHARGING == CHARGING_CONTROL
37bool charger_enabled;
38#endif
39
40#if CONFIG_TUNER 36#if CONFIG_TUNER
41 37
42bool tuner_power(bool status) 38bool tuner_power(bool status)
diff --git a/firmware/target/arm/olympus/mrobe-100/power-mr100.c b/firmware/target/arm/olympus/mrobe-100/power-mr100.c
index c3eb96b03c..46b99fbd46 100644
--- a/firmware/target/arm/olympus/mrobe-100/power-mr100.c
+++ b/firmware/target/arm/olympus/mrobe-100/power-mr100.c
@@ -29,10 +29,6 @@
29#include "logf.h" 29#include "logf.h"
30#include "usb.h" 30#include "usb.h"
31 31
32#if CONFIG_CHARGING == CHARGING_CONTROL
33bool charger_enabled;
34#endif
35
36void power_init(void) 32void power_init(void)
37{ 33{
38 /* Enable power-off bit */ 34 /* Enable power-off bit */
diff --git a/firmware/target/arm/philips/hdd1630/power-hdd1630.c b/firmware/target/arm/philips/hdd1630/power-hdd1630.c
index 03a5794791..4e7172ef6f 100755
--- a/firmware/target/arm/philips/hdd1630/power-hdd1630.c
+++ b/firmware/target/arm/philips/hdd1630/power-hdd1630.c
@@ -29,10 +29,6 @@
29#include "logf.h" 29#include "logf.h"
30#include "usb.h" 30#include "usb.h"
31 31
32#if CONFIG_CHARGING == CHARGING_CONTROL
33bool charger_enabled;
34#endif
35
36void power_init(void) 32void power_init(void)
37{ 33{
38 /* power off bit */ 34 /* power off bit */
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c
index 6522e6534b..49f7e2e049 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c
@@ -19,9 +19,10 @@
19 * KIND, either express or implied. 19 * KIND, either express or implied.
20 * 20 *
21 ****************************************************************************/ 21 ****************************************************************************/
22
23#include "config.h" 22#include "config.h"
23#include "system.h"
24#include "adc.h" 24#include "adc.h"
25#include "power.h"
25#include "powermgmt.h" 26#include "powermgmt.h"
26 27
27const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = 28const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
@@ -58,3 +59,17 @@ unsigned int battery_adc_voltage(void)
58 return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; 59 return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
59} 60}
60 61
62unsigned int input_millivolts(void)
63{
64
65 unsigned int batt_millivolts = battery_voltage();
66
67 if ((power_thread_inputs & POWER_INPUT_BATTERY) == 0) {
68 /* Just return a safe value if battery isn't connected */
69 return 4050;
70 }
71
72 return batt_millivolts;
73}
74
75
diff --git a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
index abf5790702..fe5460d609 100644
--- a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
+++ b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
@@ -33,10 +33,6 @@
33#include "logf.h" 33#include "logf.h"
34#include "usb.h" 34#include "usb.h"
35 35
36#if CONFIG_CHARGING == CHARGING_CONTROL
37bool charger_enabled;
38#endif
39
40void power_init(void) 36void power_init(void)
41{ 37{
42} 38}
diff --git a/firmware/target/sh/archos/recorder/power-recorder.c b/firmware/target/sh/archos/recorder/power-recorder.c
index d90c029890..8d7ea5fc51 100644
--- a/firmware/target/sh/archos/recorder/power-recorder.c
+++ b/firmware/target/sh/archos/recorder/power-recorder.c
@@ -25,9 +25,10 @@
25#include "kernel.h" 25#include "kernel.h"
26#include "system.h" 26#include "system.h"
27#include "power.h" 27#include "power.h"
28#include "powermgmt-target.h"
28#include "usb.h" 29#include "usb.h"
29 30
30bool charger_enabled; 31static bool charger_on;
31 32
32void power_init(void) 33void power_init(void)
33{ 34{
@@ -48,13 +49,18 @@ void charger_enable(bool on)
48 if(on) 49 if(on)
49 { 50 {
50 and_b(~0x20, &PBDRL); 51 and_b(~0x20, &PBDRL);
51 charger_enabled = 1;
52 } 52 }
53 else 53 else
54 { 54 {
55 or_b(0x20, &PBDRL); 55 or_b(0x20, &PBDRL);
56 charger_enabled = 0;
57 } 56 }
57
58 charger_on = on;
59}
60
61bool charger_enabled(void)
62{
63 return charger_on;
58} 64}
59 65
60void ide_power_enable(bool on) 66void ide_power_enable(bool on)
diff --git a/firmware/target/sh/archos/recorder/powermgmt-recorder.c b/firmware/target/sh/archos/recorder/powermgmt-recorder.c
index 6de5cc8037..7b1842016c 100644
--- a/firmware/target/sh/archos/recorder/powermgmt-recorder.c
+++ b/firmware/target/sh/archos/recorder/powermgmt-recorder.c
@@ -19,9 +19,13 @@
19 * KIND, either express or implied. 19 * KIND, either express or implied.
20 * 20 *
21 ****************************************************************************/ 21 ****************************************************************************/
22
23#include "config.h" 22#include "config.h"
23#include "system.h"
24#include <sprintf.h>
25#include "debug.h"
26#include "storage.h"
24#include "adc.h" 27#include "adc.h"
28#include "power.h"
25#include "powermgmt.h" 29#include "powermgmt.h"
26 30
27const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = 31const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
@@ -60,3 +64,434 @@ unsigned int battery_adc_voltage(void)
60{ 64{
61 return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; 65 return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
62} 66}
67
68/** Charger control **/
69#ifdef CHARGING_DEBUG_FILE
70#include "file.h"
71#define DEBUG_FILE_NAME "/powermgmt.csv"
72#define DEBUG_MESSAGE_LEN 133
73static char debug_message[DEBUG_MESSAGE_LEN];
74static int fd = -1; /* write debug information to this file */
75static int wrcount = 0;
76#endif /* CHARGING_DEBUG_FILE */
77
78/*
79 * For a complete description of the charging algorithm read
80 * docs/CHARGING_ALGORITHM.
81 */
82int long_delta; /* long term delta battery voltage */
83int short_delta; /* short term delta battery voltage */
84bool disk_activity_last_cycle = false; /* flag set to aid charger time
85 * calculation */
86char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in
87 debug menu */
88 /* percentage at which charging
89 starts */
90int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the
91 charging started or
92 stopped? */
93int powermgmt_last_cycle_level = 0; /* which level had the
94 batteries at this time? */
95int trickle_sec = 0; /* how many seconds should the
96 charger be enabled per
97 minute for trickle
98 charging? */
99int pid_p = 0; /* PID proportional term */
100int pid_i = 0; /* PID integral term */
101
102static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle
103 * voltage level */
104static int charge_max_time_idle = 0; /* max. charging duration, calculated at
105 * beginning of charging */
106static int charge_max_time_now = 0; /* max. charging duration including
107 * hdd activity */
108static int minutes_disk_activity = 0; /* count minutes of hdd use during
109 * charging */
110static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */
111
112#ifdef CHARGING_DEBUG_FILE
113static void debug_file_close(void)
114{
115 if (fd >= 0) {
116 close(fd);
117 fd = -1;
118 }
119}
120
121static void debug_file_log(void)
122{
123 if (usb_inserted()) {
124 /* It is probably too late to close the file but we can try... */
125 debug_file_close();
126 }
127 else if (fd < 0) {
128 fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT);
129
130 if (fd >= 0) {
131 snprintf(debug_message, DEBUG_MESSAGE_LEN,
132 "cycle_min, bat_millivolts, bat_percent, chgr_state"
133 " ,charge_state, pid_p, pid_i, trickle_sec\n");
134 write(fd, debug_message, strlen(debug_message));
135 wrcount = 99; /* force a flush */
136 }
137 }
138 else {
139 snprintf(debug_message, DEBUG_MESSAGE_LEN,
140 "%d, %d, %d, %d, %d, %d, %d, %d\n",
141 powermgmt_last_cycle_startstop_min, battery_voltage(),
142 battery_level(), charger_input_state, charge_state,
143 pid_p, pid_i, trickle_sec);
144 write(fd, debug_message, strlen(debug_message));
145 wrcount++;
146 }
147}
148
149static void debug_file_sync(void)
150{
151 /*
152 * If we have a lot of pending writes or if the disk is spining,
153 * fsync the debug log file.
154 */
155 if (wrcount > 10 || (wrcount > 0 && storage_disk_is_active())) {
156 if (fd >= 0)
157 fsync(fd);
158
159 wrcount = 0;
160 }
161}
162#else /* !CHARGING_DEBUG_FILE */
163#define debug_file_close()
164#define debug_file_log()
165#define debug_file_sync()
166#endif /* CHARGING_DEBUG_FILE */
167
168/*
169 * Do tasks that should be done every step.
170 */
171static void do_frequent_tasks(void)
172{
173 if (storage_disk_is_active()) {
174 /* flag hdd use for charging calculation */
175 disk_activity_last_cycle = true;
176 }
177
178 debug_file_sync();
179}
180
181/*
182 * The charger was just plugged in. If the battery level is
183 * nearly charged, just trickle. If the battery is low, start
184 * a full charge cycle. If the battery level is in between,
185 * top-off and then trickle.
186 */
187static void charger_plugged(void)
188{
189 int battery_percent = battery_level();
190
191 pid_p = 0;
192 pid_i = 0;
193 powermgmt_last_cycle_level = battery_percent;
194 powermgmt_last_cycle_startstop_min = 0;
195
196 snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in");
197
198 if (battery_percent > START_TOPOFF_CHG) {
199
200 if (battery_percent >= START_TRICKLE_CHG) {
201 charge_state = TRICKLE;
202 target_voltage = TRICKLE_VOLTAGE;
203 }
204 else {
205 charge_state = TOPOFF;
206 target_voltage = TOPOFF_VOLTAGE;
207 }
208 }
209 else {
210 /*
211 * Start the charger full strength
212 */
213 int i = CHARGE_MAX_MIN_1500 * get_battery_capacity() / 1500;
214 charge_max_time_idle = i * (100 + 35 - battery_percent) / 100;
215
216 if (charge_max_time_idle > i)
217 charge_max_time_idle = i;
218
219 charge_max_time_now = charge_max_time_idle;
220
221 snprintf(power_message, POWER_MESSAGE_LEN,
222 "ChgAt %d%% max %dm", battery_percent,
223 charge_max_time_now);
224
225 /*
226 * Enable the charger after the max time calc is done,
227 * because battery_level depends on if the charger is
228 * on.
229 */
230 DEBUGF("power: charger inserted and battery"
231 " not full, charging\n");
232 trickle_sec = 60;
233 long_delta = short_delta = 999999;
234 charge_state = CHARGING;
235 }
236}
237
238/*
239 * The charger was just unplugged.
240 */
241static void charger_unplugged(void)
242{
243 DEBUGF("power: charger disconnected, disabling\n");
244
245 charger_enable(false);
246 powermgmt_last_cycle_level = battery_level();
247 powermgmt_last_cycle_startstop_min = 0;
248 trickle_sec = 0;
249 pid_p = 0;
250 pid_i = 0;
251 charge_state = DISCHARGING;
252 snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge");
253}
254
255static void charging_step(void)
256{
257 int i;
258
259 /* alter charge time max length with extra disk use */
260 if (disk_activity_last_cycle) {
261 minutes_disk_activity++;
262 charge_max_time_now = charge_max_time_idle +
263 minutes_disk_activity*2 / 5;
264 disk_activity_last_cycle = false;
265 last_disk_activity = 0;
266 }
267 else {
268 last_disk_activity++;
269 }
270
271 /*
272 * Check the delta voltage over the last X minutes so we can do
273 * our end-of-charge logic based on the battery level change
274 * (no longer use minimum time as logic for charge end has 50
275 * minutes minimum charge built in).
276 */
277 if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) {
278 short_delta = power_history[0] -
279 power_history[CHARGE_END_SHORTD - 1];
280 }
281
282 if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) {
283 /*
284 * Scan the history: the points where measurement is taken need to
285 * be fairly static. Check prior to short delta 'area'. Also only
286 * check first and last 10 cycles (delta in middle OK).
287 */
288 long_delta = power_history[0] -
289 power_history[CHARGE_END_LONGD - 1];
290
291 for (i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++)
292 {
293 if ((power_history[i] - power_history[i+1]) > 50 ||
294 (power_history[i] - power_history[i+1]) < -50) {
295 long_delta = 777777;
296 break;
297 }
298 }
299
300 for (i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++)
301 {
302 if ((power_history[i] - power_history[i+1]) > 50 ||
303 (power_history[i] - power_history[i+1]) < -50) {
304 long_delta = 888888;
305 break;
306 }
307 }
308 }
309
310 snprintf(power_message, POWER_MESSAGE_LEN,
311 "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min,
312 charge_max_time_now);
313
314 /*
315 * End of charge criteria (any qualify):
316 * 1) Charged a long time
317 * 2) DeltaV went negative for a short time ( & long delta static)
318 * 3) DeltaV was negative over a longer period (no disk use only)
319 *
320 * Note: short_delta and long_delta are millivolts
321 */
322 if (powermgmt_last_cycle_startstop_min >= charge_max_time_now ||
323 (short_delta <= -50 && long_delta < 50) ||
324 (long_delta < -20 && last_disk_activity > CHARGE_END_LONGD)) {
325
326 int battery_percent = battery_level();
327
328 if (powermgmt_last_cycle_startstop_min > charge_max_time_now) {
329 DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, "
330 "enough!\n");
331 /*
332 * Have charged too long and deltaV detection did not
333 * work!
334 */
335 snprintf(power_message, POWER_MESSAGE_LEN,
336 "Chg tmout %d min", charge_max_time_now);
337 /*
338 * Switch to trickle charging. We skip the top-off
339 * since we've effectively done the top-off operation
340 * already since we charged for the maximum full
341 * charge time.
342 */
343 powermgmt_last_cycle_level = battery_percent;
344 powermgmt_last_cycle_startstop_min = 0;
345 charge_state = TRICKLE;
346
347 /*
348 * Set trickle charge target to a relative voltage instead
349 * of an arbitrary value - the fully charged voltage may
350 * vary according to ambient temp, battery condition etc.
351 * Trickle target is -0.15v from full voltage acheived.
352 * Topup target is -0.05v from full voltage.
353 */
354 target_voltage = power_history[0] - 150;
355
356 }
357 else {
358 if(short_delta <= -5) {
359 DEBUGF("power: short-term negative"
360 " delta, enough!\n");
361 snprintf(power_message, POWER_MESSAGE_LEN,
362 "end negd %d %dmin", short_delta,
363 powermgmt_last_cycle_startstop_min);
364 target_voltage = power_history[CHARGE_END_SHORTD - 1] - 50;
365 }
366 else {
367 DEBUGF("power: long-term small "
368 "positive delta, enough!\n");
369 snprintf(power_message, POWER_MESSAGE_LEN,
370 "end lowd %d %dmin", long_delta,
371 powermgmt_last_cycle_startstop_min);
372 target_voltage = power_history[CHARGE_END_LONGD - 1] - 50;
373 }
374
375 /*
376 * Switch to top-off charging.
377 */
378 powermgmt_last_cycle_level = battery_percent;
379 powermgmt_last_cycle_startstop_min = 0;
380 charge_state = TOPOFF;
381 }
382 }
383}
384
385static void topoff_trickle_step(void)
386{
387 unsigned int millivolts;
388
389 /*
390 *Time to switch from topoff to trickle?
391 */
392 if (charge_state == TOPOFF &&
393 powermgmt_last_cycle_startstop_min > TOPOFF_MAX_MIN) {
394
395 powermgmt_last_cycle_level = battery_level();
396 powermgmt_last_cycle_startstop_min = 0;
397 charge_state = TRICKLE;
398 target_voltage = target_voltage - 100;
399 }
400 /*
401 * Adjust trickle charge time (proportional and integral terms).
402 * Note: I considered setting the level higher if the USB is
403 * plugged in, but it doesn't appear to be necessary and will
404 * generate more heat [gvb].
405 */
406 millivolts = battery_voltage();
407
408 pid_p = ((signed)target_voltage - (signed)millivolts) / 5;
409 if (pid_p <= PID_DEADZONE && pid_p >= -PID_DEADZONE)
410 pid_p = 0;
411
412 if ((unsigned)millivolts < target_voltage) {
413 if (pid_i < 60)
414 pid_i++; /* limit so it doesn't "wind up" */
415 }
416 else {
417 if (pid_i > 0)
418 pid_i--; /* limit so it doesn't "wind up" */
419 }
420
421 trickle_sec = pid_p + pid_i;
422
423 if (trickle_sec > 60)
424 trickle_sec = 60;
425
426 if (trickle_sec < 0)
427 trickle_sec = 0;
428}
429
430void charging_algorithm_step(void)
431{
432 static int pwm_counter = 0; /* PWM total cycle in steps */
433 static int pwm_duty = 0; /* PWM duty cycle in steps */
434
435 switch (charger_input_state)
436 {
437 case CHARGER_PLUGGED:
438 charger_plugged();
439 break;
440
441 case CHARGER_UNPLUGGED:
442 charger_unplugged();
443 break;
444
445 case CHARGER:
446 case NO_CHARGER:
447 do_frequent_tasks();
448
449 if (pwm_counter > 0) {
450 if (pwm_duty > 0 && --pwm_duty <= 0)
451 charger_enable(false); /* Duty cycle expired */
452
453 if (--pwm_counter > 0)
454 return;
455
456 /* PWM cycle is complete */
457 powermgmt_last_cycle_startstop_min++;
458 debug_file_log();
459 }
460 break;
461 }
462
463 switch (charge_state)
464 {
465 case CHARGING:
466 charging_step();
467 break;
468
469 case TOPOFF:
470 case TRICKLE:
471 topoff_trickle_step();
472 break;
473
474 case DISCHARGING:
475 default:
476 break;
477 }
478
479 /* If 100%, ensure pwm_on never expires and briefly disables the
480 * charger. */
481 pwm_duty = (trickle_sec < 60) ? trickle_sec*2 : 0;
482 pwm_counter = 60*2;
483 charger_enable(trickle_sec > 0);
484}
485
486#ifdef CHARGING_DEBUG_FILE
487void charging_algorithm_close(void)
488{
489 debug_file_close();
490}
491#endif /* CHARGING_DEBUG_FILE */
492
493/* Returns true if the unit is charging the batteries. */
494bool charging_state(void)
495{
496 return charge_state == CHARGING;
497}
diff --git a/firmware/target/sh/archos/recorder/powermgmt-target.h b/firmware/target/sh/archos/recorder/powermgmt-target.h
new file mode 100644
index 0000000000..8fa2521f09
--- /dev/null
+++ b/firmware/target/sh/archos/recorder/powermgmt-target.h
@@ -0,0 +1,101 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
11 * Revisions copyright (C) 2005 by Gerald Van Baren
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#ifndef POWERMGMT_TARGET_H
23#define POWERMGMT_TARGET_H
24
25/*
26 * Define CHARGING_DEBUG_FILE to create a csv (spreadsheet) with battery
27 * information in it (one sample per minute/connect/disconnect).
28 *
29 * This is only for very low level debug.
30 */
31#undef CHARGING_DEBUG_FILE
32
33
34/* stop when N minutes have passed with avg delta being < -0.05 V */
35#define CHARGE_END_SHORTD 6
36/* stop when N minutes have passed with avg delta being < -0.02 V */
37#define CHARGE_END_LONGD 50
38
39/* Battery % to start at top-off */
40#define START_TOPOFF_CHG 85
41/* Battery % to start at trickle */
42#define START_TRICKLE_CHG 95
43/* power thread status message */
44#define POWER_MESSAGE_LEN 32
45/* minutes: maximum charging time for 1500 mAh batteries
46 * actual max time depends also on BATTERY_CAPACITY! */
47#define CHARGE_MAX_MIN_1500 450
48/* minutes: minimum charging time */
49#define CHARGE_MIN_MIN 10
50/* After charging, go to top off charge. How long should top off charge be? */
51#define TOPOFF_MAX_MIN 90
52/* which voltage is best? (millivolts) */
53#define TOPOFF_VOLTAGE 5650
54/* After top off charge, go to trickle harge. How long should trickle
55 * charge be? */
56#define TRICKLE_MAX_MIN 720 /* 12 hrs */
57/* which voltage is best? (millivolts) */
58#define TRICKLE_VOLTAGE 5450
59/* initial trickle_sec for topoff */
60#define START_TOPOFF_SEC 25
61/* initial trickle_sec for trickle */
62#define START_TRICKLE_SEC 15
63
64#define PID_DEADZONE 4 /* PID proportional deadzone */
65
66extern char power_message[POWER_MESSAGE_LEN];
67
68extern int long_delta; /* long term delta battery voltage */
69extern int short_delta; /* short term delta battery voltage */
70
71extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was
72 the charging started or
73 stopped? */
74extern int powermgmt_last_cycle_level; /* which level had the batteries
75 at this time? */
76
77extern int pid_p; /* PID proportional term */
78extern int pid_i; /* PID integral term */
79extern int trickle_sec; /* how many seconds should the
80 charger be enabled per
81 minute for trickle
82 charging? */
83void charger_enable(bool on);
84bool charger_enabled(void);
85
86/* Battery filter lengths in samples */
87#define BATT_AVE_SAMPLES 32
88
89/* No init to do */
90static inline void powermgmt_init_target(void) {}
91void charging_algorithm_step(void);
92
93#ifdef CHARGING_DEBUG_FILE
94/* Need to flush and close debug file */
95void charging_algorithm_close(void);
96#else
97/* No poweroff operation to do */
98static inline void charging_algorithm_close(void) {}
99#endif
100
101#endif /* POWERMGMT_TARGET_H */