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; |