Commit 211ce073 authored by Radek Polak's avatar Radek Polak

gta04 charging - implement charging logic to avoid charging bugs

GTA04 charging on some chargers is bugged. The problem is that by
default kernel sets charging current to 600mA and if the USB power
supply is not strong enough, then voltage drops under 4.5V. This
confuses GTA04 charging logic and the result is that GTA04 after
charged to full then discharges to 0 because charging never starts
again. See twl4030_charger.c and especially the function
twl4030_charger_enable_usb().

The solution QtMoko now uses is that we disable charging with 600mA
when QtMoko starts. That's the line:

qWriteFile("/sys/module/twl4030_charger/parameters/allow_usb", "N");

Now when USB is plugged it starts charging just with 100mA. We slowly
raise the current and check if voltage remains above 4.5V.

There is even some logic when battery is getting full we lower the
charging current (using values from battery) so that battery charges
very very slowly.

You can check charging in NeoControl - it has nice charging graph now.
parent 9a35c614
......@@ -88,6 +88,7 @@ ac(QPowerSource::Wall, "PrimaryAC", this)
, lastLogDt()
, ueventSocket(this)
, timer(this)
, maxChargeCurrent(-1)
{
qLog(Hardware) << "gta04 hardware";
......@@ -111,10 +112,9 @@ ac(QPowerSource::Wall, "PrimaryAC", this)
QtopiaIpcAdaptor::connect(adaptor, MESSAGE(headphonesInserted(bool)),
this, SLOT(headphonesInserted(bool)));
// By default limit charging current to 250mA, we will increase it once we
// know that charging voltage wont drop under 4.5V, for more info see:
// http://lists.goldelico.com/pipermail/gta04-owner/2013-September/004963.html
setMaxChargeCurrent(INIT_CURRENT);
// Y sets usb charging limit to 600mA which can be too high and causes
// charging voltage drops. We will set limit manually to prevent this.
qWriteFile("/sys/module/twl4030_charger/parameters/allow_usb", "N");
}
NeoHardware::~NeoHardware()
......@@ -217,26 +217,35 @@ void NeoHardware::updateStatus()
// Now we will try to set max_current so that phone charges reliably.
int chargerVoltage = -1;
if (chargerOn) {
if(maxChargeCurrent < 0)
setMaxChargeCurrent(INIT_CURRENT);
QByteArray chargerUevent =
qReadFile("/sys/class/power_supply/twl4030_usb/uevent");
chargerVoltage = getIntAttr("POWER_SUPPLY_VOLTAGE_NOW=", chargerUevent);
// Warn if charging voltage drops too low
if (chargerVoltage < 4500000)
if (chargerVoltage < 4500000 && chargerVoltage > 0)
qWarning() << "Charging voltage dropped to " << chargerVoltage <<
", charging might not restart when battery gets low";
// Battery discharges
if (currentNow > 0) {
// Increase charging current until 500mA if voltage is above 4.6V
if (chargerVoltage > 4600000) {
setMaxChargeCurrent(maxChargeCurrent + CURRENT_PLUS);
if(oldChargeNow != chargeNow) { // this prevents too fast updates
// Battery discharges
if (currentNow > 0) {
// Increase charging current until 500mA, but make sure that
// voltage wont drop under 4.5V, for more info see:
// http://lists.goldelico.com/pipermail/gta04-owner/2013-September/004963.html
if (chargerVoltage > 4600000) {
setMaxChargeCurrent(maxChargeCurrent + CURRENT_PLUS);
QTimer::singleShot(5000, this, SLOT(updateStatus()));
}
} else if (capacity > 90 && currentNow < -5000) { // Battery is finishing charging
setMaxChargeCurrent(maxChargeCurrent + currentNow / 2); // slow it down
}
} else if (capacity > 90 && currentNow < -5000 && oldChargeNow != chargeNow) { // Battery is finishing charging, we will slow charging
setMaxChargeCurrent(maxChargeCurrent + currentNow / 2); // slow it down
}
} else if (maxChargeCurrent > INIT_CURRENT) {
setMaxChargeCurrent(INIT_CURRENT);
maxChargeCurrent = -1;
}
oldChargeNow = chargeNow;
......
......@@ -41,7 +41,7 @@ class QSpeakerPhoneAccessoryProvider;
// Initial charging current - we will raise if until charging voltage does not
// drop under 4.6V
#define INIT_CURRENT 250000
#define INIT_CURRENT 100000
// Max charging current
#define MAX_CURRENT 500000
......
#include "neocontrol.h"
#include <QPainter>
NeoControl::NeoControl(QWidget *parent, Qt::WFlags f)
: QWidget(parent)
#ifdef QTOPIA
, chargeLogVsi("/UI/Battery/charge_log")
#endif
{
#ifdef QTOPIA
this->setWindowState(Qt::WindowMaximized);
#else
Q_UNUSED(f);
#endif
bQvga = new QPushButton(tr("Switch to QVGA"), this);
connect(bQvga, SIGNAL(clicked()), this, SLOT(qvgaClicked()));
bBack = new QPushButton(this);
connect(bBack, SIGNAL(clicked()), this, SLOT(backClicked()));
......@@ -43,7 +44,6 @@ NeoControl::NeoControl(QWidget *parent, Qt::WFlags f)
buttonLayout->addWidget(bNext);
layout = new QVBoxLayout(this);
layout->addWidget(bQvga);
layout->addWidget(label);
layout->addWidget(label4);
layout->addWidget(slider4);
......@@ -55,7 +55,7 @@ NeoControl::NeoControl(QWidget *parent, Qt::WFlags f)
layout->addWidget(chkFso);
layout->addLayout(buttonLayout);
showScreen(NeoControl::ScreenInit);
showScreen(NeoControl::ScreenCharge);
}
NeoControl::~NeoControl()
......@@ -109,7 +109,7 @@ void NeoControl::backClicked()
case ScreenSysfs:
showScreen(ScreenModem);
break;
case ScreenDisplay:
case ScreenCharge:
showScreen(ScreenSysfs);
break;
}
......@@ -132,9 +132,9 @@ void NeoControl::nextClicked()
showScreen(ScreenSysfs);
break;
case ScreenSysfs:
showScreen(ScreenDisplay);
showScreen(ScreenCharge);
break;
case ScreenDisplay:
case ScreenCharge:
break;
}
}
......@@ -147,23 +147,6 @@ void NeoControl::saveClicked()
}
}
void NeoControl::qvgaClicked()
{
QFile cal("/etc/pointercal_qvga");
if(!cal.exists())
{
cal.open(QFile::WriteOnly);
cal.write("-360 16920 -2227050 -21726 -170 20270704 56321");
cal.close();
}
QFile f("/tmp/restart-qtopia-qvga");
f.open(QFile::WriteOnly);
f.write("restart in qvga");
f.close();
QMessageBox::information(this, tr("QVGA setup"), tr("QVGA mode will be activated after restarting QtExtended with POWER button"));
}
void NeoControl::showScreen(NeoControl::Screen scr)
{
if(scr == ScreenMixer)
......@@ -182,10 +165,17 @@ void NeoControl::showScreen(NeoControl::Screen scr)
{
label->setFont(smallFont);
}
if(this->screen == ScreenCharge)
{
setFont(normalFont);
}
if(scr == ScreenCharge)
{
setFont(smallFont);
}
this->screen = scr;
bQvga->setVisible(scr == ScreenDisplay);
label->setVisible(scr == ScreenInit || scr == ScreenRtc || scr == ScreenMixer || scr == ScreenModem || scr == ScreenSysfs);
bBack->setText(scr == ScreenInit ? tr("Quit") : tr("Back"));
lineEdit->setVisible(false);
......@@ -211,7 +201,8 @@ void NeoControl::showScreen(NeoControl::Screen scr)
case ScreenModem:
updateModem();
break;
case ScreenDisplay:
case ScreenCharge:
updateCharge();
break;
case ScreenSysfs:
updateSysfs();
......@@ -222,6 +213,116 @@ void NeoControl::showScreen(NeoControl::Screen scr)
}
}
static int computeCurrent(int secs, int chargeBefore, int chargeAfter)
{
return ((chargeBefore - chargeAfter) * 36) / (10 * secs);
}
void NeoControl::paintEvent(QPaintEvent *)
{
if(screen != ScreenCharge)
{
return;
}
QList<QString> lines = chargeLog.split('\n');
if(lines.count() < 2)
return;
QList<QDateTime> dates;
QList<int> charges;
QDateTime dtMin(QDate(2999, 1, 1));
QDateTime dtMax(QDate(1899, 1, 1));
int chargeMax = 0;
for(int i = 0; i < lines.count(); i++) {
QList<QString> values = lines.at(i).split('\t');
if(values.count() < 2) {
continue;
}
QDateTime dt = QDateTime::fromString(values.at(0), "yyyy-MM-dd hh:mm:ss");
int charge = values.at(1).toInt();
dates.append(dt);
charges.append(charge);
dtMin = (dtMin < dt ? dtMin : dt);
dtMax = (dtMax > dt ? dtMax : dt);
chargeMax = (chargeMax > charge ? chargeMax : charge);
}
int w = (9 * this->width()) / 10;
int h = bNext->y() - bNext->height();
int totalSecs = dtMin.secsTo(dtMax);
int x1 = -1;
int y1 = -1;
int prevSecs = -1;
int prevCharge = -1;
if(chargeMax == 0 || totalSecs == 0)
return;
QPainter p(this);
int textY = 0x7fffffff;
int hourTextX = 0x7fffffff;
int fontW = p.fontMetrics().width('w');
int fontH = p.fontMetrics().height();
p.translate(fontW, fontH);
p.drawLine(0, h, w, h);
p.drawLine(0, 0, 0, h);
QPen pen = p.pen();
for(int i = 0; i < dates.count(); i++) {
QDateTime dt = dates.at(i);
int charge = charges.at(i);
int secs = dtMin.secsTo(dt);
int x2 = (w * secs) / totalSecs;
int y2 = h - (h * charge) / chargeMax;
// Draw time on x axis
if(abs(x2 - hourTextX) > 5 * fontW)
{
p.drawText(x2, h + fontH, dt.toString("hh:mm"));
p.setPen(Qt::darkGreen);
p.drawLine(x2, 0, x2, h);
p.setPen(pen);
hourTextX = x2;
}
// Draw charge point and charge value
p.drawEllipse(x2 - 2, y2 - 2, 4, 4);
int y = y2 + fontH / 2;
if(abs(y2 - textY) > 2 * fontH) {
QString text = QString::number(charge);
p.drawText(x2 + fontW, y, text);
textY = y;
}
// Draw charge line and in the middle write current
if(x1 >= 0) {
p.drawLine(x1, y1, x2, y2);
y = (y1 + y2) / 2;
if(abs(y - textY) > 2 * fontH) {
int current = computeCurrent(secs - prevSecs, prevCharge, charge);
p.drawText((x1 + x2) / 2 + fontW, y, QString::number(current) + "mA");
textY = y;
}
}
x1 = x2;
y1 = y2;
prevSecs = secs;
prevCharge = charge;
}
}
void NeoControl::updateRtc()
{
if(screen != ScreenRtc)
......@@ -486,3 +587,20 @@ void NeoControl::updateSysfs()
QTimer::singleShot(1000, this, SLOT(updateSysfs()));
}
void NeoControl::updateCharge()
{
if(screen != ScreenCharge)
{
return;
}
#ifdef QTOPIA
chargeLog = chargeLogVsi.value().toString();
#else
chargeLog = "2013-09-11 20:45:14\t969790\n2013-09-12 06:33:01\t344148\n2013-09-12 09:55:33\t83716\n2013-09-12 10:00:41\t100674\n2013-09-12 10:05:41\t117988\n2013-09-12 10:11:11\t137088\n2013-09-12 10:16:11\t153688\n2013-09-12 10:21:41\t172788\n2013-09-12 10:26:41\t189924\n2013-09-12 10:31:41\t207238\n2013-09-12 10:37:11\t225981\n2013-09-12 10:42:41\t244902\n2013-09-12 10:48:11\t263823\n2013-09-12 10:53:11\t281137\n2013-09-12 10:58:11\t298273\n2013-09-12 11:03:11\t314874";
#endif
QTimer::singleShot(10000, this, SLOT(updateCharge()));
update();
}
......@@ -25,6 +25,7 @@
#include <QDateTime>
#ifdef QTOPIA
#include <Qtopia>
#include <QValueSpaceItem>
#include <QtopiaApplication>
#endif
#ifdef Q_WS_WIN
......@@ -41,6 +42,9 @@ public:
NeoControl(QWidget *parent = 0, Qt::WFlags f = 0);
~NeoControl();
protected:
void paintEvent(QPaintEvent *);
private:
enum Screen
{
......@@ -49,7 +53,7 @@ private:
ScreenMixer,
ScreenModem,
ScreenSysfs,
ScreenDisplay,
ScreenCharge,
};
Screen screen;
......@@ -59,7 +63,6 @@ private:
QHBoxLayout *buttonLayout;
QLabel *label;
QLineEdit *lineEdit;
QPushButton *bQvga;
QPushButton *bBack;
QPushButton *bNext;
QPushButton *bSave;
......@@ -71,6 +74,10 @@ private:
MixerSlider *slider5;
QFont normalFont;
QFont smallFont;
QString chargeLog;
#ifdef QTOPIA
QValueSpaceItem chargeLogVsi;
#endif
void showScreen(NeoControl::Screen scr);
int openAlsaMixer();
......@@ -79,7 +86,6 @@ private:
void setQpeEnv(bool);
private slots:
void qvgaClicked();
void backClicked();
void nextClicked();
void saveClicked();
......@@ -87,6 +93,7 @@ private slots:
void updateMixer();
void updateModem();
void updateSysfs();
void updateCharge();
void muxStateChanged(int);
void fsoStateChanged(int);
void fsoChange();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment