forked from mirrors/linux
		
	ACPI: SBS: Split host controller (ACPI0001) from SBS driver (ACPI0002)
Replace poll-based host controller driver with the notify-based one. Split it out of sbs.c. Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
		
							parent
							
								
									30c08574da
								
							
						
					
					
						commit
						91087dfa51
					
				
					 4 changed files with 412 additions and 299 deletions
				
			
		| 
						 | 
				
			
			@ -60,3 +60,4 @@ obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 | 
			
		|||
obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
 | 
			
		||||
obj-y				+= cm_sbs.o
 | 
			
		||||
obj-$(CONFIG_ACPI_SBS)		+= sbs.o
 | 
			
		||||
obj-$(CONFIG_ACPI_SBS)		+= sbshc.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,8 @@
 | 
			
		|||
#include <linux/jiffies.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
 | 
			
		||||
#include "sbshc.h"
 | 
			
		||||
 | 
			
		||||
#define ACPI_SBS_COMPONENT		0x00080000
 | 
			
		||||
#define ACPI_SBS_CLASS			"sbs"
 | 
			
		||||
#define ACPI_AC_CLASS			"ac_adapter"
 | 
			
		||||
| 
						 | 
				
			
			@ -59,28 +61,6 @@ MODULE_AUTHOR("Rich Townsend");
 | 
			
		|||
MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
 | 
			
		||||
#define	xmsleep(t)	msleep(t)
 | 
			
		||||
 | 
			
		||||
#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_STS_DONE	0x80
 | 
			
		||||
#define ACPI_EC_SMB_STS_STATUS	0x1f
 | 
			
		||||
 | 
			
		||||
#define ACPI_EC_SMB_PRTCL_WRITE		0x00
 | 
			
		||||
#define ACPI_EC_SMB_PRTCL_READ		0x01
 | 
			
		||||
#define ACPI_EC_SMB_PRTCL_WORD_DATA	0x08
 | 
			
		||||
#define ACPI_EC_SMB_PRTCL_BLOCK_DATA	0x0a
 | 
			
		||||
 | 
			
		||||
#define ACPI_EC_SMB_TRANSACTION_SLEEP	1
 | 
			
		||||
#define ACPI_EC_SMB_ACCESS_SLEEP1	1
 | 
			
		||||
#define ACPI_EC_SMB_ACCESS_SLEEP2	10
 | 
			
		||||
 | 
			
		||||
#define	DEF_CAPACITY_UNIT	3
 | 
			
		||||
#define	MAH_CAPACITY_UNIT	1
 | 
			
		||||
#define	MWH_CAPACITY_UNIT	2
 | 
			
		||||
| 
						 | 
				
			
			@ -103,12 +83,6 @@ extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
 | 
			
		|||
#define	MAX_SBS_BAT			4
 | 
			
		||||
#define ACPI_SBS_BLOCK_MAX		32
 | 
			
		||||
 | 
			
		||||
#define ACPI_SBS_SMBUS_READ		1
 | 
			
		||||
#define ACPI_SBS_SMBUS_WRITE		2
 | 
			
		||||
 | 
			
		||||
#define ACPI_SBS_WORD_DATA		1
 | 
			
		||||
#define ACPI_SBS_BLOCK_DATA		2
 | 
			
		||||
 | 
			
		||||
#define	UPDATE_DELAY	10
 | 
			
		||||
 | 
			
		||||
/* 0 - every time, > 0 - by update_time */
 | 
			
		||||
| 
						 | 
				
			
			@ -124,8 +98,7 @@ static int acpi_sbs_remove(struct acpi_device *device, int type);
 | 
			
		|||
static int acpi_sbs_resume(struct acpi_device *device);
 | 
			
		||||
 | 
			
		||||
static const struct acpi_device_id sbs_device_ids[] = {
 | 
			
		||||
	{"ACPI0001", 0},
 | 
			
		||||
	{"ACPI0005", 0},
 | 
			
		||||
	{"ACPI0002", 0},
 | 
			
		||||
	{"", 0},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
 | 
			
		||||
| 
						 | 
				
			
			@ -182,8 +155,8 @@ struct acpi_battery {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
struct acpi_sbs {
 | 
			
		||||
	int base;
 | 
			
		||||
	struct acpi_device *device;
 | 
			
		||||
	struct acpi_smb_hc *hc;
 | 
			
		||||
	struct mutex mutex;
 | 
			
		||||
	int sbsm_present;
 | 
			
		||||
	int sbsm_batteries_supported;
 | 
			
		||||
| 
						 | 
				
			
			@ -199,190 +172,6 @@ struct acpi_sbs {
 | 
			
		|||
static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
 | 
			
		||||
static void acpi_sbs_update_time(void *data);
 | 
			
		||||
 | 
			
		||||
union sbs_rw_data {
 | 
			
		||||
	u16 word;
 | 
			
		||||
	u8 block[ACPI_SBS_BLOCK_MAX + 2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
 | 
			
		||||
			      char read_write, u8 command, int size,
 | 
			
		||||
			      union sbs_rw_data *data);
 | 
			
		||||
 | 
			
		||||
/* --------------------------------------------------------------------------
 | 
			
		||||
                               SMBus Communication
 | 
			
		||||
   -------------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data)
 | 
			
		||||
{
 | 
			
		||||
	u8 val;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = ec_read(sbs->base + address, &val);
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		*data = val;
 | 
			
		||||
	}
 | 
			
		||||
	xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
 | 
			
		||||
	return (err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = ec_write(sbs->base + address, data);
 | 
			
		||||
	return (err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
 | 
			
		||||
		   char read_write, u8 command, int size,
 | 
			
		||||
		   union sbs_rw_data *data)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char protocol, len = 0, temp[2] = { 0, 0 };
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (read_write == ACPI_SBS_SMBUS_READ) {
 | 
			
		||||
		protocol = ACPI_EC_SMB_PRTCL_READ;
 | 
			
		||||
	} else {
 | 
			
		||||
		protocol = ACPI_EC_SMB_PRTCL_WRITE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (size) {
 | 
			
		||||
 | 
			
		||||
	case ACPI_SBS_WORD_DATA:
 | 
			
		||||
		acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
 | 
			
		||||
		if (read_write == ACPI_SBS_SMBUS_WRITE) {
 | 
			
		||||
			acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word);
 | 
			
		||||
			acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1,
 | 
			
		||||
					  data->word >> 8);
 | 
			
		||||
		}
 | 
			
		||||
		protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA;
 | 
			
		||||
		break;
 | 
			
		||||
	case ACPI_SBS_BLOCK_DATA:
 | 
			
		||||
		acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
 | 
			
		||||
		if (read_write == ACPI_SBS_SMBUS_WRITE) {
 | 
			
		||||
			len = min_t(u8, data->block[0], 32);
 | 
			
		||||
			acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len);
 | 
			
		||||
			for (i = 0; i < len; i++)
 | 
			
		||||
				acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i,
 | 
			
		||||
						  data->block[i + 1]);
 | 
			
		||||
		}
 | 
			
		||||
		protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"unsupported transaction %d", size));
 | 
			
		||||
		return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1);
 | 
			
		||||
	acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol);
 | 
			
		||||
 | 
			
		||||
	acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
 | 
			
		||||
 | 
			
		||||
	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
 | 
			
		||||
		xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1);
 | 
			
		||||
		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
 | 
			
		||||
	}
 | 
			
		||||
	if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
 | 
			
		||||
		xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
 | 
			
		||||
		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
 | 
			
		||||
	}
 | 
			
		||||
	if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
 | 
			
		||||
	    || (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"transaction %d error", size));
 | 
			
		||||
		return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (read_write == ACPI_SBS_SMBUS_WRITE) {
 | 
			
		||||
		return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (size) {
 | 
			
		||||
 | 
			
		||||
	case ACPI_SBS_WORD_DATA:
 | 
			
		||||
		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp);
 | 
			
		||||
		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1);
 | 
			
		||||
		data->word = (temp[1] << 8) | temp[0];
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case ACPI_SBS_BLOCK_DATA:
 | 
			
		||||
		len = 0;
 | 
			
		||||
		acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len);
 | 
			
		||||
		len = min_t(u8, len, 32);
 | 
			
		||||
		for (i = 0; i < len; i++)
 | 
			
		||||
			acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i,
 | 
			
		||||
					 data->block + i + 1);
 | 
			
		||||
		data->block[0] = len;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"unsupported transaction %d", size));
 | 
			
		||||
		return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word)
 | 
			
		||||
{
 | 
			
		||||
	union sbs_rw_data data;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
 | 
			
		||||
	result = acpi_ec_sbs_access(sbs, addr,
 | 
			
		||||
				    ACPI_SBS_SMBUS_READ, func,
 | 
			
		||||
				    ACPI_SBS_WORD_DATA, &data);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_ec_sbs_access() failed"));
 | 
			
		||||
	} else {
 | 
			
		||||
		*word = data.word;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str)
 | 
			
		||||
{
 | 
			
		||||
	union sbs_rw_data data;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
 | 
			
		||||
	result = acpi_ec_sbs_access(sbs, addr,
 | 
			
		||||
				    ACPI_SBS_SMBUS_READ, func,
 | 
			
		||||
				    ACPI_SBS_BLOCK_DATA, &data);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_ec_sbs_access() failed"));
 | 
			
		||||
	} else {
 | 
			
		||||
		strncpy(str, (const char *)data.block + 1, data.block[0]);
 | 
			
		||||
		str[data.block[0]] = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word)
 | 
			
		||||
{
 | 
			
		||||
	union sbs_rw_data data;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
 | 
			
		||||
	data.word = word;
 | 
			
		||||
 | 
			
		||||
	result = acpi_ec_sbs_access(sbs, addr,
 | 
			
		||||
				    ACPI_SBS_SMBUS_WRITE, func,
 | 
			
		||||
				    ACPI_SBS_WORD_DATA, &data);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_ec_sbs_access() failed"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sbs_zombie(struct acpi_sbs *sbs)
 | 
			
		||||
{
 | 
			
		||||
	return (sbs->zombie);
 | 
			
		||||
| 
						 | 
				
			
			@ -433,11 +222,11 @@ static int acpi_battery_get_present(struct acpi_battery *battery)
 | 
			
		|||
	int result = 0;
 | 
			
		||||
	int is_present = 0;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(battery->sbs,
 | 
			
		||||
				    ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
 | 
			
		||||
				    ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&state);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
	}
 | 
			
		||||
	if (!result) {
 | 
			
		||||
		is_present = (state & 0x000f) & (1 << battery->id);
 | 
			
		||||
| 
						 | 
				
			
			@ -461,19 +250,19 @@ static int acpi_battery_select(struct acpi_battery *battery)
 | 
			
		|||
		 * it causes charging to halt on SBSELs */
 | 
			
		||||
 | 
			
		||||
		result =
 | 
			
		||||
		    acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
 | 
			
		||||
		    acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&state);
 | 
			
		||||
		if (result) {
 | 
			
		||||
			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
					"acpi_sbs_read_word() failed"));
 | 
			
		||||
					"acpi_smbus_read() failed"));
 | 
			
		||||
			goto end;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		foo = (state & 0x0fff) | (1 << (battery->id + 12));
 | 
			
		||||
		result =
 | 
			
		||||
		    acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo);
 | 
			
		||||
		    acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&foo, 2);
 | 
			
		||||
		if (result) {
 | 
			
		||||
			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
					"acpi_sbs_write_word() failed"));
 | 
			
		||||
					"acpi_smbus_write() failed"));
 | 
			
		||||
			goto end;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -487,11 +276,11 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
 | 
			
		|||
	int result = 0;
 | 
			
		||||
	s16 battery_system_info;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04,
 | 
			
		||||
				    &battery_system_info);
 | 
			
		||||
	result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBSM_SMBUS_ADDR, 0x04,
 | 
			
		||||
				    (u8 *)&battery_system_info);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
	sbs->sbsm_present = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -504,50 +293,49 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
 | 
			
		|||
 | 
			
		||||
static int acpi_battery_get_info(struct acpi_battery *battery)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_sbs *sbs = battery->sbs;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
	s16 battery_mode;
 | 
			
		||||
	s16 specification_info;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
 | 
			
		||||
				    &battery_mode);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x03,
 | 
			
		||||
				    (u8 *)&battery_mode);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
	battery->info.capacity_mode = (battery_mode & 0x8000) >> 15;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10,
 | 
			
		||||
				    &battery->info.full_charge_capacity);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x10,
 | 
			
		||||
				    (u8 *)&battery->info.full_charge_capacity);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18,
 | 
			
		||||
				    &battery->info.design_capacity);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x18,
 | 
			
		||||
				    (u8 *)&battery->info.design_capacity);
 | 
			
		||||
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19,
 | 
			
		||||
				    &battery->info.design_voltage);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x19,
 | 
			
		||||
				    (u8 *)&battery->info.design_voltage);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a,
 | 
			
		||||
				    &specification_info);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x1a,
 | 
			
		||||
				    (u8 *)&specification_info);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -579,32 +367,32 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
 | 
			
		|||
		battery->info.ipscale = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c,
 | 
			
		||||
				    &battery->info.serial_number);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x1c,
 | 
			
		||||
				    (u8 *)&battery->info.serial_number);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20,
 | 
			
		||||
				   battery->info.manufacturer_name);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x20,
 | 
			
		||||
				   (u8 *)battery->info.manufacturer_name);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_str() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21,
 | 
			
		||||
				   battery->info.device_name);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x21,
 | 
			
		||||
				   (u8 *)battery->info.device_name);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_str() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22,
 | 
			
		||||
				   battery->info.device_chemistry);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x22,
 | 
			
		||||
				   (u8 *)battery->info.device_chemistry);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_str() failed"));
 | 
			
		||||
| 
						 | 
				
			
			@ -617,38 +405,37 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
 | 
			
		|||
 | 
			
		||||
static int acpi_battery_get_state(struct acpi_battery *battery)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_sbs *sbs = battery->sbs;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09,
 | 
			
		||||
				    &battery->state.voltage);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x09,
 | 
			
		||||
				    (u8 *)&battery->state.voltage);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a,
 | 
			
		||||
				    &battery->state.amperage);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x0a,
 | 
			
		||||
				    (u8 *)&battery->state.amperage);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f,
 | 
			
		||||
				    &battery->state.remaining_capacity);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x0f,
 | 
			
		||||
				    (u8 *)&battery->state.remaining_capacity);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16,
 | 
			
		||||
				    &battery->state.battery_state);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x16,
 | 
			
		||||
				    (u8 *)&battery->state.battery_state);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -658,14 +445,13 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
 | 
			
		|||
 | 
			
		||||
static int acpi_battery_get_alarm(struct acpi_battery *battery)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_sbs *sbs = battery->sbs;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
 | 
			
		||||
				    &battery->alarm.remaining_capacity);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
 | 
			
		||||
				    (u8 *)&battery->alarm.remaining_capacity);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -677,7 +463,6 @@ static int acpi_battery_get_alarm(struct acpi_battery *battery)
 | 
			
		|||
static int acpi_battery_set_alarm(struct acpi_battery *battery,
 | 
			
		||||
				  unsigned long alarm)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_sbs *sbs = battery->sbs;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
	s16 battery_mode;
 | 
			
		||||
	int foo;
 | 
			
		||||
| 
						 | 
				
			
			@ -693,29 +478,30 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery,
 | 
			
		|||
 | 
			
		||||
	if (alarm > 0) {
 | 
			
		||||
		result =
 | 
			
		||||
		    acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
 | 
			
		||||
				       &battery_mode);
 | 
			
		||||
		    acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x03,
 | 
			
		||||
				       (u8 *)&battery_mode);
 | 
			
		||||
		if (result) {
 | 
			
		||||
			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
					"acpi_sbs_read_word() failed"));
 | 
			
		||||
					"acpi_smbus_read() failed"));
 | 
			
		||||
			goto end;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		battery_mode &= 0xbfff;
 | 
			
		||||
		result =
 | 
			
		||||
		    acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
 | 
			
		||||
					battery_mode & 0xbfff);
 | 
			
		||||
		    acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
 | 
			
		||||
					(u8 *)&battery_mode, 2);
 | 
			
		||||
		if (result) {
 | 
			
		||||
			ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
					"acpi_sbs_write_word() failed"));
 | 
			
		||||
					"acpi_smbus_write() failed"));
 | 
			
		||||
			goto end;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	foo = alarm / (battery->info.capacity_mode ? 10 : 1);
 | 
			
		||||
	result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo);
 | 
			
		||||
	result = acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01, (u8 *)&foo, 2);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_write_word() failed"));
 | 
			
		||||
				"acpi_smbus_write() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -726,7 +512,6 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery,
 | 
			
		|||
 | 
			
		||||
static int acpi_battery_set_mode(struct acpi_battery *battery)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_sbs *sbs = battery->sbs;
 | 
			
		||||
	int result = 0;
 | 
			
		||||
	s16 battery_mode;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -734,11 +519,11 @@ static int acpi_battery_set_mode(struct acpi_battery *battery)
 | 
			
		|||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs,
 | 
			
		||||
				    ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
 | 
			
		||||
				    ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -747,19 +532,19 @@ static int acpi_battery_set_mode(struct acpi_battery *battery)
 | 
			
		|||
	} else {
 | 
			
		||||
		battery_mode |= 0x8000;
 | 
			
		||||
	}
 | 
			
		||||
	result = acpi_sbs_write_word(sbs,
 | 
			
		||||
				     ACPI_SB_SMBUS_ADDR, 0x03, battery_mode);
 | 
			
		||||
	result = acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD,
 | 
			
		||||
				     ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode, 2);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_write_word() failed"));
 | 
			
		||||
				"acpi_smbus_write() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs,
 | 
			
		||||
				    ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
 | 
			
		||||
	result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
 | 
			
		||||
				    ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode);
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -815,12 +600,12 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs)
 | 
			
		|||
	int result = 0;
 | 
			
		||||
	s16 charger_status;
 | 
			
		||||
 | 
			
		||||
	result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13,
 | 
			
		||||
				    &charger_status);
 | 
			
		||||
	result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBC_SMBUS_ADDR, 0x13,
 | 
			
		||||
				    (u8 *)&charger_status);
 | 
			
		||||
 | 
			
		||||
	if (result) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR,
 | 
			
		||||
				"acpi_sbs_read_word() failed"));
 | 
			
		||||
				"acpi_smbus_read() failed"));
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1614,15 +1399,6 @@ static int acpi_sbs_add(struct acpi_device *device)
 | 
			
		|||
	struct acpi_sbs *sbs = NULL;
 | 
			
		||||
	int result = 0, remove_result = 0;
 | 
			
		||||
	int id;
 | 
			
		||||
	acpi_status status = AE_OK;
 | 
			
		||||
	unsigned long val;
 | 
			
		||||
 | 
			
		||||
	status =
 | 
			
		||||
	    acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
 | 
			
		||||
	if (ACPI_FAILURE(status)) {
 | 
			
		||||
		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC"));
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
 | 
			
		||||
	if (!sbs) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1635,8 +1411,8 @@ static int acpi_sbs_add(struct acpi_device *device)
 | 
			
		|||
 | 
			
		||||
	sbs_mutex_lock(sbs);
 | 
			
		||||
 | 
			
		||||
	sbs->base = 0xff & (val >> 8);
 | 
			
		||||
	sbs->device = device;
 | 
			
		||||
	sbs->hc = acpi_driver_data(device->parent);
 | 
			
		||||
 | 
			
		||||
	strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
 | 
			
		||||
	strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										309
									
								
								drivers/acpi/sbshc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								drivers/acpi/sbshc.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,309 @@
 | 
			
		|||
/*
 | 
			
		||||
 * SMBus driver for ACPI Embedded Controller (v0.1)
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2007 Alexey Starikovskiy
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <acpi/acpi_bus.h>
 | 
			
		||||
#include <acpi/acpi_drivers.h>
 | 
			
		||||
#include <acpi/actypes.h>
 | 
			
		||||
#include <linux/wait.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/interrupt.h>
 | 
			
		||||
#include "sbshc.h"
 | 
			
		||||
 | 
			
		||||
#define ACPI_SMB_HC_CLASS	"smbus_host_controller"
 | 
			
		||||
#define ACPI_SMB_HC_DEVICE_NAME	"ACPI SMBus HC"
 | 
			
		||||
 | 
			
		||||
struct acpi_smb_hc {
 | 
			
		||||
	struct acpi_ec *ec;
 | 
			
		||||
	struct mutex lock;
 | 
			
		||||
	wait_queue_head_t wait;
 | 
			
		||||
	u8 offset;
 | 
			
		||||
	u8 query_bit;
 | 
			
		||||
	smbus_alarm_callback callback;
 | 
			
		||||
	void *context;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int acpi_smbus_hc_add(struct acpi_device *device);
 | 
			
		||||
static int acpi_smbus_hc_remove(struct acpi_device *device, int type);
 | 
			
		||||
 | 
			
		||||
static const struct acpi_device_id sbs_device_ids[] = {
 | 
			
		||||
	{"ACPI0001", 0},
 | 
			
		||||
	{"ACPI0005", 0},
 | 
			
		||||
	{"", 0},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
 | 
			
		||||
 | 
			
		||||
static struct acpi_driver acpi_smb_hc_driver = {
 | 
			
		||||
	.name = "smbus_hc",
 | 
			
		||||
	.class = ACPI_SMB_HC_CLASS,
 | 
			
		||||
	.ids = sbs_device_ids,
 | 
			
		||||
	.ops = {
 | 
			
		||||
		.add = acpi_smbus_hc_add,
 | 
			
		||||
		.remove = acpi_smbus_hc_remove,
 | 
			
		||||
		},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
union acpi_smb_status {
 | 
			
		||||
	u8 raw;
 | 
			
		||||
	struct {
 | 
			
		||||
		u8 status:5;
 | 
			
		||||
		u8 reserved:1;
 | 
			
		||||
		u8 alarm:1;
 | 
			
		||||
		u8 done:1;
 | 
			
		||||
	} fields;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum acpi_smb_status_codes {
 | 
			
		||||
	SMBUS_OK = 0,
 | 
			
		||||
	SMBUS_UNKNOWN_FAILURE = 0x07,
 | 
			
		||||
	SMBUS_DEVICE_ADDRESS_NACK = 0x10,
 | 
			
		||||
	SMBUS_DEVICE_ERROR = 0x11,
 | 
			
		||||
	SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12,
 | 
			
		||||
	SMBUS_UNKNOWN_ERROR = 0x13,
 | 
			
		||||
	SMBUS_DEVICE_ACCESS_DENIED = 0x17,
 | 
			
		||||
	SMBUS_TIMEOUT = 0x18,
 | 
			
		||||
	SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19,
 | 
			
		||||
	SMBUS_BUSY = 0x1a,
 | 
			
		||||
	SMBUS_PEC_ERROR = 0x1f,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum acpi_smb_offset {
 | 
			
		||||
	ACPI_SMB_PROTOCOL = 0,	/* protocol, PEC */
 | 
			
		||||
	ACPI_SMB_STATUS = 1,	/* status */
 | 
			
		||||
	ACPI_SMB_ADDRESS = 2,	/* address */
 | 
			
		||||
	ACPI_SMB_COMMAND = 3,	/* command */
 | 
			
		||||
	ACPI_SMB_DATA = 4,	/* 32 data registers */
 | 
			
		||||
	ACPI_SMB_BLOCK_COUNT = 0x24,	/* number of data bytes */
 | 
			
		||||
	ACPI_SMB_ALARM_ADDRESS = 0x25,	/* alarm address */
 | 
			
		||||
	ACPI_SMB_ALARM_DATA = 0x26,	/* 2 bytes alarm data */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data)
 | 
			
		||||
{
 | 
			
		||||
	return ec_read(hc->offset + address, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data)
 | 
			
		||||
{
 | 
			
		||||
	return ec_write(hc->offset + address, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int smb_check_done(struct acpi_smb_hc *hc)
 | 
			
		||||
{
 | 
			
		||||
	union acpi_smb_status status = {.raw = 0};
 | 
			
		||||
	smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw);
 | 
			
		||||
	return status.fields.done && (status.fields.status == SMBUS_OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout)
 | 
			
		||||
{
 | 
			
		||||
	if (wait_event_timeout(hc->wait, smb_check_done(hc),
 | 
			
		||||
			       msecs_to_jiffies(timeout)))
 | 
			
		||||
		return 0;
 | 
			
		||||
	else
 | 
			
		||||
		return -ETIME;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol, u8 address,
 | 
			
		||||
		    u8 command, u8 *data, u8 length)
 | 
			
		||||
{
 | 
			
		||||
	int ret = -EFAULT, i;
 | 
			
		||||
	u8 temp, sz = 0;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&hc->lock);
 | 
			
		||||
	if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
 | 
			
		||||
		goto end;
 | 
			
		||||
	if (temp) {
 | 
			
		||||
		ret = -EBUSY;
 | 
			
		||||
		goto end;
 | 
			
		||||
	}
 | 
			
		||||
	smb_hc_write(hc, ACPI_SMB_COMMAND, command);
 | 
			
		||||
	smb_hc_write(hc, ACPI_SMB_COMMAND, command);
 | 
			
		||||
	if (!(protocol & 0x01)) {
 | 
			
		||||
		smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length);
 | 
			
		||||
		for (i = 0; i < length; ++i)
 | 
			
		||||
			smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]);
 | 
			
		||||
	}
 | 
			
		||||
	smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1);
 | 
			
		||||
	smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Wait for completion. Save the status code, data size,
 | 
			
		||||
	 * and data into the return package (if required by the protocol).
 | 
			
		||||
	 */
 | 
			
		||||
	ret = wait_transaction_complete(hc, 1000);
 | 
			
		||||
	if (ret || !(protocol & 0x01))
 | 
			
		||||
		goto end;
 | 
			
		||||
	switch (protocol) {
 | 
			
		||||
	case SMBUS_RECEIVE_BYTE:
 | 
			
		||||
	case SMBUS_READ_BYTE:
 | 
			
		||||
		sz = 1;
 | 
			
		||||
		break;
 | 
			
		||||
	case SMBUS_READ_WORD:
 | 
			
		||||
		sz = 2;
 | 
			
		||||
		break;
 | 
			
		||||
	case SMBUS_READ_BLOCK:
 | 
			
		||||
		if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) {
 | 
			
		||||
			ret = -EFAULT;
 | 
			
		||||
			goto end;
 | 
			
		||||
		}
 | 
			
		||||
		sz &= 0x1f;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	for (i = 0; i < sz; ++i)
 | 
			
		||||
		smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]);
 | 
			
		||||
      end:
 | 
			
		||||
	mutex_unlock(&hc->lock);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
 | 
			
		||||
		    u8 command, u8 *data)
 | 
			
		||||
{
 | 
			
		||||
	return acpi_smbus_transaction(hc, protocol, address, command, data, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL_GPL(acpi_smbus_read);
 | 
			
		||||
 | 
			
		||||
int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address,
 | 
			
		||||
		     u8 command, u8 *data, u8 length)
 | 
			
		||||
{
 | 
			
		||||
	return acpi_smbus_transaction(hc, protocol, address, command, data, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL_GPL(acpi_smbus_write);
 | 
			
		||||
 | 
			
		||||
int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
 | 
			
		||||
			         smbus_alarm_callback callback, void *context)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&hc->lock);
 | 
			
		||||
	hc->callback = callback;
 | 
			
		||||
	hc->context = context;
 | 
			
		||||
	mutex_unlock(&hc->lock);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL_GPL(acpi_smbus_register_callback);
 | 
			
		||||
 | 
			
		||||
int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&hc->lock);
 | 
			
		||||
	hc->callback = NULL;
 | 
			
		||||
	hc->context = NULL;
 | 
			
		||||
	mutex_unlock(&hc->lock);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback);
 | 
			
		||||
 | 
			
		||||
static void acpi_smbus_callback(void *context)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_smb_hc *hc = context;
 | 
			
		||||
 | 
			
		||||
	if (hc->callback)
 | 
			
		||||
		hc->callback(hc->context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int smbus_alarm(void *context)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_smb_hc *hc = context;
 | 
			
		||||
	union acpi_smb_status status;
 | 
			
		||||
	if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw))
 | 
			
		||||
		return 0;
 | 
			
		||||
	/* Check if it is only a completion notify */
 | 
			
		||||
	if (status.fields.done)
 | 
			
		||||
		wake_up(&hc->wait);
 | 
			
		||||
	if (!status.fields.alarm)
 | 
			
		||||
		return 0;
 | 
			
		||||
	mutex_lock(&hc->lock);
 | 
			
		||||
	smb_hc_write(hc, ACPI_SMB_STATUS, status.raw);
 | 
			
		||||
	if (hc->callback)
 | 
			
		||||
		acpi_os_execute(OSL_GPE_HANDLER, acpi_smbus_callback, hc);
 | 
			
		||||
	mutex_unlock(&hc->lock);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef int (*acpi_ec_query_func) (void *data);
 | 
			
		||||
 | 
			
		||||
extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
 | 
			
		||||
			      acpi_handle handle, acpi_ec_query_func func,
 | 
			
		||||
			      void *data);
 | 
			
		||||
 | 
			
		||||
static int acpi_smbus_hc_add(struct acpi_device *device)
 | 
			
		||||
{
 | 
			
		||||
	int status;
 | 
			
		||||
	unsigned long val;
 | 
			
		||||
	struct acpi_smb_hc *hc;
 | 
			
		||||
 | 
			
		||||
	if (!device)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
 | 
			
		||||
	if (ACPI_FAILURE(status)) {
 | 
			
		||||
		printk(KERN_ERR PREFIX "error obtaining _EC.\n");
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
 | 
			
		||||
	strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
 | 
			
		||||
 | 
			
		||||
	hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
 | 
			
		||||
	if (!hc)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	mutex_init(&hc->lock);
 | 
			
		||||
	init_waitqueue_head(&hc->wait);
 | 
			
		||||
 | 
			
		||||
	hc->ec = acpi_driver_data(device->parent);
 | 
			
		||||
	hc->offset = (val >> 8) & 0xff;
 | 
			
		||||
	hc->query_bit = val & 0xff;
 | 
			
		||||
	acpi_driver_data(device) = hc;
 | 
			
		||||
 | 
			
		||||
	acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
 | 
			
		||||
	printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n",
 | 
			
		||||
		hc->ec, hc->offset, hc->query_bit);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
 | 
			
		||||
 | 
			
		||||
static int acpi_smbus_hc_remove(struct acpi_device *device, int type)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_smb_hc *hc;
 | 
			
		||||
 | 
			
		||||
	if (!device)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	hc = acpi_driver_data(device);
 | 
			
		||||
	acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
 | 
			
		||||
	kfree(hc);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init acpi_smb_hc_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int result;
 | 
			
		||||
 | 
			
		||||
	result = acpi_bus_register_driver(&acpi_smb_hc_driver);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __exit acpi_smb_hc_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	acpi_bus_unregister_driver(&acpi_smb_hc_driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(acpi_smb_hc_init);
 | 
			
		||||
module_exit(acpi_smb_hc_exit);
 | 
			
		||||
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
MODULE_AUTHOR("Alexey Starikovskiy");
 | 
			
		||||
MODULE_DESCRIPTION("ACPI SMBus HC driver");
 | 
			
		||||
							
								
								
									
										27
									
								
								drivers/acpi/sbshc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								drivers/acpi/sbshc.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
struct acpi_smb_hc;
 | 
			
		||||
enum acpi_smb_protocol {
 | 
			
		||||
	SMBUS_WRITE_QUICK = 2,
 | 
			
		||||
	SMBUS_READ_QUICK = 3,
 | 
			
		||||
	SMBUS_SEND_BYTE = 4,
 | 
			
		||||
	SMBUS_RECEIVE_BYTE = 5,
 | 
			
		||||
	SMBUS_WRITE_BYTE = 6,
 | 
			
		||||
	SMBUS_READ_BYTE = 7,
 | 
			
		||||
	SMBUS_WRITE_WORD  = 8,
 | 
			
		||||
	SMBUS_READ_WORD  = 9,
 | 
			
		||||
	SMBUS_WRITE_BLOCK = 0xa,
 | 
			
		||||
	SMBUS_READ_BLOCK = 0xb,
 | 
			
		||||
	SMBUS_PROCESS_CALL = 0xc,
 | 
			
		||||
	SMBUS_BLOCK_PROCESS_CALL = 0xd,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const u8 SMBUS_PEC = 0x80;
 | 
			
		||||
 | 
			
		||||
typedef void (*smbus_alarm_callback)(void *context);
 | 
			
		||||
 | 
			
		||||
extern int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
 | 
			
		||||
	       u8 command, u8 * data);
 | 
			
		||||
extern int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 slave_address,
 | 
			
		||||
		u8 command, u8 * data, u8 length);
 | 
			
		||||
extern int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
 | 
			
		||||
			         smbus_alarm_callback callback, void *context);
 | 
			
		||||
extern int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc);
 | 
			
		||||
		Loading…
	
		Reference in a new issue