summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-12-21 18:10:36 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-12-21 18:10:36 +0000
commit5667682dd204a07c52f057506fd2eef05bf63f2e (patch)
treea5f4f3cb22751362a9ed7774698ca55d27819d16 /firmware/target
parentc3c15cce88481a2504eb492ba06b6a691d8e998d (diff)
downloadrockbox-5667682dd204a07c52f057506fd2eef05bf63f2e.tar.gz
rockbox-5667682dd204a07c52f057506fd2eef05bf63f2e.zip
Gigabeat S: Implement charging and power control to charge from AC or USB. Hold MENU while plugging USB cable to charge from USB without connecting. Under Windows, plugging USB for charging only but not connecting still needs to be properly handled (driver popup issue) but it will charge when connected normally-- no issue under Linux. Some accomodating changes made to powermgmt.c will soon be made nicer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/imx31/debug-imx31.c2
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/adc-imx31.c30
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/adc-target.h9
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c25
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/mc13783-target.h4
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-imx31.c51
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-imx31.h3
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c883
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h114
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-imx31.c25
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-target.h5
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/usb-imx31.c10
12 files changed, 1082 insertions, 79 deletions
diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c
index df2489eafb..b72390cb63 100644
--- a/firmware/target/arm/imx31/debug-imx31.c
+++ b/firmware/target/arm/imx31/debug-imx31.c
@@ -139,6 +139,7 @@ bool __dbg_ports(void)
139 MC13783_INTERRUPT_SENSE0, 139 MC13783_INTERRUPT_SENSE0,
140 MC13783_INTERRUPT_STATUS1, 140 MC13783_INTERRUPT_STATUS1,
141 MC13783_INTERRUPT_SENSE1, 141 MC13783_INTERRUPT_SENSE1,
142 MC13783_CHARGER,
142 MC13783_RTC_TIME, 143 MC13783_RTC_TIME,
143 MC13783_RTC_ALARM, 144 MC13783_RTC_ALARM,
144 MC13783_RTC_DAY, 145 MC13783_RTC_DAY,
@@ -151,6 +152,7 @@ bool __dbg_ports(void)
151 "Int Sense0", 152 "Int Sense0",
152 "Int Stat1 ", 153 "Int Stat1 ",
153 "Int Sense1", 154 "Int Sense1",
155 "Charger ",
154 "RTC Time ", 156 "RTC Time ",
155 "RTC Alarm ", 157 "RTC Alarm ",
156 "RTC Day ", 158 "RTC Day ",
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c b/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c
index 85ef15b9b4..3c66c42adc 100644
--- a/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/adc-imx31.c
@@ -84,6 +84,30 @@ unsigned short adc_read(int channel)
84 return (channel & 4) ? MC13783_ADD2r(data) : MC13783_ADD1r(data); 84 return (channel & 4) ? MC13783_ADD2r(data) : MC13783_ADD1r(data);
85} 85}
86 86
87bool adc_enable_channel(int channel, bool enable)
88{
89 uint32_t bit, mask;
90
91 switch (channel)
92 {
93 case ADC_CHARGER_CURRENT:
94 mask = MC13783_CHRGICON;
95 break;
96
97 case ADC_BATTERY_TEMP:
98 mask = MC13783_RTHEN;
99 break;
100
101 default:
102 return false;
103 }
104
105 bit = enable ? mask : 0;
106
107 return mc13783_write_masked(MC13783_ADC0, bit, mask)
108 != MC13783_DATA_ERROR;
109}
110
87/* Called by mc13783 interrupt thread when conversion is complete */ 111/* Called by mc13783 interrupt thread when conversion is complete */
88void adc_done(void) 112void adc_done(void)
89{ 113{
@@ -98,9 +122,9 @@ void adc_init(void)
98 /* Init so first reads get data */ 122 /* Init so first reads get data */
99 last_adc_read[0] = last_adc_read[1] = current_tick-1; 123 last_adc_read[0] = last_adc_read[1] = current_tick-1;
100 124
101 /* Enable increment-by-read, thermistor, charge current */ 125 /* Enable increment-by-read, turn off extra conversions. */
102 mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1 | 126 mc13783_write(MC13783_ADC0, MC13783_ADINC2 | MC13783_ADINC1);
103 MC13783_RTHEN | MC13783_CHRGICON); 127
104 /* Enable ADC, set multi-channel mode */ 128 /* Enable ADC, set multi-channel mode */
105 mc13783_write(MC13783_ADC1, MC13783_ADEN); 129 mc13783_write(MC13783_ADC1, MC13783_ADEN);
106 130
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-target.h b/firmware/target/arm/imx31/gigabeat-s/adc-target.h
index efa665dd98..00027e05df 100644
--- a/firmware/target/arm/imx31/gigabeat-s/adc-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/adc-target.h
@@ -47,7 +47,14 @@
47#define ADC_READ_ERROR 0xFFFF 47#define ADC_READ_ERROR 0xFFFF
48 48
49void adc_done(void); 49void adc_done(void);
50/* Enable conversion of specified channel (if switchoff is possible) */
51bool adc_enable_channel(int channel, bool enable);
52
53/* Implemented in powermgmt-imx31.c */
50int battery_adc_charge_current(void); 54int battery_adc_charge_current(void);
51unsigned int battery_adc_temp(void); 55int battery_adc_temp(void);
56unsigned int application_supply_adc_voltage(void);
57unsigned int chrgraw_adc_voltage(void);
58unsigned int cccv_regulator_dissipation(void);
52 59
53#endif 60#endif
diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
index e6238112d1..fc9ad719a6 100644
--- a/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-gigabeat-s.c
@@ -28,6 +28,7 @@
28#include "button-target.h" 28#include "button-target.h"
29#include "usb-target.h" 29#include "usb-target.h"
30#include "power-imx31.h" 30#include "power-imx31.h"
31#include "powermgmt-target.h"
31 32
32/* Gigabeat S definitions for static MC13783 event registration */ 33/* Gigabeat S definitions for static MC13783 event registration */
33 34
@@ -45,26 +46,26 @@ static const struct mc13783_event mc13783_events[] =
45 .mask = MC13783_ONOFD1M, 46 .mask = MC13783_ONOFD1M,
46 .callback = button_power_event, 47 .callback = button_power_event,
47 }, 48 },
48#ifdef HAVE_HEADPHONE_DETECTION 49 [MC13783_SE1_EVENT] = /* Main charger detection */
49 [MC13783_ONOFD2_EVENT] = /* Headphone jack */
50 {
51 .set = MC13783_EVENT_SET1,
52 .mask = MC13783_ONOFD2M,
53 .callback = headphone_detect_event,
54 },
55#endif
56 [MC13783_CHGDET_EVENT] = /* Charger detection */
57 { 50 {
58 .set = MC13783_EVENT_SET0, 51 .set = MC13783_EVENT_SET0,
59 .mask = MC13783_CHGDETM, 52 .mask = MC13783_SE1M,
60 .callback = charger_detect_event, 53 .callback = charger_main_detect_event,
61 }, 54 },
62 [MC13783_USB4V4_EVENT] = /* USB insertion */ 55 [MC13783_USB_EVENT] = /* USB insertion/USB charger detection */
63 { 56 {
64 .set = MC13783_EVENT_SET0, 57 .set = MC13783_EVENT_SET0,
65 .mask = MC13783_USBM, 58 .mask = MC13783_USBM,
66 .callback = usb_connect_event, 59 .callback = usb_connect_event,
67 }, 60 },
61#ifdef HAVE_HEADPHONE_DETECTION
62 [MC13783_ONOFD2_EVENT] = /* Headphone jack */
63 {
64 .set = MC13783_EVENT_SET1,
65 .mask = MC13783_ONOFD2M,
66 .callback = headphone_detect_event,
67 },
68#endif
68}; 69};
69 70
70const struct mc13783_event_list mc13783_event_list = 71const struct mc13783_event_list mc13783_event_list =
diff --git a/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h b/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h
index 26ab3c6e21..2ae3be1819 100644
--- a/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/mc13783-target.h
@@ -31,8 +31,8 @@ enum mc13783_event_ids
31#ifdef HAVE_HEADPHONE_DETECTION 31#ifdef HAVE_HEADPHONE_DETECTION
32 MC13783_ONOFD2_EVENT, /* Headphone jack */ 32 MC13783_ONOFD2_EVENT, /* Headphone jack */
33#endif 33#endif
34 MC13783_CHGDET_EVENT, /* Charger detection */ 34 MC13783_SE1_EVENT, /* Main charger detection */
35 MC13783_USB4V4_EVENT, /* USB insertion */ 35 MC13783_USB_EVENT, /* USB insertion */
36}; 36};
37 37
38#endif /* MC13783_TARGET_H */ 38#endif /* MC13783_TARGET_H */
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
index 17008cec4b..39724c7b75 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/power-imx31.c
@@ -20,6 +20,8 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "config.h" 21#include "config.h"
22#include "system.h" 22#include "system.h"
23#include "usb.h"
24#include "usb_core.h"
23#include "power.h" 25#include "power.h"
24#include "power-imx31.h" 26#include "power-imx31.h"
25#include "backlight.h" 27#include "backlight.h"
@@ -29,35 +31,47 @@
29#include "i2c-imx31.h" 31#include "i2c-imx31.h"
30 32
31extern struct i2c_node si4700_i2c_node; 33extern struct i2c_node si4700_i2c_node;
34static unsigned int power_status = POWER_INPUT_NONE;
32 35
33static bool charger_detect = false; 36/* Detect which power sources are present. */
34
35/* This is called from the mc13783 interrupt thread */
36void charger_detect_event(void)
37{
38 charger_detect =
39 mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_CHGDETS;
40}
41
42unsigned int power_input_status(void) 37unsigned int power_input_status(void)
43{ 38{
44 unsigned int status = POWER_INPUT_NONE; 39 unsigned int status = power_status;
45 40
46 if ((GPIO3_DR & (1 << 20)) != 0) 41 if (GPIO3_DR & (1 << 20))
47 status |= POWER_INPUT_BATTERY; 42 status |= POWER_INPUT_BATTERY;
48 43
49 if (charger_detect) 44 if (usb_allowed_current() < 500)
50 status |= POWER_INPUT_MAIN_CHARGER; 45 {
46 /* ACK that USB is connected but NOT chargeable */
47 status &= ~(POWER_INPUT_USB_CHARGER & POWER_INPUT_CHARGER);
48 }
51 49
52 return status; 50 return status;
53} 51}
54 52
55/* Returns true if the unit is charging the batteries. */ 53/* Detect changes in presence of the AC adaptor. */
56bool charging_state(void) 54void charger_main_detect_event(void)
57{ 55{
58 return false; 56 if (mc13783_read(MC13783_INTERRUPT_SENSE0) & MC13783_SE1S)
57 power_status |= POWER_INPUT_MAIN_CHARGER;
58 else
59 power_status &= ~POWER_INPUT_MAIN_CHARGER;
59} 60}
60 61
62/* Detect changes in USB bus power. Called from usb connect event handler. */
63void charger_usb_detect_event(int status)
64{
65 /* USB plugged does not imply charging is possible or even
66 * powering the device to maintain the battery. */
67 if (status == USB_INSERTED)
68 power_status |= POWER_INPUT_USB_CHARGER;
69 else
70 power_status &= ~POWER_INPUT_USB_CHARGER;
71}
72
73/* charging_state is implemented in powermgmt-imx31.c */
74
61void ide_power_enable(bool on) 75void ide_power_enable(bool on)
62{ 76{
63 if (!on) 77 if (!on)
@@ -129,9 +143,8 @@ void power_off(void)
129void power_init(void) 143void power_init(void)
130{ 144{
131 /* Poll initial state */ 145 /* Poll initial state */
132 charger_detect_event(); 146 charger_main_detect_event();
133 147
134 /* Enable detect event */ 148 /* Enable detect event */
135 mc13783_enable_event(MC13783_CHGDET_EVENT); 149 mc13783_enable_event(MC13783_SE1_EVENT);
136} 150}
137
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h b/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
index 86ba4ac815..9294de102c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
+++ b/firmware/target/arm/imx31/gigabeat-s/power-imx31.h
@@ -21,6 +21,7 @@
21#ifndef POWER_IMX31_H 21#ifndef POWER_IMX31_H
22#define POWER_IMX31_H 22#define POWER_IMX31_H
23 23
24void charger_detect_event(void); 24void charger_main_detect_event(void);
25void charger_usb_detect_event(int status);
25 26
26#endif /* POWER_IMX31_H */ 27#endif /* POWER_IMX31_H */
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
index 796c781f73..c44e7ccdda 100644
--- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c
@@ -7,8 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese 10 * Copyright (c) 2008 by Michael Sevakis
11 * Revisions copyright (C) 2005 by Gerald Van Baren
12 * 11 *
13 * This program is free software; you can redistribute it and/or 12 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License 13 * modify it under the terms of the GNU General Public License
@@ -19,12 +18,40 @@
19 * KIND, either express or implied. 18 * KIND, either express or implied.
20 * 19 *
21 ****************************************************************************/ 20 ****************************************************************************/
22 21#include <stdlib.h>
23/* FIXME: This is just the Gigabeat F/X file with a different name... */
24
25#include "config.h" 22#include "config.h"
23#include "system.h"
24#include "thread.h"
25#include "mc13783.h"
26#include "adc.h" 26#include "adc.h"
27#include "powermgmt.h" 27#include "powermgmt.h"
28#include "power.h"
29#include "power-imx31.h"
30
31/* TODO: Battery tests to get the right values! */
32const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
33{
34 3450
35};
36
37const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
38{
39 3400
40};
41
42/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
43const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
44{
45 /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */
46 { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 },
47};
48
49/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
50const unsigned short percent_to_volt_charge[11] =
51{
52 /* Toshiba Gigabeat Li Ion 830mAH */
53 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990
54};
28 55
29/** 56/**
30 * Fixed-point natural log 57 * Fixed-point natural log
@@ -56,31 +83,6 @@ static long flog(int x)
56 return y; 83 return y;
57} 84}
58 85
59
60const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
61{
62 3450
63};
64
65const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
66{
67 3400
68};
69
70/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
71const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
72{
73 /* Toshiba Gigabeat Li Ion 830mAH figured from discharge curve */
74 { 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990 },
75};
76
77/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
78const unsigned short percent_to_volt_charge[11] =
79{
80 /* Toshiba Gigabeat Li Ion 830mAH */
81 3480, 3550, 3590, 3610, 3630, 3650, 3700, 3760, 3800, 3910, 3990
82};
83
84/* Returns battery voltage from ADC [millivolts] */ 86/* Returns battery voltage from ADC [millivolts] */
85unsigned int battery_adc_voltage(void) 87unsigned int battery_adc_voltage(void)
86{ 88{
@@ -88,6 +90,17 @@ unsigned int battery_adc_voltage(void)
88 return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400; 90 return ((adc_read(ADC_BATTERY) * 2303) >> 10) + 2400;
89} 91}
90 92
93/* Returns the application supply voltage from ADC [millvolts] */
94unsigned int application_supply_adc_voltage(void)
95{
96 return ((adc_read(ADC_APPLICATION_SUPPLY) * 2303) >> 10) + 2400;
97}
98
99unsigned int chrgraw_adc_voltage(void)
100{
101 return (adc_read(ADC_CHARGER_VOLTAGE) * 23023) >> 10;
102}
103
91/* Returns battery charge current from ADC [milliamps] */ 104/* Returns battery charge current from ADC [milliamps] */
92int battery_adc_charge_current(void) 105int battery_adc_charge_current(void)
93{ 106{
@@ -95,11 +108,31 @@ int battery_adc_charge_current(void)
95 * Negative reading = battery to charger terminal 108 * Negative reading = battery to charger terminal
96 * ADC reading -512-511 = -2875mA-2875mA */ 109 * ADC reading -512-511 = -2875mA-2875mA */
97 unsigned int value = adc_read(ADC_CHARGER_CURRENT); 110 unsigned int value = adc_read(ADC_CHARGER_CURRENT);
98 return (((int)value << 22) >> 22) * 2881 >> 9; 111 int I;
112
113 if (value == ADC_READ_ERROR)
114 return INT_MIN;
115
116 I = ((((int32_t)value << 22) >> 22) * 2881) >> 9;
117 return ILEVEL_ADJUST_IN(I);
118}
119
120/* Estimate power dissipation in the charge path regulator in mW. */
121unsigned int cccv_regulator_dissipation(void)
122{
123 /* BATTISNS is shorted to BATT so we don't need to use the
124 * battery current reading. */
125 int chrgraw = (adc_read(ADC_CHARGER_VOLTAGE) * 230225) >> 10;
126 int batt = ((adc_read(ADC_BATTERY) * 23023) >> 10) + 24000;
127 int ichrgsn = adc_read(ADC_CHARGER_CURRENT);
128 ichrgsn = ((((int32_t)ichrgsn << 22) >> 22) * 2881) >> 9;
129 ichrgsn = abs(ichrgsn);
130
131 return (chrgraw - ichrgsn - batt)*ILEVEL_ADJUST_IN(ichrgsn) / 10000;
99} 132}
100 133
101/* Returns battery temperature from ADC [deg-C] */ 134/* Returns battery temperature from ADC [deg-C] */
102unsigned int battery_adc_temp(void) 135int battery_adc_temp(void)
103{ 136{
104 unsigned int value = adc_read(ADC_BATTERY_TEMP); 137 unsigned int value = adc_read(ADC_BATTERY_TEMP);
105 /* E[volts] = value * 2.3V / 1023 138 /* E[volts] = value * 2.3V / 1023
@@ -117,10 +150,784 @@ unsigned int battery_adc_temp(void)
117 * Fixed-point output matches the floating-point version for each ADC 150 * Fixed-point output matches the floating-point version for each ADC
118 * value. 151 * value.
119 */ 152 */
120 int R = 2070000 * value; 153 if (value > 0)
121 long long ln = flog(R) + 83196; 154 {
122 long long t0 = 425890304133ll; 155 int R = 2070000 * value;
123 long long t1 = 1000000*ln; 156 long long ln = flog(R) + 83196;
124 long long t3 = ln*ln*ln / 13418057; 157 long long t0 = 425890304133ll;
125 return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100; 158 long long t1 = 1000000*ln;
159 long long t3 = ln*ln*ln / 13418057;
160 return ((32754211579494400ll / (t0 + t1 + t3)) - 27315) / 100;
161 }
162
163 return INT_MIN;
164}
165
166/** Charger control **/
167
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. */
170static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */
171static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */
172static int charger_total_timer = 0; /* Total allowed charging time */
173static int icharger_ave = 0; /* Filtered charging current */
174static bool charger_close = false; /* Shutdown notification */
175static bool service_wdt = true; /* Service the watchdog timer, if things
176 go very wrong, cease and shut down. */
177static uint32_t charger_setting = 0; /* Current ICHRG and VCHRG regulator
178 * setting (register bits) */
179#define CHARGER_ADJUST ((uint32_t)-1)/* Force change in regulator setting */
180static int autorecharge_counter = 0 ; /* Battery < threshold debounce */
181static int chgcurr_timer = 0; /* Countdown to CHGCURR error */
182#define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */
183#define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */
184#define CHGCURR_TIMEOUT (2*2) /* 2s debounce */
185
186/* Temperature monitoring */
187static enum
188{
189 TEMP_STATE_NORMAL = 0, /* Within range */
190 TEMP_STATE_WAIT = 1, /* Went out of range, wait to come back */
191 TEMP_LOW_LIMIT = 0, /* Min temp */
192 TEMP_HIGH_LIMIT = 1, /* Max temp */
193} temp_state = TEMP_STATE_NORMAL;
194
195/* Set power thread priority for charging mode or not */
196static inline void charging_set_thread_priority(bool charging)
197{
198#ifdef HAVE_PRIORITY_SCHEDULING
199 thread_set_priority(THREAD_ID_CURRENT,
200 charging ? PRIORITY_REALTIME : PRIORITY_SYSTEM);
201#endif
202 (void)charging;
203}
204
205/* Update filtered charger current - exponential moving average */
206static bool charger_current_filter_step(void)
207{
208 int value = battery_adc_charge_current();
209
210 if (value == ADC_READ_ERROR)
211 return false;
212
213 icharger_ave += value - (icharger_ave / ICHARGER_AVE_SAMPLES);
214 return true;
215}
216
217/* Return true if the main charger is connected. */
218static bool main_charger_connected(void)
219{
220 return (power_status &
221 POWER_INPUT_MAIN_CHARGER &
222 POWER_INPUT_CHARGER) != 0;
223}
224
225/* Return the voltage level which should automatically trigger
226 * another recharge cycle based upon which power source is available.
227 * Assumes at least one is. */
228static unsigned int auto_recharge_voltage(void)
229{
230 if (main_charger_connected())
231 return BATT_VAUTO_RECHARGE;
232 else
233 return BATT_USB_VAUTO_RECHARGE;
234}
235
236#ifndef NO_LOW_BATTERY_SHUTDOWN
237/* Return greater of supply (BP) or filtered battery voltage. */
238static unsigned int input_millivolts(void)
239{
240 unsigned int app_millivolts = application_supply_adc_voltage();
241 unsigned int bat_millivolts = battery_voltage();
242
243 return MAX(app_millivolts, bat_millivolts);
244}
245#endif
246
247/* Get smoothed readings for initializing filtered data. */
248static int stat_battery_reading(int type)
249{
250 int high = INT_MIN, low = INT_MAX;
251 int value = 0;
252 int i;
253
254 for (i = 0; i < 7; i++)
255 {
256 int reading = ADC_READ_ERROR;
257
258 sleep(2); /* Get unique readings */
259
260 switch (type)
261 {
262 case ADC_BATTERY:
263 reading = battery_adc_voltage();
264 break;
265
266 case ADC_CHARGER_CURRENT:
267 reading = battery_adc_charge_current();
268 break;
269 }
270
271 if (reading == ADC_READ_ERROR)
272 return INT_MIN;
273
274 if (reading > high)
275 high = reading;
276
277 if (reading < low)
278 low = reading;
279
280 value += reading;
281 }
282
283 /* Discard extremes */
284 return (value - high - low) / 5;
285}
286
287/* Update filtered battery voltage instead of waiting for filter
288 * decay. */
289static bool update_filtered_battery_voltage(void)
290{
291 int millivolts = stat_battery_reading(ADC_BATTERY);
292
293 if (millivolts != INT_MIN)
294 {
295 set_filtered_battery_voltage(millivolts);
296 return true;
297 }
298
299 return false;
300}
301
302/* Sets the charge current limit based upon state. charge_state should be
303 * set before calling. */
304static bool adjust_charger_current(void)
305{
306 static const uint8_t charger_bits[][2] =
307 {
308 [DISCHARGING] =
309 {
310 /* These are actually zeros but reflect this setting */
311 MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
312 MC13783_ICHRG_0MA | MC13783_VCHRG_4_050V,
313 },
314 /* Main(+USB): Charge slowly from the adapter until voltage is
315 * sufficient for normal charging.
316 *
317 * USB: The truth is that things will probably not make it this far.
318 * Cover the case, just in case the disk isn't used and it is
319 * manageable. */
320 [TRICKLE] =
321 {
322 BATTERY_ITRICKLE | BATTERY_VCHARGING,
323 BATTERY_ITRICKLE_USB | BATTERY_VCHARGING
324 },
325 [TOPOFF] =
326 {
327 BATTERY_IFAST | BATTERY_VCHARGING,
328 BATTERY_IFAST_USB | BATTERY_VCHARGING
329 },
330 [CHARGING] =
331 {
332 BATTERY_IFAST | BATTERY_VCHARGING,
333 BATTERY_IFAST_USB | BATTERY_VCHARGING
334 },
335 /* Must maintain battery when on USB power only - utterly nasty
336 * but true and something retailos does (it will even end up charging
337 * the battery but not reporting that it is doing so).
338 * Float lower than MAX - could end up slightly discharging after
339 * a full charge but this is safer than maxing it out. */
340 [CHARGING+1] =
341 {
342 BATTERY_IFLOAT_USB | BATTERY_VFLOAT_USB,
343 BATTERY_IMAINTAIN_USB | BATTERY_VMAINTAIN_USB
344 },
345#if 0
346 /* Slower settings to so that the linear regulator doesn't dissipate
347 * an excessive amount of power when coming out of precharge state. */
348 [CHARGING+2] =
349 {
350 BATTERY_ISLOW | BATTERY_VCHARGING,
351 BATTERY_ISLOW_USB | BATTEYR_VCHARGING
352 },
353#endif
354 };
355
356 bool success = false;
357 int usb_select;
358 uint32_t i;
359
360 usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB)
361 ? 1 : 0;
362
363 if (charge_state == DISCHARGING && usb_select == 1)
364 {
365 /* USB-only, DISCHARGING, = maintaining battery */
366 int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1;
367 charger_setting = charger_bits[CHARGING+1][select];
368 }
369 else
370 {
371 /* Take very good care not to write garbage. */
372 int state = charge_state;
373
374 if (state < DISCHARGING || state > CHARGING)
375 state = DISCHARGING;
376
377 charger_setting = charger_bits[state][usb_select];
378 }
379
380 if (charger_setting != 0)
381 {
382 charging_set_thread_priority(true);
383
384 /* Turn regulator logically ON. Hardware may still override. */
385 i = mc13783_write_masked(MC13783_CHARGER,
386 charger_setting | MC13783_CHRGRAWPDEN,
387 MC13783_ICHRG | MC13783_VCHRG |
388 MC13783_CHRGRAWPDEN);
389
390 if (i != MC13783_DATA_ERROR)
391 {
392 int icharger;
393
394 /* Enable charge current conversion */
395 adc_enable_channel(ADC_CHARGER_CURRENT, true);
396
397 /* Charge path regulator turn on takes ~100ms max. */
398 sleep(HZ/10);
399
400 icharger = stat_battery_reading(ADC_CHARGER_CURRENT);
401
402 if (icharger != INT_MIN)
403 {
404 icharger_ave = icharger * ICHARGER_AVE_SAMPLES;
405
406 if (update_filtered_battery_voltage())
407 return true;
408 }
409 }
410
411 /* Force regulator OFF. */
412 charge_state = CHARGE_STATE_ERROR;
413 }
414
415 /* Turn regulator OFF. */
416 icharger_ave = 0;
417 i = mc13783_write_masked(MC13783_CHARGER, charger_bits[0][0],
418 MC13783_ICHRG | MC13783_VCHRG |
419 MC13783_CHRGRAWPDEN);
420
421 if (MC13783_DATA_ERROR == i)
422 {
423 /* Failed. Force poweroff by not servicing the watchdog. */
424 service_wdt = false;
425 }
426 else if (0 == charger_setting)
427 {
428 /* Here because OFF was requested state */
429 success = true;
430 }
431
432 charger_setting = 0;
433
434 adc_enable_channel(ADC_CHARGER_CURRENT, false);
435 update_filtered_battery_voltage();
436 charging_set_thread_priority(false);
437
438 return success;
439}
440
441/* Stop the charger - if USB only then the regulator will not really be
442 * turned off. ERROR or DISABLED will turn it off however. */
443static void stop_charger(void)
444{
445 charger_total_timer = 0;
446
447 if (charge_state > DISCHARGING)
448 charge_state = DISCHARGING;
449
450 adjust_charger_current();
451}
452
453/* Return OK if it is acceptable to start the regulator. */
454static bool charging_ok(void)
455{
456 bool ok = charge_state >= DISCHARGING; /* Not an error condition? */
457
458 if (ok)
459 {
460 /* Is the battery even connected? */
461 ok = (power_status & POWER_INPUT_BATTERY) != 0;
462 }
463
464 if (ok)
465 {
466 /* No tolerance for any over/under temp - wait for it to
467 * come back into safe range. */
468 static const signed char temp_ranges[2][2] =
469 {
470 { 0, 45 }, /* Temperature range before beginning charging */
471 { 5, 40 }, /* Temperature range after out-of-range detected */
472 };
473
474 int temp = battery_adc_temp();
475 const signed char *range = temp_ranges[temp_state];
476
477 ok = temp >= range[TEMP_LOW_LIMIT] &&
478 temp <= range[TEMP_HIGH_LIMIT];
479
480 switch (temp_state)
481 {
482 case TEMP_STATE_NORMAL:
483 if (!ok)
484 temp_state = TEMP_STATE_WAIT;
485 break;
486
487 case TEMP_STATE_WAIT:
488 if (ok)
489 temp_state = TEMP_STATE_NORMAL;
490 break;
491
492 default:
493 break;
494 }
495 }
496
497 if (ok)
498 {
499 /* Any events that should stop the regulator? */
500
501 /* Overvoltage at CHRGRAW? */
502 ok = (int_sense0 & MC13783_CHGOVS) == 0;
503
504 if (ok)
505 {
506 /* CHGCURR sensed? */
507 ok = (int_sense0 & MC13783_CHGCURRS) != 0;
508
509 if (!ok)
510 {
511 /* Debounce transient states */
512 if (chgcurr_timer > 0)
513 {
514 chgcurr_timer--;
515 ok = true;
516 }
517 }
518 else
519 {
520 chgcurr_timer = CHGCURR_TIMEOUT;
521 }
522 }
523
524 /* Charger may need to be reinserted */
525 if (!ok)
526 charge_state = CHARGE_STATE_ERROR;
527 }
528
529 if (charger_setting != 0)
530 {
531 if (ok)
532 {
533 /* Watch to not overheat FET (nothing should go over about 1012.7mW).
534 * Trying a higher voltage AC adapter can work (up to 6.90V) but
535 * we'll just reject that. Reducing current for adapters that bring
536 * CHRGRAW to > 4.900V is another possible action. */
537 ok = cccv_regulator_dissipation() < 1150;
538 if (!ok)
539 charge_state = CHARGE_STATE_ERROR;
540 }
541
542 if (!ok)
543 {
544 int state = charge_state;
545
546 if (state > DISCHARGING)
547 state = DISCHARGING;
548
549 /* Force off for all states including maintaining the battery level
550 * on USB. */
551 charge_state = CHARGE_STATE_ERROR;
552 stop_charger();
553 charge_state = state;
554 }
555 }
556
557 return ok;
558}
559
560void powermgmt_init_target(void)
561{
562#ifdef IMX31_ALLOW_CHARGING
563 const uint32_t regval_w =
564 MC13783_VCHRG_4_050V | MC13783_ICHRG_0MA |
565 MC13783_ICHRGTR_0MA | MC13783_OVCTRL_6_90V;
566
567 /* Use watchdog to shut system down if we lose control of the charging
568 * hardware. */
569 watchdog_init(WATCHDOG_TIMEOUT);
570
571 mc13783_write(MC13783_CHARGER, regval_w);
572
573 if (mc13783_read(MC13783_CHARGER) == regval_w)
574 {
575 /* Divide CHRGRAW input by 10 */
576 mc13783_clear(MC13783_ADC0, MC13783_CHRGRAWDIV);
577 /* Turn off BATTDETB. It's worthless on MESx0V since the battery
578 * isn't removable (nor the thermistor). */
579 mc13783_clear(MC13783_POWER_CONTROL0, MC13783_BATTDETEN);
580 }
581 else
582 {
583 /* Register has the wrong value - set error condition and disable
584 * since something is wrong. */
585 charge_state = CHARGE_STATE_DISABLED;
586 stop_charger();
587 }
588#else
589 /* Disable charger use */
590 charge_state = CHARGE_STATE_DISABLED;
591#endif
592}
593
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. */
609bool charging_state(void)
610{
611 switch (charge_state)
612 {
613 case TRICKLE:
614 case TOPOFF:
615 case CHARGING:
616 return true;
617 default:
618 return false;
619 }
620}
621
622/* Filtered battery charge current */
623int battery_charge_current(void)
624{
625 return icharger_ave / ICHARGER_AVE_SAMPLES;
626}
627
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)
647{
648 adc_enable_channel(ADC_BATTERY_TEMP, true);
649 autorecharge_counter = -1;
650}
651
652static void charger_unplugged(void)
653{
654 /* Charger pulled - turn off current sources (though hardware
655 * will have done that anyway). */
656 if (charge_state > CHARGE_STATE_DISABLED)
657 {
658 /* Reset state and clear any error. If disabled, the charger
659 * will not have been started or will have been stopped already. */
660 stop_charger();
661 charge_state = DISCHARGING;
662 }
663
664 /* Might need to reevaluate these bits in charger_none. */
665 power_status &= ~(POWER_INPUT | POWER_INPUT_CHARGER);
666 temp_state = TEMP_STATE_NORMAL;
667 autorecharge_counter = 0;
668 chgcurr_timer = 0;
669
670 adc_enable_channel(ADC_BATTERY_TEMP, false);
671}
672
673static void charger_none(void)
674{
675 unsigned int pwr = power_input_status();
676
677 if (power_status != pwr)
678 {
679 /* If battery switch state changed, reset filter. */
680 if ((power_status ^ pwr) & POWER_INPUT_BATTERY)
681 update_filtered_battery_voltage();
682
683 power_status = pwr;
684
685 if (charge_state == CHARGE_STATE_DISABLED)
686 return;
687
688 if ((pwr & (POWER_INPUT | POWER_INPUT_CHARGER)) == POWER_INPUT_USB)
689 {
690 /* USB connected but not configured. Maintain battery to the
691 * greatest degree possible. It probably won't be enough but the
692 * discharge won't be so severe. */
693 charger_plugged();
694 charger_setting = CHARGER_ADJUST;
695 }
696 else
697 {
698 charger_unplugged();
699 power_status = pwr; /* Restore status */
700 }
701 }
702 else if (charger_setting != 0)
703 {
704 /* Maintaining - keep filter going and check charge state */
705 int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
706
707 if (!charger_current_filter_step())
708 {
709 /* Failed to read current */
710 charge_state = CHARGE_STATE_ERROR;
711 }
712
713 charging_ok();
714 }
715}
716
717static void charger_control(void)
718{
719 unsigned int pwr = power_input_status();
720
721 if (power_status != pwr)
722 {
723 unsigned int changed = power_status ^ pwr;
724
725 power_status = pwr;
726
727 /* If battery switch state changed, reset filter. */
728 if (changed & POWER_INPUT_BATTERY)
729 update_filtered_battery_voltage();
730
731 if (charger_setting != 0)
732 charger_setting = CHARGER_ADJUST;
733
734 if (charge_state == DISCHARGING)
735 {
736 if (main_charger_connected())
737 {
738 /* If main is connected, ignore USB plugs. */
739 if (changed & POWER_INPUT_MAIN_CHARGER)
740 {
741 /* Main charger plugged - try charge */
742 autorecharge_counter = -1;
743 }
744 }
745 else if (pwr & POWER_INPUT_USB_CHARGER
746 & POWER_INPUT_CHARGER)
747 {
748 if (changed & POWER_INPUT_USB_CHARGER)
749 {
750 /* USB charger plugged - try charge */
751 autorecharge_counter = -1;
752 }
753 }
754 }
755 }
756
757 if (charger_setting != 0 && !charger_current_filter_step())
758 {
759 /* Failed to read current */
760 charge_state = CHARGE_STATE_ERROR;
761 }
762
763 int_sense0 = mc13783_read(MC13783_INTERRUPT_SENSE0);
764
765 if (!charging_ok())
766 return;
767
768 switch (charge_state)
769 {
770 case DISCHARGING:
771 {
772 /* Battery voltage may have dropped and a charge cycle should
773 * start again. Debounced. */
774 if (autorecharge_counter < 0)
775 {
776 /* Try starting a cycle now regardless of battery level to
777 * allow user to ensure the battery is topped off. It
778 * will soon turn off if already full. */
779 autorecharge_counter = 0;
780 }
781 else if (battery_voltage() > auto_recharge_voltage())
782 {
783 /* Still above threshold - reset counter */
784 autorecharge_counter = AUTORECHARGE_COUNTDOWN;
785 break;
786 }
787 else if (autorecharge_counter > 0)
788 {
789 /* Coundown to restart */
790 autorecharge_counter--;
791 break;
792 }
793
794 charging_set_thread_priority(true);
795
796 if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
797 {
798 /* Battery is deeply discharged - precharge at lower current. */
799 charge_state = TRICKLE;
800 }
801 else
802 {
803 /* Ok for fast charge */
804 charge_state = CHARGING;
805 }
806
807 charger_setting = CHARGER_ADJUST;
808 charger_total_timer = CHARGER_TOTAL_TIMER*60*2;
809 break;
810 } /* DISCHARGING: */
811
812 case TRICKLE: /* Very low - precharge */
813 {
814 if (battery_voltage() <= BATT_VTRICKLE_CHARGE)
815 break;
816
817 /* Switch to normal charge mode. */
818 charge_state = CHARGING;
819 charger_setting = CHARGER_ADJUST;
820 break;
821 } /* TRICKLE: */
822
823 case CHARGING: /* Constant-current stage */
824 case TOPOFF: /* Constant-voltage stage */
825 {
826 /* Reg. mode is more informative than an operational necessity. */
827 charge_state = (int_sense0 & MC13783_CCCVS) ? TOPOFF : CHARGING;
828
829 if (main_charger_connected())
830 {
831 /* Monitor and stop if current drops below threshold. */
832 if (battery_charge_current() > BATTERY_ICHARGE_COMPLETE)
833 break;
834 }
835 else
836 {
837 /* Accurate I-level can't be determined since device also
838 * powers through the I sense. This simply stops the reporting
839 * of charging but the regulator remains on. */
840 if (battery_voltage() <= BATT_USB_VSTOP)
841 break;
842 }
843
844 stop_charger();
845 break;
846 } /* CHARGING: TOPOFF: */
847
848 default:
849 break;
850 } /* switch */
851
852 /* Check if charger timer expired and stop it if so. */
853 if (charger_total_timer > 0 && --charger_total_timer == 0)
854 {
855 charge_state = CHARGE_STATE_ERROR;
856 stop_charger(); /* Time ran out - error */
857 }
858}
859
860/* Main charging algorithm - called from powermgmt.c */
861void charging_algorithm_small_step(void)
862{
863 if (service_wdt)
864 watchdog_service();
865
866 /* Switch by input state */
867 switch (charger_input_state)
868 {
869 case NO_CHARGER:
870 charger_none();
871 break;
872
873 case CHARGER_PLUGGED:
874 charger_plugged();
875 break;
876
877 case CHARGER:
878 charger_control();
879 break;
880
881 case CHARGER_UNPLUGGED:
882 charger_unplugged();
883 break;
884 } /* switch */
885
886 if (charger_close)
887 {
888 if (charge_state != CHARGE_STATE_DISABLED)
889 {
890 /* Disable starts while shutting down */
891 charge_state = CHARGE_STATE_DISABLED;
892 stop_charger();
893 }
894
895 charger_close = false;
896 return;
897 }
898
899 if (charger_setting != 0)
900 {
901 if ((mc13783_read(MC13783_CHARGER) & (MC13783_ICHRG | MC13783_VCHRG)) !=
902 charger_setting)
903 {
904 /* The hardware setting doesn't match. It could have turned the
905 * charger off in a race of plugging/unplugging or the setting
906 * was changed in one of the calls. */
907 adjust_charger_current();
908 }
909 }
910}
911
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
919 * signal the charging thread to prepare to quit. */
920void charging_algorithm_close(void)
921{
922 charger_close = true;
923
924 /* Power management thread will set it false again */
925 while (charger_close)
926 sleep(HZ/10);
927}
928
929#ifdef BOOTLOADER
930void sys_poweroff(void)
931{
126} 932}
933#endif /* BOOTLOADER */
diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
new file mode 100644
index 0000000000..8ad4af8d18
--- /dev/null
+++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h
@@ -0,0 +1,114 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef POWERMGMT_TARGET_H
22#define POWERMGMT_TARGET_H
23
24/* Can't just run this code willy-nilly. Do not allow charger engagement
25 * without carefully verifying compatibility.
26 *
27 * Things to check:
28 * 1) Charge path configuration for the PMIC.
29 * 2) Correct thermistor reading
30 * 3) Accurate voltage readings
31 * 4) Accurate current sense for the charge path as the sense resistor may
32 * deviate from the 0.1 ohms assumed by the charge path regulator.
33 */
34#ifdef TOSHIBA_GIGABEAT_S
35/*
36 * Gigabeat S qualifications:
37 * 1) Setup for dual-supply mode with separate inputs and providing USB
38 * charging capability through external components.
39 * 2) Curve obtained experimentally - extreme deviation from "optimized"
40 * characteristics.
41 * 3) Verified at battery terminals - no deviation from datasheet formula.
42 * 4) 0.316 ohms <=?? - verified by comparitive current readings on device
43 * with ammeter readings and measurement of on-board components.
44 */
45#ifndef BOOTLOADER
46#define IMX31_ALLOW_CHARGING
47#endif
48
49#else
50#warning This iMX31 target requires validation of charging algorithm - charging disabled
51#endif
52
53#define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */
54#define BATT_VSLOW_CHARGE 3500 /* Lower-current charge mode below
55 * this level */
56#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_VSTOP 4140 /* When to "stop" when USB only */
59#define BATT_TOO_LOW 2400 /* No battery? Short? Can't
60 read below 2400mV. */
61#define CHARGER_TOTAL_TIMER 300 /* minutes */
62
63/* .316 ohms is closest standard value as measured in 1% tolerance - adjust
64 * relative to .100 ohm which is what the PMIC is "tuned" for. */
65#define ILEVEL_ADJUST_IN(I) (100*(I) / 316)
66#define ILEVEL_ADJUST_OUT(I) (316*(I) / 100)
67
68/* Relative draw to battery capacity - adjusted for sense resistor */
69#define BATTERY_ICHARGE_COMPLETE (505*9/100) /* 9% of nominal max output */
70/* All charging modes use 4.200V for regulator */
71#define BATTERY_VCHARGING MC13783_VCHRG_4_200V
72/* Slow charging - MAIN - Still below 3.5V (avoid excessive reg. dissipation) */
73/* #define BATTERY_ISLOW */
74/* Fast charging - MAIN */
75#define BATTERY_IFAST MC13783_ICHRG_1596MA /* 505mA */
76/* Trickle charging low battery - MAIN (~10% Imax) */
77#define BATTERY_ITRICKLE MC13783_ICHRG_177MA /* 56mA */
78/* Slow charging - USB - Still below 3.5V (avoid excessive reg. dissipation) */
79/* #define BATTERY_ISLOW_USB */
80/* Fast charging - USB */
81#define BATTERY_IFAST_USB MC13783_ICHRG_1152MA /* 365mA */
82/* Trickle charging low battery - USB (Ibat = Icccv - Idevice) */
83#define BATTERY_ITRICKLE_USB MC13783_ICHRG_532MA /* 168mA */
84/* Maintain charge - USB 500mA */
85#define BATTERY_IFLOAT_USB MC13783_ICHRG_1152MA /* 365mA */
86#define BATTERY_VFLOAT_USB MC13783_VCHRG_4_150V
87/* Maintain charge - USB 100mA */
88#define BATTERY_IMAINTAIN_USB MC13783_ICHRG_266MA /* 84mA */
89#define BATTERY_VMAINTAIN_USB MC13783_VCHRG_4_150V
90
91/* Battery filter lengths in samples */
92#define BATT_AVE_SAMPLES 32
93#define ICHARGER_AVE_SAMPLES 32
94
95/* Provide filtered charge current */
96int battery_charge_current(void);
97
98#ifndef SIMULATOR
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
114#endif /* POWERMGMT_TARGET_H */
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c
index 6d4797e9df..7d778fb8fd 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/system-imx31.c
@@ -32,6 +32,31 @@
32#include "clkctl-imx31.h" 32#include "clkctl-imx31.h"
33#include "mc13783.h" 33#include "mc13783.h"
34 34
35/* Initialize the watchdog timer */
36void watchdog_init(unsigned int half_seconds)
37{
38 uint16_t wcr = WDOG_WCR_WTw(half_seconds) | /* Timeout */
39 WDOG_WCR_WOE | /* WDOG output enabled */
40 WDOG_WCR_WDA | /* WDOG assertion - no effect */
41 WDOG_WCR_SRS | /* System reset - no effect */
42 WDOG_WCR_WRE; /* Generate a WDOG signal */
43
44 imx31_clkctl_module_clock_gating(CG_WDOG, CGM_ON_RUN_WAIT);
45
46 WDOG_WCR = wcr;
47 WDOG_WSR = 0x5555;
48 WDOG_WCR = wcr | WDOG_WCR_WDE; /* Enable timer - hardware does
49 not allow a disable now */
50 WDOG_WSR = 0xaaaa;
51}
52
53/* Service the watchdog timer */
54void watchdog_service(void)
55{
56 WDOG_WSR = 0x5555;
57 WDOG_WSR = 0xaaaa;
58}
59
35int system_memory_guard(int newmode) 60int system_memory_guard(int newmode)
36{ 61{
37 (void)newmode; 62 (void)newmode;
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h
index 31f1342c9e..fbf7b23f3b 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/system-target.h
@@ -39,6 +39,11 @@ static inline void udelay(unsigned int usecs)
39} 39}
40#endif 40#endif
41 41
42/* Service the watchdog timer - serviced from the power thread every minute */
43void watchdog_init(unsigned int half_seconds);
44void watchdog_service(void);
45
46/* Prepare for transition to firmware */
42void system_prepare_fw_start(void); 47void system_prepare_fw_start(void);
43void tick_stop(void); 48void tick_stop(void);
44void kernel_device_init(void); 49void kernel_device_init(void);
diff --git a/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c b/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c
index 382bc326b7..a7314861c3 100644
--- a/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/usb-imx31.c
@@ -28,6 +28,7 @@
28#include "usb_drv.h" 28#include "usb_drv.h"
29#include "usb-target.h" 29#include "usb-target.h"
30#include "clkctl-imx31.h" 30#include "clkctl-imx31.h"
31#include "power-imx31.h"
31#include "mc13783.h" 32#include "mc13783.h"
32 33
33static int usb_status = USB_EXTRACTED; 34static int usb_status = USB_EXTRACTED;
@@ -53,8 +54,11 @@ static void enable_transceiver(bool enable)
53 54
54void usb_connect_event(void) 55void usb_connect_event(void)
55{ 56{
56 uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0); 57 uint32_t status = mc13783_read(MC13783_INTERRUPT_SENSE0);
57 usb_status = (status & MC13783_USB4V4S) ? USB_INSERTED : USB_EXTRACTED; 58 usb_status = (status & MC13783_USB4V4S) ?
59 USB_INSERTED : USB_EXTRACTED;
60 /* Notify power that USB charging is potentially available */
61 charger_usb_detect_event(usb_status);
58} 62}
59 63
60int usb_detect(void) 64int usb_detect(void)
@@ -81,7 +85,7 @@ void usb_init_device(void)
81 usb_connect_event(); 85 usb_connect_event();
82 86
83 /* Enable PMIC event */ 87 /* Enable PMIC event */
84 mc13783_enable_event(MC13783_USB4V4_EVENT); 88 mc13783_enable_event(MC13783_USB_EVENT);
85} 89}
86 90
87void usb_enable(bool on) 91void usb_enable(bool on)