diff options
| -rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 5838c69b2fb3..e954f2af5724 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include <linux/pci.h> | 35 | #include <linux/pci.h> |
| 36 | #include <linux/pci_hotplug.h> | 36 | #include <linux/pci_hotplug.h> |
| 37 | #include <linux/leds.h> | 37 | #include <linux/leds.h> |
| 38 | #include <linux/dmi.h> | ||
| 38 | 39 | ||
| 39 | #define EEEPC_LAPTOP_VERSION "0.1" | 40 | #define EEEPC_LAPTOP_VERSION "0.1" |
| 40 | #define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" | 41 | #define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" |
| @@ -159,6 +160,7 @@ struct eeepc_laptop { | |||
| 159 | acpi_handle handle; /* the handle of the acpi device */ | 160 | acpi_handle handle; /* the handle of the acpi device */ |
| 160 | u32 cm_supported; /* the control methods supported | 161 | u32 cm_supported; /* the control methods supported |
| 161 | by this BIOS */ | 162 | by this BIOS */ |
| 163 | bool cpufv_disabled; | ||
| 162 | u16 event_count[128]; /* count for each event */ | 164 | u16 event_count[128]; /* count for each event */ |
| 163 | 165 | ||
| 164 | struct platform_device *platform_device; | 166 | struct platform_device *platform_device; |
| @@ -378,6 +380,8 @@ static ssize_t store_cpufv(struct device *dev, | |||
| 378 | struct eeepc_cpufv c; | 380 | struct eeepc_cpufv c; |
| 379 | int rv, value; | 381 | int rv, value; |
| 380 | 382 | ||
| 383 | if (eeepc->cpufv_disabled) | ||
| 384 | return -EPERM; | ||
| 381 | if (get_cpufv(eeepc, &c)) | 385 | if (get_cpufv(eeepc, &c)) |
| 382 | return -ENODEV; | 386 | return -ENODEV; |
| 383 | rv = parse_arg(buf, count, &value); | 387 | rv = parse_arg(buf, count, &value); |
| @@ -389,6 +393,41 @@ static ssize_t store_cpufv(struct device *dev, | |||
| 389 | return rv; | 393 | return rv; |
| 390 | } | 394 | } |
| 391 | 395 | ||
| 396 | static ssize_t show_cpufv_disabled(struct device *dev, | ||
| 397 | struct device_attribute *attr, | ||
| 398 | char *buf) | ||
| 399 | { | ||
| 400 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
| 401 | |||
| 402 | return sprintf(buf, "%d\n", eeepc->cpufv_disabled); | ||
| 403 | } | ||
| 404 | |||
| 405 | static ssize_t store_cpufv_disabled(struct device *dev, | ||
| 406 | struct device_attribute *attr, | ||
| 407 | const char *buf, size_t count) | ||
| 408 | { | ||
| 409 | struct eeepc_laptop *eeepc = dev_get_drvdata(dev); | ||
| 410 | int rv, value; | ||
| 411 | |||
| 412 | rv = parse_arg(buf, count, &value); | ||
| 413 | if (rv < 0) | ||
| 414 | return rv; | ||
| 415 | |||
| 416 | switch (value) { | ||
| 417 | case 0: | ||
| 418 | if (eeepc->cpufv_disabled) | ||
| 419 | pr_warning("cpufv enabled (not officially supported " | ||
| 420 | "on this model)\n"); | ||
| 421 | eeepc->cpufv_disabled = false; | ||
| 422 | return rv; | ||
| 423 | case 1: | ||
| 424 | return -EPERM; | ||
| 425 | default: | ||
| 426 | return -EINVAL; | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | |||
| 392 | static struct device_attribute dev_attr_cpufv = { | 431 | static struct device_attribute dev_attr_cpufv = { |
| 393 | .attr = { | 432 | .attr = { |
| 394 | .name = "cpufv", | 433 | .name = "cpufv", |
| @@ -404,12 +443,22 @@ static struct device_attribute dev_attr_available_cpufv = { | |||
| 404 | .show = show_available_cpufv | 443 | .show = show_available_cpufv |
| 405 | }; | 444 | }; |
| 406 | 445 | ||
| 446 | static struct device_attribute dev_attr_cpufv_disabled = { | ||
| 447 | .attr = { | ||
| 448 | .name = "cpufv_disabled", | ||
| 449 | .mode = 0644 }, | ||
| 450 | .show = show_cpufv_disabled, | ||
| 451 | .store = store_cpufv_disabled | ||
| 452 | }; | ||
| 453 | |||
| 454 | |||
| 407 | static struct attribute *platform_attributes[] = { | 455 | static struct attribute *platform_attributes[] = { |
| 408 | &dev_attr_camera.attr, | 456 | &dev_attr_camera.attr, |
| 409 | &dev_attr_cardr.attr, | 457 | &dev_attr_cardr.attr, |
| 410 | &dev_attr_disp.attr, | 458 | &dev_attr_disp.attr, |
| 411 | &dev_attr_cpufv.attr, | 459 | &dev_attr_cpufv.attr, |
| 412 | &dev_attr_available_cpufv.attr, | 460 | &dev_attr_available_cpufv.attr, |
| 461 | &dev_attr_cpufv_disabled.attr, | ||
| 413 | NULL | 462 | NULL |
| 414 | }; | 463 | }; |
| 415 | 464 | ||
| @@ -1261,6 +1310,42 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event) | |||
| 1261 | } | 1310 | } |
| 1262 | } | 1311 | } |
| 1263 | 1312 | ||
| 1313 | static void eeepc_dmi_check(struct eeepc_laptop *eeepc) | ||
| 1314 | { | ||
| 1315 | const char *model; | ||
| 1316 | |||
| 1317 | /* | ||
| 1318 | * Blacklist for setting cpufv (cpu speed). | ||
| 1319 | * | ||
| 1320 | * EeePC 4G ("701") implements CFVS, but it is not supported | ||
| 1321 | * by the pre-installed OS, and the original option to change it | ||
| 1322 | * in the BIOS setup screen was removed in later versions. | ||
| 1323 | * | ||
| 1324 | * Judging by the lack of "Super Hybrid Engine" on Asus product pages, | ||
| 1325 | * this applies to all "701" models (4G/4G Surf/2G Surf). | ||
| 1326 | * | ||
| 1327 | * So Asus made a deliberate decision not to support it on this model. | ||
| 1328 | * We have several reports that using it can cause the system to hang | ||
| 1329 | * | ||
| 1330 | * The hang has also been reported on a "702" (Model name "8G"?). | ||
| 1331 | * | ||
| 1332 | * We avoid dmi_check_system() / dmi_match(), because they use | ||
| 1333 | * substring matching. We don't want to affect the "701SD" | ||
| 1334 | * and "701SDX" models, because they do support S.H.E. | ||
| 1335 | */ | ||
| 1336 | |||
| 1337 | model = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
| 1338 | if (!model) | ||
| 1339 | return; | ||
| 1340 | |||
| 1341 | if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) { | ||
| 1342 | eeepc->cpufv_disabled = true; | ||
| 1343 | pr_info("model %s does not officially support setting cpu " | ||
| 1344 | "speed\n", model); | ||
| 1345 | pr_info("cpufv disabled to avoid instability\n"); | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1264 | static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) | 1349 | static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) |
| 1265 | { | 1350 | { |
| 1266 | int dummy; | 1351 | int dummy; |
| @@ -1342,6 +1427,8 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) | |||
| 1342 | strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); | 1427 | strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); |
| 1343 | device->driver_data = eeepc; | 1428 | device->driver_data = eeepc; |
| 1344 | 1429 | ||
| 1430 | eeepc_dmi_check(eeepc); | ||
| 1431 | |||
| 1345 | result = eeepc_acpi_init(eeepc, device); | 1432 | result = eeepc_acpi_init(eeepc, device); |
| 1346 | if (result) | 1433 | if (result) |
| 1347 | goto fail_platform; | 1434 | goto fail_platform; |
