diff -uNr linux-2.6.16.1/Makefile linux-2.6.16.1-imac/Makefile --- linux-2.6.16.1/Makefile 2006-04-01 23:47:09.000000000 +0000 +++ linux-2.6.16.1-imac/Makefile 2006-04-01 23:51:51.000000000 +0000 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 16 -EXTRAVERSION = .1 +EXTRAVERSION = .1-imac NAME=Sliding Snow Leopard # *DOCUMENTATION* diff -uNr linux-2.6.16.1/arch/i386/kernel/dmi_scan.c linux-2.6.16.1-imac/arch/i386/kernel/dmi_scan.c --- linux-2.6.16.1/arch/i386/kernel/dmi_scan.c 2006-04-01 23:47:09.000000000 +0000 +++ linux-2.6.16.1-imac/arch/i386/kernel/dmi_scan.c 2006-04-01 23:51:38.000000000 +0000 @@ -5,6 +5,7 @@ #include #include #include +#include static char * __init dmi_string(struct dmi_header *dm, u8 s) { @@ -184,39 +185,66 @@ } } -void __init dmi_scan_machine(void) +static int __init dmi_present(char __iomem *p) { u8 buf[15]; + memcpy_fromio(buf, p, 15); + if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) { + u16 num = (buf[13] << 8) | buf[12]; + u16 len = (buf[7] << 8) | buf[6]; + u32 base = (buf[11] << 24) | (buf[10] << 16) | + (buf[9] << 8) | buf[8]; + + /* + * DMI version 0.0 means that the real version is taken from + * the SMBIOS version, which we don't know at this point. + */ + if (buf[14] != 0) + printk(KERN_INFO "DMI %d.%d present.\n", + buf[14] >> 4, buf[14] & 0xF); + else + printk(KERN_INFO "DMI present.\n"); + if (dmi_table(base,len, num, dmi_decode) == 0) + return 0; + } + + return 1; +} + +void __init dmi_scan_machine(void) +{ char __iomem *p, *q; + int rc; - /* - * no iounmap() for that ioremap(); it would be a no-op, but it's - * so early in setup that sucker gets confused into doing what - * it shouldn't if we actually call it. - */ - p = ioremap(0xF0000, 0x10000); - if (p == NULL) - goto out; - for (q = p; q < p + 0x10000; q += 16) { - memcpy_fromio(buf, q, 15); - if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) { - u16 num = (buf[13] << 8) | buf[12]; - u16 len = (buf[7] << 8) | buf[6]; - u32 base = (buf[11] << 24) | (buf[10] << 16) | - (buf[9] << 8) | buf[8]; - - /* - * DMI version 0.0 means that the real version is taken from - * the SMBIOS version, which we don't know at this point. - */ - if (buf[14] != 0) - printk(KERN_INFO "DMI %d.%d present.\n", - buf[14] >> 4, buf[14] & 0xF); - else - printk(KERN_INFO "DMI present.\n"); + if (efi_enabled) { + if (!efi.smbios) + goto out; + + p = efi.smbios; + + if (p == NULL) + goto out; + + rc = dmi_present(p + 0x10); /* offset of _DMI_ string */ - if (dmi_table(base,len, num, dmi_decode) == 0) + if (!rc) + return; + + } else { + + /* + * no iounmap() for that ioremap(); it would be a no-op, but + * it's so early in setup that sucker gets confused into doing + * what it shouldn't if we actually call it. + */ + p = ioremap(0xF0000, 0x10000); + if (p == NULL) + goto out; + + for (q = p; q < p + 0x10000; q += 16) { + rc = dmi_present(q); + if (!rc) return; } } diff -uNr linux-2.6.16.1/arch/i386/kernel/efi.c linux-2.6.16.1-imac/arch/i386/kernel/efi.c --- linux-2.6.16.1/arch/i386/kernel/efi.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/arch/i386/kernel/efi.c 2006-04-01 23:51:38.000000000 +0000 @@ -395,7 +395,7 @@ printk(KERN_INFO " ACPI=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { - efi.smbios = (void *) config_tables[i].table; + efi.smbios = __va(config_tables[i].table); printk(KERN_INFO " SMBIOS=0x%lx ", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { diff -uNr linux-2.6.16.1/drivers/Makefile linux-2.6.16.1-imac/drivers/Makefile --- linux-2.6.16.1/drivers/Makefile 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/Makefile 2006-04-01 23:51:38.000000000 +0000 @@ -33,7 +33,7 @@ obj-y += base/ block/ misc/ mfd/ net/ media/ obj-$(CONFIG_NUBUS) += nubus/ obj-$(CONFIG_ATM) += atm/ -obj-$(CONFIG_PPC_PMAC) += macintosh/ +obj-y += macintosh/ obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_FC4) += fc4/ obj-$(CONFIG_SCSI) += scsi/ diff -uNr linux-2.6.16.1/drivers/acpi/Kconfig linux-2.6.16.1-imac/drivers/acpi/Kconfig --- linux-2.6.16.1/drivers/acpi/Kconfig 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/Kconfig 2006-04-01 23:51:38.000000000 +0000 @@ -96,6 +96,13 @@ /proc/acpi/battery. If you have a mobile system with a battery, say Y. +config ACPI_SBS + tristate "Smart Battery System" + depends on ACPI_AC && ACPI_BATTERY && I2C + default y + help + This driver adds support for the Smart Battery System + config ACPI_BUTTON tristate "Button" default y diff -uNr linux-2.6.16.1/drivers/acpi/Makefile linux-2.6.16.1-imac/drivers/acpi/Makefile --- linux-2.6.16.1/drivers/acpi/Makefile 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/Makefile 2006-04-01 23:51:38.000000000 +0000 @@ -39,6 +39,7 @@ obj-y += bus.o glue.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BATTERY) += battery.o +obj-$(CONFIG_ACPI_SBS) += i2c_acpi_ec.o acpi_sbs.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_FAN) += fan.o diff -uNr linux-2.6.16.1/drivers/acpi/ac.c linux-2.6.16.1-imac/drivers/acpi/ac.c --- linux-2.6.16.1/drivers/acpi/ac.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/ac.c 2006-04-01 23:51:38.000000000 +0000 @@ -324,5 +324,12 @@ return_VOID; } +struct proc_dir_entry *acpi_get_ac_dir(void) +{ + return (acpi_ac_dir); +} + +EXPORT_SYMBOL(acpi_get_ac_dir); + module_init(acpi_ac_init); module_exit(acpi_ac_exit); diff -uNr linux-2.6.16.1/drivers/acpi/acpi_sbs.c linux-2.6.16.1-imac/drivers/acpi/acpi_sbs.c --- linux-2.6.16.1/drivers/acpi/acpi_sbs.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/acpi_sbs.c 2006-04-01 23:51:38.000000000 +0000 @@ -0,0 +1,2268 @@ +/* + * acpi_sbs.c - ACPI Smart Battery System Driver ($Revision: 1.2 $) + * + * Copyright (c) 2005 Rich Townsend + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c_acpi_ec.h" + +extern struct proc_dir_entry *acpi_get_ac_dir(void); +extern struct proc_dir_entry *acpi_get_battery_dir(void); + +#define ACPI_SBS_COMPONENT 0x00080000 +#define ACPI_SBS_CLASS "sbs" +#define ACPI_SBS_HID "ACPI0002" +#define ACPI_SBS_DRIVER_NAME "ACPI Smart Battery System Driver" +#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_SB_DEVICE_NAME "Smart Battery" +#define ACPI_SBSM_DEVICE_NAME "Smart Battery System Manager" +#define ACPI_SBSEL_DEVICE_NAME "Smart Battery Selector" +#define ACPI_SBC_DEVICE_NAME "Smart Battery Charger" +#define ACPI_SB_DIR_NAME "SB%d" +#define ACPI_SBSM_DIR_NAME "SBSM" +#define ACPI_SBSEL_DIR_NAME "SBSEL" +#define ACPI_SBC_DIR_NAME "SBC" +#define ACPI_SBSM_CLASS 1 +#define ACPI_SBSEL_CLASS 2 +#define ACPI_LBATTERY_DIR_NAME "BAT%d" +#define ACPI_LADAPTER_DIR_NAME "AC0" +#define ACPI_SBH_SMBUS_ADDR 0x8 +#define ACPI_SBC_SMBUS_ADDR 0x9 +#define ACPI_SBSM_SMBUS_ADDR 0xa +#define ACPI_SB_SMBUS_ADDR 0xb +#define ACPI_SBS_TIMEOUT_DELAY HZ/50 + +#define _COMPONENT ACPI_SBS_COMPONENT + +ACPI_MODULE_NAME("acpi_sbs") + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rich Townsend"); +MODULE_DESCRIPTION("Smart Battery System ACPI interface driver"); + +static u16 capacity_mode = 2; +module_param(capacity_mode, ushort, 2); + +static u16 mac_hack = 0; +module_param(mac_hack, ushort, 0); + +static int acpi_sbs_add(struct acpi_device *device); +static int acpi_sbs_remove(struct acpi_device *device, int type); + +static struct acpi_driver acpi_sbs_driver = { + .name = ACPI_SBS_DRIVER_NAME, + .class = ACPI_SBS_CLASS, + .ids = ACPI_SBS_HID, + .ops = { + .add = acpi_sbs_add, + .remove = acpi_sbs_remove, + }, +}; + +struct acpi_sb_info { + u8 capacity_mode; + u16 max_error; + u16 full_charge_capacity; + u16 charging_amperage; + u16 charging_voltage; + u16 cycle_count; + u16 design_capacity; + u16 design_voltage; + u8 specid; + u16 vscale; + u16 ipscale; + u16 manufacture_day; + u16 manufacture_month; + u16 manufacture_year; + u16 serial_number; + char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3]; + char device_name[I2C_SMBUS_BLOCK_MAX + 3]; + char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3]; +}; + +struct acpi_sb_state { + u8 relearn_flag; + u16 temperature; + u16 voltage; + s16 amperage; + s16 average_amperage; + u16 relative_state_of_charge; + u16 absolute_state_of_charge; + u16 remaining_capacity; + u16 run_time_to_empty; + u16 average_time_to_empty; + u16 average_time_to_full; + u16 battery_status; +}; + +struct acpi_sb_alarm { + u16 remaining_capacity; + u16 remaining_time; +}; + +struct acpi_sb { + u8 id; + u8 init_state; + struct acpi_sbs *sbs; + struct acpi_sb_info info; + struct acpi_sb_state state; + struct acpi_sb_alarm alarm; + struct proc_dir_entry *sb_entry; + struct proc_dir_entry *lbattery_entry; +}; + +struct acpi_sbsm_info { + u8 batteries_supported; + u8 battery_system_revision; + u16 vscale; + u16 ipscale; + u8 charging_indicator; +}; + +struct acpi_sbsm_state { + u8 smb_x; + u8 power_by_x; + u8 charge_x; + u8 connected_x; + u8 present_x; + u8 ac_present; + u8 power_not_good; +}; + +struct acpi_sbsm { + u8 class; + struct acpi_sbs *sbs; + struct acpi_sbsm_info info; + struct acpi_sbsm_state state; + struct proc_dir_entry *sbsm_entry; +}; + +struct acpi_sbc_info { + u8 charger_spec; + u8 selector_support; +}; + +struct acpi_sbc_state { + u8 power_fail; + u8 battery_present; + u8 ac_present; +}; + +struct acpi_sbc { + struct acpi_sbs *sbs; + struct acpi_sbc_info info; + struct acpi_sbc_state state; + struct proc_dir_entry *sbc_entry; + struct proc_dir_entry *ladapter_entry; +}; + +struct acpi_sbs { + acpi_handle handle; + struct acpi_device *device; + struct acpi_ec_smbus *smbus; + struct acpi_sb *sb; + struct acpi_sbsm *sbsm; + struct acpi_sbc *sbc; + struct semaphore sem; +}; + +/* -------------------------------------------------------------------------- + SMBus Communication + -------------------------------------------------------------------------- */ + +static s32 +acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, u16 addr, u8 func, + u16 * word, + void (*err_handler) (struct acpi_ec_smbus * smbus)) +{ + union i2c_smbus_data data; + s32 result = 0; + + ACPI_FUNCTION_TRACE("acpi_sbs_smbus_read_word"); + + result = + smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, + I2C_SMBUS_READ, func, + I2C_SMBUS_WORD_DATA, &data); + if (result) { + if (err_handler) + err_handler(smbus); + } else { + *word = data.word; + } + + return_VALUE(result); +} + +static s32 +acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, u16 addr, u8 func, + char *str, + void (*err_handler) (struct acpi_ec_smbus * smbus)) +{ + union i2c_smbus_data data; + s32 result = 0; + + ACPI_FUNCTION_TRACE("acpi_sbs_smbus_read_str"); + + result = smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, + I2C_SMBUS_READ, func, + I2C_SMBUS_BLOCK_DATA, &data); + if (result) { + if (err_handler) + err_handler(smbus); + } else { + strncpy(str, (const char *)data.block + 1, data.block[0]); + str[data.block[0]] = 0; + } + + return_VALUE(result); +} + +static void acpi_sb_smbus_err_handler(struct acpi_ec_smbus *smbus) +{ + union i2c_smbus_data data; + s32 result = 0; + + ACPI_FUNCTION_TRACE("acpi_sb_smbus_err_handler"); + + result = + smbus->adapter.algo->smbus_xfer(&smbus->adapter, ACPI_SB_SMBUS_ADDR, + 0, I2C_SMBUS_READ, 0x16, + I2C_SMBUS_BLOCK_DATA, &data); + + switch (data.word & 0x000f) { + case 0x0000: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "unexpected bus error\n")); + break; + case 0x0001: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "busy\n")); + break; + case 0x0002: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "reserved command\n")); + break; + case 0x0003: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "unsupported command\n")); + break; + case 0x0004: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "access denied\n")); + break; + case 0x0005: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "overflow/underflow\n")); + break; + case 0x0006: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "bad size\n")); + break; + case 0x0007: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "unknown error\n")); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "unrecognized error\n")); + } +} + +static s32 +acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, u16 addr, u8 func, + u16 word, + void (*err_handler) (struct acpi_ec_smbus * smbus)) +{ + union i2c_smbus_data data; + s32 result = 0; + + ACPI_FUNCTION_TRACE("acpi_sbs_smbus_write_word"); + + data.word = word; + + result = + smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, + I2C_SMBUS_WRITE, func, + I2C_SMBUS_WORD_DATA, &data); + if (result) + if (err_handler) + err_handler(smbus); + + return_VALUE(result); +} + +/* -------------------------------------------------------------------------- + Smart Battery System Management + -------------------------------------------------------------------------- */ + +/* Smart Battery */ + +static int acpi_sb_is_present(struct acpi_sb *sb) +{ + u16 battery_system_state; + int result; + int is_present; + + ACPI_FUNCTION_TRACE("acpi_sbs_is_sb_present"); + + if (sb->sbs->sbsm) { + result = acpi_sbs_smbus_read_word(sb->sbs->smbus, + ACPI_SBSM_SMBUS_ADDR, 0x01, + &battery_system_state, NULL); + if (!result) { + is_present = + (battery_system_state & 0x000f) & (1 << sb->id); + } + else + is_present = 0; + } else { + is_present = 0; + } + + return_VALUE(is_present); +} + +static int acpi_sb_select(struct acpi_sb *sb) +{ + struct acpi_ec_smbus *smbus = sb->sbs->smbus; + int result = 0; + u16 battery_system_state; + + ACPI_FUNCTION_TRACE("acpi_sb_select"); + + if (sb->sbs->sbsm) { + + /* Take special care not to knobble other nibbles of + * battery_system_state (aka selector_state), since + * it causes charging to halt on SBSELs */ + + result = + acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, + &battery_system_state, NULL); + if (result) + goto end; + + result = + acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, + (battery_system_state & 0x0fff) | + (1 << (sb->id + 12)), NULL); + } + + end: + if(mac_hack) + result = 0; + return_VALUE(result); +} + +static int acpi_sb_get_info(struct acpi_sb *sb) +{ + struct acpi_ec_smbus *smbus = sb->sbs->smbus; + int result = 0; + u16 battery_mode; + u16 specification_info; + u16 manufacture_date; + + ACPI_FUNCTION_TRACE("acpi_sb_get_info"); + + if ((result = acpi_sb_select(sb))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode, + &acpi_sb_smbus_err_handler))) + goto end; + sb->info.capacity_mode = (battery_mode & 0x8000) >> 15; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0c, + &sb->info.max_error, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10, + &sb->info.full_charge_capacity, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x14, + &sb->info.charging_amperage, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x15, + &sb->info.charging_voltage, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x17, + &sb->info.cycle_count, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18, + &sb->info.design_capacity, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19, + &sb->info.design_voltage, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a, + &specification_info, + &acpi_sb_smbus_err_handler))) + goto end; + + sb->info.specid = specification_info & 0x00ff; + + switch ((specification_info & 0x0f00) >> 8) { + case 1: + sb->info.vscale = 10; + break; + case 2: + sb->info.vscale = 100; + break; + case 3: + sb->info.vscale = 1000; + break; + default: + sb->info.vscale = 1; + } + + switch ((specification_info & 0xf000) >> 12) { + case 1: + sb->info.ipscale = 10; + break; + case 2: + sb->info.ipscale = 100; + break; + case 3: + sb->info.ipscale = 1000; + break; + default: + sb->info.ipscale = 1; + } + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1b, + &manufacture_date, + &acpi_sb_smbus_err_handler))) + goto end; + + sb->info.manufacture_day = manufacture_date & 0x001f; + sb->info.manufacture_month = (manufacture_date & 0x01e0) >> 5; + sb->info.manufacture_year = ((manufacture_date & 0xfe00) >> 9) + 1980; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c, + &sb->info.serial_number, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20, + sb->info.manufacturer_name, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21, + sb->info.device_name, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22, + sb->info.device_chemistry, + &acpi_sb_smbus_err_handler))) + goto end; + + end: + return_VALUE(result); +} + +static int acpi_sb_get_state(struct acpi_sb *sb) +{ + struct acpi_ec_smbus *smbus = sb->sbs->smbus; + int result = 0; + u16 battery_mode; + + ACPI_FUNCTION_TRACE("acpi_sb_get_state"); + + if ((result = acpi_sb_select(sb))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode, + &acpi_sb_smbus_err_handler))) + goto end; + sb->state.relearn_flag = (battery_mode & 0x0080) >> 7; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x08, + &sb->state.temperature, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09, + &sb->state.voltage, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a, + &sb->state.amperage, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0b, + &sb->state.average_amperage, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0d, + &sb->state. + relative_state_of_charge, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0e, + &sb->state. + absolute_state_of_charge, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f, + &sb->state.remaining_capacity, + &acpi_sb_smbus_err_handler))) + goto end; + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x11, + &sb->state.run_time_to_empty, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12, + &sb->state.average_time_to_empty, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13, + &sb->state.average_time_to_full, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16, + &sb->state.battery_status, + &acpi_sb_smbus_err_handler))) + goto end; + + end: + return_VALUE(result); +} + +static int acpi_sb_get_alarm(struct acpi_sb *sb) +{ + struct acpi_ec_smbus *smbus = sb->sbs->smbus; + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_sb_get_alarm"); + + if ((result = acpi_sb_select(sb))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, + &sb->alarm.remaining_capacity, + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x02, + &sb->alarm.remaining_time, + &acpi_sb_smbus_err_handler))) + goto end; + end: + return_VALUE(result); +} + +static int acpi_sb_set_alarm(struct acpi_sb *sb) +{ + struct acpi_ec_smbus *smbus = sb->sbs->smbus; + int result = 0; + u16 battery_mode; + + ACPI_FUNCTION_TRACE("acpi_sb_set_alarm"); + + if ((result = acpi_sb_select(sb))) + goto end; + + /* If necessary, enable the alarm */ + + if (sb->alarm.remaining_capacity > 0 || sb->alarm.remaining_time > 0) { + if ((result = + acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode, + &acpi_sb_smbus_err_handler))) + goto end; + if ((result = + acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, + battery_mode & 0xbfff, + &acpi_sb_smbus_err_handler))) + goto end; + } + + if ((result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, + sb->alarm.remaining_capacity / + (sb->info. + capacity_mode ? 10 : 1), + &acpi_sb_smbus_err_handler))) + goto end; + + if ((result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x02, + sb->alarm.remaining_time, + &acpi_sb_smbus_err_handler))) + goto end; + end: + return_VALUE(result); +} + +static int acpi_sb_init(struct acpi_sb *sb) +{ + int result = 0; + u16 battery_mode; + + ACPI_FUNCTION_TRACE("acpi_sb_init"); + + if (capacity_mode) { + result = acpi_sbs_smbus_read_word(sb->sbs->smbus, + ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode, NULL); + if (!result) { + if (capacity_mode == 1) { + battery_mode &= 0x7fff; + } else { + battery_mode |= 0x8000; + } + result = + acpi_sbs_smbus_write_word(sb->sbs->smbus, + ACPI_SB_SMBUS_ADDR, + 0x03, battery_mode, NULL); + if (result) { + printk(KERN_ALERT PREFIX + "SBS: problem setting capacity_mode\n"); + goto end; + } + } else { + printk(KERN_ALERT PREFIX + "SBS: problem setting capacity_mode\n"); + goto end; + } + } + + + result = acpi_sb_get_info(sb); + if (result) { + goto end; + } + + result = acpi_sb_get_state(sb); + if (result) { + goto end; + } + + end: + return_VALUE(result); +} + +static int acpi_sb_check_init_state(struct acpi_sb *sb) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_sb_check_init_state"); + + if (!sb->init_state) { + result = acpi_sb_init(sb); + if (result) { + goto end; + } + sb->init_state = 1; + } + end: + return_VALUE(result); +} + +/* Smart Battery System Manager / Smart Battery Selector */ + +static int acpi_sbsm_get_info(struct acpi_sbsm *sbsm) +{ + struct acpi_ec_smbus *smbus = sbsm->sbs->smbus; + int result = 0; + u16 battery_system_info; + + ACPI_FUNCTION_TRACE("acpi_sbsm_get_info"); + + if ((result = + acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04, + &battery_system_info, NULL))) + goto end; + + sbsm->info.batteries_supported = battery_system_info & 0x000f; + sbsm->info.battery_system_revision = + (battery_system_info & 0x00f0) >> 4; + + if (sbsm->class == ACPI_SBSM_CLASS) { + switch ((battery_system_info & 0x0f00) >> 8) { + case 1: + sbsm->info.vscale = 10; + break; + case 2: + sbsm->info.vscale = 100; + break; + case 3: + sbsm->info.vscale = 1000; + break; + default: + sbsm->info.vscale = 1; + } + + switch ((battery_system_info & 0xf000) >> 12) { + case 1: + sbsm->info.ipscale = 10; + break; + case 2: + sbsm->info.ipscale = 100; + break; + case 3: + sbsm->info.ipscale = 1000; + break; + default: + sbsm->info.ipscale = 1; + } + + sbsm->info.charging_indicator = 0; + } else { + sbsm->info.vscale = 1; + sbsm->info.ipscale = 1; + + sbsm->info.charging_indicator = + (battery_system_info & 0x0100) >> 8; + } + + end: + return_VALUE(result); +} + +static int acpi_sbsm_get_state(struct acpi_sbsm *sbsm) +{ + struct acpi_ec_smbus *smbus = sbsm->sbs->smbus; + int result = 0; + u16 battery_system_state; + + ACPI_FUNCTION_TRACE("acpi_sbsm_get_state"); + + if ((result = + acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, + &battery_system_state, NULL))) + goto end; + + if (sbsm->class == ACPI_SBSM_CLASS) { + sbsm->state.smb_x = (battery_system_state & 0xf000) >> 12; + sbsm->state.power_by_x = (battery_system_state & 0x0f00) >> 8; + sbsm->state.charge_x = (battery_system_state & 0x00f0) >> 4; + sbsm->state.connected_x = sbsm->state.charge_x; + sbsm->state.present_x = battery_system_state & 0x000f; + + if ((result = + acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x02, + &battery_system_state, NULL))) + goto end; + + sbsm->state.ac_present = battery_system_state & 0x0001; + sbsm->state.power_not_good = battery_system_state & 0x0002; + } else { + sbsm->state.smb_x = (battery_system_state & 0xf000) >> 12; + sbsm->state.power_by_x = (battery_system_state & 0x0f00) >> 8; + sbsm->state.connected_x = (battery_system_state & 0x00f0) >> 4; + sbsm->state.present_x = battery_system_state & 0x000f; + if (sbsm->state.power_by_x == 0xf) { + sbsm->state.power_by_x = 0x0; + sbsm->state.connected_x = ~sbsm->state.connected_x; + sbsm->state.charge_x = sbsm->state.connected_x; + sbsm->state.ac_present = 1; + } else if (sbsm->state.power_by_x == 0x0) { + sbsm->state.charge_x = 0x0; + sbsm->state.ac_present = 1; + } else { + sbsm->state.charge_x = 0x0; + sbsm->state.ac_present = 0; + } + sbsm->state.power_not_good = 0; + } + + end: + return_VALUE(result); +} + +/* Smart Battery Charger */ + +static int acpi_sbc_get_info(struct acpi_sbc *sbc) +{ + struct acpi_ec_smbus *smbus = sbc->sbs->smbus; + int result = 0; + u16 charger_spec_info; + + ACPI_FUNCTION_TRACE("acpi_sbc_get_info"); + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x11, + &charger_spec_info, NULL))) + goto end; + + sbc->info.charger_spec = charger_spec_info & 0x000f; + sbc->info.selector_support = (charger_spec_info & 0x0010) >> 4; + + end: + return_VALUE(result); +} + +static int acpi_sbc_get_state(struct acpi_sbc *sbc) +{ + struct acpi_ec_smbus *smbus = sbc->sbs->smbus; + int result = 0; + u16 charger_status; + + ACPI_FUNCTION_TRACE("acpi_sbc_get_state"); + + if ((result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13, + &charger_status, NULL))) + goto end; + + sbc->state.power_fail = (charger_status & 0x2000) >> 13; + sbc->state.battery_present = (charger_status & 0x4000) >> 14; + sbc->state.ac_present = (charger_status & 0x8000) >> 15; + + end: + return_VALUE(result); +} + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_sbs_dir; + +/* Generic Routines */ + +static int +acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, + struct proc_dir_entry *parent_dir, + char *dir_name, + struct file_operations *info_fops, + struct file_operations *state_fops, + struct file_operations *alarm_fops, void *data) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_sbs_generic_add_fs"); + + if (!*dir) { + *dir = proc_mkdir(dir_name, parent_dir); + if (!*dir) + return_VALUE(-ENODEV); + (*dir)->owner = THIS_MODULE; + } + + /* 'info' [R] */ + if (info_fops) { + entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_SBS_FILE_INFO)); + else { + entry->proc_fops = info_fops; + entry->data = data; + entry->owner = THIS_MODULE; + } + } + + /* 'state' [R] */ + if (state_fops) { + entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_SBS_FILE_STATE)); + else { + entry->proc_fops = state_fops; + entry->data = data; + entry->owner = THIS_MODULE; + } + } + + /* 'alarm' [R/W] */ + if (alarm_fops) { + entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IFREG | S_IRUGO | S_IWUSR, *dir); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_SBS_FILE_ALARM)); + else { + entry->proc_fops = alarm_fops; + entry->data = data; + entry->owner = THIS_MODULE; + } + } + + return_VALUE(0); +} + +static void +acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir, + struct proc_dir_entry *parent_dir) +{ + ACPI_FUNCTION_TRACE("acpi_sbs_generic_remove_fs"); + + if (*dir) { + remove_proc_entry(ACPI_SBS_FILE_INFO, *dir); + remove_proc_entry(ACPI_SBS_FILE_STATE, *dir); + remove_proc_entry(ACPI_SBS_FILE_ALARM, *dir); + remove_proc_entry((*dir)->name, parent_dir); + *dir = NULL; + } + +} + +/* Smart Battery Interface */ + +static int acpi_sb_read_info(struct seq_file *seq, void *offset) +{ + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + u32 cscale; + + ACPI_FUNCTION_TRACE("acpi_sb_read_info"); + + down(&sb->sbs->sem); + + if (acpi_sb_is_present(sb)) { + seq_printf(seq, "present: yes\n"); + } else { + seq_printf(seq, "present: no\n"); + goto end; + } + + if (sb->info.capacity_mode) + cscale = sb->info.vscale * sb->info.ipscale; + else + cscale = sb->info.ipscale; + + seq_printf(seq, "design voltage: %d mV\n", + sb->info.design_voltage * sb->info.vscale); + + seq_printf(seq, "design capacity: %d%s", + sb->info.design_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + + seq_printf(seq, "full charge capacity: %d%s", + sb->info.full_charge_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + + seq_printf(seq, "cycle count: %d\n", sb->info.cycle_count); + + seq_printf(seq, "charge reporting error: %d%%\n", sb->info.max_error); + + seq_printf(seq, "SB specification: "); + switch (sb->info.specid) { + case 0x10: + seq_printf(seq, "v1.0\n"); + break; + case 0x21: + seq_printf(seq, "v1.1 (without PEC)\n"); + break; + case 0x31: + seq_printf(seq, "v1.1 (with PEC)\n"); + break; + default: + seq_printf(seq, "unknown\n"); + } + + seq_printf(seq, "manufacturer name: %s\n", + sb->info.manufacturer_name); + + seq_printf(seq, "manufacture date: %4d-%2d-%2d\n", + sb->info.manufacture_year, + sb->info.manufacture_month, sb->info.manufacture_day); + + seq_printf(seq, "serial number: %d\n", + sb->info.serial_number); + + seq_printf(seq, "device name: %s\n", sb->info.device_name); + + seq_printf(seq, "device chemistry: %s\n", + sb->info.device_chemistry); + + end: + up(&sb->sbs->sem); + + return_VALUE(0); +} + +static int acpi_sb_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sb_read_info, PDE(inode)->data); +} + +static int acpi_sb_read_state(struct seq_file *seq, void *offset) +{ + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + int result = 0; + u32 cscale; + + ACPI_FUNCTION_TRACE("acpi_sb_read_state"); + + down(&sb->sbs->sem); + + if (acpi_sb_is_present(sb)) { + seq_printf(seq, "present: yes\n"); + } else { + seq_printf(seq, "present: no\n"); + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + result = acpi_sb_get_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery state\n"); + goto end; + } + + if (sb->info.capacity_mode) + cscale = sb->info.vscale * sb->info.ipscale; + else + cscale = sb->info.ipscale; + + if (sb->state.amperage < 0) { + seq_printf(seq, "charging state: discharging\n"); + seq_printf(seq, "current: %d mA\n", + -sb->state.amperage * sb->info.ipscale); + } else if (sb->state.amperage > 0) { + seq_printf(seq, "charging state: charging\n"); + seq_printf(seq, "current: %d mA\n", + sb->state.amperage * sb->info.ipscale); + } else { + seq_printf(seq, "charging state: charged\n"); + seq_printf(seq, "current: 0 mA\n"); + } + + seq_printf(seq, "average current %d mA\n", + abs(sb->state.average_amperage * sb->info.ipscale)); + + seq_printf(seq, "voltage: %d mV\n", + sb->state.voltage * sb->info.vscale); + + seq_printf(seq, "temperature: %d.%d C\n", + sb->state.temperature / 10 - 273, + sb->state.temperature % 10 + 1); + + seq_printf(seq, "relative charge: %d%%\n", + sb->state.relative_state_of_charge); + + seq_printf(seq, "absolute charge: %d%%\n", + sb->state.absolute_state_of_charge); + + seq_printf(seq, "remaining capacity: %d%s", + sb->state.remaining_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + + if (sb->state.run_time_to_empty == 65535) + seq_printf(seq, "run time to empty: n/a\n"); + else + seq_printf(seq, "run time to empty: %dh %02dm\n", + sb->state.run_time_to_empty / 60, + sb->state.run_time_to_empty % 60); + + if (sb->state.average_time_to_empty == 65535) + seq_printf(seq, "average time to empty: n/a\n"); + else + seq_printf(seq, "average time to empty: %dh %02dm\n", + sb->state.average_time_to_empty / 60, + sb->state.average_time_to_empty % 60); + + if (sb->state.average_time_to_full == 65535) + seq_printf(seq, "average time to full: n/a\n"); + else + seq_printf(seq, "average time to full: %dh %02dm\n", + sb->state.average_time_to_full / 60, + sb->state.average_time_to_full % 60); + + end: + up(&sb->sbs->sem); + + return_VALUE(result); +} + +static int acpi_sb_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sb_read_state, PDE(inode)->data); +} + +static int acpi_sb_read_alarm(struct seq_file *seq, void *offset) +{ + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + int result = 0; + u32 cscale; + + ACPI_FUNCTION_TRACE("acpi_sb_read_alarm"); + + down(&sb->sbs->sem); + + if (acpi_sb_is_present(sb)) { + seq_printf(seq, "present: yes\n"); + } else { + seq_printf(seq, "present: no\n"); + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + result = acpi_sb_get_alarm(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery alarm\n"); + goto end; + } + + if (sb->info.capacity_mode) + cscale = sb->info.vscale * sb->info.ipscale; + else + cscale = sb->info.ipscale; + + seq_printf(seq, "remain capacity alarm: "); + if (sb->alarm.remaining_capacity) + seq_printf(seq, "%d%s", + sb->alarm.remaining_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + else + seq_printf(seq, "disabled\n"); + + seq_printf(seq, "remain time alarm: "); + if (sb->alarm.remaining_time) + seq_printf(seq, "%dh %02dm\n", + sb->alarm.remaining_time / 60, + sb->alarm.remaining_time % 60); + else + seq_printf(seq, "disabled\n"); + + end: + up(&sb->sbs->sem); + + return_VALUE(result); +} + +static ssize_t +acpi_sb_write_alarm(struct file *file, const char __user * buffer, + size_t count, loff_t * ppos) +{ + struct seq_file *seq = (struct seq_file *)file->private_data; + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + char alarm_string[16] = { '\0' }; + int alarm_values[3]; + char *term; + int result; + + ACPI_FUNCTION_TRACE("acpi_sb_write_alarm"); + + down(&sb->sbs->sem); + + if (!acpi_sb_is_present(sb)) { + result = -ENODEV; + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + if (count > sizeof(alarm_string) - 1) { + result = -EINVAL; + goto end; + } + + if (copy_from_user(alarm_string, buffer, count)) { + result = -EFAULT; + goto end; + } + + alarm_string[count] = 0; + term = get_options(alarm_string, 3, alarm_values); + + if (alarm_values[0] > 0) { + if (alarm_values[1] >= 0 && alarm_values[1] <= 65535) { + sb->alarm.remaining_capacity = alarm_values[1]; + } else { + result = -EINVAL; + goto end; + } + } + + if (alarm_values[0] > 1) { + if (alarm_values[2] >= 0 && alarm_values[2] <= 65535) { + sb->alarm.remaining_time = alarm_values[2]; + } else { + result = -EINVAL; + goto end; + } + } + + result = acpi_sb_set_alarm(sb); + + end: + up(&sb->sbs->sem); + + if (result) + return_VALUE(result); + else + return_VALUE(count); +} + +static int acpi_sb_alarm_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sb_read_alarm, PDE(inode)->data); +} + +static struct file_operations acpi_sb_info_fops = { + .owner = THIS_MODULE, + .open = acpi_sb_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_sb_state_fops = { + .owner = THIS_MODULE, + .open = acpi_sb_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_sb_alarm_fops = { + .owner = THIS_MODULE, + .open = acpi_sb_alarm_open_fs, + .read = seq_read, + .write = acpi_sb_write_alarm, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Smart Battery System Manager / Smart Battery Selector Interface */ + +static int acpi_sbsm_read_info(struct seq_file *seq, void *offset) +{ + struct acpi_sbsm *sbsm = (struct acpi_sbsm *)seq->private; + u8 id; + + ACPI_FUNCTION_TRACE("acpi_sbsm_read_info"); + + down(&sbsm->sbs->sem); + + seq_printf(seq, "batteries supported: "); + for (id = 0; id <= 3; id++) { + if (sbsm->info.batteries_supported & (1 << id)) + seq_printf(seq, " " ACPI_SB_DIR_NAME, id); + } + seq_printf(seq, "\n"); + + if (sbsm->class == ACPI_SBSM_CLASS) { + seq_printf(seq, "SBSM specification: "); + + switch (sbsm->info.battery_system_revision) { + case 0x0008: + seq_printf(seq, "v1.0 (without PEC)\n"); + break; + case 0x0009: + seq_printf(seq, "v1.0 (with PEC)\n"); + break; + default: + seq_printf(seq, "unknown\n"); + } + } else { + seq_printf(seq, "SBSEL specification: "); + + switch (sbsm->info.battery_system_revision) { + case 0x0001: + seq_printf(seq, "v1.0\n"); + break; + case 0x0002: + seq_printf(seq, "v1.1 (without PEC)\n"); + break; + case 0x0003: + seq_printf(seq, "v1.1 (with PEC)\n"); + break; + default: + seq_printf(seq, "unknown\n"); + } + } + + up(&sbsm->sbs->sem); + + return_VALUE(0); +} + +static int acpi_sbsm_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sbsm_read_info, PDE(inode)->data); +} + +static int acpi_sbsm_read_state(struct seq_file *seq, void *offset) +{ + struct acpi_sbsm *sbsm = (struct acpi_sbsm *)seq->private; + int result = 0; + u8 id; + + ACPI_FUNCTION_TRACE("acpi_sbsm_read_state"); + + down(&sbsm->sbs->sem); + + result = acpi_sbsm_get_state(sbsm); + + if (result) { + seq_printf(seq, + "ERROR: Unable to read battery system manager / selector state\n"); + goto end; + } + + if (sbsm->class == ACPI_SBSEL_CLASS) { + result = acpi_sbsm_get_state(sbsm); + if (result) { + seq_printf(seq, + "ERROR: Unable to read charger state\n"); + goto end; + } + } + + seq_printf(seq, "batteries present: "); + if (!sbsm->state.present_x) { + seq_printf(seq, " none"); + } else { + for (id = 0; id <= 3; id++) { + if (sbsm->state.present_x & (1 << id)) + seq_printf(seq, " " ACPI_SB_DIR_NAME, id); + } + } + seq_printf(seq, "\n"); + + seq_printf(seq, "batteries charging: "); + if (sbsm->class == ACPI_SBSEL_CLASS && !sbsm->info.charging_indicator) { + if (sbsm->sbs->sbc->state.battery_present) + seq_printf(seq, " yes\n"); + else + seq_printf(seq, " no\n"); + } else { + if (!sbsm->state.charge_x) { + seq_printf(seq, " none"); + } else { + for (id = 0; id <= 3; id++) { + if (sbsm->state.charge_x & (1 << id)) + seq_printf(seq, " " ACPI_SB_DIR_NAME, + id); + } + } + seq_printf(seq, "\n"); + } + + if (sbsm->state.ac_present) { + seq_printf(seq, "system powered by: AC\n"); + } else { + seq_printf(seq, "system powered by: "); + for (id = 0; id <= 3; id++) { + if (sbsm->state.power_by_x & (1 << id)) + seq_printf(seq, " " ACPI_SB_DIR_NAME, id); + } + seq_printf(seq, "\n"); + } + + if (sbsm->state.ac_present) { + if (sbsm->class == ACPI_SBSM_CLASS) + seq_printf(seq, "AC sufficient: %s\n", + sbsm->state.power_not_good ? "no" : "yes"); + else + seq_printf(seq, "AC sufficient: %s\n", + sbsm->sbs->sbc->state. + power_fail ? "no" : "yes"); + } else { + seq_printf(seq, "AC sufficient: n/a\n"); + } + + end: + up(&sbsm->sbs->sem); + + return_VALUE(result); +} + +static int acpi_sbsm_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sbsm_read_state, PDE(inode)->data); +} + +static struct file_operations acpi_sbsm_info_fops = { + .owner = THIS_MODULE, + .open = acpi_sbsm_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_sbsm_state_fops = { + .owner = THIS_MODULE, + .open = acpi_sbsm_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Smart Battery Charger Interface */ + +static int acpi_sbc_read_info(struct seq_file *seq, void *offset) +{ + struct acpi_sbc *sbc = (struct acpi_sbc *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_sbc_read_info"); + + down(&sbc->sbs->sem); + + seq_printf(seq, "SBC specification: "); + + switch (sbc->info.charger_spec) { + case 0x0001: + seq_printf(seq, "v1.0\n"); + break; + case 0x0002: + seq_printf(seq, "v1.1 (without PEC)\n"); + break; + case 0x0003: + seq_printf(seq, "v1.1 (with PEC)\n"); + break; + default: + seq_printf(seq, "unknown\n"); + } + + seq_printf(seq, "Embedded SBSEL support: %s\n", + sbc->info.selector_support ? "yes" : "no"); + + up(&sbc->sbs->sem); + + return_VALUE(0); +} + +static int acpi_sbc_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sbc_read_info, PDE(inode)->data); +} + +static int acpi_sbc_read_state(struct seq_file *seq, void *offset) +{ + struct acpi_sbc *sbc = (struct acpi_sbc *)seq->private; + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_sbc_read_state"); + + down(&sbc->sbs->sem); + + result = acpi_sbc_get_state(sbc); + + if (result) { + seq_printf(seq, + "ERROR: Unable to read battery charger state\n"); + goto end; + } + + seq_printf(seq, "battery charging: %s\n", + sbc->state.battery_present ? "yes" : "no"); + + seq_printf(seq, "system powered by: %s\n", + sbc->state.ac_present ? "AC" : "battery"); + + if (sbc->state.ac_present) + seq_printf(seq, "AC sufficient: %s\n", + sbc->state.power_fail ? "no" : "yes"); + else + seq_printf(seq, "AC sufficient: n/a\n"); + + end: + up(&sbc->sbs->sem); + + return_VALUE(result); +} + +static int acpi_sbc_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_sbc_read_state, PDE(inode)->data); +} + +static struct file_operations acpi_sbc_info_fops = { + .owner = THIS_MODULE, + .open = acpi_sbc_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_sbc_state_fops = { + .owner = THIS_MODULE, + .open = acpi_sbc_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Legacy Battery Interface */ + +static struct proc_dir_entry *acpi_lbattery_dir = NULL; + +static int acpi_lbattery_read_info(struct seq_file *seq, void *offset) +{ + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + u32 cscale; + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_lbattery_read_info"); + + down(&sb->sbs->sem); + + if (acpi_sb_is_present(sb)) { + seq_printf(seq, "present: yes\n"); + } else { + seq_printf(seq, "present: no\n"); + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + if (sb->info.capacity_mode) + cscale = sb->info.vscale * sb->info.ipscale; + else + cscale = sb->info.ipscale; + + seq_printf(seq, "design capacity: %d%s", + sb->info.design_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + + seq_printf(seq, "last full capacity: %d%s", + sb->info.full_charge_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + + seq_printf(seq, "battery technology: rechargeable\n"); + + seq_printf(seq, "design voltage: %d mV\n", + sb->info.design_voltage * sb->info.vscale); + + seq_printf(seq, "design capacity warning: unknown\n"); + seq_printf(seq, "design capacity low: unknown\n"); + seq_printf(seq, "capacity granularity 1: unknown\n"); + seq_printf(seq, "capacity granularity 2: unknown\n"); + + seq_printf(seq, "model number: %s\n", sb->info.device_name); + + seq_printf(seq, "serial number: %d\n", + sb->info.serial_number); + + seq_printf(seq, "battery type: %s\n", + sb->info.device_chemistry); + + seq_printf(seq, "OEM info: %s\n", + sb->info.manufacturer_name); + + end: + up(&sb->sbs->sem); + + return_VALUE(result); +} + +static int acpi_lbattery_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_lbattery_read_info, PDE(inode)->data); +} + +static int acpi_lbattery_read_state(struct seq_file *seq, void *offset) +{ + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + int result = 0; + u32 cscale; + + ACPI_FUNCTION_TRACE("acpi_lbattery_read_state"); + + down(&sb->sbs->sem); + + if (acpi_sb_is_present(sb)) { + seq_printf(seq, "present: yes\n"); + } else { + seq_printf(seq, "present: no\n"); + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + result = acpi_sb_get_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery state\n"); + goto end; + } + + if (sb->info.capacity_mode) + cscale = sb->info.vscale * sb->info.ipscale; + else + cscale = sb->info.ipscale; + + if (sb->state.battery_status & 0x0010) + seq_printf(seq, "capacity state: critical\n"); + else + seq_printf(seq, "capacity state: ok\n"); + + if ((s16) sb->state.amperage < 0) { + seq_printf(seq, "charging state: discharging\n"); + if (sb->state.average_time_to_empty) + seq_printf(seq, "present rate: %d%s\n", + sb->state.remaining_capacity * cscale * 60 / + sb->state.average_time_to_empty, + sb->info.capacity_mode ? "0 mW" : " mA"); + else + seq_printf(seq, "present rate: n/a\n"); + } else if ((s16) sb->state.amperage > 0) { + seq_printf(seq, "charging state: charging\n"); + if (sb->state.average_time_to_full) + seq_printf(seq, "present rate: %d%s\n", + (sb->info.full_charge_capacity - + sb->state.remaining_capacity) * cscale * 60 / + sb->state.average_time_to_full, + sb->info.capacity_mode ? "0 mW" : " mA"); + else + seq_printf(seq, "present rate: n/a\n"); + } else { + seq_printf(seq, "charging state: charged\n"); + seq_printf(seq, "present rate: 0 %s\n", + sb->info.capacity_mode ? "mW" : "mA"); + } + + seq_printf(seq, "remaining capacity: %d%s", + sb->state.remaining_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + + seq_printf(seq, "present voltage: %d mV\n", + sb->state.voltage * sb->info.vscale); + + end: + up(&sb->sbs->sem); + + return_VALUE(result); +} + +static int acpi_lbattery_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_lbattery_read_state, PDE(inode)->data); +} + +static int acpi_lbattery_read_alarm(struct seq_file *seq, void *offset) +{ + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + int result = 0; + u32 cscale; + + ACPI_FUNCTION_TRACE("acpi_lbattery_read_alarm"); + + down(&sb->sbs->sem); + + if (!acpi_sb_is_present(sb)) { + seq_printf(seq, "present: no\n"); + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + result = acpi_sb_get_alarm(sb); + + if (result) { + seq_printf(seq, "ERROR: Unable to read battery alarm\n"); + goto end; + } + + if (sb->info.capacity_mode) + cscale = sb->info.vscale * sb->info.ipscale; + else + cscale = sb->info.ipscale; + + seq_printf(seq, "alarm: "); + if (sb->alarm.remaining_capacity) + seq_printf(seq, "%d%s", + sb->alarm.remaining_capacity * cscale, + sb->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + else + seq_printf(seq, "disabled\n"); + + end: + up(&sb->sbs->sem); + + return_VALUE(result); +} + +static ssize_t +acpi_lbattery_write_alarm(struct file *file, const char __user * buffer, + size_t count, loff_t * ppos) +{ + struct seq_file *seq = (struct seq_file *)file->private_data; + struct acpi_sb *sb = (struct acpi_sb *)seq->private; + char alarm_string[6] = { '\0' }; + int alarm_values[2]; + char *term; + int result; + + ACPI_FUNCTION_TRACE("acpi_lbattery_write_alarm"); + + down(&sb->sbs->sem); + + if (!acpi_sb_is_present(sb)) { + result = -ENODEV; + goto end; + } + + result = acpi_sb_check_init_state(sb); + if (result) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + if (count > sizeof(alarm_string) - 1) { + result = -EINVAL; + goto end; + } + + if (copy_from_user(alarm_string, buffer, count)) { + result = -EFAULT; + goto end; + } + + alarm_string[count] = 0; + term = get_options(alarm_string, 2, alarm_values); + + if (alarm_values[0] > 0) { + if (alarm_values[1] >= 0 && alarm_values[1] <= 65535) { + sb->alarm.remaining_capacity = alarm_values[1]; + } else { + result = -EINVAL; + goto end; + } + } + + result = acpi_sb_set_alarm(sb); + + end: + up(&sb->sbs->sem); + + if (result) + return_VALUE(result); + else + return_VALUE(count); +} + +static int acpi_lbattery_alarm_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_lbattery_read_alarm, PDE(inode)->data); +} + +static struct file_operations acpi_lbattery_info_fops = { + .owner = THIS_MODULE, + .open = acpi_lbattery_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_lbattery_state_fops = { + .owner = THIS_MODULE, + .open = acpi_lbattery_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_lbattery_alarm_fops = { + .owner = THIS_MODULE, + .open = acpi_lbattery_alarm_open_fs, + .read = seq_read, + .write = acpi_lbattery_write_alarm, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Legacy AC Adapter Interface */ + +static struct proc_dir_entry *acpi_ladapter_dir = NULL; + +static int acpi_ladapter_read_state(struct seq_file *seq, void *offset) +{ + struct acpi_sbc *sbc = (struct acpi_sbc *)seq->private; + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_ladapter_read_state"); + + down(&sbc->sbs->sem); + + result = acpi_sbc_get_state(sbc); + if (result) { + seq_printf(seq, "ERROR: Unable to read charger state\n"); + goto end; + } + + seq_printf(seq, "state: %s\n", + sbc->state.ac_present ? "on-line" : "off-line"); + + end: + up(&sbc->sbs->sem); + + return_VALUE(result); +} + +static int acpi_ladapter_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_ladapter_read_state, PDE(inode)->data); +} + +static struct file_operations acpi_ladapter_state_fops = { + .owner = THIS_MODULE, + .open = acpi_ladapter_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +/* Smart Battery */ + +static int acpi_sb_add(struct acpi_sbs *sbs, u8 id) +{ + int is_present; + int result; + char dir_name[5]; + struct acpi_sb *sb; + + ACPI_FUNCTION_TRACE("acpi_sb_add"); + + sb = &sbs->sb[id]; + + sb->init_state = 0; + sb->id = id; + sb->sbs = sbs; + + is_present = acpi_sb_is_present(sb); + + if (is_present) { + result = acpi_sb_init(sb); + if (result) { + goto end; + } + sb->init_state = 1; + } + + result = sprintf(dir_name, ACPI_SB_DIR_NAME, id); + result = acpi_sbs_generic_add_fs(&sb->sb_entry, + acpi_device_dir(sbs->device), + dir_name, + &acpi_sb_info_fops, + &acpi_sb_state_fops, + &acpi_sb_alarm_fops, sb); + + if (result) { + goto end; + } + printk(KERN_INFO PREFIX "SBS: %s Slot [" ACPI_SB_DIR_NAME "] (battery %s)\n", + ACPI_SB_DEVICE_NAME, id, is_present ? "present" : "absent"); + + result = sprintf(dir_name, ACPI_LBATTERY_DIR_NAME, id); + if (result < 0) { + goto end; + } + result = acpi_sbs_generic_add_fs(&sb->lbattery_entry, + acpi_lbattery_dir, + dir_name, + &acpi_lbattery_info_fops, + &acpi_lbattery_state_fops, + &acpi_lbattery_alarm_fops, sb); + if (result) { + goto end; + } + + end: + return_VALUE(result); +} + +static void acpi_sb_remove(struct acpi_sbs *sbs, u8 id) +{ + ACPI_FUNCTION_TRACE("acpi_sb_remove"); + + acpi_sbs_generic_remove_fs(&(sbs->sb[id].sb_entry), + acpi_device_dir(sbs->device)); + + acpi_sbs_generic_remove_fs(&(sbs->sb[id].lbattery_entry), + acpi_lbattery_dir); +} + +/* Smart Battery System Manager / Smart Battery Selector */ + +static int acpi_sbsm_add(struct acpi_sbs *sbs) +{ + int result; + u16 battery_system_revision; + + ACPI_FUNCTION_TRACE("acpi_sbsm_add"); + + sbs->sbsm->sbs = sbs; + + /* Identify whether this is a System Manager or Selector */ + + result = + acpi_sbs_smbus_read_word(sbs->smbus, ACPI_SBSM_SMBUS_ADDR, 0x04, + &battery_system_revision, NULL); + if (result) + goto end; + + if ((battery_system_revision & 0x00f0) == 0x0080 || + (battery_system_revision & 0x00f0) == 0x0090) + sbs->sbsm->class = ACPI_SBSM_CLASS; + else + sbs->sbsm->class = ACPI_SBSEL_CLASS; + + result = acpi_sbsm_get_info(sbs->sbsm); + if (result) + goto end; + + result = acpi_sbsm_get_state(sbs->sbsm); + if (result) + goto end; + + if (sbs->sbsm->class == ACPI_SBSM_CLASS) { + result = acpi_sbs_generic_add_fs(&(sbs->sbsm->sbsm_entry), + acpi_device_dir(sbs->device), + ACPI_SBSM_DIR_NAME, + &acpi_sbsm_info_fops, + &acpi_sbsm_state_fops, + NULL, sbs->sbsm); + if (result) + goto end; + + printk(KERN_INFO PREFIX "SBS: %s [%s]\n", ACPI_SBSM_DEVICE_NAME, + ACPI_SBSM_DIR_NAME); + } else { + result = acpi_sbs_generic_add_fs(&(sbs->sbsm->sbsm_entry), + acpi_device_dir(sbs->device), + ACPI_SBSEL_DIR_NAME, + &acpi_sbsm_info_fops, + &acpi_sbsm_state_fops, + NULL, sbs->sbsm); + if (result) + goto end; + + printk(KERN_INFO PREFIX "SBS: %s [%s]\n", ACPI_SBSEL_DEVICE_NAME, + ACPI_SBSEL_DIR_NAME); + } + + end: + return_VALUE(result); +} + +static void acpi_sbsm_remove(struct acpi_sbs *sbs) +{ + ACPI_FUNCTION_TRACE("acpi_sbsm_remove"); + + acpi_sbs_generic_remove_fs(&sbs->sbsm->sbsm_entry, + acpi_device_dir(sbs->device)); +} + +/* Smart Battery Charger */ + +static int acpi_sbc_add(struct acpi_sbs *sbs) +{ + int result; + + ACPI_FUNCTION_TRACE("acpi_sbc_add"); + + sbs->sbc->sbs = sbs; + + result = acpi_sbc_get_info(sbs->sbc); + if (result) + goto end; + + result = acpi_sbc_get_state(sbs->sbc); + if (result) + goto end; + + result = acpi_sbs_generic_add_fs(&sbs->sbc->sbc_entry, + acpi_device_dir(sbs->device), + ACPI_SBC_DIR_NAME, + &acpi_sbc_info_fops, + &acpi_sbc_state_fops, NULL, sbs->sbc); + if (result) + goto end; + + printk(KERN_INFO PREFIX "SBS: %s [%s]\n", ACPI_SBC_DEVICE_NAME, + ACPI_SBC_DIR_NAME); + + result = acpi_sbs_generic_add_fs(&sbs->sbc->ladapter_entry, + acpi_ladapter_dir, + ACPI_LADAPTER_DIR_NAME, + NULL, + &acpi_ladapter_state_fops, + NULL, sbs->sbc); + end: + return_VALUE(result); +} + +static void acpi_sbc_remove(struct acpi_sbs *sbs) +{ + ACPI_FUNCTION_TRACE("acpi_sbc_remove"); + + acpi_sbs_generic_remove_fs(&sbs->sbc->sbc_entry, + acpi_device_dir(sbs->device)); + + acpi_sbs_generic_remove_fs(&sbs->sbc->ladapter_entry, + acpi_ladapter_dir); +} + +/* Smart Battery System */ + +static int acpi_sbs_add(struct acpi_device *device) +{ + struct acpi_sbs *sbs = NULL; + struct acpi_ec_hc *ec_hc = NULL; + int result, remove_result = 0; + unsigned long sbs_obj; + u8 id; + + ACPI_FUNCTION_TRACE("acpi_sbs_add"); + + sbs = kmalloc(sizeof(struct acpi_sbs), GFP_KERNEL); + if (!sbs) + return_VALUE(-ENOMEM); + + memset(sbs, 0, sizeof(struct acpi_sbs)); + + ec_hc = acpi_get_ec_hc(device); + if (!ec_hc) { + printk(KERN_ALERT PREFIX "SBS: no driver found for EC HC SMBus\n"); + result = -ENODEV; + goto end; + } + + sbs->device = device; + sbs->smbus = ec_hc->smbus; + sema_init(&sbs->sem, 1); + + strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SBS_CLASS); + acpi_driver_data(device) = sbs; + + result = acpi_sbs_generic_add_fs(&(acpi_device_dir(device)), + acpi_sbs_dir, + acpi_device_bid(device), + NULL, NULL, NULL, sbs); + if (result) + goto end; + + printk(KERN_INFO PREFIX "SBS: %s [%s]\n", + acpi_device_name(device), acpi_device_bid(device)); + + /* Add the SBC */ + + sbs->sbc = kmalloc(sizeof(struct acpi_sbc), GFP_KERNEL); + if (!sbs->sbc) { + result = -ENOMEM; + goto end; + } + memset(sbs->sbc, 0, sizeof(struct acpi_sbc)); + + result = acpi_sbc_add(sbs); + if (result) + goto end; + + /* Add the SBSM / SBSEL, if they are present */ + + result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); + if (ACPI_FAILURE(result)) { + printk(KERN_ALERT PREFIX "Error obtaining _SBS object\n"); + result = -EIO; + goto end; + } + + sbs->sbsm = NULL; + + if (sbs_obj > 0) { + sbs->sbsm = kmalloc(sizeof(struct acpi_sbsm), GFP_KERNEL); + if (!sbs->sbsm) { + result = -ENOMEM; + goto end; + } + memset(sbs->sbsm, 0, sizeof(struct acpi_sbsm)); + + result = acpi_sbsm_add(sbs); + if(result) { + kfree(sbs->sbsm); + sbs->sbsm = NULL; + } + } + + /* Add the batteries */ + + sbs->sb = kmalloc(4 * sizeof(struct acpi_sb), GFP_KERNEL); + if (!sbs->sb) { + result = -ENOMEM; + goto end; + } + memset(sbs->sb, 0, 4 * sizeof(struct acpi_sb)); + + if (!sbs->sbsm) { + result = acpi_sb_add(sbs, 0); + if (result) + goto end; + } else { + for (id = 0; id <= 3; id++) { + if (sbs->sbsm->info.batteries_supported & (1 << id)) { + result = acpi_sb_add(sbs, id); + if (result) + goto end; + } + } + } + + sbs->handle = device->handle; + printk(KERN_ALERT PREFIX "SBS: HANDLE %p\n", sbs->handle); + + printk(KERN_INFO PREFIX "SBS: name %s, class %s, bid %s\n", + acpi_device_name(device), + acpi_device_class(device), acpi_device_bid(device)); + + end: + if (result) { + remove_result = acpi_sbs_remove(device, 0); + } + return_VALUE(result); +} + +int acpi_sbs_remove(struct acpi_device *device, int type) +{ + struct acpi_sbs *sbs = (struct acpi_sbs *)acpi_driver_data(device); + u8 id; + + ACPI_FUNCTION_TRACE("acpi_sbs_remove"); + + if (!device || !sbs) + return_VALUE(-EINVAL); + + if (sbs->sb) { + for (id = 0; id <= 3; id++) { + acpi_sb_remove(sbs, id); + } + kfree(sbs->sb); + } + + if (sbs->sbsm) { + acpi_sbsm_remove(sbs); + kfree(sbs->sbsm); + } + + if (sbs->sbc) { + acpi_sbc_remove(sbs); + kfree(sbs->sbc); + } + + acpi_sbs_generic_remove_fs(&acpi_device_dir(device), acpi_sbs_dir); + + kfree(sbs); + + return_VALUE(0); +} + +static int __init acpi_sbs_init(void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_sbs_init"); + + if (capacity_mode > 2) + return_VALUE(-EINVAL); + + acpi_sbs_dir = proc_mkdir(ACPI_SBS_CLASS, acpi_root_dir); + if (!acpi_sbs_dir) + return_VALUE(-ENODEV); + acpi_sbs_dir->owner = THIS_MODULE; + + acpi_lbattery_dir = acpi_get_battery_dir(); + if (!acpi_lbattery_dir) { + printk(KERN_ALERT PREFIX "SBS: cannot get acpi battery directory\n"); + return_VALUE(-ENODEV); + } + + acpi_ladapter_dir = acpi_get_ac_dir(); + if (!acpi_ladapter_dir) { + printk(KERN_ALERT PREFIX "SBS: cannot get acpi ac directory\n"); + return_VALUE(-ENODEV); + } + + result = acpi_bus_register_driver(&acpi_sbs_driver); + if (result < 0) { + remove_proc_entry(ACPI_SBS_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + +static void __exit acpi_sbs_exit(void) +{ + ACPI_FUNCTION_TRACE("acpi_sbs_exit"); + + acpi_bus_unregister_driver(&acpi_sbs_driver); + + remove_proc_entry(ACPI_SBS_CLASS, acpi_root_dir); + + return_VOID; +} + +module_init(acpi_sbs_init); +module_exit(acpi_sbs_exit); diff -uNr linux-2.6.16.1/drivers/acpi/battery.c linux-2.6.16.1-imac/drivers/acpi/battery.c --- linux-2.6.16.1/drivers/acpi/battery.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/battery.c 2006-04-01 23:51:38.000000000 +0000 @@ -808,5 +808,12 @@ return_VOID; } +struct proc_dir_entry *acpi_get_battery_dir(void) +{ + return (acpi_battery_dir); +} + +EXPORT_SYMBOL(acpi_get_battery_dir); + module_init(acpi_battery_init); module_exit(acpi_battery_exit); diff -uNr linux-2.6.16.1/drivers/acpi/blacklist.c linux-2.6.16.1-imac/drivers/acpi/blacklist.c --- linux-2.6.16.1/drivers/acpi/blacklist.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/blacklist.c 2006-04-01 23:51:38.000000000 +0000 @@ -32,6 +32,7 @@ #include #include #include +#include enum acpi_blacklist_predicates { all_versions, @@ -80,6 +81,9 @@ int year; char *s = dmi_get_system_info(DMI_BIOS_DATE); + if (efi_enabled) + return 0; + if (!s) return 0; if (!*s) diff -uNr linux-2.6.16.1/drivers/acpi/ec.c linux-2.6.16.1-imac/drivers/acpi/ec.c --- linux-2.6.16.1/drivers/acpi/ec.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/ec.c 2006-04-01 23:51:38.000000000 +0000 @@ -991,7 +991,6 @@ int result = 0; acpi_status status = AE_OK; union acpi_ec *ec = NULL; - unsigned long uid; ACPI_FUNCTION_TRACE("acpi_ec_add"); @@ -1014,10 +1013,9 @@ acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock); - /* If our UID matches the UID for the ECDT-enumerated EC, - we now have the *real* EC info, so kill the makeshift one. */ - acpi_evaluate_integer(ec->common.handle, "_UID", NULL, &uid); - if (ec_ecdt && ec_ecdt->common.uid == uid) { + /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: + http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ + if (ec_ecdt) { acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); @@ -1062,7 +1060,6 @@ int result = 0; acpi_status status = AE_OK; union acpi_ec *ec = NULL; - unsigned long uid; ACPI_FUNCTION_TRACE("acpi_ec_add"); @@ -1088,10 +1085,9 @@ acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock); - /* If our UID matches the UID for the ECDT-enumerated EC, - we now have the *real* EC info, so kill the makeshift one. */ - acpi_evaluate_integer(ec->common.handle, "_UID", NULL, &uid); - if (ec_ecdt && ec_ecdt->common.uid == uid) { + /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: + http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ + if (ec_ecdt) { acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); diff -uNr linux-2.6.16.1/drivers/acpi/ec.h linux-2.6.16.1-imac/drivers/acpi/ec.h --- linux-2.6.16.1/drivers/acpi/ec.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/ec.h 2006-04-01 23:51:38.000000000 +0000 @@ -0,0 +1,70 @@ +/* + * ec.h - ACPI Embedded Controller Driver ($Revision: 1.2 $) + * + * Copyright (C) 2004 Luming Yu + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +union acpi_ec { + struct { + u32 mode; + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + } common; + + struct { + u32 mode; + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + unsigned int expect_event; + atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ + atomic_t pending_gpe; + struct semaphore sem; + wait_queue_head_t wait; + } burst; + + struct { + u32 mode; + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + struct semaphore sem; + spinlock_t lock; + } polling; +}; + +extern int acpi_ec_read(union acpi_ec *ec, u8 address, u32 * data); +extern int acpi_ec_write(union acpi_ec *ec, u8 address, u8 data); diff -uNr linux-2.6.16.1/drivers/acpi/i2c_acpi_ec.c linux-2.6.16.1-imac/drivers/acpi/i2c_acpi_ec.c --- linux-2.6.16.1/drivers/acpi/i2c_acpi_ec.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/i2c_acpi_ec.c 2006-04-01 23:51:38.000000000 +0000 @@ -0,0 +1,423 @@ +/* + * SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $) + * + * Copyright (c) 2002, 2005 Ducrot Bruno + * Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks) + * + * 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 version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c_acpi_ec.h" + +//#define xudelay(t) +#define xudelay(t) udelay(t) + +//#define xmsleep(t) +#define xmsleep(t) msleep(t) + +#define ACPI_EC_HC_COMPONENT 0x00080000 +#define ACPI_EC_HC_CLASS "ec_hc_smbus" +#define ACPI_EC_HC_HID "ACPI0001" +#define ACPI_EC_HC_DRIVER_NAME "ACPI EC HC smbus driver" +#define ACPI_EC_HC_DEVICE_NAME "EC HC smbus" + +#define _COMPONENT ACPI_EC_HC_COMPONENT + +ACPI_MODULE_NAME("acpi_smbus") + +static int acpi_ec_hc_add(struct acpi_device *device); +static int acpi_ec_hc_remove(struct acpi_device *device, int type); + +static struct acpi_driver acpi_ec_hc_driver = { + .name = ACPI_EC_HC_DRIVER_NAME, + .class = ACPI_EC_HC_CLASS, + .ids = ACPI_EC_HC_HID, + .ops = { + .add = acpi_ec_hc_add, + .remove = acpi_ec_hc_remove, + }, +}; + +/* Various bit mask for EC_SC (R) */ +#define OBF 0x01 +#define IBF 0x02 +#define CMD 0x08 +#define BURST 0x10 +#define SCI_EVT 0x20 +#define SMI_EVT 0x40 + +/* Commands for EC_SC (W) */ +#define RD_EC 0x80 +#define WR_EC 0x81 +#define BE_EC 0x82 +#define BD_EC 0x83 +#define QR_EC 0x84 + +/* + * ACPI 2.0 chapter 13 SMBus 2.0 EC register model + */ + +#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ +#define ACPI_EC_SMB_STS 0x01 /* status */ +#define ACPI_EC_SMB_ADDR 0x02 /* address */ +#define ACPI_EC_SMB_CMD 0x03 /* command */ +#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ +#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ +#define ACPI_EC_SMB_ALRM_A 0x25 /* alarm address */ +#define ACPI_EC_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ + +#define ACPI_EC_SMB_STS_DONE 0x80 +#define ACPI_EC_SMB_STS_ALRM 0x40 +#define ACPI_EC_SMB_STS_RES 0x20 +#define ACPI_EC_SMB_STS_STATUS 0x1f + +#define ACPI_EC_SMB_STATUS_OK 0x00 +#define ACPI_EC_SMB_STATUS_FAIL 0x07 +#define ACPI_EC_SMB_STATUS_DNAK 0x10 +#define ACPI_EC_SMB_STATUS_DERR 0x11 +#define ACPI_EC_SMB_STATUS_CMD_DENY 0x12 +#define ACPI_EC_SMB_STATUS_UNKNOWN 0x13 +#define ACPI_EC_SMB_STATUS_ACC_DENY 0x17 +#define ACPI_EC_SMB_STATUS_TIMEOUT 0x18 +#define ACPI_EC_SMB_STATUS_NOTSUP 0x19 +#define ACPI_EC_SMB_STATUS_BUSY 0x1A +#define ACPI_EC_SMB_STATUS_PEC 0x1F + +#define ACPI_EC_SMB_PRTCL_WRITE 0x00 +#define ACPI_EC_SMB_PRTCL_READ 0x01 +#define ACPI_EC_SMB_PRTCL_QUICK 0x02 +#define ACPI_EC_SMB_PRTCL_BYTE 0x04 +#define ACPI_EC_SMB_PRTCL_BYTE_DATA 0x06 +#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 +#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a +#define ACPI_EC_SMB_PRTCL_PROC_CALL 0x0c +#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL 0x0d +#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA 0x4a +#define ACPI_EC_SMB_PRTCL_PEC 0x80 + +/* Length of pre/post transaction sleep (msec) */ +#define ACPI_EC_SMB_TRANSACTION_SLEEP 1 + +static int +acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data) +{ + //u32 val; + int err; + + ACPI_FUNCTION_TRACE("acpi_ec_smb_read"); + + //err = acpi_ec_read(smbus->ec, smbus->base + address, &val); + /* + err = ec_read(smbus->base + address, &val); + if (!err) + *data = val; + */ + err = ec_read(smbus->base + address, data); + + xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); + + return err; +} + +static int +acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data) +{ + int err; + + ACPI_FUNCTION_TRACE("acpi_ec_smb_write"); + + //err = acpi_ec_write(smbus->ec, smbus->base + address, data); + err = ec_write(smbus->base + address, data); + + xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); + + return err; +} + +static s32 +acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + struct acpi_ec_smbus *smbus = adap->algo_data; + unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 }; + int i; + + ACPI_FUNCTION_TRACE("acpi_ec_smb_access"); + + if (read_write == I2C_SMBUS_READ) { + protocol = ACPI_EC_SMB_PRTCL_READ; + } else { + protocol = ACPI_EC_SMB_PRTCL_WRITE; + } + pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0; + + switch (size) { + + case I2C_SMBUS_QUICK: + protocol |= ACPI_EC_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); + protocol |= ACPI_EC_SMB_PRTCL_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); + protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA; + break; + + case I2C_SMBUS_WORD_DATA: + acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, + data->word >> 8); + } + protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec; + break; + + case I2C_SMBUS_BLOCK_DATA: + acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + len = min_t(u8, data->block[0], 32); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); + for (i = 0; i < len; i++) + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, + data->block[i + 1]); + } + protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = min_t(u8, data->block[0], 32); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); + if (read_write == I2C_SMBUS_WRITE) + for (i = 0; i < len; i++) + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, + data->block[i + 1]); + protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8); + protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_BLOCK_PROC_CALL: + protocol |= pec; + len = min_t(u8, data->block[0], 31); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); + for (i = 0; i < len; i++) + acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, + data->block[i + 1]); + protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + default: + printk(KERN_WARNING PREFIX "EC SMBus adapter: " + "Unsupported transaction %d\n", size); + return -1; + } + + acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1); + acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol); + + acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); + + if (~temp[0] & ACPI_EC_SMB_STS_DONE) { + xudelay(500); + acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); + } + if (~temp[0] & ACPI_EC_SMB_STS_DONE) { + xmsleep(10); + acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); + } + if ((~temp[0] & ACPI_EC_SMB_STS_DONE) + || (temp[0] & ACPI_EC_SMB_STS_STATUS)) + return -1; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte); + break; + + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0); + acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1); + data->word = (temp[1] << 8) | temp[0]; + break; + + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + len = 0; + acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len); + len = min_t(u8, len, 32); + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < len; i++) + acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i, + data->block + i + 1); + data->block[0] = len; + break; + } + + return 0; +} + +static u32 acpi_ec_smb_func(struct i2c_adapter *adapter) +{ + ACPI_FUNCTION_TRACE("acpi_ec_smb_func"); + + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_HWPEC_CALC; +} + +static struct i2c_algorithm acpi_ec_smbus_algorithm = { + .smbus_xfer = acpi_ec_smb_access, + .functionality = acpi_ec_smb_func, +}; + +static int acpi_ec_hc_add(struct acpi_device *device) +{ + int status; + unsigned long val; + struct acpi_ec_hc *ec_hc; + struct acpi_ec_smbus *smbus; + + ACPI_FUNCTION_TRACE("acpi_ec_hc_add"); + + if (!device) + return_VALUE(-EINVAL); + + ec_hc = kmalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL); + if (!ec_hc) + return_VALUE(-ENOMEM); + memset(ec_hc, 0, sizeof(struct acpi_ec_hc)); + + smbus = kmalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL); + if (!smbus) { + kfree(ec_hc); + return_VALUE(-ENOMEM); + } + memset(smbus, 0, sizeof(struct acpi_ec_smbus)); + + ec_hc->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS); + acpi_driver_data(device) = ec_hc; + + status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING PREFIX "Error obtaining _EC\n"); + kfree(ec_hc->smbus); + kfree(smbus); + return_VALUE(-EIO); + } + + smbus->ec = acpi_driver_data(device->parent); + smbus->base = (val & 0xff00ull) >> 8; + smbus->alert = val & 0xffull; + + printk(KERN_DEBUG "base: 0x%.x alert: 0x%.2x\n", smbus->base, smbus->alert); + + smbus->adapter.owner = THIS_MODULE; + smbus->adapter.algo = &acpi_ec_smbus_algorithm; + smbus->adapter.algo_data = smbus; + + if (i2c_add_adapter(&smbus->adapter)) { + printk(KERN_WARNING PREFIX "EC SMBus adapter: " + "Failed to register adapter\n"); + kfree(smbus); + kfree(ec_hc); + return_VALUE(-EIO); + } + + ec_hc->smbus = smbus; + + printk(KERN_INFO PREFIX "EC SMBus adapter: name %s, class %s, bid %s\n", + acpi_device_name(device), + acpi_device_class(device), acpi_device_bid(device) + ); + + return_VALUE(AE_OK); +} + +static int acpi_ec_hc_remove(struct acpi_device *device, int type) +{ + struct acpi_ec_hc *ec_hc; + + ACPI_FUNCTION_TRACE("acpi_ec_hc_remove"); + + if (!device) + return_VALUE(-EINVAL); + + ec_hc = acpi_driver_data(device); + + i2c_del_adapter(&ec_hc->smbus->adapter); + kfree(ec_hc->smbus); + kfree(ec_hc); + + return_VALUE(AE_OK); +} + +static int __init acpi_ec_hc_init(void) +{ + ACPI_FUNCTION_TRACE("acpi_ec_hc_init"); + return acpi_bus_register_driver(&acpi_ec_hc_driver); +} + +static void __exit acpi_ec_hc_exit(void) +{ + ACPI_FUNCTION_TRACE("acpi_ec_hc_exit"); + acpi_bus_unregister_driver(&acpi_ec_hc_driver); +} + +struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_get_ec_hc"); + return ((struct acpi_ec_hc *)acpi_driver_data(device->parent)); +} + +EXPORT_SYMBOL(acpi_get_ec_hc); + +module_init(acpi_ec_hc_init); +module_exit(acpi_ec_hc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ducrot Bruno"); +MODULE_DESCRIPTION("ACPI EC SMBus driver"); diff -uNr linux-2.6.16.1/drivers/acpi/i2c_acpi_ec.h linux-2.6.16.1-imac/drivers/acpi/i2c_acpi_ec.h --- linux-2.6.16.1/drivers/acpi/i2c_acpi_ec.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/i2c_acpi_ec.h 2006-04-01 23:51:38.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $) + * + * Copyright (c) 2002, 2005 Ducrot Bruno + * + * 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 version 2. + */ + +#include "ec.h" + +struct acpi_ec_smbus { + struct i2c_adapter adapter; + union acpi_ec *ec; + int base; + int alert; +}; + +struct acpi_ec_hc { + acpi_handle handle; + struct acpi_ec_smbus *smbus; +}; + +struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device); diff -uNr linux-2.6.16.1/drivers/acpi/osl.c linux-2.6.16.1-imac/drivers/acpi/osl.c --- linux-2.6.16.1/drivers/acpi/osl.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/acpi/osl.c 2006-04-01 23:51:38.000000000 +0000 @@ -208,6 +208,10 @@ void acpi_os_unmap_memory(void __iomem * virt, acpi_size size) { + /* Don't unmap memory which was not mapped by acpi_os_map_memory */ + if (efi_enabled && + (efi_mem_attributes(virt_to_phys(virt)) & EFI_MEMORY_WB)) + return; iounmap(virt); } EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); diff -uNr linux-2.6.16.1/drivers/firmware/efivars.c linux-2.6.16.1-imac/drivers/firmware/efivars.c --- linux-2.6.16.1/drivers/firmware/efivars.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/firmware/efivars.c 2006-04-01 23:51:38.000000000 +0000 @@ -110,7 +110,7 @@ struct efi_variable { efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; efi_guid_t VendorGuid; - unsigned long DataSize; + u64 DataSize; __u8 Data[1024]; efi_status_t Status; __u32 Attributes; diff -uNr linux-2.6.16.1/drivers/macintosh/Kconfig linux-2.6.16.1-imac/drivers/macintosh/Kconfig --- linux-2.6.16.1/drivers/macintosh/Kconfig 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/macintosh/Kconfig 2006-04-01 23:51:38.000000000 +0000 @@ -1,6 +1,6 @@ menu "Macintosh device drivers" - depends on PPC || MAC + depends on PPC || MAC || X86 config ADB bool "Apple Desktop Bus (ADB) support" @@ -135,7 +135,7 @@ config MAC_EMUMOUSEBTN bool "Support for mouse button 2+3 emulation" - depends on INPUT_ADBHID +# depends on INPUT_ADBHID help This provides generic support for emulating the 2nd and 3rd mouse button with keypresses. If you say Y here, the emulation is still diff -uNr linux-2.6.16.1/drivers/pci/msi.c linux-2.6.16.1-imac/drivers/pci/msi.c --- linux-2.6.16.1/drivers/pci/msi.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/pci/msi.c 2006-04-01 23:51:38.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,27 @@ u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; #endif +#ifdef CONFIG_X86 + +static int __init apple_msi_disable(struct dmi_system_id *d) +{ + if (!pci_msi_quirk) { + printk(KERN_INFO "Apple machine detected. MSI disabled\n"); + pci_msi_quirk = 1; + } + return 0; +} + +static struct dmi_system_id __initdata msi_dmi_table[] = { + { + apple_msi_disable, "Apple", + { DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), }, + }, + { } +}; + +#endif + static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) { memset(p, 0, NR_IRQS * sizeof(struct msi_desc)); @@ -362,6 +384,10 @@ if (!status) return status; +#ifdef CONFIG_X86 + dmi_check_system(msi_dmi_table); +#endif + if (pci_msi_quirk) { pci_msi_enable = 0; printk(KERN_WARNING "PCI: MSI quirk detected. MSI disabled.\n"); diff -uNr linux-2.6.16.1/drivers/scsi/ata_piix.c linux-2.6.16.1-imac/drivers/scsi/ata_piix.c --- linux-2.6.16.1/drivers/scsi/ata_piix.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/scsi/ata_piix.c 2006-04-01 23:51:38.000000000 +0000 @@ -95,6 +95,8 @@ #define DRV_NAME "ata_piix" #define DRV_VERSION "1.05" +#define ATA_ENABLE_PATA 1 + enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ ICH5_PMR = 0x90, /* port mapping register */ @@ -141,6 +143,7 @@ { 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix4_pata }, { 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, { 0x8086, 0x25a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, + { 0x8086, 0x27df, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich5_pata }, #endif /* NOTE: The following PCI ids must be kept in sync with the diff -uNr linux-2.6.16.1/drivers/usb/input/hid-core.c linux-2.6.16.1-imac/drivers/usb/input/hid-core.c --- linux-2.6.16.1/drivers/usb/input/hid-core.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/usb/input/hid-core.c 2006-04-01 23:51:38.000000000 +0000 @@ -1602,6 +1602,8 @@ { USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN }, { USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN }, { USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x0217, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x0218, HID_QUIRK_POWERBOOK_HAS_FN }, { USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN }, { USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN }, diff -uNr linux-2.6.16.1/drivers/usb/storage/usb.c linux-2.6.16.1-imac/drivers/usb/storage/usb.c --- linux-2.6.16.1/drivers/usb/storage/usb.c 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/usb/storage/usb.c 2006-04-01 23:51:38.000000000 +0000 @@ -103,7 +103,7 @@ MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); MODULE_LICENSE("GPL"); -static unsigned int delay_use = 5; +static unsigned int delay_use = 0; module_param(delay_use, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); diff -uNr linux-2.6.16.1/drivers/video/Kconfig linux-2.6.16.1-imac/drivers/video/Kconfig --- linux-2.6.16.1/drivers/video/Kconfig 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/video/Kconfig 2006-04-01 23:51:38.000000000 +0000 @@ -465,6 +465,15 @@ You will get a boot time penguin logo at no additional cost. Please read . If unsure, say Y. +config FB_IMAC + bool "Intel Based Macs FB" + depends on (FB = y) && X86 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the Inel Based Mac's + config VIDEO_SELECT bool depends on FB_VESA diff -uNr linux-2.6.16.1/drivers/video/Makefile linux-2.6.16.1-imac/drivers/video/Makefile --- linux-2.6.16.1/drivers/video/Makefile 2006-03-20 05:53:29.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/video/Makefile 2006-04-01 23:51:38.000000000 +0000 @@ -97,6 +97,7 @@ # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o +obj-$(CONFIG_FB_IMAC) += imacfb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o obj-$(CONFIG_FB_OF) += offb.o diff -uNr linux-2.6.16.1/drivers/video/imacfb.c linux-2.6.16.1-imac/drivers/video/imacfb.c --- linux-2.6.16.1/drivers/video/imacfb.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.16.1-imac/drivers/video/imacfb.c 2006-04-01 23:52:16.000000000 +0000 @@ -0,0 +1,427 @@ +/* + * framebuffer driver for Intel Based Mac's + * + * (c) 2006 Edgar Hucek + * Original imac driver written by Gerd Knorr + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include