/*
* sbs.c - ACPI Smart Battery System Driver ($Revision: 2.0 $)
*
* Copyright (c) 2007 Alexey Starikovskiy <astarikovskiy@suse.de>
* Copyright (c) 2005-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com>
* Copyright (c) 2005 Rich Townsend <rhdt@bartol.udel.edu>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#ifdef CONFIG_ACPI_PROCFS_POWER
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#endif
#include <linux/acpi.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#ifdef CONFIG_ACPI_SYSFS_POWER
#include <linux/power_supply.h>
#endif
#include "sbshc.h"
#define ACPI_SBS_CLASS "sbs"
#define ACPI_AC_CLASS "ac_adapter"
#define ACPI_BATTERY_CLASS "battery"
#define ACPI_SBS_DEVICE_NAME "Smart Battery System"
#define ACPI_SBS_FILE_INFO "info"
#define ACPI_SBS_FILE_STATE "state"
#define ACPI_SBS_FILE_ALARM "alarm"
#define ACPI_BATTERY_DIR_NAME "BAT%i"
#define ACPI_AC_DIR_NAME "AC0"
#define ACPI_SBS_NOTIFY_STATUS 0x80
#define ACPI_SBS_NOTIFY_INFO 0x81
MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
MODULE_LICENSE("GPL");
static unsigned int cache_time = 1000;
module_param(cache_time, uint, 0644);
MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
extern struct proc_dir_entry *acpi_lock_ac_dir(void);
extern struct proc_dir_entry *acpi_lock_battery_dir(void);
extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
#define MAX_SBS_BAT 4
#define ACPI_SBS_BLOCK_MAX 32
static const struct acpi_device_id sbs_device_ids[] = {
{"ACPI0002", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
struct acpi_battery {
#ifdef CONFIG_ACPI_SYSFS_POWER
struct power_supply bat;
#endif
struct acpi_sbs *sbs;
#ifdef CONFIG_ACPI_PROCFS_POWER
struct proc_dir_entry *proc_entry;
#endif
unsigned long update_time;
char name[8];
char manufacturer_name[ACPI_SBS_BLOCK_MAX];
char device_name[ACPI_SBS_BLOCK_MAX];
char device_chemistry[ACPI_SBS_BLOCK_MAX];
u16 alarm_capacity;
u16 full_charge_capacity;
u16 design_capacity;
u16 design_voltage;
u16 serial_number;
u16 cycle_count;
u16 temp_now;
u16 voltage_now;
s16 current_now;
s16 current_avg;
u16 capacity_now;
u16 state_of_charge;
u16 state;
u16 mode;
u16 spec;
u8 id;
u8 present:1;
u8 have_sysfs_alarm:1;
};
#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat);
struct acpi_sbs {
#ifdef CONFIG_ACPI_SYSFS_POWER
struct power_supply charger;
#endif
struct acpi_device *device;
struct acpi_smb_hc *hc;
struct mutex lock;
#ifdef CONFIG_ACPI_PROCFS_POWER
struct proc_dir_entry *charger_entry;
#endif
struct acpi_battery battery[MAX_SBS_BAT];
u8 batteries_supported:4;
u8 manager_present:1;
u8 charger_present:1;
};
#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
static inline int battery_scale(int log)
{
int scale = 1;
while (log--)
scale *= 10;
return scale;
}
static inline int acpi_battery_vscale(struct acpi_battery *battery)
{
return battery_scale((battery->spec & 0x0f00) >> 8);
}
static inline int acpi_battery_ipscale(struct acpi_battery *battery)
{
return battery_scale((battery->spec & 0xf000) >> 12);
}
static inline int acpi_battery_mode(struct acpi_battery *battery)
{
return (battery->mode & 0x8000);
}
static inline int acpi_battery_scale(struct acpi_battery *battery)
{
return (acpi_battery_mode(battery) ? 10 : 1) *
acpi_battery_ipscale(battery);
}
#ifdef CONFIG_ACPI_SYSFS_POWER
static int sbs_get_ac_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct acpi_sbs *sbs = to_acpi_sbs(psy);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE