]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/platform/x86/eeepc-laptop.c
eeepc-laptop: Use ACPI handle to identify rfkill port
[linux-2.6.git] / drivers / platform / x86 / eeepc-laptop.c
index 6a47bb7066d8587410938665bd5558d9795379b7..2c1abf63957f23d0353c209af843f8e8bcf3c50c 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/fb.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
+#include <linux/slab.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 #include <linux/uaccess.h>
@@ -52,7 +53,7 @@ MODULE_LICENSE("GPL");
 
 static bool hotplug_disabled;
 
-module_param(hotplug_disabled, bool, 0644);
+module_param(hotplug_disabled, bool, 0444);
 MODULE_PARM_DESC(hotplug_disabled,
                 "Disable hotplug for wireless device. "
                 "If your laptop need that, please report to "
@@ -164,11 +165,11 @@ struct eeepc_laptop {
        u16 event_count[128];           /* count for each event */
 
        struct platform_device *platform_device;
+       struct acpi_device *device;             /* the device we are in */
        struct device *hwmon_device;
        struct backlight_device *backlight_device;
 
        struct input_dev *inputdev;
-       struct key_entry *keymap;
 
        struct rfkill *wlan_rfkill;
        struct rfkill *bluetooth_rfkill;
@@ -528,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev,
        queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
 }
 
+static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
+{
+       struct eeepc_laptop *eeepc;
+
+       eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
+
+       return get_acpi(eeepc, CM_ASL_TPD);
+}
+
 static int eeepc_led_init(struct eeepc_laptop *eeepc)
 {
        int rv;
@@ -542,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
 
        eeepc->tpd_led.name = "eeepc::touchpad";
        eeepc->tpd_led.brightness_set = tpd_led_set;
+       if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */
+         eeepc->tpd_led.brightness_get = tpd_led_get;
        eeepc->tpd_led.max_brightness = 1;
 
        rv = led_classdev_register(&eeepc->platform_device->dev,
@@ -573,11 +585,14 @@ static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
        return true;
 }
 
-static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
+static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
 {
+       struct pci_dev *port;
        struct pci_dev *dev;
        struct pci_bus *bus;
        bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
+       bool absent;
+       u32 l;
 
        if (eeepc->wlan_rfkill)
                rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
@@ -585,9 +600,33 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
        mutex_lock(&eeepc->hotplug_lock);
 
        if (eeepc->hotplug_slot) {
-               bus = pci_find_bus(0, 1);
+               port = acpi_get_pci_dev(handle);
+               if (!port) {
+                       pr_warning("Unable to find port\n");
+                       goto out_unlock;
+               }
+
+               bus = port->subordinate;
+
                if (!bus) {
-                       pr_warning("Unable to find PCI bus 1?\n");
+                       pr_warning("Unable to find PCI bus?\n");
+                       goto out_unlock;
+               }
+
+               if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
+                       pr_err("Unable to read PCI config space?\n");
+                       goto out_unlock;
+               }
+
+               absent = (l == 0xffffffff);
+
+               if (blocked != absent) {
+                       pr_warning("BIOS says wireless lan is %s, "
+                                       "but the pci device is %s\n",
+                               blocked ? "blocked" : "unblocked",
+                               absent ? "absent" : "present");
+                       pr_warning("skipped wireless hotplug as probably "
+                                       "inappropriate for this model\n");
                        goto out_unlock;
                }
 
@@ -617,6 +656,17 @@ out_unlock:
        mutex_unlock(&eeepc->hotplug_lock);
 }
 
+static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node)
+{
+       acpi_status status = AE_OK;
+       acpi_handle handle;
+
+       status = acpi_get_handle(NULL, node, &handle);
+
+       if (ACPI_SUCCESS(status))
+               eeepc_rfkill_hotplug(eeepc, handle);
+}
+
 static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
 {
        struct eeepc_laptop *eeepc = data;
@@ -624,7 +674,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
        if (event != ACPI_NOTIFY_BUS_CHECK)
                return;
 
-       eeepc_rfkill_hotplug(eeepc);
+       eeepc_rfkill_hotplug(eeepc, handle);
 }
 
 static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
@@ -642,6 +692,11 @@ static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
                                                     eeepc);
                if (ACPI_FAILURE(status))
                        pr_warning("Failed to register notify on %s\n", node);
+               /*
+                * Refresh pci hotplug in case the rfkill state was
+                * changed during setup.
+                */
+               eeepc_rfkill_hotplug(eeepc, handle);
        } else
                return -ENODEV;
 
@@ -663,6 +718,12 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
                if (ACPI_FAILURE(status))
                        pr_err("Error removing rfkill notify handler %s\n",
                                node);
+                       /*
+                        * Refresh pci hotplug in case the rfkill
+                        * state was changed after
+                        * eeepc_unregister_rfkill_notifier()
+                        */
+               eeepc_rfkill_hotplug(eeepc, handle);
        }
 }
 
@@ -786,11 +847,7 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
                rfkill_destroy(eeepc->wlan_rfkill);
                eeepc->wlan_rfkill = NULL;
        }
-       /*
-        * Refresh pci hotplug in case the rfkill state was changed after
-        * eeepc_unregister_rfkill_notifier()
-        */
-       eeepc_rfkill_hotplug(eeepc);
+
        if (eeepc->hotplug_slot)
                pci_hp_deregister(eeepc->hotplug_slot);
 
@@ -859,11 +916,6 @@ static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
        eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
        eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
        eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
-       /*
-        * Refresh pci hotplug in case the rfkill state was changed during
-        * setup.
-        */
-       eeepc_rfkill_hotplug(eeepc);
 
 exit:
        if (result && result != -ENODEV)
@@ -898,8 +950,11 @@ static int eeepc_hotk_restore(struct device *device)
        struct eeepc_laptop *eeepc = dev_get_drvdata(device);
 
        /* Refresh both wlan rfkill state and pci hotplug */
-       if (eeepc->wlan_rfkill)
-               eeepc_rfkill_hotplug(eeepc);
+       if (eeepc->wlan_rfkill) {
+               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P5");
+               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P6");
+               eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P7");
+       }
 
        if (eeepc->bluetooth_rfkill)
                rfkill_set_sw_state(eeepc->bluetooth_rfkill,
@@ -1096,7 +1151,7 @@ static int update_bl_status(struct backlight_device *bd)
        return set_brightness(bd, bd->props.brightness);
 }
 
-static struct backlight_ops eeepcbl_ops = {
+static const struct backlight_ops eeepcbl_ops = {
        .get_brightness = read_brightness,
        .update_status = update_bl_status,
 };
@@ -1113,18 +1168,21 @@ static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
 
 static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
 {
+       struct backlight_properties props;
        struct backlight_device *bd;
 
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.type = BACKLIGHT_PLATFORM;
+       props.max_brightness = 15;
        bd = backlight_device_register(EEEPC_LAPTOP_FILE,
-                                      &eeepc->platform_device->dev,
-                                      eeepc, &eeepcbl_ops);
+                                      &eeepc->platform_device->dev, eeepc,
+                                      &eeepcbl_ops, &props);
        if (IS_ERR(bd)) {
                pr_err("Could not register eeepc backlight device\n");
                eeepc->backlight_device = NULL;
                return PTR_ERR(bd);
        }
        eeepc->backlight_device = bd;
-       bd->props.max_brightness = 15;
        bd->props.brightness = read_brightness(bd);
        bd->props.power = FB_BLANK_UNBLANK;
        backlight_update_status(bd);
@@ -1173,9 +1231,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
        eeepc->inputdev = input;
        return 0;
 
- err_free_keymap:
+err_free_keymap:
        sparse_keymap_free(input);
- err_free_dev:
+err_free_dev:
        input_free_device(input);
        return error;
 }
@@ -1183,9 +1241,10 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
 static void eeepc_input_exit(struct eeepc_laptop *eeepc)
 {
        if (eeepc->inputdev) {
+               sparse_keymap_free(eeepc->inputdev);
                input_unregister_device(eeepc->inputdev);
-               kfree(eeepc->keymap);
        }
+       eeepc->inputdev = NULL;
 }
 
 /*
@@ -1288,7 +1347,7 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
 {
        int dummy;
 
-       /* Some BIOSes do not report cm although it is avaliable.
+       /* Some BIOSes do not report cm although it is available.
           Check if cm_getv[cm] works and, if yes, assume cm should be set. */
        if (!(eeepc->cm_supported & (1 << cm))
            && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
@@ -1306,16 +1365,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
        cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
 }
 
-static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
-                          struct acpi_device *device)
+static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
 {
        unsigned int init_flags;
        int result;
 
-       result = acpi_bus_get_status(device);
+       result = acpi_bus_get_status(eeepc->device);
        if (result)
                return result;
-       if (!device->status.present) {
+       if (!eeepc->device->status.present) {
                pr_err("Hotkey device not present, aborting\n");
                return -ENODEV;
        }
@@ -1364,12 +1422,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
        strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
        strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
        device->driver_data = eeepc;
+       eeepc->device = device;
 
        eeepc->hotplug_disabled = hotplug_disabled;
 
        eeepc_dmi_check(eeepc);
 
-       result = eeepc_acpi_init(eeepc, device);
+       result = eeepc_acpi_init(eeepc);
        if (result)
                goto fail_platform;
        eeepc_enable_camera(eeepc);