ec.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 887 行 · 第 1/2 页

C
887
字号
/* *  acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) * *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * *  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/types.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <asm/io.h>#include <acpi/acpi_bus.h>#include <acpi/acpi_drivers.h>#include <acpi/actypes.h>#define _COMPONENT		ACPI_EC_COMPONENTACPI_MODULE_NAME		("acpi_ec")#define ACPI_EC_COMPONENT		0x00100000#define ACPI_EC_CLASS			"embedded_controller"#define ACPI_EC_HID			"PNP0C09"#define ACPI_EC_DRIVER_NAME		"ACPI Embedded Controller Driver"#define ACPI_EC_DEVICE_NAME		"Embedded Controller"#define ACPI_EC_FILE_INFO		"info"#define ACPI_EC_FLAG_OBF	0x01	/* Output buffer full */#define ACPI_EC_FLAG_IBF	0x02	/* Input buffer full */#define ACPI_EC_FLAG_SCI	0x20	/* EC-SCI occurred */#define ACPI_EC_EVENT_OBF	0x01	/* Output buffer full */#define ACPI_EC_EVENT_IBE	0x02	/* Input buffer empty */#define ACPI_EC_UDELAY		100	/* Poll @ 100us increments */#define ACPI_EC_UDELAY_COUNT	1000	/* Wait 10ms max. during EC ops */#define ACPI_EC_UDELAY_GLK	1000	/* Wait 1ms max. to get global lock */#define ACPI_EC_COMMAND_READ	0x80#define ACPI_EC_COMMAND_WRITE	0x81#define ACPI_EC_COMMAND_QUERY	0x84static int acpi_ec_add (struct acpi_device *device);static int acpi_ec_remove (struct acpi_device *device, int type);static int acpi_ec_start (struct acpi_device *device);static int acpi_ec_stop (struct acpi_device *device, int type);static struct acpi_driver acpi_ec_driver = {	.name =		ACPI_EC_DRIVER_NAME,	.class =	ACPI_EC_CLASS,	.ids =		ACPI_EC_HID,	.ops =		{				.add =		acpi_ec_add,				.remove =	acpi_ec_remove,				.start =	acpi_ec_start,				.stop =		acpi_ec_stop,			},};struct acpi_ec {	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;	spinlock_t			lock;};/* If we find an EC via the ECDT, we need to keep a ptr to its context */static struct acpi_ec	*ec_ecdt;/* External interfaces use first EC only, so remember */static struct acpi_device *first_ec;/* --------------------------------------------------------------------------                             Transaction Management   -------------------------------------------------------------------------- */static intacpi_ec_wait (	struct acpi_ec		*ec,	u8			event){	u32			acpi_ec_status = 0;	u32			i = ACPI_EC_UDELAY_COUNT;	if (!ec)		return -EINVAL;	/* Poll the EC status register waiting for the event to occur. */	switch (event) {	case ACPI_EC_EVENT_OBF:		do {			acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr);			if (acpi_ec_status & ACPI_EC_FLAG_OBF)				return 0;			udelay(ACPI_EC_UDELAY);		} while (--i>0);		break;	case ACPI_EC_EVENT_IBE:		do {			acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr);			if (!(acpi_ec_status & ACPI_EC_FLAG_IBF))				return 0;			udelay(ACPI_EC_UDELAY);		} while (--i>0);		break;	default:		return -EINVAL;	}	return -ETIME;}static intacpi_ec_read (	struct acpi_ec		*ec,	u8			address,	u32			*data){	acpi_status		status = AE_OK;	int			result = 0;	unsigned long		flags = 0;	u32			glk = 0;	ACPI_FUNCTION_TRACE("acpi_ec_read");	if (!ec || !data)		return_VALUE(-EINVAL);	*data = 0;	if (ec->global_lock) {		status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);		if (ACPI_FAILURE(status))			return_VALUE(-ENODEV);	}		spin_lock_irqsave(&ec->lock, flags);	acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->command_addr);	result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);	if (result)		goto end;	acpi_hw_low_level_write(8, address, &ec->data_addr);	result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);	if (result)		goto end;	acpi_hw_low_level_read(8, data, &ec->data_addr);	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n",		*data, address));end:	spin_unlock_irqrestore(&ec->lock, flags);	if (ec->global_lock)		acpi_release_global_lock(glk);	return_VALUE(result);}static intacpi_ec_write (	struct acpi_ec		*ec,	u8			address,	u8			data){	int			result = 0;	acpi_status		status = AE_OK;	unsigned long		flags = 0;	u32			glk = 0;	ACPI_FUNCTION_TRACE("acpi_ec_write");	if (!ec)		return_VALUE(-EINVAL);	if (ec->global_lock) {		status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);		if (ACPI_FAILURE(status))			return_VALUE(-ENODEV);	}	spin_lock_irqsave(&ec->lock, flags);	acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->command_addr);	result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);	if (result)		goto end;	acpi_hw_low_level_write(8, address, &ec->data_addr);	result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);	if (result)		goto end;	acpi_hw_low_level_write(8, data, &ec->data_addr);	result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);	if (result)		goto end;	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n",		data, address));end:	spin_unlock_irqrestore(&ec->lock, flags);	if (ec->global_lock)		acpi_release_global_lock(glk);	return_VALUE(result);}/* * Externally callable EC access functions. For now, assume 1 EC only */intec_read(u8 addr, u8 *val){	struct acpi_ec *ec;	int err;	u32 temp_data;	if (!first_ec)		return -ENODEV;	ec = acpi_driver_data(first_ec);	err = acpi_ec_read(ec, addr, &temp_data);	if (!err) {		*val = temp_data;		return 0;	}	else		return err;}intec_write(u8 addr, u8 val){	struct acpi_ec *ec;	int err;	if (!first_ec)		return -ENODEV;	ec = acpi_driver_data(first_ec);	err = acpi_ec_write(ec, addr, val);	return err;}static intacpi_ec_query (	struct acpi_ec		*ec,	u32			*data){	int			result = 0;	acpi_status		status = AE_OK;	unsigned long		flags = 0;	u32			glk = 0;	ACPI_FUNCTION_TRACE("acpi_ec_query");	if (!ec || !data)		return_VALUE(-EINVAL);	*data = 0;	if (ec->global_lock) {		status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);		if (ACPI_FAILURE(status))			return_VALUE(-ENODEV);	}	/*	 * Query the EC to find out which _Qxx method we need to evaluate.	 * Note that successful completion of the query causes the ACPI_EC_SCI	 * bit to be cleared (and thus clearing the interrupt source).	 */	spin_lock_irqsave(&ec->lock, flags);	acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->command_addr);	result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);	if (result)		goto end;		acpi_hw_low_level_read(8, data, &ec->data_addr);	if (!*data)		result = -ENODATA;end:	spin_unlock_irqrestore(&ec->lock, flags);	if (ec->global_lock)		acpi_release_global_lock(glk);	return_VALUE(result);}/* --------------------------------------------------------------------------                                Event Management   -------------------------------------------------------------------------- */struct acpi_ec_query_data {	acpi_handle		handle;	u8			data;};static voidacpi_ec_gpe_query (	void			*ec_cxt){	struct acpi_ec		*ec = (struct acpi_ec *) ec_cxt;	u32			value = 0;	unsigned long		flags = 0;	static char		object_name[5] = {'_','Q','0','0','\0'};	const char		hex[] = {'0','1','2','3','4','5','6','7',				         '8','9','A','B','C','D','E','F'};	ACPI_FUNCTION_TRACE("acpi_ec_gpe_query");	if (!ec_cxt)		goto end;		spin_lock_irqsave(&ec->lock, flags);	acpi_hw_low_level_read(8, &value, &ec->command_addr);	spin_unlock_irqrestore(&ec->lock, flags);	/* TBD: Implement asynch events!	 * NOTE: All we care about are EC-SCI's.  Other EC events are	 * handled via polling (yuck!).  This is because some systems	 * treat EC-SCIs as level (versus EDGE!) triggered, preventing	 *  a purely interrupt-driven approach (grumble, grumble).	 */	if (!(value & ACPI_EC_FLAG_SCI))		goto end;	if (acpi_ec_query(ec, &value))		goto end;		object_name[2] = hex[((value >> 4) & 0x0F)];	object_name[3] = hex[(value & 0x0F)];	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name));	acpi_evaluate_object(ec->handle, object_name, NULL, NULL);end:	acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR);}static u32acpi_ec_gpe_handler (	void			*data){	acpi_status		status = AE_OK;	struct acpi_ec		*ec = (struct acpi_ec *) data;	if (!ec)		return ACPI_INTERRUPT_NOT_HANDLED;	acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR);	status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE,		acpi_ec_gpe_query, ec);	if (status == AE_OK)		return ACPI_INTERRUPT_HANDLED;	else		return ACPI_INTERRUPT_NOT_HANDLED;}/* --------------------------------------------------------------------------                             Address Space Management   -------------------------------------------------------------------------- */static acpi_statusacpi_ec_space_setup (	acpi_handle		region_handle,	u32			function,	void			*handler_context,	void			**return_context){	/*	 * The EC object is in the handler context and is needed	 * when calling the acpi_ec_space_handler.	 */	if(function == ACPI_REGION_DEACTIVATE) 		*return_context = NULL;	else 		*return_context = handler_context;	return AE_OK;}static acpi_statusacpi_ec_space_handler (	u32			function,	acpi_physical_address	address,	u32			bit_width,	acpi_integer		*value,	void			*handler_context,	void			*region_context){	int			result = 0;	struct acpi_ec		*ec = NULL;	u32			temp = 0;	ACPI_FUNCTION_TRACE("acpi_ec_space_handler");	if ((address > 0xFF) || (bit_width != 8) || !value || !handler_context)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?