ACPI: add support for Smart Battery
Most batteries today are ACPI "Control Method" batteries, but some models ship with the older "Smart Battery" that requires this code. Rich Townsend and Bruno Ducrot were the original authors. Vladimir Lebedev updated to run on latest kernel. http://bugzilla.kernel.org/show_bug.cgi?id=3734 Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
Родитель
37672d4c52
Коммит
3f86b83243
|
@ -352,6 +352,18 @@ config ACPI_HOTPLUG_MEMORY
|
||||||
If one selects "m," this driver can be loaded using the following
|
If one selects "m," this driver can be loaded using the following
|
||||||
command:
|
command:
|
||||||
$>modprobe acpi_memhotplug
|
$>modprobe acpi_memhotplug
|
||||||
|
|
||||||
|
config ACPI_SBS
|
||||||
|
tristate "Smart Battery System (EXPERIMENTAL)"
|
||||||
|
depends on X86 && I2C
|
||||||
|
depends on EXPERIMENTAL
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This driver adds support for the Smart Battery System.
|
||||||
|
Depends on I2C (Device Drivers ---> I2C support)
|
||||||
|
A "Smart Battery" is quite old and quite rare compared
|
||||||
|
to today's ACPI "Control Method" battery.
|
||||||
|
|
||||||
endif # ACPI
|
endif # ACPI
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -58,3 +58,5 @@ obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
|
||||||
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
||||||
obj-y += scan.o motherboard.o
|
obj-y += scan.o motherboard.o
|
||||||
obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
|
obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
|
||||||
|
obj-y += cm_sbs.o
|
||||||
|
obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o
|
||||||
|
|
|
@ -50,6 +50,9 @@ ACPI_MODULE_NAME("acpi_ac")
|
||||||
MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME);
|
MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
extern struct proc_dir_entry *acpi_lock_ac_dir(void);
|
||||||
|
extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
|
||||||
|
|
||||||
static int acpi_ac_add(struct acpi_device *device);
|
static int acpi_ac_add(struct acpi_device *device);
|
||||||
static int acpi_ac_remove(struct acpi_device *device, int type);
|
static int acpi_ac_remove(struct acpi_device *device, int type);
|
||||||
static int acpi_ac_open_fs(struct inode *inode, struct file *file);
|
static int acpi_ac_open_fs(struct inode *inode, struct file *file);
|
||||||
|
@ -278,17 +281,16 @@ static int acpi_ac_remove(struct acpi_device *device, int type)
|
||||||
|
|
||||||
static int __init acpi_ac_init(void)
|
static int __init acpi_ac_init(void)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result;
|
||||||
|
|
||||||
|
|
||||||
acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir);
|
acpi_ac_dir = acpi_lock_ac_dir();
|
||||||
if (!acpi_ac_dir)
|
if (!acpi_ac_dir)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
acpi_ac_dir->owner = THIS_MODULE;
|
|
||||||
|
|
||||||
result = acpi_bus_register_driver(&acpi_ac_driver);
|
result = acpi_bus_register_driver(&acpi_ac_driver);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir);
|
acpi_unlock_ac_dir(acpi_ac_dir);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +302,7 @@ static void __exit acpi_ac_exit(void)
|
||||||
|
|
||||||
acpi_bus_unregister_driver(&acpi_ac_driver);
|
acpi_bus_unregister_driver(&acpi_ac_driver);
|
||||||
|
|
||||||
remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir);
|
acpi_unlock_ac_dir(acpi_ac_dir);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,9 @@ ACPI_MODULE_NAME("acpi_battery")
|
||||||
MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME);
|
MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
extern struct proc_dir_entry *acpi_lock_battery_dir(void);
|
||||||
|
extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
|
||||||
|
|
||||||
static int acpi_battery_add(struct acpi_device *device);
|
static int acpi_battery_add(struct acpi_device *device);
|
||||||
static int acpi_battery_remove(struct acpi_device *device, int type);
|
static int acpi_battery_remove(struct acpi_device *device, int type);
|
||||||
|
|
||||||
|
@ -750,17 +753,15 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
|
||||||
|
|
||||||
static int __init acpi_battery_init(void)
|
static int __init acpi_battery_init(void)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result;
|
||||||
|
|
||||||
|
acpi_battery_dir = acpi_lock_battery_dir();
|
||||||
acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir);
|
|
||||||
if (!acpi_battery_dir)
|
if (!acpi_battery_dir)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
acpi_battery_dir->owner = THIS_MODULE;
|
|
||||||
|
|
||||||
result = acpi_bus_register_driver(&acpi_battery_driver);
|
result = acpi_bus_register_driver(&acpi_battery_driver);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
|
acpi_unlock_battery_dir(acpi_battery_dir);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,7 +773,7 @@ static void __exit acpi_battery_exit(void)
|
||||||
|
|
||||||
acpi_bus_unregister_driver(&acpi_battery_driver);
|
acpi_bus_unregister_driver(&acpi_battery_driver);
|
||||||
|
|
||||||
remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
|
acpi_unlock_battery_dir(acpi_battery_dir);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* 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/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <acpi/acpi_bus.h>
|
||||||
|
#include <acpi/acpi_drivers.h>
|
||||||
|
#include <acpi/acmacros.h>
|
||||||
|
#include <acpi/actypes.h>
|
||||||
|
#include <acpi/acutils.h>
|
||||||
|
|
||||||
|
ACPI_MODULE_NAME("cm_sbs")
|
||||||
|
#define ACPI_AC_CLASS "ac_adapter"
|
||||||
|
#define ACPI_BATTERY_CLASS "battery"
|
||||||
|
#define ACPI_SBS_COMPONENT 0x00080000
|
||||||
|
#define _COMPONENT ACPI_SBS_COMPONENT
|
||||||
|
static struct proc_dir_entry *acpi_ac_dir;
|
||||||
|
static struct proc_dir_entry *acpi_battery_dir;
|
||||||
|
|
||||||
|
static struct semaphore cm_sbs_sem;
|
||||||
|
|
||||||
|
static int lock_ac_dir_cnt = 0;
|
||||||
|
static int lock_battery_dir_cnt = 0;
|
||||||
|
|
||||||
|
struct proc_dir_entry *acpi_lock_ac_dir(void)
|
||||||
|
{
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_lock_ac_dir");
|
||||||
|
|
||||||
|
down(&cm_sbs_sem);
|
||||||
|
if (!acpi_ac_dir) {
|
||||||
|
acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir);
|
||||||
|
}
|
||||||
|
if (acpi_ac_dir) {
|
||||||
|
lock_ac_dir_cnt++;
|
||||||
|
} else {
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||||
|
"Cannot create %s\n", ACPI_AC_CLASS));
|
||||||
|
}
|
||||||
|
up(&cm_sbs_sem);
|
||||||
|
return (acpi_ac_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(acpi_lock_ac_dir);
|
||||||
|
|
||||||
|
void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir_param)
|
||||||
|
{
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_unlock_ac_dir");
|
||||||
|
|
||||||
|
down(&cm_sbs_sem);
|
||||||
|
if (acpi_ac_dir_param) {
|
||||||
|
lock_ac_dir_cnt--;
|
||||||
|
}
|
||||||
|
if (lock_ac_dir_cnt == 0 && acpi_ac_dir_param && acpi_ac_dir) {
|
||||||
|
remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir);
|
||||||
|
acpi_ac_dir = 0;
|
||||||
|
}
|
||||||
|
up(&cm_sbs_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(acpi_unlock_ac_dir);
|
||||||
|
|
||||||
|
struct proc_dir_entry *acpi_lock_battery_dir(void)
|
||||||
|
{
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_lock_battery_dir");
|
||||||
|
|
||||||
|
down(&cm_sbs_sem);
|
||||||
|
if (!acpi_battery_dir) {
|
||||||
|
acpi_battery_dir =
|
||||||
|
proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir);
|
||||||
|
}
|
||||||
|
if (acpi_battery_dir) {
|
||||||
|
lock_battery_dir_cnt++;
|
||||||
|
} else {
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||||
|
"Cannot create %s\n", ACPI_BATTERY_CLASS));
|
||||||
|
}
|
||||||
|
up(&cm_sbs_sem);
|
||||||
|
return (acpi_battery_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(acpi_lock_battery_dir);
|
||||||
|
|
||||||
|
void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param)
|
||||||
|
{
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_unlock_battery_dir");
|
||||||
|
|
||||||
|
down(&cm_sbs_sem);
|
||||||
|
if (acpi_battery_dir_param) {
|
||||||
|
lock_battery_dir_cnt--;
|
||||||
|
}
|
||||||
|
if (lock_battery_dir_cnt == 0 && acpi_battery_dir_param
|
||||||
|
&& acpi_battery_dir) {
|
||||||
|
remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
|
||||||
|
acpi_battery_dir = 0;
|
||||||
|
}
|
||||||
|
up(&cm_sbs_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(acpi_unlock_battery_dir);
|
||||||
|
|
||||||
|
static int __init acpi_cm_sbs_init(void)
|
||||||
|
{
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_cm_sbs_init");
|
||||||
|
|
||||||
|
if (acpi_disabled)
|
||||||
|
return_VALUE(0);
|
||||||
|
|
||||||
|
init_MUTEX(&cm_sbs_sem);
|
||||||
|
|
||||||
|
return_VALUE(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
subsys_initcall(acpi_cm_sbs_init);
|
|
@ -0,0 +1,420 @@
|
||||||
|
/*
|
||||||
|
* SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $)
|
||||||
|
*
|
||||||
|
* 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 <linux/version.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
|
#include "i2c_ec.h"
|
||||||
|
|
||||||
|
#define xudelay(t) udelay(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
|
||||||
|
#define ACPI_EC_SMB_ACCESS_SLEEP1 1
|
||||||
|
#define ACPI_EC_SMB_ACCESS_SLEEP2 10
|
||||||
|
|
||||||
|
static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_ec_smb_read");
|
||||||
|
|
||||||
|
err = ec_read(smbus->base + address, &val);
|
||||||
|
if (!err) {
|
||||||
|
*data = val;
|
||||||
|
}
|
||||||
|
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 = ec_write(smbus->base + address, data);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
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:
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "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(ACPI_EC_SMB_ACCESS_SLEEP2);
|
||||||
|
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)) {
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "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;
|
||||||
|
|
||||||
|
smbus->adapter.owner = THIS_MODULE;
|
||||||
|
smbus->adapter.algo = &acpi_ec_smbus_algorithm;
|
||||||
|
smbus->adapter.algo_data = smbus;
|
||||||
|
|
||||||
|
if (i2c_add_adapter(&smbus->adapter)) {
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
|
||||||
|
"EC SMBus adapter: Failed to register adapter\n"));
|
||||||
|
kfree(smbus);
|
||||||
|
kfree(ec_hc);
|
||||||
|
return_VALUE(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
ec_hc->smbus = smbus;
|
||||||
|
|
||||||
|
printk(KERN_INFO PREFIX "%s [%s]\n",
|
||||||
|
acpi_device_name(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)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
ACPI_FUNCTION_TRACE("acpi_ec_hc_init");
|
||||||
|
result = acpi_bus_register_driver(&acpi_ec_hc_driver);
|
||||||
|
if (result < 0) {
|
||||||
|
return_VALUE(-ENODEV);
|
||||||
|
}
|
||||||
|
return_VALUE(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче