| Darrick J. Wong | de584af | 2009-09-18 12:41:09 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * A hwmon driver for ACPI 4.0 power meters | 
 | 3 |  * Copyright (C) 2009 IBM | 
 | 4 |  * | 
 | 5 |  * Author: Darrick J. Wong <djwong@us.ibm.com> | 
 | 6 |  * | 
 | 7 |  * This program is free software; you can redistribute it and/or modify | 
 | 8 |  * it under the terms of the GNU General Public License as published by | 
 | 9 |  * the Free Software Foundation; either version 2 of the License, or | 
 | 10 |  * (at your option) any later version. | 
 | 11 |  * | 
 | 12 |  * This program is distributed in the hope that it will be useful, | 
 | 13 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 14 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 15 |  * GNU General Public License for more details. | 
 | 16 |  * | 
 | 17 |  * You should have received a copy of the GNU General Public License | 
 | 18 |  * along with this program; if not, write to the Free Software | 
 | 19 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 | 20 |  */ | 
 | 21 |  | 
 | 22 | #include <linux/module.h> | 
 | 23 | #include <linux/hwmon.h> | 
 | 24 | #include <linux/hwmon-sysfs.h> | 
 | 25 | #include <linux/jiffies.h> | 
 | 26 | #include <linux/mutex.h> | 
 | 27 | #include <linux/dmi.h> | 
 | 28 | #include <linux/kdev_t.h> | 
 | 29 | #include <linux/sched.h> | 
 | 30 | #include <linux/time.h> | 
 | 31 | #include <acpi/acpi_drivers.h> | 
 | 32 | #include <acpi/acpi_bus.h> | 
 | 33 |  | 
 | 34 | #define ACPI_POWER_METER_NAME		"power_meter" | 
 | 35 | ACPI_MODULE_NAME(ACPI_POWER_METER_NAME); | 
 | 36 | #define ACPI_POWER_METER_DEVICE_NAME	"Power Meter" | 
 | 37 | #define ACPI_POWER_METER_CLASS		"power_meter_resource" | 
 | 38 |  | 
 | 39 | #define NUM_SENSORS			17 | 
 | 40 |  | 
 | 41 | #define POWER_METER_CAN_MEASURE	(1 << 0) | 
 | 42 | #define POWER_METER_CAN_TRIP	(1 << 1) | 
 | 43 | #define POWER_METER_CAN_CAP	(1 << 2) | 
 | 44 | #define POWER_METER_CAN_NOTIFY	(1 << 3) | 
 | 45 | #define POWER_METER_IS_BATTERY	(1 << 8) | 
 | 46 | #define UNKNOWN_HYSTERESIS	0xFFFFFFFF | 
 | 47 |  | 
 | 48 | #define METER_NOTIFY_CONFIG	0x80 | 
 | 49 | #define METER_NOTIFY_TRIP	0x81 | 
 | 50 | #define METER_NOTIFY_CAP	0x82 | 
 | 51 | #define METER_NOTIFY_CAPPING	0x83 | 
 | 52 | #define METER_NOTIFY_INTERVAL	0x84 | 
 | 53 |  | 
 | 54 | #define POWER_AVERAGE_NAME	"power1_average" | 
 | 55 | #define POWER_CAP_NAME		"power1_cap" | 
 | 56 | #define POWER_AVG_INTERVAL_NAME	"power1_average_interval" | 
 | 57 | #define POWER_ALARM_NAME	"power1_alarm" | 
 | 58 |  | 
 | 59 | static int cap_in_hardware; | 
 | 60 | static int force_cap_on; | 
 | 61 |  | 
 | 62 | static int can_cap_in_hardware(void) | 
 | 63 | { | 
 | 64 | 	return force_cap_on || cap_in_hardware; | 
 | 65 | } | 
 | 66 |  | 
 | 67 | static struct acpi_device_id power_meter_ids[] = { | 
 | 68 | 	{"ACPI000D", 0}, | 
 | 69 | 	{"", 0}, | 
 | 70 | }; | 
 | 71 | MODULE_DEVICE_TABLE(acpi, power_meter_ids); | 
 | 72 |  | 
 | 73 | struct acpi_power_meter_capabilities { | 
 | 74 | 	acpi_integer		flags; | 
 | 75 | 	acpi_integer		units; | 
 | 76 | 	acpi_integer		type; | 
 | 77 | 	acpi_integer		accuracy; | 
 | 78 | 	acpi_integer		sampling_time; | 
 | 79 | 	acpi_integer		min_avg_interval; | 
 | 80 | 	acpi_integer		max_avg_interval; | 
 | 81 | 	acpi_integer		hysteresis; | 
 | 82 | 	acpi_integer		configurable_cap; | 
 | 83 | 	acpi_integer		min_cap; | 
 | 84 | 	acpi_integer		max_cap; | 
 | 85 | }; | 
 | 86 |  | 
 | 87 | struct acpi_power_meter_resource { | 
 | 88 | 	struct acpi_device	*acpi_dev; | 
 | 89 | 	acpi_bus_id		name; | 
 | 90 | 	struct mutex		lock; | 
 | 91 | 	struct device		*hwmon_dev; | 
 | 92 | 	struct acpi_power_meter_capabilities	caps; | 
 | 93 | 	acpi_string		model_number; | 
 | 94 | 	acpi_string		serial_number; | 
 | 95 | 	acpi_string		oem_info; | 
 | 96 | 	acpi_integer		power; | 
 | 97 | 	acpi_integer		cap; | 
 | 98 | 	acpi_integer		avg_interval; | 
 | 99 | 	int			sensors_valid; | 
 | 100 | 	unsigned long		sensors_last_updated; | 
 | 101 | 	struct sensor_device_attribute	sensors[NUM_SENSORS]; | 
 | 102 | 	int			num_sensors; | 
 | 103 | 	int			trip[2]; | 
 | 104 | 	int			num_domain_devices; | 
 | 105 | 	struct acpi_device	**domain_devices; | 
 | 106 | 	struct kobject		*holders_dir; | 
 | 107 | }; | 
 | 108 |  | 
 | 109 | struct ro_sensor_template { | 
 | 110 | 	char *label; | 
 | 111 | 	ssize_t (*show)(struct device *dev, | 
 | 112 | 			struct device_attribute *devattr, | 
 | 113 | 			char *buf); | 
 | 114 | 	int index; | 
 | 115 | }; | 
 | 116 |  | 
 | 117 | struct rw_sensor_template { | 
 | 118 | 	char *label; | 
 | 119 | 	ssize_t (*show)(struct device *dev, | 
 | 120 | 			struct device_attribute *devattr, | 
 | 121 | 			char *buf); | 
 | 122 | 	ssize_t (*set)(struct device *dev, | 
 | 123 | 		       struct device_attribute *devattr, | 
 | 124 | 		       const char *buf, size_t count); | 
 | 125 | 	int index; | 
 | 126 | }; | 
 | 127 |  | 
 | 128 | /* Averaging interval */ | 
 | 129 | static int update_avg_interval(struct acpi_power_meter_resource *resource) | 
 | 130 | { | 
 | 131 | 	unsigned long long data; | 
 | 132 | 	acpi_status status; | 
 | 133 |  | 
 | 134 | 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GAI", | 
 | 135 | 				       NULL, &data); | 
 | 136 | 	if (ACPI_FAILURE(status)) { | 
 | 137 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GAI")); | 
 | 138 | 		return -ENODEV; | 
 | 139 | 	} | 
 | 140 |  | 
 | 141 | 	resource->avg_interval = data; | 
 | 142 | 	return 0; | 
 | 143 | } | 
 | 144 |  | 
 | 145 | static ssize_t show_avg_interval(struct device *dev, | 
 | 146 | 				 struct device_attribute *devattr, | 
 | 147 | 				 char *buf) | 
 | 148 | { | 
 | 149 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 150 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 151 |  | 
 | 152 | 	mutex_lock(&resource->lock); | 
 | 153 | 	update_avg_interval(resource); | 
 | 154 | 	mutex_unlock(&resource->lock); | 
 | 155 |  | 
 | 156 | 	return sprintf(buf, "%llu\n", resource->avg_interval); | 
 | 157 | } | 
 | 158 |  | 
 | 159 | static ssize_t set_avg_interval(struct device *dev, | 
 | 160 | 				struct device_attribute *devattr, | 
 | 161 | 				const char *buf, size_t count) | 
 | 162 | { | 
 | 163 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 164 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 165 | 	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | 
 | 166 | 	struct acpi_object_list args = { 1, &arg0 }; | 
 | 167 | 	int res; | 
 | 168 | 	unsigned long temp; | 
 | 169 | 	unsigned long long data; | 
 | 170 | 	acpi_status status; | 
 | 171 |  | 
 | 172 | 	res = strict_strtoul(buf, 10, &temp); | 
 | 173 | 	if (res) | 
 | 174 | 		return res; | 
 | 175 |  | 
 | 176 | 	if (temp > resource->caps.max_avg_interval || | 
 | 177 | 	    temp < resource->caps.min_avg_interval) | 
 | 178 | 		return -EINVAL; | 
 | 179 | 	arg0.integer.value = temp; | 
 | 180 |  | 
 | 181 | 	mutex_lock(&resource->lock); | 
 | 182 | 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", | 
 | 183 | 				       &args, &data); | 
 | 184 | 	if (!ACPI_FAILURE(status)) | 
 | 185 | 		resource->avg_interval = temp; | 
 | 186 | 	mutex_unlock(&resource->lock); | 
 | 187 |  | 
 | 188 | 	if (ACPI_FAILURE(status)) { | 
 | 189 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PAI")); | 
 | 190 | 		return -EINVAL; | 
 | 191 | 	} | 
 | 192 |  | 
 | 193 | 	/* _PAI returns 0 on success, nonzero otherwise */ | 
 | 194 | 	if (data) | 
 | 195 | 		return -EINVAL; | 
 | 196 |  | 
 | 197 | 	return count; | 
 | 198 | } | 
 | 199 |  | 
 | 200 | /* Cap functions */ | 
 | 201 | static int update_cap(struct acpi_power_meter_resource *resource) | 
 | 202 | { | 
 | 203 | 	unsigned long long data; | 
 | 204 | 	acpi_status status; | 
 | 205 |  | 
 | 206 | 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GHL", | 
 | 207 | 				       NULL, &data); | 
 | 208 | 	if (ACPI_FAILURE(status)) { | 
 | 209 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GHL")); | 
 | 210 | 		return -ENODEV; | 
 | 211 | 	} | 
 | 212 |  | 
 | 213 | 	resource->cap = data; | 
 | 214 | 	return 0; | 
 | 215 | } | 
 | 216 |  | 
 | 217 | static ssize_t show_cap(struct device *dev, | 
 | 218 | 			struct device_attribute *devattr, | 
 | 219 | 			char *buf) | 
 | 220 | { | 
 | 221 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 222 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 223 |  | 
 | 224 | 	mutex_lock(&resource->lock); | 
 | 225 | 	update_cap(resource); | 
 | 226 | 	mutex_unlock(&resource->lock); | 
 | 227 |  | 
 | 228 | 	return sprintf(buf, "%llu\n", resource->cap * 1000); | 
 | 229 | } | 
 | 230 |  | 
 | 231 | static ssize_t set_cap(struct device *dev, struct device_attribute *devattr, | 
 | 232 | 		       const char *buf, size_t count) | 
 | 233 | { | 
 | 234 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 235 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 236 | 	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | 
 | 237 | 	struct acpi_object_list args = { 1, &arg0 }; | 
 | 238 | 	int res; | 
 | 239 | 	unsigned long temp; | 
 | 240 | 	unsigned long long data; | 
 | 241 | 	acpi_status status; | 
 | 242 |  | 
 | 243 | 	res = strict_strtoul(buf, 10, &temp); | 
 | 244 | 	if (res) | 
 | 245 | 		return res; | 
 | 246 |  | 
 | 247 | 	temp /= 1000; | 
 | 248 | 	if (temp > resource->caps.max_cap || temp < resource->caps.min_cap) | 
 | 249 | 		return -EINVAL; | 
 | 250 | 	arg0.integer.value = temp; | 
 | 251 |  | 
 | 252 | 	mutex_lock(&resource->lock); | 
 | 253 | 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", | 
 | 254 | 				       &args, &data); | 
 | 255 | 	if (!ACPI_FAILURE(status)) | 
 | 256 | 		resource->cap = temp; | 
 | 257 | 	mutex_unlock(&resource->lock); | 
 | 258 |  | 
 | 259 | 	if (ACPI_FAILURE(status)) { | 
 | 260 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SHL")); | 
 | 261 | 		return -EINVAL; | 
 | 262 | 	} | 
 | 263 |  | 
 | 264 | 	/* _SHL returns 0 on success, nonzero otherwise */ | 
 | 265 | 	if (data) | 
 | 266 | 		return -EINVAL; | 
 | 267 |  | 
 | 268 | 	return count; | 
 | 269 | } | 
 | 270 |  | 
 | 271 | /* Power meter trip points */ | 
 | 272 | static int set_acpi_trip(struct acpi_power_meter_resource *resource) | 
 | 273 | { | 
 | 274 | 	union acpi_object arg_objs[] = { | 
 | 275 | 		{ACPI_TYPE_INTEGER}, | 
 | 276 | 		{ACPI_TYPE_INTEGER} | 
 | 277 | 	}; | 
 | 278 | 	struct acpi_object_list args = { 2, arg_objs }; | 
 | 279 | 	unsigned long long data; | 
 | 280 | 	acpi_status status; | 
 | 281 |  | 
 | 282 | 	/* Both trip levels must be set */ | 
 | 283 | 	if (resource->trip[0] < 0 || resource->trip[1] < 0) | 
 | 284 | 		return 0; | 
 | 285 |  | 
 | 286 | 	/* This driver stores min, max; ACPI wants max, min. */ | 
 | 287 | 	arg_objs[0].integer.value = resource->trip[1]; | 
 | 288 | 	arg_objs[1].integer.value = resource->trip[0]; | 
 | 289 |  | 
 | 290 | 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PTP", | 
 | 291 | 				       &args, &data); | 
 | 292 | 	if (ACPI_FAILURE(status)) { | 
 | 293 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTP")); | 
 | 294 | 		return -EINVAL; | 
 | 295 | 	} | 
 | 296 |  | 
 | 297 | 	return data; | 
 | 298 | } | 
 | 299 |  | 
 | 300 | static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, | 
 | 301 | 			const char *buf, size_t count) | 
 | 302 | { | 
 | 303 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 
 | 304 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 305 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 306 | 	int res; | 
 | 307 | 	unsigned long temp; | 
 | 308 |  | 
 | 309 | 	res = strict_strtoul(buf, 10, &temp); | 
 | 310 | 	if (res) | 
 | 311 | 		return res; | 
 | 312 |  | 
 | 313 | 	temp /= 1000; | 
 | 314 | 	if (temp < 0) | 
 | 315 | 		return -EINVAL; | 
 | 316 |  | 
 | 317 | 	mutex_lock(&resource->lock); | 
 | 318 | 	resource->trip[attr->index - 7] = temp; | 
 | 319 | 	res = set_acpi_trip(resource); | 
 | 320 | 	mutex_unlock(&resource->lock); | 
 | 321 |  | 
 | 322 | 	if (res) | 
 | 323 | 		return res; | 
 | 324 |  | 
 | 325 | 	return count; | 
 | 326 | } | 
 | 327 |  | 
 | 328 | /* Power meter */ | 
 | 329 | static int update_meter(struct acpi_power_meter_resource *resource) | 
 | 330 | { | 
 | 331 | 	unsigned long long data; | 
 | 332 | 	acpi_status status; | 
 | 333 | 	unsigned long local_jiffies = jiffies; | 
 | 334 |  | 
 | 335 | 	if (time_before(local_jiffies, resource->sensors_last_updated + | 
 | 336 | 			msecs_to_jiffies(resource->caps.sampling_time)) && | 
 | 337 | 			resource->sensors_valid) | 
 | 338 | 		return 0; | 
 | 339 |  | 
 | 340 | 	status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM", | 
 | 341 | 				       NULL, &data); | 
 | 342 | 	if (ACPI_FAILURE(status)) { | 
 | 343 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMM")); | 
 | 344 | 		return -ENODEV; | 
 | 345 | 	} | 
 | 346 |  | 
 | 347 | 	resource->power = data; | 
 | 348 | 	resource->sensors_valid = 1; | 
 | 349 | 	resource->sensors_last_updated = jiffies; | 
 | 350 | 	return 0; | 
 | 351 | } | 
 | 352 |  | 
 | 353 | static ssize_t show_power(struct device *dev, | 
 | 354 | 			  struct device_attribute *devattr, | 
 | 355 | 			  char *buf) | 
 | 356 | { | 
 | 357 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 358 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 359 |  | 
 | 360 | 	mutex_lock(&resource->lock); | 
 | 361 | 	update_meter(resource); | 
 | 362 | 	mutex_unlock(&resource->lock); | 
 | 363 |  | 
 | 364 | 	return sprintf(buf, "%llu\n", resource->power * 1000); | 
 | 365 | } | 
 | 366 |  | 
 | 367 | /* Miscellaneous */ | 
 | 368 | static ssize_t show_str(struct device *dev, | 
 | 369 | 			struct device_attribute *devattr, | 
 | 370 | 			char *buf) | 
 | 371 | { | 
 | 372 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 
 | 373 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 374 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 375 | 	acpi_string val; | 
 | 376 |  | 
 | 377 | 	switch (attr->index) { | 
 | 378 | 	case 0: | 
 | 379 | 		val = resource->model_number; | 
 | 380 | 		break; | 
 | 381 | 	case 1: | 
 | 382 | 		val = resource->serial_number; | 
 | 383 | 		break; | 
 | 384 | 	case 2: | 
 | 385 | 		val = resource->oem_info; | 
 | 386 | 		break; | 
 | 387 | 	default: | 
 | 388 | 		BUG(); | 
 | 389 | 	} | 
 | 390 |  | 
 | 391 | 	return sprintf(buf, "%s\n", val); | 
 | 392 | } | 
 | 393 |  | 
 | 394 | static ssize_t show_val(struct device *dev, | 
 | 395 | 			struct device_attribute *devattr, | 
 | 396 | 			char *buf) | 
 | 397 | { | 
 | 398 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 
 | 399 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 400 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 401 | 	acpi_integer val = 0; | 
 | 402 |  | 
 | 403 | 	switch (attr->index) { | 
 | 404 | 	case 0: | 
 | 405 | 		val = resource->caps.min_avg_interval; | 
 | 406 | 		break; | 
 | 407 | 	case 1: | 
 | 408 | 		val = resource->caps.max_avg_interval; | 
 | 409 | 		break; | 
 | 410 | 	case 2: | 
 | 411 | 		val = resource->caps.min_cap * 1000; | 
 | 412 | 		break; | 
 | 413 | 	case 3: | 
 | 414 | 		val = resource->caps.max_cap * 1000; | 
 | 415 | 		break; | 
 | 416 | 	case 4: | 
 | 417 | 		if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS) | 
 | 418 | 			return sprintf(buf, "unknown\n"); | 
 | 419 |  | 
 | 420 | 		val = resource->caps.hysteresis * 1000; | 
 | 421 | 		break; | 
 | 422 | 	case 5: | 
 | 423 | 		if (resource->caps.flags & POWER_METER_IS_BATTERY) | 
 | 424 | 			val = 1; | 
 | 425 | 		else | 
 | 426 | 			val = 0; | 
 | 427 | 		break; | 
 | 428 | 	case 6: | 
 | 429 | 		if (resource->power > resource->cap) | 
 | 430 | 			val = 1; | 
 | 431 | 		else | 
 | 432 | 			val = 0; | 
 | 433 | 		break; | 
 | 434 | 	case 7: | 
 | 435 | 	case 8: | 
 | 436 | 		if (resource->trip[attr->index - 7] < 0) | 
 | 437 | 			return sprintf(buf, "unknown\n"); | 
 | 438 |  | 
 | 439 | 		val = resource->trip[attr->index - 7] * 1000; | 
 | 440 | 		break; | 
 | 441 | 	default: | 
 | 442 | 		BUG(); | 
 | 443 | 	} | 
 | 444 |  | 
 | 445 | 	return sprintf(buf, "%llu\n", val); | 
 | 446 | } | 
 | 447 |  | 
 | 448 | static ssize_t show_accuracy(struct device *dev, | 
 | 449 | 			     struct device_attribute *devattr, | 
 | 450 | 			     char *buf) | 
 | 451 | { | 
 | 452 | 	struct acpi_device *acpi_dev = to_acpi_device(dev); | 
 | 453 | 	struct acpi_power_meter_resource *resource = acpi_dev->driver_data; | 
 | 454 | 	unsigned int acc = resource->caps.accuracy; | 
 | 455 |  | 
 | 456 | 	return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000); | 
 | 457 | } | 
 | 458 |  | 
 | 459 | static ssize_t show_name(struct device *dev, | 
 | 460 | 			 struct device_attribute *devattr, | 
 | 461 | 			 char *buf) | 
 | 462 | { | 
 | 463 | 	return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME); | 
 | 464 | } | 
 | 465 |  | 
 | 466 | /* Sensor descriptions.  If you add a sensor, update NUM_SENSORS above! */ | 
 | 467 | static struct ro_sensor_template meter_ro_attrs[] = { | 
 | 468 | {POWER_AVERAGE_NAME, show_power, 0}, | 
 | 469 | {"power1_accuracy", show_accuracy, 0}, | 
 | 470 | {"power1_average_interval_min", show_val, 0}, | 
 | 471 | {"power1_average_interval_max", show_val, 1}, | 
 | 472 | {"power1_is_battery", show_val, 5}, | 
 | 473 | {NULL, NULL, 0}, | 
 | 474 | }; | 
 | 475 |  | 
 | 476 | static struct rw_sensor_template meter_rw_attrs[] = { | 
 | 477 | {POWER_AVG_INTERVAL_NAME, show_avg_interval, set_avg_interval, 0}, | 
 | 478 | {NULL, NULL, NULL, 0}, | 
 | 479 | }; | 
 | 480 |  | 
 | 481 | static struct ro_sensor_template misc_cap_attrs[] = { | 
 | 482 | {"power1_cap_min", show_val, 2}, | 
 | 483 | {"power1_cap_max", show_val, 3}, | 
 | 484 | {"power1_cap_hyst", show_val, 4}, | 
 | 485 | {POWER_ALARM_NAME, show_val, 6}, | 
 | 486 | {NULL, NULL, 0}, | 
 | 487 | }; | 
 | 488 |  | 
 | 489 | static struct ro_sensor_template ro_cap_attrs[] = { | 
 | 490 | {POWER_CAP_NAME, show_cap, 0}, | 
 | 491 | {NULL, NULL, 0}, | 
 | 492 | }; | 
 | 493 |  | 
 | 494 | static struct rw_sensor_template rw_cap_attrs[] = { | 
 | 495 | {POWER_CAP_NAME, show_cap, set_cap, 0}, | 
 | 496 | {NULL, NULL, NULL, 0}, | 
 | 497 | }; | 
 | 498 |  | 
 | 499 | static struct rw_sensor_template trip_attrs[] = { | 
 | 500 | {"power1_average_min", show_val, set_trip, 7}, | 
 | 501 | {"power1_average_max", show_val, set_trip, 8}, | 
 | 502 | {NULL, NULL, NULL, 0}, | 
 | 503 | }; | 
 | 504 |  | 
 | 505 | static struct ro_sensor_template misc_attrs[] = { | 
 | 506 | {"name", show_name, 0}, | 
 | 507 | {"power1_model_number", show_str, 0}, | 
 | 508 | {"power1_oem_info", show_str, 2}, | 
 | 509 | {"power1_serial_number", show_str, 1}, | 
 | 510 | {NULL, NULL, 0}, | 
 | 511 | }; | 
 | 512 |  | 
 | 513 | /* Read power domain data */ | 
 | 514 | static void remove_domain_devices(struct acpi_power_meter_resource *resource) | 
 | 515 | { | 
 | 516 | 	int i; | 
 | 517 |  | 
 | 518 | 	if (!resource->num_domain_devices) | 
 | 519 | 		return; | 
 | 520 |  | 
 | 521 | 	for (i = 0; i < resource->num_domain_devices; i++) { | 
 | 522 | 		struct acpi_device *obj = resource->domain_devices[i]; | 
 | 523 | 		if (!obj) | 
 | 524 | 			continue; | 
 | 525 |  | 
 | 526 | 		sysfs_remove_link(resource->holders_dir, | 
 | 527 | 				  kobject_name(&obj->dev.kobj)); | 
 | 528 | 		put_device(&obj->dev); | 
 | 529 | 	} | 
 | 530 |  | 
 | 531 | 	kfree(resource->domain_devices); | 
 | 532 | 	kobject_put(resource->holders_dir); | 
 | 533 | } | 
 | 534 |  | 
 | 535 | static int read_domain_devices(struct acpi_power_meter_resource *resource) | 
 | 536 | { | 
 | 537 | 	int res = 0; | 
 | 538 | 	int i; | 
 | 539 | 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 
 | 540 | 	union acpi_object *pss; | 
 | 541 | 	acpi_status status; | 
 | 542 |  | 
 | 543 | 	status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMD", NULL, | 
 | 544 | 				      &buffer); | 
 | 545 | 	if (ACPI_FAILURE(status)) { | 
 | 546 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMD")); | 
 | 547 | 		return -ENODEV; | 
 | 548 | 	} | 
 | 549 |  | 
 | 550 | 	pss = buffer.pointer; | 
 | 551 | 	if (!pss || | 
 | 552 | 	    pss->type != ACPI_TYPE_PACKAGE) { | 
 | 553 | 		dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME | 
 | 554 | 			"Invalid _PMD data\n"); | 
 | 555 | 		res = -EFAULT; | 
 | 556 | 		goto end; | 
 | 557 | 	} | 
 | 558 |  | 
 | 559 | 	if (!pss->package.count) | 
 | 560 | 		goto end; | 
 | 561 |  | 
 | 562 | 	resource->domain_devices = kzalloc(sizeof(struct acpi_device *) * | 
 | 563 | 					   pss->package.count, GFP_KERNEL); | 
 | 564 | 	if (!resource->domain_devices) { | 
 | 565 | 		res = -ENOMEM; | 
 | 566 | 		goto end; | 
 | 567 | 	} | 
 | 568 |  | 
 | 569 | 	resource->holders_dir = kobject_create_and_add("measures", | 
 | 570 | 					&resource->acpi_dev->dev.kobj); | 
 | 571 | 	if (!resource->holders_dir) { | 
 | 572 | 		res = -ENOMEM; | 
 | 573 | 		goto exit_free; | 
 | 574 | 	} | 
 | 575 |  | 
 | 576 | 	resource->num_domain_devices = pss->package.count; | 
 | 577 |  | 
 | 578 | 	for (i = 0; i < pss->package.count; i++) { | 
 | 579 | 		struct acpi_device *obj; | 
 | 580 | 		union acpi_object *element = &(pss->package.elements[i]); | 
 | 581 |  | 
 | 582 | 		/* Refuse non-references */ | 
 | 583 | 		if (element->type != ACPI_TYPE_LOCAL_REFERENCE) | 
 | 584 | 			continue; | 
 | 585 |  | 
 | 586 | 		/* Create a symlink to domain objects */ | 
 | 587 | 		resource->domain_devices[i] = NULL; | 
 | 588 | 		status = acpi_bus_get_device(element->reference.handle, | 
 | 589 | 					     &resource->domain_devices[i]); | 
 | 590 | 		if (ACPI_FAILURE(status)) | 
 | 591 | 			continue; | 
 | 592 |  | 
 | 593 | 		obj = resource->domain_devices[i]; | 
 | 594 | 		get_device(&obj->dev); | 
 | 595 |  | 
 | 596 | 		res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj, | 
 | 597 | 				      kobject_name(&obj->dev.kobj)); | 
 | 598 | 		if (res) { | 
 | 599 | 			put_device(&obj->dev); | 
 | 600 | 			resource->domain_devices[i] = NULL; | 
 | 601 | 		} | 
 | 602 | 	} | 
 | 603 |  | 
 | 604 | 	res = 0; | 
 | 605 | 	goto end; | 
 | 606 |  | 
 | 607 | exit_free: | 
 | 608 | 	kfree(resource->domain_devices); | 
 | 609 | end: | 
 | 610 | 	kfree(buffer.pointer); | 
 | 611 | 	return res; | 
 | 612 | } | 
 | 613 |  | 
 | 614 | /* Registration and deregistration */ | 
 | 615 | static int register_ro_attrs(struct acpi_power_meter_resource *resource, | 
 | 616 | 			     struct ro_sensor_template *ro) | 
 | 617 | { | 
 | 618 | 	struct device *dev = &resource->acpi_dev->dev; | 
 | 619 | 	struct sensor_device_attribute *sensors = | 
 | 620 | 		&resource->sensors[resource->num_sensors]; | 
 | 621 | 	int res = 0; | 
 | 622 |  | 
 | 623 | 	while (ro->label) { | 
 | 624 | 		sensors->dev_attr.attr.name = ro->label; | 
 | 625 | 		sensors->dev_attr.attr.mode = S_IRUGO; | 
 | 626 | 		sensors->dev_attr.show = ro->show; | 
 | 627 | 		sensors->index = ro->index; | 
 | 628 |  | 
 | 629 | 		res = device_create_file(dev, &sensors->dev_attr); | 
 | 630 | 		if (res) { | 
 | 631 | 			sensors->dev_attr.attr.name = NULL; | 
 | 632 | 			goto error; | 
 | 633 | 		} | 
 | 634 | 		sensors++; | 
 | 635 | 		resource->num_sensors++; | 
 | 636 | 		ro++; | 
 | 637 | 	} | 
 | 638 |  | 
 | 639 | error: | 
 | 640 | 	return res; | 
 | 641 | } | 
 | 642 |  | 
 | 643 | static int register_rw_attrs(struct acpi_power_meter_resource *resource, | 
 | 644 | 			     struct rw_sensor_template *rw) | 
 | 645 | { | 
 | 646 | 	struct device *dev = &resource->acpi_dev->dev; | 
 | 647 | 	struct sensor_device_attribute *sensors = | 
 | 648 | 		&resource->sensors[resource->num_sensors]; | 
 | 649 | 	int res = 0; | 
 | 650 |  | 
 | 651 | 	while (rw->label) { | 
 | 652 | 		sensors->dev_attr.attr.name = rw->label; | 
 | 653 | 		sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR; | 
 | 654 | 		sensors->dev_attr.show = rw->show; | 
 | 655 | 		sensors->dev_attr.store = rw->set; | 
 | 656 | 		sensors->index = rw->index; | 
 | 657 |  | 
 | 658 | 		res = device_create_file(dev, &sensors->dev_attr); | 
 | 659 | 		if (res) { | 
 | 660 | 			sensors->dev_attr.attr.name = NULL; | 
 | 661 | 			goto error; | 
 | 662 | 		} | 
 | 663 | 		sensors++; | 
 | 664 | 		resource->num_sensors++; | 
 | 665 | 		rw++; | 
 | 666 | 	} | 
 | 667 |  | 
 | 668 | error: | 
 | 669 | 	return res; | 
 | 670 | } | 
 | 671 |  | 
 | 672 | static void remove_attrs(struct acpi_power_meter_resource *resource) | 
 | 673 | { | 
 | 674 | 	int i; | 
 | 675 |  | 
 | 676 | 	for (i = 0; i < resource->num_sensors; i++) { | 
 | 677 | 		if (!resource->sensors[i].dev_attr.attr.name) | 
 | 678 | 			continue; | 
 | 679 | 		device_remove_file(&resource->acpi_dev->dev, | 
 | 680 | 				   &resource->sensors[i].dev_attr); | 
 | 681 | 	} | 
 | 682 |  | 
 | 683 | 	remove_domain_devices(resource); | 
 | 684 |  | 
 | 685 | 	resource->num_sensors = 0; | 
 | 686 | } | 
 | 687 |  | 
 | 688 | static int setup_attrs(struct acpi_power_meter_resource *resource) | 
 | 689 | { | 
 | 690 | 	int res = 0; | 
 | 691 |  | 
 | 692 | 	res = read_domain_devices(resource); | 
 | 693 | 	if (res) | 
 | 694 | 		return res; | 
 | 695 |  | 
 | 696 | 	if (resource->caps.flags & POWER_METER_CAN_MEASURE) { | 
 | 697 | 		res = register_ro_attrs(resource, meter_ro_attrs); | 
 | 698 | 		if (res) | 
 | 699 | 			goto error; | 
 | 700 | 		res = register_rw_attrs(resource, meter_rw_attrs); | 
 | 701 | 		if (res) | 
 | 702 | 			goto error; | 
 | 703 | 	} | 
 | 704 |  | 
 | 705 | 	if (resource->caps.flags & POWER_METER_CAN_CAP) { | 
 | 706 | 		if (!can_cap_in_hardware()) { | 
 | 707 | 			dev_err(&resource->acpi_dev->dev, | 
 | 708 | 				"Ignoring unsafe software power cap!\n"); | 
 | 709 | 			goto skip_unsafe_cap; | 
 | 710 | 		} | 
 | 711 |  | 
 | 712 | 		if (resource->caps.configurable_cap) { | 
 | 713 | 			res = register_rw_attrs(resource, rw_cap_attrs); | 
 | 714 | 			if (res) | 
 | 715 | 				goto error; | 
 | 716 | 		} else { | 
 | 717 | 			res = register_ro_attrs(resource, ro_cap_attrs); | 
 | 718 | 			if (res) | 
 | 719 | 				goto error; | 
 | 720 | 		} | 
 | 721 | 		res = register_ro_attrs(resource, misc_cap_attrs); | 
 | 722 | 		if (res) | 
 | 723 | 			goto error; | 
 | 724 | 	} | 
 | 725 | skip_unsafe_cap: | 
 | 726 |  | 
 | 727 | 	if (resource->caps.flags & POWER_METER_CAN_TRIP) { | 
 | 728 | 		res = register_rw_attrs(resource, trip_attrs); | 
 | 729 | 		if (res) | 
 | 730 | 			goto error; | 
 | 731 | 	} | 
 | 732 |  | 
 | 733 | 	res = register_ro_attrs(resource, misc_attrs); | 
 | 734 | 	if (res) | 
 | 735 | 		goto error; | 
 | 736 |  | 
 | 737 | 	return res; | 
 | 738 | error: | 
 | 739 | 	remove_domain_devices(resource); | 
 | 740 | 	remove_attrs(resource); | 
 | 741 | 	return res; | 
 | 742 | } | 
 | 743 |  | 
 | 744 | static void free_capabilities(struct acpi_power_meter_resource *resource) | 
 | 745 | { | 
 | 746 | 	acpi_string *str; | 
 | 747 | 	int i; | 
 | 748 |  | 
 | 749 | 	str = &resource->model_number; | 
 | 750 | 	for (i = 0; i < 3; i++, str++) | 
 | 751 | 		kfree(*str); | 
 | 752 | } | 
 | 753 |  | 
 | 754 | static int read_capabilities(struct acpi_power_meter_resource *resource) | 
 | 755 | { | 
 | 756 | 	int res = 0; | 
 | 757 | 	int i; | 
 | 758 | 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 
 | 759 | 	struct acpi_buffer state = { 0, NULL }; | 
 | 760 | 	struct acpi_buffer format = { sizeof("NNNNNNNNNNN"), "NNNNNNNNNNN" }; | 
 | 761 | 	union acpi_object *pss; | 
 | 762 | 	acpi_string *str; | 
 | 763 | 	acpi_status status; | 
 | 764 |  | 
 | 765 | 	status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMC", NULL, | 
 | 766 | 				      &buffer); | 
 | 767 | 	if (ACPI_FAILURE(status)) { | 
 | 768 | 		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMC")); | 
 | 769 | 		return -ENODEV; | 
 | 770 | 	} | 
 | 771 |  | 
 | 772 | 	pss = buffer.pointer; | 
 | 773 | 	if (!pss || | 
 | 774 | 	    pss->type != ACPI_TYPE_PACKAGE || | 
 | 775 | 	    pss->package.count != 14) { | 
 | 776 | 		dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME | 
 | 777 | 			"Invalid _PMC data\n"); | 
 | 778 | 		res = -EFAULT; | 
 | 779 | 		goto end; | 
 | 780 | 	} | 
 | 781 |  | 
 | 782 | 	/* Grab all the integer data at once */ | 
 | 783 | 	state.length = sizeof(struct acpi_power_meter_capabilities); | 
 | 784 | 	state.pointer = &resource->caps; | 
 | 785 |  | 
 | 786 | 	status = acpi_extract_package(pss, &format, &state); | 
 | 787 | 	if (ACPI_FAILURE(status)) { | 
 | 788 | 		ACPI_EXCEPTION((AE_INFO, status, "Invalid data")); | 
 | 789 | 		res = -EFAULT; | 
 | 790 | 		goto end; | 
 | 791 | 	} | 
 | 792 |  | 
 | 793 | 	if (resource->caps.units) { | 
 | 794 | 		dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME | 
 | 795 | 			"Unknown units %llu.\n", | 
 | 796 | 			resource->caps.units); | 
 | 797 | 		res = -EINVAL; | 
 | 798 | 		goto end; | 
 | 799 | 	} | 
 | 800 |  | 
 | 801 | 	/* Grab the string data */ | 
 | 802 | 	str = &resource->model_number; | 
 | 803 |  | 
 | 804 | 	for (i = 11; i < 14; i++) { | 
 | 805 | 		union acpi_object *element = &(pss->package.elements[i]); | 
 | 806 |  | 
 | 807 | 		if (element->type != ACPI_TYPE_STRING) { | 
 | 808 | 			res = -EINVAL; | 
 | 809 | 			goto error; | 
 | 810 | 		} | 
 | 811 |  | 
 | 812 | 		*str = kzalloc(sizeof(u8) * (element->string.length + 1), | 
 | 813 | 			       GFP_KERNEL); | 
 | 814 | 		if (!*str) { | 
 | 815 | 			res = -ENOMEM; | 
 | 816 | 			goto error; | 
 | 817 | 		} | 
 | 818 |  | 
 | 819 | 		strncpy(*str, element->string.pointer, element->string.length); | 
 | 820 | 		str++; | 
 | 821 | 	} | 
 | 822 |  | 
 | 823 | 	dev_info(&resource->acpi_dev->dev, "Found ACPI power meter.\n"); | 
 | 824 | 	goto end; | 
 | 825 | error: | 
 | 826 | 	str = &resource->model_number; | 
 | 827 | 	for (i = 0; i < 3; i++, str++) | 
 | 828 | 		kfree(*str); | 
 | 829 | end: | 
 | 830 | 	kfree(buffer.pointer); | 
 | 831 | 	return res; | 
 | 832 | } | 
 | 833 |  | 
 | 834 | /* Handle ACPI event notifications */ | 
 | 835 | static void acpi_power_meter_notify(struct acpi_device *device, u32 event) | 
 | 836 | { | 
 | 837 | 	struct acpi_power_meter_resource *resource; | 
 | 838 | 	int res; | 
 | 839 |  | 
 | 840 | 	if (!device || !acpi_driver_data(device)) | 
 | 841 | 		return; | 
 | 842 |  | 
 | 843 | 	resource = acpi_driver_data(device); | 
 | 844 |  | 
 | 845 | 	mutex_lock(&resource->lock); | 
 | 846 | 	switch (event) { | 
 | 847 | 	case METER_NOTIFY_CONFIG: | 
 | 848 | 		free_capabilities(resource); | 
 | 849 | 		res = read_capabilities(resource); | 
 | 850 | 		if (res) | 
 | 851 | 			break; | 
 | 852 |  | 
 | 853 | 		remove_attrs(resource); | 
 | 854 | 		setup_attrs(resource); | 
 | 855 | 		break; | 
 | 856 | 	case METER_NOTIFY_TRIP: | 
 | 857 | 		sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); | 
 | 858 | 		update_meter(resource); | 
 | 859 | 		break; | 
 | 860 | 	case METER_NOTIFY_CAP: | 
 | 861 | 		sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME); | 
 | 862 | 		update_cap(resource); | 
 | 863 | 		break; | 
 | 864 | 	case METER_NOTIFY_INTERVAL: | 
 | 865 | 		sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME); | 
 | 866 | 		update_avg_interval(resource); | 
 | 867 | 		break; | 
 | 868 | 	case METER_NOTIFY_CAPPING: | 
 | 869 | 		sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME); | 
 | 870 | 		dev_info(&device->dev, "Capping in progress.\n"); | 
 | 871 | 		break; | 
 | 872 | 	default: | 
 | 873 | 		BUG(); | 
 | 874 | 	} | 
 | 875 | 	mutex_unlock(&resource->lock); | 
 | 876 |  | 
 | 877 | 	acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS, | 
 | 878 | 					dev_name(&device->dev), event, 0); | 
 | 879 | } | 
 | 880 |  | 
 | 881 | static int acpi_power_meter_add(struct acpi_device *device) | 
 | 882 | { | 
 | 883 | 	int res; | 
 | 884 | 	struct acpi_power_meter_resource *resource; | 
 | 885 |  | 
 | 886 | 	if (!device) | 
 | 887 | 		return -EINVAL; | 
 | 888 |  | 
 | 889 | 	resource = kzalloc(sizeof(struct acpi_power_meter_resource), | 
 | 890 | 			   GFP_KERNEL); | 
 | 891 | 	if (!resource) | 
 | 892 | 		return -ENOMEM; | 
 | 893 |  | 
 | 894 | 	resource->sensors_valid = 0; | 
 | 895 | 	resource->acpi_dev = device; | 
 | 896 | 	mutex_init(&resource->lock); | 
 | 897 | 	strcpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME); | 
 | 898 | 	strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); | 
 | 899 | 	device->driver_data = resource; | 
 | 900 |  | 
 | 901 | 	free_capabilities(resource); | 
 | 902 | 	res = read_capabilities(resource); | 
 | 903 | 	if (res) | 
 | 904 | 		goto exit_free; | 
 | 905 |  | 
 | 906 | 	resource->trip[0] = resource->trip[1] = -1; | 
 | 907 |  | 
 | 908 | 	res = setup_attrs(resource); | 
 | 909 | 	if (res) | 
 | 910 | 		goto exit_free; | 
 | 911 |  | 
 | 912 | 	resource->hwmon_dev = hwmon_device_register(&device->dev); | 
 | 913 | 	if (IS_ERR(resource->hwmon_dev)) { | 
 | 914 | 		res = PTR_ERR(resource->hwmon_dev); | 
 | 915 | 		goto exit_remove; | 
 | 916 | 	} | 
 | 917 |  | 
 | 918 | 	res = 0; | 
 | 919 | 	goto exit; | 
 | 920 |  | 
 | 921 | exit_remove: | 
 | 922 | 	remove_attrs(resource); | 
 | 923 | exit_free: | 
 | 924 | 	kfree(resource); | 
 | 925 | exit: | 
 | 926 | 	return res; | 
 | 927 | } | 
 | 928 |  | 
 | 929 | static int acpi_power_meter_remove(struct acpi_device *device, int type) | 
 | 930 | { | 
 | 931 | 	struct acpi_power_meter_resource *resource; | 
 | 932 |  | 
 | 933 | 	if (!device || !acpi_driver_data(device)) | 
 | 934 | 		return -EINVAL; | 
 | 935 |  | 
 | 936 | 	resource = acpi_driver_data(device); | 
 | 937 | 	hwmon_device_unregister(resource->hwmon_dev); | 
 | 938 |  | 
 | 939 | 	free_capabilities(resource); | 
 | 940 | 	remove_attrs(resource); | 
 | 941 |  | 
 | 942 | 	kfree(resource); | 
 | 943 | 	return 0; | 
 | 944 | } | 
 | 945 |  | 
 | 946 | static int acpi_power_meter_resume(struct acpi_device *device) | 
 | 947 | { | 
 | 948 | 	struct acpi_power_meter_resource *resource; | 
 | 949 |  | 
 | 950 | 	if (!device || !acpi_driver_data(device)) | 
 | 951 | 		return -EINVAL; | 
 | 952 |  | 
 | 953 | 	resource = acpi_driver_data(device); | 
 | 954 | 	free_capabilities(resource); | 
 | 955 | 	read_capabilities(resource); | 
 | 956 |  | 
 | 957 | 	return 0; | 
 | 958 | } | 
 | 959 |  | 
 | 960 | static struct acpi_driver acpi_power_meter_driver = { | 
 | 961 | 	.name = "power_meter", | 
 | 962 | 	.class = ACPI_POWER_METER_CLASS, | 
 | 963 | 	.ids = power_meter_ids, | 
 | 964 | 	.ops = { | 
 | 965 | 		.add = acpi_power_meter_add, | 
 | 966 | 		.remove = acpi_power_meter_remove, | 
 | 967 | 		.resume = acpi_power_meter_resume, | 
 | 968 | 		.notify = acpi_power_meter_notify, | 
 | 969 | 		}, | 
 | 970 | }; | 
 | 971 |  | 
 | 972 | /* Module init/exit routines */ | 
 | 973 | static int __init enable_cap_knobs(const struct dmi_system_id *d) | 
 | 974 | { | 
 | 975 | 	cap_in_hardware = 1; | 
 | 976 | 	return 0; | 
 | 977 | } | 
 | 978 |  | 
 | 979 | static struct dmi_system_id __initdata pm_dmi_table[] = { | 
 | 980 | 	{ | 
 | 981 | 		enable_cap_knobs, "IBM Active Energy Manager", | 
 | 982 | 		{ | 
 | 983 | 			DMI_MATCH(DMI_SYS_VENDOR, "IBM") | 
 | 984 | 		}, | 
 | 985 | 	}, | 
 | 986 | 	{} | 
 | 987 | }; | 
 | 988 |  | 
 | 989 | static int __init acpi_power_meter_init(void) | 
 | 990 | { | 
 | 991 | 	int result; | 
 | 992 |  | 
 | 993 | 	if (acpi_disabled) | 
 | 994 | 		return -ENODEV; | 
 | 995 |  | 
 | 996 | 	dmi_check_system(pm_dmi_table); | 
 | 997 |  | 
 | 998 | 	result = acpi_bus_register_driver(&acpi_power_meter_driver); | 
 | 999 | 	if (result < 0) | 
 | 1000 | 		return -ENODEV; | 
 | 1001 |  | 
 | 1002 | 	return 0; | 
 | 1003 | } | 
 | 1004 |  | 
 | 1005 | static void __exit acpi_power_meter_exit(void) | 
 | 1006 | { | 
 | 1007 | 	acpi_bus_unregister_driver(&acpi_power_meter_driver); | 
 | 1008 | } | 
 | 1009 |  | 
 | 1010 | MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); | 
 | 1011 | MODULE_DESCRIPTION("ACPI 4.0 power meter driver"); | 
 | 1012 | MODULE_LICENSE("GPL"); | 
 | 1013 |  | 
 | 1014 | module_param(force_cap_on, bool, 0644); | 
 | 1015 | MODULE_PARM_DESC(force_cap_on, "Enable power cap even it is unsafe to do so."); | 
 | 1016 |  | 
 | 1017 | module_init(acpi_power_meter_init); | 
 | 1018 | module_exit(acpi_power_meter_exit); |