From 495a43b29ca4ddc7bede1ce1fdfe1daddd6dcc0c Mon Sep 17 00:00:00 2001 From: lhh Date: Fri, 19 Nov 2010 11:13:41 +0800 Subject: [PATCH] add stc3100 battery driver --- arch/arm/mach-rk29/board-rk29sdk.c | 7 + drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/stc3100_battery.c | 309 +++++++++++++++++++++++++++++ 4 files changed, 323 insertions(+) create mode 100644 drivers/power/stc3100_battery.c diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index 8e9b8fdcd79f..1ff06bb853ff 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -410,6 +410,13 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = { .flags = 0, }, #endif +#if defined (CONFIG_BATTERY_STC3100) + { + .type = "stc3100-battery", + .addr = 0x70, + .flags = 0, + }, +#endif }; #endif diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index f909f2f8f9fc..5bac56fd646b 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -110,6 +110,12 @@ config CHARGER_PCF50633 help Say Y to include support for NXP PCF50633 Main Battery Charger. +config BATTERY_STC3100 + tristate "STC3100 battery driver" + depends on I2C && ARCH_RK29 + help + Say Y here to enable support for batteries with STC3100(I2C) chip. + config BATTERY_RK2818 tristate "RK2818 battery" depends on RK28_ADC diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 10caf2200b85..8ef2adcfa713 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_RK2818) += rk2818_battery.o +obj-$(CONFIG_BATTERY_STC3100) += stc3100_battery.o diff --git a/drivers/power/stc3100_battery.c b/drivers/power/stc3100_battery.c new file mode 100644 index 000000000000..5abec0ef7404 --- /dev/null +++ b/drivers/power/stc3100_battery.c @@ -0,0 +1,309 @@ +/* drivers/power/stc3100_battery + * STC31000 battery driver + * + * Copyright (C) 2009 Rockchip Corporation. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define DRIVER_VERSION "1.0.0" + + +#define DC_CHECKPIN RK29_PIN4_PA4 ///da check charge pin +#define BATTAERY_CAPACITY_MAH 2000 /// define battery capacity mah +#define RSENSE_RESISTANCE 10 /// Rsense resistance m¦¸ + +#define STC3100_REG_MODE 0x00 +#define STC3100_REG_CTRL 0x01 +#define STC3100_REG_RSOCL 0x02 /* Relative State-of-Charge */ +#define STC3100_REG_RSOCH 0x03 +#define STC3100_REG_AIL 0x06 +#define STC3100_REG_AIH 0x07 +#define STC3100_REG_VOLTL 0x08 +#define STC3100_REG_VOLTH 0x09 +#define STC3100_REG_TEMPL 0x0A +#define STC3100_REG_TEMPH 0x0B + +#define STC3100_SPEED 300 * 1000 + +struct stc3100_device_info { + struct device *dev; + struct power_supply bat; + struct delayed_work work; + unsigned int interval; + struct i2c_client *client; +}; + +static enum power_supply_property stc3100_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, +}; + + +static int stc3100_write_regs(struct i2c_client *client, u8 reg, u8 const buf[], __u16 len) +{ + int ret; + ret = i2c_master_reg8_send(client, reg, buf, (int)len, STC3100_SPEED); + return ret; +} +/* + * Common code for STC3100 devices read + */ +static int stc3100_read_regs(struct i2c_client *client, u8 reg, u8 buf[], unsigned len) +{ + int ret; + ret = i2c_master_reg8_recv(client, reg, buf, len, STC3100_SPEED); + return ret; +} + +/* + * Return the battery temperature in Celsius degrees + * Or < 0 if something fails. + */ +static int stc3100_battery_temperature(struct stc3100_device_info *di) +{ + int ret; + u8 regs[2]; + + ret = stc3100_read_regs(di->client,STC3100_REG_TEMPL,regs,2); + if (ret) { + dev_err(di->dev, "error reading temperature\n"); + return ret; + } + + return (((((int)regs[1]&0xf)<<8) & (int)regs[0])>>3); ///temperature (¡ãC) = Temperature_code * 0.125. +} + +/* + * Return the battery Voltage in milivolts + * Or < 0 if something fails. + */ +static int stc3100_battery_voltage(struct stc3100_device_info *di) +{ + int ret; + u8 regs[2]; + + ret = stc3100_read_regs(di->client,STC3100_REG_VOLTL,regs,2); + if (ret) { + dev_err(di->dev, "error reading voltage\n"); + return ret; + } + + return (((((int)regs[1]&0xf)<<8) & (int)regs[0])*61/25); //voltage (mV) = Voltage_code * 2.44. +} + +/* + * Return the battery average current + * Note that current can be negative signed as well + * Or 0 if something fails. + */ +static int stc3100_battery_current(struct stc3100_device_info *di) +{ + int ret; + u8 regs[2]; + + ret = stc3100_read_regs(di->client,STC3100_REG_AIL,regs,2); + if (ret) { + dev_err(di->dev, "error reading current\n"); + return 0; + } + + return (((((int)regs[1]&0xf)<<8) & (int)regs[0])*1177/(100*RSENSE_RESISTANCE)); ///current (mA) = current_code* 11.77 / Rsense (m¦¸) +} + +/* + * Return the battery Relative State-of-Charge + * Or < 0 if something fails. + */ +static int stc3100_battery_rsoc(struct stc3100_device_info *di) +{ + int ret; + u8 regs[2]; + + ret = stc3100_read_regs(di->client,STC3100_REG_RSOCL,regs,2); + if (ret) { + dev_err(di->dev, "error reading relative State-of-Charge\n"); + return ret; + } + + return (((((int)regs[1])<<8) & (int)regs[0])*67/(10*RSENSE_RESISTANCE)); ////charge data (mA.h) = 6.70 * charge_code / Rsense (m¦¸). +} + +static int dc_charge_status(void) +{ + if(gpio_get_value(DC_CHECKPIN)) + return POWER_SUPPLY_STATUS_CHARGING; + else + return POWER_SUPPLY_STATUS_NOT_CHARGING; +} + +static int stc3100_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct stc3100_device_info *di = container_of(psy, struct stc3100_device_info, bat); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = dc_charge_status(); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_PRESENT: + val->intval = stc3100_battery_voltage(di); + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = val->intval <= 0 ? 0 : 1; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = stc3100_battery_current(di); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = stc3100_battery_rsoc(di); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = stc3100_battery_temperature(di); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stc3100_powersupply_init(struct stc3100_device_info *di) +{ + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = stc3100_battery_props; + di->bat.num_properties = ARRAY_SIZE(stc3100_battery_props); + di->bat.get_property = stc3100_battery_get_property; + di->bat.external_power_changed = NULL; +} + +static void stc3100_battery_update_status(struct stc3100_device_info *di) +{ + power_supply_changed(&di->bat); +} + +static void stc3100_battery_work(struct work_struct *work) +{ + struct stc3100_device_info *di = container_of(work, struct stc3100_device_info, work.work); + + stc3100_battery_update_status(di); + /* reschedule for the next time */ + schedule_delayed_work(&di->work, di->interval); +} + +static int stc3100_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct stc3100_device_info *di; + int retval = 0; + u8 regs[2] = {0x10,0x1d}; ///init regs mode ctrl + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "failed to allocate device info data\n"); + retval = -ENOMEM; + goto batt_failed_2; + } + + i2c_set_clientdata(client, di); + di->dev = &client->dev; + di->bat.name = "stc3100-battery"; + di->client = client; + stc3100_write_regs(client, STC3100_REG_MODE, regs, 2); + /* 4 seconds between monotor runs interval */ + di->interval = msecs_to_jiffies(4 * 1000); + stc3100_powersupply_init(di); + + retval = power_supply_register(&client->dev, &di->bat); + if (retval) { + dev_err(&client->dev, "failed to register battery\n"); + goto batt_failed_3; + } + + INIT_DELAYED_WORK(&di->work, stc3100_battery_work); + schedule_delayed_work(&di->work, di->interval); + + dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); + + return 0; + +batt_failed_3: + kfree(di); +batt_failed_2: + return retval; +} + +static int stc3100_battery_remove(struct i2c_client *client) +{ + struct stc3100_device_info *di = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&di->work); + power_supply_unregister(&di->bat); + + kfree(di->bat.name); + + kfree(di); + + return 0; +} + +/* + * Module stuff + */ + +static const struct i2c_device_id stc3100_id[] = { + { "stc3100", 0 }, + {}, +}; + +static struct i2c_driver stc3100_battery_driver = { + .driver = { + .name = "stc3100-battery", + }, + .probe = stc3100_battery_probe, + .remove = stc3100_battery_remove, + .id_table = stc3100_id, +}; + +static int __init stc3100_battery_init(void) +{ + int ret; + + ret = i2c_add_driver(&stc3100_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27200 driver\n"); + + return ret; +} +module_init(stc3100_battery_init); + +static void __exit stc3100_battery_exit(void) +{ + i2c_del_driver(&stc3100_battery_driver); +} +module_exit(stc3100_battery_exit); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("BQ27x00 battery monitor driver"); +MODULE_LICENSE("GPL"); -- 2.34.1