blob: 2fe6f466e42cf10b6a702f12024fe3822aeafbef [file] [log] [blame]
/*
* Copyright 2012, Google 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 Google Inc. 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.
*/
#include <inttypes.h>
#include <unistd.h>
#include "stout.h"
#include "mosys/log.h"
#include "mosys/platform.h"
/* returns 0 to indicate success, <0 to indicate failure */
static int stout_wait_battery_auth_complete(struct platform_intf *intf)
{
uint8_t status;
do {
if (ec_command(intf, STOUT_ECCMD_GET_BATTERY_AUTH_STATUS,
NULL, 0, &status, 1) < 0)
return -1;
if (status == 0xff)
return -1;
else if (status == 0xaa)
break;
sleep(2);
} while (1);
return 0;
}
/*
* stout_get_battery_fud - return battery first use date
*
* @intf: platform interface
*
* returns pointer to first use date string if successful.
* returns NULL to indicate failure.
*/
static const char *stout_get_battery_fud(struct platform_intf *intf)
{
static char first_use_date[11];
uint8_t status, date_hi, date_lo;
int day, month, year;
if (ecram_read(intf, STOUT_ECMEM_BATTERY_STATUS, &status,
STOUT_ECCMD_MEM_READ) < 0)
return NULL;
/* Bit 7: main battery attached. */
if ((status & 0x80) == 0) {
lprintf(LOG_DEBUG, "%s: no battery present.\n", __func__);
return NULL;
}
if (stout_wait_battery_auth_complete(intf) < 0) {
lprintf(LOG_DEBUG, "%s: Battery auth wait failed.\n", __func__);
return NULL;
}
if (ec_command(intf, STOUT_ECCMD_GET_BATTERY_FIRST_USE_DATE_HI, NULL,
0, &date_hi, 1) < 0 )
return NULL;
if (ec_command(intf, STOUT_ECCMD_GET_BATTERY_FIRST_USE_DATE_LO, NULL,
0, &date_lo, 1) < 0 )
return NULL;
year = ((date_hi & 0xFE) >> 1) + 1980;
month = (date_hi & 0x01) * 8 + ((date_lo & 0xE0) >> 5);
day = (date_lo & 0x1F);
sprintf(first_use_date, "%04d/%02d/%02d", year, month, day);
return first_use_date;
}
/*
* stout_set_battery_fud - Sets battery first use date.
*
* @intf: platform interface
* day: day of month to set
* month: month of year to set
* year: year to set
*
* returns 0 if successful or < 0 if failure.
*/
static int stout_set_battery_fud(struct platform_intf *intf,
int day, int month, int year)
{
uint8_t status, date_hi, date_lo;
if (day < 0 || day > 31 || month < 0 || month > 12 || year < 1980) {
lprintf(LOG_DEBUG, "%s: invalid date setting - %d %d %d.\n",
__func__, day, month, year);
return -1;
}
if (ecram_read(intf, STOUT_ECMEM_BATTERY_STATUS, &status,
STOUT_ECCMD_MEM_READ) < 0)
return -1;
/* Bit 7: main battery attached. */
if ((status & 0x80) == 0) {
lprintf(LOG_DEBUG, "%s: no battery present.\n", __func__);
return -1;
}
if (stout_wait_battery_auth_complete(intf) < 0) {
lprintf(LOG_DEBUG, "%s: Battery auth wait failed.\n", __func__);
return -1;
}
/* Make sure FUD has not already been set. */
if (ec_command(intf, STOUT_ECCMD_GET_BATTERY_FIRST_USE_DATE_STATUS,
NULL, 0, &status, 1) < 0)
return -1;
if (status == 0) {
lprintf(LOG_DEBUG, "%s: FUD already set.\n", __func__);
return -1;
}
/* Attempt to set FUD */
lprintf(LOG_DEBUG, "%s: Set FUD to %04d/%02d/%02d.\n", __func__,
year, month, day);
date_hi = ((year - 1980) << 1) + (month >> 3);
date_lo = (month & 0x07) << 5;
date_lo |= day;
if (ecram_write(intf, STOUT_ECMEM_BATTERY_FIRST_USE_DATE_HI,
date_hi) < 0)
return -1;
if (ecram_write(intf, STOUT_ECMEM_BATTERY_FIRST_USE_DATE_LO,
date_lo) < 0)
return -1;
if (ec_command(intf, STOUT_ECCMD_LATCH_BATTERY_FIRST_USE_DATE,
NULL, 0, &status, 1) < 0)
return -1;
return 0;
}
/*
* stout_update_battery_fw - Attempts to update battery firmware.
*
* @intf: platform interface
*
* returns 0 if successful or < 0 if failure.
*/
static int stout_update_battery_fw(struct platform_intf *intf)
{
uint8_t status;
stout_ec_command cmd = STOUT_ECCMD_MEM_READ;
if (ecram_read(intf, STOUT_ECMEM_BATTERY_STATUS, &status, cmd) < 0)
return -1;
/* Bit 7: main battery attached. */
if ((status & 0x80) == 0) {
lprintf(LOG_DEBUG, "%s: no battery present.\n", __func__);
return -1;
}
/* Check if update needed. */
if (ec_command(intf, STOUT_ECCMD_BATTERY_FW_UPDATE_NEEDED,
NULL, 0, &status, 1) < 0)
return -1;
if (status != 0) {
lprintf(LOG_DEBUG, "%s: no battery fw update needed: %d.\n",
__func__, status);
return -1;
}
/* Start update. */
if (ecram_read(intf, STOUT_ECMEM_BATTERY_FW_UPDATE, &status, cmd) < 0)
return -1;
lprintf(LOG_DEBUG, "%s: Starting FW update: %x.\n",
__func__, status);
if (ecram_write(intf, STOUT_ECMEM_BATTERY_FW_UPDATE, status | 0x20) < 0)
return -1;
/* Wait for update completion. */
while (1) {
if (ec_command(intf, STOUT_ECCMD_BATTERY_FW_UPDATE_STATUS,
NULL, 0, &status, 1) < 0)
return -1;
if (status == 1)
break;
sleep(1);
}
lprintf(LOG_DEBUG, "%s: finished FW update: %x.\n",
__func__, status);
/* Clear update flag and check competion status. */
if (ecram_read(intf, STOUT_ECMEM_BATTERY_FW_UPDATE, &status, cmd) < 0)
return -1;
if (ecram_write(intf, STOUT_ECMEM_BATTERY_FW_UPDATE,
status & ~(0x20)) < 0)
return -1;
if (ec_command(intf, STOUT_ECCMD_BATTERY_FW_UPDATE_COMPLETION_STATUS,
NULL, 0, &status, 1) < 0)
return -1;
if (status == 0) {
lprintf(LOG_DEBUG, "%s: battery FW updated.\n",
__func__);
return 0;
} else {
lprintf(LOG_DEBUG, "%s: battery FW update failed: %d.\n",
__func__, status);
return -1;
}
}
struct battery_cb stout_battery_cb = {
.get_fud = stout_get_battery_fud,
.set_fud = stout_set_battery_fud,
.update = stout_update_battery_fw,
};