--- /dev/null
+/*
+ * Copyright (c) 2008, Titanium Mirror, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * - Neither the name of the Technische Universität Berlin nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * HIL implementation for the BQ2403X charge controller chip family.
+ *
+ * @author R. Steve McKown <smckown@gmail.com>
+ */
+
+#include "BQ2403X.h"
+
+module BQ2403XP {
+ provides {
+ interface BQ2403X;
+ interface Init;
+ }
+ uses {
+ interface GeneralIO as BQstat1;
+ interface GeneralIO as BQstat2;
+ interface GeneralIO as ACPGn;
+ interface GeneralIO as USBPGn;
+
+ interface GpioInterrupt as IntBQstat1;
+ interface GpioInterrupt as IntBQstat2;
+ interface GpioInterrupt as IntACPGn;
+ interface GpioInterrupt as IntUSBPGn;
+
+ interface Timer<TMilli>;
+ }
+}
+implementation {
+ const static unsigned DELAY = 256; /* 1/4 sec in binary milliseconds */
+ bool enabled;
+ uint8_t state;
+ uint8_t lastState;
+
+ uint8_t bits2charge()
+ {
+ /* Conversion table:
+ * Name BQstat1 BQstat2 Value Desired
+ * ----------------------------------------------
+ * Not charging 1 1 3 0
+ * Precharge 0 0 0 1
+ * Fast charge 0 1 1 2
+ * Charge done 1 0 2 3
+ */
+ return ((call BQstat1.get() << 1) + call BQstat2.get() + 1) & 0x3;
+ }
+
+ uint8_t bits2power()
+ {
+ /* Conversion table:
+ * Name ACPGn USBPGn Value Desired
+ * ----------------------------------------
+ * On Battery 1 1 3 0
+ * USB power 1 0 2 1
+ * AC power 0 1 1 2
+ * Both AC+USB 0 0 0 3
+ */
+ return ~((call ACPGn.get() << 5) + (call USBPGn.get() << 4)) & 0x30;
+ }
+
+ void setInt()
+ {
+ /* Too many race conditions. Implement IO pins as toggles w/deglitch.
+ * Or, poll. Best solution is probably to interrupt on state change then
+ * poll a bit later to deal with deglitch.
+ */
+ if (call BQstat1.get())
+ call IntBQstat1.enableFallingEdge();
+ else
+ call IntBQstat1.enableRisingEdge();
+ if (call BQstat2.get())
+ call IntBQstat2.enableFallingEdge();
+ else
+ call IntBQstat2.enableRisingEdge();
+ if (call ACPGn.get())
+ call IntACPGn.enableFallingEdge();
+ else
+ call IntACPGn.enableRisingEdge();
+ if (call USBPGn.get())
+ call IntUSBPGn.enableFallingEdge();
+ else
+ call IntUSBPGn.enableRisingEdge();
+ }
+
+ void unsetInt()
+ {
+ call IntBQstat1.disable();
+ call IntBQstat2.disable();
+ call IntACPGn.disable();
+ call IntUSBPGn.disable();
+ }
+
+ void update()
+ {
+ atomic {
+ state = bits2power() + bits2charge();
+ setInt();
+ }
+ }
+
+ command error_t Init.init()
+ {
+ update();
+ lastState = state;
+ return SUCCESS;
+ }
+
+ async command void BQ2403X.enable()
+ {
+ atomic {
+ enabled = TRUE;
+ setInt();
+ }
+ }
+
+ async command void BQ2403X.disable()
+ {
+ atomic {
+ enabled = FALSE;
+ unsetInt();
+ }
+ }
+
+ async command bq2403x_state_t BQ2403X.getState()
+ {
+ return state;
+ }
+
+ async command bq2403x_state_t BQ2403X.getCharge()
+ {
+ return state & BQ2403X_CHG_MASK;
+ }
+
+ async command bq2403x_state_t BQ2403X.getPower()
+ {
+ return state & BQ2403X_PWR_MASK;
+ }
+
+ async command bool BQ2403X.isCharging()
+ {
+ return (state + 1) & 0x2;
+ }
+
+ async command bool BQ2403X.battOnly()
+ {
+ return call BQ2403X.getPower() == 0;
+ }
+
+ task void signalChange()
+ {
+ bq2403x_state_t cstate;
+
+ atomic cstate = state;
+ signal BQ2403X.change(cstate, lastState);
+ lastState = cstate;
+ }
+
+ task void startTimer()
+ {
+ call Timer.startOneShot(DELAY);
+ }
+
+ async event void IntBQstat1.fired()
+ {
+ post startTimer();
+ }
+
+ async event void IntBQstat2.fired()
+ {
+ post startTimer();
+ }
+
+ async event void IntACPGn.fired()
+ {
+ post startTimer();
+ }
+
+ async event void IntUSBPGn.fired()
+ {
+ post startTimer();
+ }
+
+ event void Timer.fired()
+ {
+ atomic {
+ update();
+ if (enabled)
+ post signalChange();
+ }
+ }
+
+ default async event void BQ2403X.change(bq2403x_state_t current,
+ bq2403x_state_t last) {}
+}