Commit bf0a40b7 authored by Len Brown's avatar Len Brown
Browse files

Pull thinkpad into release branch

parents 2cde4afa 32afbf07
Loading
Loading
Loading
Loading
+19 −6
Original line number Diff line number Diff line
@@ -105,10 +105,15 @@ The version of thinkpad-acpi's sysfs interface is exported by the driver
as a driver attribute (see below).

Sysfs driver attributes are on the driver's sysfs attribute space,
for 2.6.20 this is /sys/bus/platform/drivers/thinkpad_acpi/.
for 2.6.23 this is /sys/bus/platform/drivers/thinkpad_acpi/ and
/sys/bus/platform/drivers/thinkpad_hwmon/

Sysfs device attributes are on the driver's sysfs attribute space,
for 2.6.20 this is /sys/devices/platform/thinkpad_acpi/.
Sysfs device attributes are on the thinkpad_acpi device sysfs attribute
space, for 2.6.23 this is /sys/devices/platform/thinkpad_acpi/.

Sysfs device attributes for the sensors and fan are on the
thinkpad_hwmon device's sysfs attribute space, but you should locate it
looking for a hwmon device with the name attribute of "thinkpad".

Driver version
--------------
@@ -766,7 +771,7 @@ Temperature sensors
-------------------

procfs: /proc/acpi/ibm/thermal
sysfs device attributes: (hwmon) temp*_input
sysfs device attributes: (hwmon "thinkpad") temp*_input

Most ThinkPads include six or more separate temperature sensors but only
expose the CPU temperature through the standard ACPI methods.  This
@@ -989,7 +994,9 @@ Fan control and monitoring: fan speed, fan enable/disable
---------------------------------------------------------

procfs: /proc/acpi/ibm/fan
sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable
sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
			  pwm1_enable
sysfs hwmon driver attributes: fan_watchdog

NOTE NOTE NOTE: fan control operations are disabled by default for
safety reasons.  To enable them, the module parameter "fan_control=1"
@@ -1131,7 +1138,7 @@ hwmon device attribute fan1_input:
	which can take up to two minutes.  May return rubbish on older
	ThinkPads.

driver attribute fan_watchdog:
hwmon driver attribute fan_watchdog:
	Fan safety watchdog timer interval, in seconds.  Minimum is
	1 second, maximum is 120 seconds.  0 disables the watchdog.

@@ -1233,3 +1240,9 @@ Sysfs interface changelog:
		layer, the radio switch generates input event EV_RADIO,
		and the driver enables hot key handling by default in
		the firmware.

0x020000:	ABI fix: added a separate hwmon platform device and
		driver, which must be located by name (thinkpad)
		and the hwmon class for libsensors4 (lm-sensors 3)
		compatibility.  Moved all hwmon attributes to this
		new platform device.
+170 −37
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@
 */

#define IBM_VERSION "0.16"
#define TPACPI_SYSFS_VERSION 0x010000
#define TPACPI_SYSFS_VERSION 0x020000

/*
 *  Changelog:
@@ -117,6 +117,12 @@ IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");

#define __unused __attribute__ ((unused))

static enum {
	TPACPI_LIFE_INIT = 0,
	TPACPI_LIFE_RUNNING,
	TPACPI_LIFE_EXITING,
} tpacpi_lifecycle;

/****************************************************************************
 ****************************************************************************
 *
@@ -342,6 +348,9 @@ static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
{
	struct ibm_struct *ibm = data;

	if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
		return;

	if (!ibm || !ibm->acpi || !ibm->acpi->notify)
		return;

@@ -517,8 +526,10 @@ static char *next_cmd(char **cmds)
 ****************************************************************************/

static struct platform_device *tpacpi_pdev;
static struct platform_device *tpacpi_sensors_pdev;
static struct class_device *tpacpi_hwmon;
static struct input_dev *tpacpi_inputdev;
static struct mutex tpacpi_inputdev_send_mutex;


static int tpacpi_resume_handler(struct platform_device *pdev)
@@ -543,6 +554,12 @@ static struct platform_driver tpacpi_pdriver = {
	.resume = tpacpi_resume_handler,
};

static struct platform_driver tpacpi_hwmon_pdriver = {
	.driver = {
		.name = IBM_HWMON_DRVR_NAME,
		.owner = THIS_MODULE,
	},
};

/*************************************************************************
 * thinkpad-acpi driver attributes
@@ -692,6 +709,8 @@ static int parse_strtoul(const char *buf,
{
	char *endp;

	while (*buf && isspace(*buf))
		buf++;
	*value = simple_strtoul(buf, &endp, 0);
	while (*endp && isspace(*endp))
		endp++;
@@ -989,6 +1008,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)

	int res, i;
	int status;
	int hkeyv;

	vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");

@@ -1014,19 +1034,36 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
			return res;

		/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
		   A30, R30, R31, T20-22, X20-21, X22-24 */
		tp_features.hotkey_mask =
			acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
		   A30, R30, R31, T20-22, X20-21, X22-24.  Detected by checking
		   for HKEY interface version 0x100 */
		if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
			if ((hkeyv >> 8) != 1) {
				printk(IBM_ERR "unknown version of the "
				       "HKEY interface: 0x%x\n", hkeyv);
				printk(IBM_ERR "please report this to %s\n",
				       IBM_MAIL);
			} else {
				/*
				 * MHKV 0x100 in A31, R40, R40e,
				 * T4x, X31, and later
				 * */
				tp_features.hotkey_mask = 1;
			}
		}

		vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
			str_supported(tp_features.hotkey_mask));

		if (tp_features.hotkey_mask) {
			/* MHKA available in A31, R40, R40e, T4x, X31, and later */
			if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
					"MHKA", "qd"))
					"MHKA", "qd")) {
				printk(IBM_ERR
				       "missing MHKA handler, "
				       "please report this to %s\n",
				       IBM_MAIL);
				hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */
			}
		}

		res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);
		if (!res && tp_features.hotkey_mask) {
@@ -1131,6 +1168,8 @@ static void tpacpi_input_send_key(unsigned int scancode,
				  unsigned int keycode)
{
	if (keycode != KEY_RESERVED) {
		mutex_lock(&tpacpi_inputdev_send_mutex);

		input_report_key(tpacpi_inputdev, keycode, 1);
		if (keycode == KEY_UNKNOWN)
			input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
@@ -1142,6 +1181,8 @@ static void tpacpi_input_send_key(unsigned int scancode,
			input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
				    scancode);
		input_sync(tpacpi_inputdev);

		mutex_unlock(&tpacpi_inputdev_send_mutex);
	}
}

@@ -1149,18 +1190,47 @@ static void tpacpi_input_send_radiosw(void)
{
	int wlsw;

	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw))
	mutex_lock(&tpacpi_inputdev_send_mutex);

	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
		input_report_switch(tpacpi_inputdev,
				    SW_RADIO, !!wlsw);
		input_sync(tpacpi_inputdev);
	}

	mutex_unlock(&tpacpi_inputdev_send_mutex);
}

static void hotkey_notify(struct ibm_struct *ibm, u32 event)
{
	u32 hkey;
	unsigned int keycode, scancode;
	int send_acpi_ev = 0;
	int send_acpi_ev;
	int ignore_acpi_ev;

	if (event != 0x80) {
		printk(IBM_ERR "unknown HKEY notification event %d\n", event);
		/* forward it to userspace, maybe it knows how to handle it */
		acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
						ibm->acpi->device->dev.bus_id,
						event, 0);
		return;
	}

	while (1) {
		if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
			printk(IBM_ERR "failed to retrieve HKEY event\n");
			return;
		}

		if (hkey == 0) {
			/* queue empty */
			return;
		}

		send_acpi_ev = 0;
		ignore_acpi_ev = 0;

	if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
		switch (hkey >> 12) {
		case 1:
			/* 0x1000-0x1FFF: key presses */
@@ -1182,9 +1252,11 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
			 * eat up known LID events */
			if (hkey != 0x5001 && hkey != 0x5002) {
				printk(IBM_ERR
					"unknown LID-related hotkey event: 0x%04x\n",
				       "unknown LID-related HKEY event: 0x%04x\n",
				       hkey);
				send_acpi_ev = 1;
			} else {
				ignore_acpi_ev = 1;
			}
			break;
		case 7:
@@ -1202,23 +1274,20 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
			printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey);
			send_acpi_ev = 1;
		}
	} else {
		printk(IBM_ERR "unknown hotkey notification event %d\n", event);
		hkey = 0;
		send_acpi_ev = 1;
	}

		/* Legacy events */
	if (send_acpi_ev || hotkey_report_mode < 2)
		if (!ignore_acpi_ev && (send_acpi_ev || hotkey_report_mode < 2)) {
			acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey);
		}

		/* netlink events */
	if (send_acpi_ev) {
		if (!ignore_acpi_ev && send_acpi_ev) {
			acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
							ibm->acpi->device->dev.bus_id,
							event, hkey);
		}
	}
}

static void hotkey_resume(void)
{
@@ -2812,7 +2881,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)

	switch(thermal_read_mode) {
	case TPACPI_THERMAL_TPEC_16:
		res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
		res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
				&thermal_temp_input16_group);
		if (res)
			return res;
@@ -2820,7 +2889,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
	case TPACPI_THERMAL_TPEC_8:
	case TPACPI_THERMAL_ACPI_TMP07:
	case TPACPI_THERMAL_ACPI_UPDT:
		res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
		res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
				&thermal_temp_input8_group);
		if (res)
			return res;
@@ -2837,13 +2906,13 @@ static void thermal_exit(void)
{
	switch(thermal_read_mode) {
	case TPACPI_THERMAL_TPEC_16:
		sysfs_remove_group(&tpacpi_pdev->dev.kobj,
		sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
				   &thermal_temp_input16_group);
		break;
	case TPACPI_THERMAL_TPEC_8:
	case TPACPI_THERMAL_ACPI_TMP07:
	case TPACPI_THERMAL_ACPI_UPDT:
		sysfs_remove_group(&tpacpi_pdev->dev.kobj,
		sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
				   &thermal_temp_input16_group);
		break;
	case TPACPI_THERMAL_NONE:
@@ -3626,7 +3695,7 @@ static struct device_attribute dev_attr_fan_fan1_input =
	__ATTR(fan1_input, S_IRUGO,
		fan_fan1_input_show, NULL);

/* sysfs fan fan_watchdog (driver) ------------------------------------- */
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
				     char *buf)
{
@@ -3768,10 +3837,10 @@ static int __init fan_init(struct ibm_init_struct *iibm)

	if (fan_status_access_mode != TPACPI_FAN_NONE ||
	    fan_control_access_mode != TPACPI_FAN_WR_NONE) {
		rc = sysfs_create_group(&tpacpi_pdev->dev.kobj,
		rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
					 &fan_attr_group);
		if (!(rc < 0))
			rc = driver_create_file(&tpacpi_pdriver.driver,
			rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
					&driver_attr_fan_watchdog);
		if (rc < 0)
			return rc;
@@ -3854,8 +3923,8 @@ static void fan_exit(void)
	vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n");

	/* FIXME: can we really do this unconditionally? */
	sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group);
	driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog);
	sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
	driver_remove_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog);

	cancel_delayed_work(&fan_watchdog_task);
	flush_scheduled_work();
@@ -3888,6 +3957,9 @@ static void fan_watchdog_fire(struct work_struct *ignored)
{
	int rc;

	if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
		return;

	printk(IBM_NOTICE "fan watchdog: enabling fan\n");
	rc = fan_set_enable();
	if (rc < 0) {
@@ -3908,7 +3980,8 @@ static void fan_watchdog_reset(void)
	if (fan_watchdog_active)
		cancel_delayed_work(&fan_watchdog_task);

	if (fan_watchdog_maxinterval > 0) {
	if (fan_watchdog_maxinterval > 0 &&
	    tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
		fan_watchdog_active = 1;
		if (!schedule_delayed_work(&fan_watchdog_task,
				msecs_to_jiffies(fan_watchdog_maxinterval
@@ -4302,6 +4375,19 @@ static struct ibm_struct fan_driver_data = {
 ****************************************************************************
 ****************************************************************************/

/* sysfs name ---------------------------------------------------------- */
static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
			   struct device_attribute *attr,
			   char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%s\n", IBM_NAME);
}

static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
	__ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);

/* --------------------------------------------------------------------- */

/* /proc support */
static struct proc_dir_entry *proc_dir;

@@ -4674,6 +4760,8 @@ static int __init thinkpad_acpi_module_init(void)
{
	int ret, i;

	tpacpi_lifecycle = TPACPI_LIFE_INIT;

	/* Parameter checking */
	if (hotkey_report_mode > 2)
		return -EINVAL;
@@ -4702,19 +4790,31 @@ static int __init thinkpad_acpi_module_init(void)

	ret = platform_driver_register(&tpacpi_pdriver);
	if (ret) {
		printk(IBM_ERR "unable to register platform driver\n");
		printk(IBM_ERR "unable to register main platform driver\n");
		thinkpad_acpi_module_exit();
		return ret;
	}
	tp_features.platform_drv_registered = 1;

	ret = platform_driver_register(&tpacpi_hwmon_pdriver);
	if (ret) {
		printk(IBM_ERR "unable to register hwmon platform driver\n");
		thinkpad_acpi_module_exit();
		return ret;
	}
	tp_features.sensors_pdrv_registered = 1;

	ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
	if (!ret) {
		tp_features.platform_drv_attrs_registered = 1;
		ret = tpacpi_create_driver_attributes(&tpacpi_hwmon_pdriver.driver);
	}
	if (ret) {
		printk(IBM_ERR "unable to create sysfs driver attributes\n");
		thinkpad_acpi_module_exit();
		return ret;
	}
	tp_features.platform_drv_attrs_registered = 1;
	tp_features.sensors_pdrv_attrs_registered = 1;


	/* Device initialization */
@@ -4727,7 +4827,26 @@ static int __init thinkpad_acpi_module_init(void)
		thinkpad_acpi_module_exit();
		return ret;
	}
	tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev);
	tpacpi_sensors_pdev = platform_device_register_simple(
							IBM_HWMON_DRVR_NAME,
							-1, NULL, 0);
	if (IS_ERR(tpacpi_sensors_pdev)) {
		ret = PTR_ERR(tpacpi_sensors_pdev);
		tpacpi_sensors_pdev = NULL;
		printk(IBM_ERR "unable to register hwmon platform device\n");
		thinkpad_acpi_module_exit();
		return ret;
	}
	ret = device_create_file(&tpacpi_sensors_pdev->dev,
				 &dev_attr_thinkpad_acpi_pdev_name);
	if (ret) {
		printk(IBM_ERR
			"unable to create sysfs hwmon device attributes\n");
		thinkpad_acpi_module_exit();
		return ret;
	}
	tp_features.sensors_pdev_attrs_registered = 1;
	tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
	if (IS_ERR(tpacpi_hwmon)) {
		ret = PTR_ERR(tpacpi_hwmon);
		tpacpi_hwmon = NULL;
@@ -4735,6 +4854,7 @@ static int __init thinkpad_acpi_module_init(void)
		thinkpad_acpi_module_exit();
		return ret;
	}
	mutex_init(&tpacpi_inputdev_send_mutex);
	tpacpi_inputdev = input_allocate_device();
	if (!tpacpi_inputdev) {
		printk(IBM_ERR "unable to allocate input device\n");
@@ -4769,6 +4889,7 @@ static int __init thinkpad_acpi_module_init(void)
		tp_features.input_device_registered = 1;
	}

	tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
	return 0;
}

@@ -4776,6 +4897,8 @@ static void thinkpad_acpi_module_exit(void)
{
	struct ibm_struct *ibm, *itmp;

	tpacpi_lifecycle = TPACPI_LIFE_EXITING;

	list_for_each_entry_safe_reverse(ibm, itmp,
					 &tpacpi_all_drivers,
					 all_drivers) {
@@ -4794,12 +4917,22 @@ static void thinkpad_acpi_module_exit(void)
	if (tpacpi_hwmon)
		hwmon_device_unregister(tpacpi_hwmon);

	if (tp_features.sensors_pdev_attrs_registered)
		device_remove_file(&tpacpi_sensors_pdev->dev,
				   &dev_attr_thinkpad_acpi_pdev_name);
	if (tpacpi_sensors_pdev)
		platform_device_unregister(tpacpi_sensors_pdev);
	if (tpacpi_pdev)
		platform_device_unregister(tpacpi_pdev);

	if (tp_features.sensors_pdrv_attrs_registered)
		tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver);
	if (tp_features.platform_drv_attrs_registered)
		tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);

	if (tp_features.sensors_pdrv_registered)
		platform_driver_unregister(&tpacpi_hwmon_pdriver);

	if (tp_features.platform_drv_registered)
		platform_driver_unregister(&tpacpi_pdriver);

+21 −16
Original line number Diff line number Diff line
@@ -58,13 +58,14 @@

#define IBM_NAME "thinkpad"
#define IBM_DESC "ThinkPad ACPI Extras"
#define IBM_FILE "thinkpad_acpi"
#define IBM_FILE IBM_NAME "_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net"

#define IBM_PROC_DIR "ibm"
#define IBM_ACPI_EVENT_PREFIX "ibm"
#define IBM_DRVR_NAME IBM_FILE
#define IBM_HWMON_DRVR_NAME IBM_NAME "_hwmon"

#define IBM_LOG IBM_FILE ": "
#define IBM_ERR	   KERN_ERR    IBM_LOG
@@ -171,6 +172,7 @@ static int parse_strtoul(const char *buf, unsigned long max,

/* Device model */
static struct platform_device *tpacpi_pdev;
static struct platform_device *tpacpi_sensors_pdev;
static struct class_device *tpacpi_hwmon;
static struct platform_driver tpacpi_pdriver;
static struct input_dev *tpacpi_inputdev;
@@ -233,22 +235,25 @@ struct ibm_init_struct {

static struct {
#ifdef CONFIG_THINKPAD_ACPI_BAY
	u16 bay_status:1;
	u16 bay_eject:1;
	u16 bay_status2:1;
	u16 bay_eject2:1;
	u32 bay_status:1;
	u32 bay_eject:1;
	u32 bay_status2:1;
	u32 bay_eject2:1;
#endif
	u16 bluetooth:1;
	u16 hotkey:1;
	u16 hotkey_mask:1;
	u16 hotkey_wlsw:1;
	u16 light:1;
	u16 light_status:1;
	u16 wan:1;
	u16 fan_ctrl_status_undef:1;
	u16 input_device_registered:1;
	u16 platform_drv_registered:1;
	u16 platform_drv_attrs_registered:1;
	u32 bluetooth:1;
	u32 hotkey:1;
	u32 hotkey_mask:1;
	u32 hotkey_wlsw:1;
	u32 light:1;
	u32 light_status:1;
	u32 wan:1;
	u32 fan_ctrl_status_undef:1;
	u32 input_device_registered:1;
	u32 platform_drv_registered:1;
	u32 platform_drv_attrs_registered:1;
	u32 sensors_pdrv_registered:1;
	u32 sensors_pdrv_attrs_registered:1;
	u32 sensors_pdev_attrs_registered:1;
} tp_features;

struct thinkpad_id_data {