Merge tag 'drm-misc-next-2022-01-27' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-block.git] / drivers / platform / x86 / thinkpad_acpi.c
index ccbfda2b00953cfd101afddec8afbbb2cc62179b..99fbe90c926ecf00e670f4e2be46b37afd7a1f31 100644 (file)
@@ -334,12 +334,10 @@ static struct {
        u32 battery_force_primary: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;
        u32 hotkey_poll_active:1;
        u32 has_adaptive_kbd:1;
+       u32 kbd_lang:1;
 } tp_features;
 
 static struct {
@@ -882,14 +880,14 @@ static int dispatch_proc_show(struct seq_file *m, void *v)
 
 static int dispatch_proc_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, dispatch_proc_show, PDE_DATA(inode));
+       return single_open(file, dispatch_proc_show, pde_data(inode));
 }
 
 static ssize_t dispatch_proc_write(struct file *file,
                        const char __user *userbuf,
                        size_t count, loff_t *pos)
 {
-       struct ibm_struct *ibm = PDE_DATA(file_inode(file));
+       struct ibm_struct *ibm = pde_data(file_inode(file));
        char *kernbuf;
        int ret;
 
@@ -985,20 +983,6 @@ static void tpacpi_shutdown_handler(struct platform_device *pdev)
        }
 }
 
-static struct platform_driver tpacpi_pdriver = {
-       .driver = {
-               .name = TPACPI_DRVR_NAME,
-               .pm = &tpacpi_pm,
-       },
-       .shutdown = tpacpi_shutdown_handler,
-};
-
-static struct platform_driver tpacpi_hwmon_pdriver = {
-       .driver = {
-               .name = TPACPI_HWMON_DRVR_NAME,
-       },
-};
-
 /*************************************************************************
  * sysfs support helpers
  */
@@ -1481,53 +1465,6 @@ static ssize_t uwb_emulstate_store(struct device_driver *drv, const char *buf,
 static DRIVER_ATTR_RW(uwb_emulstate);
 #endif
 
-/* --------------------------------------------------------------------- */
-
-static struct driver_attribute *tpacpi_driver_attributes[] = {
-       &driver_attr_debug_level, &driver_attr_version,
-       &driver_attr_interface_version,
-};
-
-static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
-{
-       int i, res;
-
-       i = 0;
-       res = 0;
-       while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
-               res = driver_create_file(drv, tpacpi_driver_attributes[i]);
-               i++;
-       }
-
-#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
-       if (!res && dbg_wlswemul)
-               res = driver_create_file(drv, &driver_attr_wlsw_emulstate);
-       if (!res && dbg_bluetoothemul)
-               res = driver_create_file(drv, &driver_attr_bluetooth_emulstate);
-       if (!res && dbg_wwanemul)
-               res = driver_create_file(drv, &driver_attr_wwan_emulstate);
-       if (!res && dbg_uwbemul)
-               res = driver_create_file(drv, &driver_attr_uwb_emulstate);
-#endif
-
-       return res;
-}
-
-static void tpacpi_remove_driver_attributes(struct device_driver *drv)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
-               driver_remove_file(drv, tpacpi_driver_attributes[i]);
-
-#ifdef THINKPAD_ACPI_DEBUGFACILITIES
-       driver_remove_file(drv, &driver_attr_wlsw_emulstate);
-       driver_remove_file(drv, &driver_attr_bluetooth_emulstate);
-       driver_remove_file(drv, &driver_attr_wwan_emulstate);
-       driver_remove_file(drv, &driver_attr_uwb_emulstate);
-#endif
-}
-
 /*************************************************************************
  * Firmware Data
  */
@@ -3001,7 +2938,14 @@ static struct attribute *adaptive_kbd_attributes[] = {
        NULL
 };
 
+static umode_t hadaptive_kbd_attr_is_visible(struct kobject *kobj,
+                                            struct attribute *attr, int n)
+{
+       return tp_features.has_adaptive_kbd ? attr->mode : 0;
+}
+
 static const struct attribute_group adaptive_kbd_attr_group = {
+       .is_visible = hadaptive_kbd_attr_is_visible,
        .attrs = adaptive_kbd_attributes,
 };
 
@@ -3098,8 +3042,6 @@ static void hotkey_exit(void)
        hotkey_poll_stop_sync();
        mutex_unlock(&hotkey_mutex);
 #endif
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group);
-
        dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
                   "restoring original HKEY status and mask\n");
        /* yes, there is a bitwise or below, we want the
@@ -3438,7 +3380,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                str_supported(tp_features.hotkey));
 
        if (!tp_features.hotkey)
-               return 1;
+               return -ENODEV;
 
        quirks = tpacpi_check_quirks(tpacpi_hotkey_qtable,
                                     ARRAY_SIZE(tpacpi_hotkey_qtable));
@@ -3494,14 +3436,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                         */
                        if (acpi_evalf(hkey_handle, &hotkey_adaptive_all_mask,
                                       "MHKA", "dd", 2)) {
-                               if (hotkey_adaptive_all_mask != 0) {
+                               if (hotkey_adaptive_all_mask != 0)
                                        tp_features.has_adaptive_kbd = true;
-                                       res = sysfs_create_group(
-                                               &tpacpi_pdev->dev.kobj,
-                                               &adaptive_kbd_attr_group);
-                                       if (res)
-                                               goto err_exit;
-                               }
                        } else {
                                tp_features.has_adaptive_kbd = false;
                                hotkey_adaptive_all_mask = 0x0U;
@@ -3531,7 +3467,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                 * the first hotkey_mask_get to return hotkey_orig_mask */
                res = hotkey_mask_get();
                if (res)
-                       goto err_exit;
+                       return res;
 
                hotkey_orig_mask = hotkey_acpi_mask;
        } else {
@@ -3555,9 +3491,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        }
 
        tabletsw_state = hotkey_init_tablet_mode();
-       res = sysfs_create_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group);
-       if (res)
-               goto err_exit;
 
        /* Set up key map */
        keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable,
@@ -3570,8 +3503,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                        TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL);
        if (!hotkey_keycode_map) {
                pr_err("failed to allocate memory for key map\n");
-               res = -ENOMEM;
-               goto err_exit;
+               return -ENOMEM;
        }
 
        input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN);
@@ -3652,12 +3584,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        hotkey_poll_setup_safe(true);
 
        return 0;
-
-err_exit:
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group);
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj, &adaptive_kbd_attr_group);
-
-       return (res < 0) ? res : 1;
 }
 
 /* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
@@ -4402,7 +4328,14 @@ static struct attribute *bluetooth_attributes[] = {
        NULL
 };
 
+static umode_t bluetooth_attr_is_visible(struct kobject *kobj,
+                                        struct attribute *attr, int n)
+{
+       return tp_features.bluetooth ? attr->mode : 0;
+}
+
 static const struct attribute_group bluetooth_attr_group = {
+       .is_visible = bluetooth_attr_is_visible,
        .attrs = bluetooth_attributes,
 };
 
@@ -4424,11 +4357,7 @@ static void bluetooth_shutdown(void)
 
 static void bluetooth_exit(void)
 {
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj,
-                       &bluetooth_attr_group);
-
        tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID);
-
        bluetooth_shutdown();
 }
 
@@ -4535,24 +4464,14 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
        }
 
        if (!tp_features.bluetooth)
-               return 1;
+               return -ENODEV;
 
        res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
                                &bluetooth_tprfk_ops,
                                RFKILL_TYPE_BLUETOOTH,
                                TPACPI_RFK_BLUETOOTH_SW_NAME,
                                true);
-       if (res)
-               return res;
-
-       res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
-                               &bluetooth_attr_group);
-       if (res) {
-               tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID);
-               return res;
-       }
-
-       return 0;
+       return res;
 }
 
 /* procfs -------------------------------------------------------------- */
@@ -4659,7 +4578,14 @@ static struct attribute *wan_attributes[] = {
        NULL
 };
 
+static umode_t wan_attr_is_visible(struct kobject *kobj, struct attribute *attr,
+                                  int n)
+{
+       return tp_features.wan ? attr->mode : 0;
+}
+
 static const struct attribute_group wan_attr_group = {
+       .is_visible = wan_attr_is_visible,
        .attrs = wan_attributes,
 };
 
@@ -4681,11 +4607,7 @@ static void wan_shutdown(void)
 
 static void wan_exit(void)
 {
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj,
-               &wan_attr_group);
-
        tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID);
-
        wan_shutdown();
 }
 
@@ -4722,25 +4644,14 @@ static int __init wan_init(struct ibm_init_struct *iibm)
        }
 
        if (!tp_features.wan)
-               return 1;
+               return -ENODEV;
 
        res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
                                &wan_tprfk_ops,
                                RFKILL_TYPE_WWAN,
                                TPACPI_RFK_WWAN_SW_NAME,
                                true);
-       if (res)
-               return res;
-
-       res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
-                               &wan_attr_group);
-
-       if (res) {
-               tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID);
-               return res;
-       }
-
-       return 0;
+       return res;
 }
 
 /* procfs -------------------------------------------------------------- */
@@ -4862,7 +4773,7 @@ static int __init uwb_init(struct ibm_init_struct *iibm)
        }
 
        if (!tp_features.uwb)
-               return 1;
+               return -ENODEV;
 
        res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID,
                                &uwb_tprfk_ops,
@@ -4955,7 +4866,7 @@ static int __init video_init(struct ibm_init_struct *iibm)
                str_supported(video_supported != TPACPI_VIDEO_NONE),
                video_supported);
 
-       return (video_supported != TPACPI_VIDEO_NONE) ? 0 : 1;
+       return (video_supported != TPACPI_VIDEO_NONE) ? 0 : -ENODEV;
 }
 
 static void video_exit(void)
@@ -5363,7 +5274,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
        if (!kbdlight_is_supported()) {
                tp_features.kbdlight = 0;
                vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n");
-               return 1;
+               return -ENODEV;
        }
 
        kbdlight_brightness = kbdlight_sysfs_get(NULL);
@@ -5553,7 +5464,7 @@ static int __init light_init(struct ibm_init_struct *iibm)
                str_supported(tp_features.light_status));
 
        if (!tp_features.light)
-               return 1;
+               return -ENODEV;
 
        rc = led_classdev_register(&tpacpi_pdev->dev,
                                   &tpacpi_led_thinklight.led_classdev);
@@ -5641,30 +5552,35 @@ static ssize_t cmos_command_store(struct device *dev,
 
 static DEVICE_ATTR_WO(cmos_command);
 
+static struct attribute *cmos_attributes[] = {
+       &dev_attr_cmos_command.attr,
+       NULL
+};
+
+static umode_t cmos_attr_is_visible(struct kobject *kobj,
+                                   struct attribute *attr, int n)
+{
+       return cmos_handle ? attr->mode : 0;
+}
+
+static const struct attribute_group cmos_attr_group = {
+       .is_visible = cmos_attr_is_visible,
+       .attrs = cmos_attributes,
+};
+
 /* --------------------------------------------------------------------- */
 
 static int __init cmos_init(struct ibm_init_struct *iibm)
 {
-       int res;
-
        vdbg_printk(TPACPI_DBG_INIT,
-               "initializing cmos commands subdriver\n");
+                   "initializing cmos commands subdriver\n");
 
        TPACPI_ACPIHANDLE_INIT(cmos);
 
        vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
-               str_supported(cmos_handle != NULL));
-
-       res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
-       if (res)
-               return res;
-
-       return (cmos_handle) ? 0 : 1;
-}
+                   str_supported(cmos_handle != NULL));
 
-static void cmos_exit(void)
-{
-       device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+       return cmos_handle ? 0 : -ENODEV;
 }
 
 static int cmos_read(struct seq_file *m)
@@ -5705,7 +5621,6 @@ static struct ibm_struct cmos_driver_data = {
        .name = "cmos",
        .read = cmos_read,
        .write = cmos_write,
-       .exit = cmos_exit,
 };
 
 /*************************************************************************
@@ -5910,6 +5825,7 @@ static int __init tpacpi_init_led(unsigned int led)
                tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
 
        tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
+       tpacpi_leds[led].led_classdev.flags = LED_RETAIN_AT_SHUTDOWN;
        tpacpi_leds[led].led = led;
 
        return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
@@ -6010,7 +5926,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
                str_supported(led_supported), led_supported);
 
        if (led_supported == TPACPI_LED_NONE)
-               return 1;
+               return -ENODEV;
 
        tpacpi_leds = kcalloc(TPACPI_LED_NUMLEDS, sizeof(*tpacpi_leds),
                              GFP_KERNEL);
@@ -6139,7 +6055,7 @@ static int __init beep_init(struct ibm_init_struct *iibm)
 
        tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1);
 
-       return (beep_handle) ? 0 : 1;
+       return (beep_handle) ? 0 : -ENODEV;
 }
 
 static int beep_read(struct seq_file *m)
@@ -6216,7 +6132,6 @@ struct ibm_thermal_sensors_struct {
 };
 
 static enum thermal_access_mode thermal_read_mode;
-static const struct attribute_group *thermal_attr_group;
 static bool thermal_use_labels;
 
 /* idx is zero-based */
@@ -6370,14 +6285,6 @@ static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
        &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
 
 static struct attribute *thermal_temp_input_attr[] = {
-       THERMAL_ATTRS(8),
-       THERMAL_ATTRS(9),
-       THERMAL_ATTRS(10),
-       THERMAL_ATTRS(11),
-       THERMAL_ATTRS(12),
-       THERMAL_ATTRS(13),
-       THERMAL_ATTRS(14),
-       THERMAL_ATTRS(15),
        THERMAL_ATTRS(0),
        THERMAL_ATTRS(1),
        THERMAL_ATTRS(2),
@@ -6386,15 +6293,37 @@ static struct attribute *thermal_temp_input_attr[] = {
        THERMAL_ATTRS(5),
        THERMAL_ATTRS(6),
        THERMAL_ATTRS(7),
+       THERMAL_ATTRS(8),
+       THERMAL_ATTRS(9),
+       THERMAL_ATTRS(10),
+       THERMAL_ATTRS(11),
+       THERMAL_ATTRS(12),
+       THERMAL_ATTRS(13),
+       THERMAL_ATTRS(14),
+       THERMAL_ATTRS(15),
        NULL
 };
 
-static const struct attribute_group thermal_temp_input16_group = {
-       .attrs = thermal_temp_input_attr
-};
+static umode_t thermal_attr_is_visible(struct kobject *kobj,
+                                      struct attribute *attr, int n)
+{
+       if (thermal_read_mode == TPACPI_THERMAL_NONE)
+               return 0;
+
+       if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) ||
+           attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) ||
+           attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) ||
+           attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) {
+               if (thermal_read_mode != TPACPI_THERMAL_TPEC_16)
+                       return 0;
+       }
+
+       return attr->mode;
+}
 
-static const struct attribute_group thermal_temp_input8_group = {
-       .attrs = &thermal_temp_input_attr[8]
+static const struct attribute_group thermal_attr_group = {
+       .is_visible = thermal_attr_is_visible,
+       .attrs = thermal_temp_input_attr,
 };
 
 #undef THERMAL_SENSOR_ATTR_TEMP
@@ -6418,7 +6347,14 @@ static struct attribute *temp_label_attributes[] = {
        NULL
 };
 
+static umode_t temp_label_attr_is_visible(struct kobject *kobj,
+                                         struct attribute *attr, int n)
+{
+       return thermal_use_labels ? attr->mode : 0;
+}
+
 static const struct attribute_group temp_label_attr_group = {
+       .is_visible = temp_label_attr_is_visible,
        .attrs = temp_label_attributes,
 };
 
@@ -6429,7 +6365,6 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
        u8 t, ta1, ta2, ver = 0;
        int i;
        int acpi_tmp7;
-       int res;
 
        vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
 
@@ -6504,42 +6439,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
                str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
                thermal_read_mode);
 
-       switch (thermal_read_mode) {
-       case TPACPI_THERMAL_TPEC_16:
-               thermal_attr_group = &thermal_temp_input16_group;
-               break;
-       case TPACPI_THERMAL_TPEC_8:
-       case TPACPI_THERMAL_ACPI_TMP07:
-       case TPACPI_THERMAL_ACPI_UPDT:
-               thermal_attr_group = &thermal_temp_input8_group;
-               break;
-       case TPACPI_THERMAL_NONE:
-       default:
-               return 1;
-       }
-
-       res = sysfs_create_group(&tpacpi_hwmon->kobj, thermal_attr_group);
-       if (res)
-               return res;
-
-       if (thermal_use_labels) {
-               res = sysfs_create_group(&tpacpi_hwmon->kobj, &temp_label_attr_group);
-               if (res) {
-                       sysfs_remove_group(&tpacpi_hwmon->kobj, thermal_attr_group);
-                       return res;
-               }
-       }
-
-       return 0;
-}
-
-static void thermal_exit(void)
-{
-       if (thermal_attr_group)
-               sysfs_remove_group(&tpacpi_hwmon->kobj, thermal_attr_group);
-
-       if (thermal_use_labels)
-               sysfs_remove_group(&tpacpi_hwmon->kobj, &temp_label_attr_group);
+       return thermal_read_mode != TPACPI_THERMAL_NONE ? 0 : -ENODEV;
 }
 
 static int thermal_read(struct seq_file *m)
@@ -6566,7 +6466,6 @@ static int thermal_read(struct seq_file *m)
 static struct ibm_struct thermal_driver_data = {
        .name = "thermal",
        .read = thermal_read,
-       .exit = thermal_exit,
 };
 
 /*************************************************************************
@@ -6951,25 +6850,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
 
        /* if it is unknown, we don't handle it: it wouldn't be safe */
        if (tp_features.bright_unkfw)
-               return 1;
+               return -ENODEV;
 
        if (!brightness_enable) {
                dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
                           "brightness support disabled by module parameter\n");
-               return 1;
+               return -ENODEV;
        }
 
        if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
                if (brightness_enable > 1) {
                        pr_info("Standard ACPI backlight interface available, not loading native one\n");
-                       return 1;
+                       return -ENODEV;
                } else if (brightness_enable == 1) {
                        pr_warn("Cannot enable backlight brightness support, ACPI is already handling it.  Refer to the acpi_backlight kernel parameter.\n");
-                       return 1;
+                       return -ENODEV;
                }
        } else if (!tp_features.bright_acpimode) {
                pr_notice("ACPI backlight interface not available\n");
-               return 1;
+               return -ENODEV;
        }
 
        pr_notice("ACPI native brightness control enabled\n");
@@ -7002,7 +6901,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
                return -EINVAL;
 
        if (tpacpi_brightness_get_raw(&b) < 0)
-               return 1;
+               return -ENODEV;
 
        memset(&props, 0, sizeof(struct backlight_properties));
        props.type = BACKLIGHT_PLATFORM;
@@ -7592,7 +7491,7 @@ static int __init volume_create_alsa_mixer(void)
                          sizeof(struct tpacpi_alsa_data), &card);
        if (rc < 0 || !card) {
                pr_err("Failed to create ALSA card structures: %d\n", rc);
-               return 1;
+               return -ENODEV;
        }
 
        BUG_ON(!card->private_data);
@@ -7651,7 +7550,7 @@ static int __init volume_create_alsa_mixer(void)
 
 err_exit:
        snd_card_free(card);
-       return 1;
+       return -ENODEV;
 }
 
 #define TPACPI_VOL_Q_MUTEONLY  0x0001  /* Mute-only control available */
@@ -7700,7 +7599,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
        if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) {
                pr_err("UCMS step volume mode not implemented, please contact %s\n",
                       TPACPI_MAIL);
-               return 1;
+               return -ENODEV;
        }
 
        if (volume_capabilities >= TPACPI_VOL_CAP_MAX)
@@ -7713,7 +7612,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
        if (!alsa_enable) {
                vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
                            "ALSA mixer disabled by parameter, not loading volume subdriver...\n");
-               return 1;
+               return -ENODEV;
        }
 
        quirks = tpacpi_check_quirks(volume_quirk_table,
@@ -7726,7 +7625,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
                else if (quirks & TPACPI_VOL_Q_LEVEL)
                        tp_features.mixer_no_level_control = 0;
                else
-                       return 1; /* no mixer */
+                       return -ENODEV; /* no mixer */
                break;
        case TPACPI_VOL_CAP_VOLMUTE:
                tp_features.mixer_no_level_control = 0;
@@ -7735,7 +7634,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
                tp_features.mixer_no_level_control = 1;
                break;
        default:
-               return 1;
+               return -ENODEV;
        }
 
        if (volume_capabilities != TPACPI_VOL_CAP_AUTO)
@@ -7907,7 +7806,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
 {
        pr_info("volume: disabled as there is no ALSA support in this kernel\n");
 
-       return 1;
+       return -ENODEV;
 }
 
 static struct ibm_struct volume_driver_data = {
@@ -8741,17 +8640,45 @@ static ssize_t fan_watchdog_store(struct device_driver *drv, const char *buf,
 static DRIVER_ATTR_RW(fan_watchdog);
 
 /* --------------------------------------------------------------------- */
+
 static struct attribute *fan_attributes[] = {
-       &dev_attr_pwm1_enable.attr, &dev_attr_pwm1.attr,
+       &dev_attr_pwm1_enable.attr,
+       &dev_attr_pwm1.attr,
        &dev_attr_fan1_input.attr,
-       NULL, /* for fan2_input */
+       &dev_attr_fan2_input.attr,
        NULL
 };
 
+static umode_t fan_attr_is_visible(struct kobject *kobj, struct attribute *attr,
+                                  int n)
+{
+       if (fan_status_access_mode == TPACPI_FAN_NONE &&
+           fan_control_access_mode == TPACPI_FAN_WR_NONE)
+               return 0;
+
+       if (attr == &dev_attr_fan2_input.attr) {
+               if (!tp_features.second_fan)
+                       return 0;
+       }
+
+       return attr->mode;
+}
+
 static const struct attribute_group fan_attr_group = {
+       .is_visible = fan_attr_is_visible,
        .attrs = fan_attributes,
 };
 
+static struct attribute *fan_driver_attributes[] = {
+       &driver_attr_fan_watchdog.attr,
+       NULL
+};
+
+static const struct attribute_group fan_driver_attr_group = {
+       .is_visible = fan_attr_is_visible,
+       .attrs = fan_driver_attributes,
+};
+
 #define TPACPI_FAN_Q1  0x0001          /* Unitialized HFSP */
 #define TPACPI_FAN_2FAN        0x0002          /* EC 0x31 bit 0 selects fan2 */
 #define TPACPI_FAN_2CTL        0x0004          /* selects fan2 control */
@@ -8779,7 +8706,6 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
 
 static int __init fan_init(struct ibm_init_struct *iibm)
 {
-       int rc;
        unsigned long quirks;
 
        vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
@@ -8826,7 +8752,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
                        }
                } else {
                        pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
-                       return 1;
+                       return -ENODEV;
                }
        }
 
@@ -8875,28 +8801,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
        if (fan_status_access_mode != TPACPI_FAN_NONE)
                fan_get_status_safe(NULL);
 
-       if (fan_status_access_mode != TPACPI_FAN_NONE ||
-           fan_control_access_mode != TPACPI_FAN_WR_NONE) {
-               if (tp_features.second_fan) {
-                       /* attach second fan tachometer */
-                       fan_attributes[ARRAY_SIZE(fan_attributes)-2] =
-                                       &dev_attr_fan2_input.attr;
-               }
-               rc = sysfs_create_group(&tpacpi_hwmon->kobj,
-                                        &fan_attr_group);
-               if (rc < 0)
-                       return rc;
+       if (fan_status_access_mode == TPACPI_FAN_NONE &&
+           fan_control_access_mode == TPACPI_FAN_WR_NONE)
+               return -ENODEV;
 
-               rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
-                                       &driver_attr_fan_watchdog);
-               if (rc < 0) {
-                       sysfs_remove_group(&tpacpi_hwmon->kobj,
-                                       &fan_attr_group);
-                       return rc;
-               }
-               return 0;
-       } else
-               return 1;
+       return 0;
 }
 
 static void fan_exit(void)
@@ -8904,11 +8813,6 @@ static void fan_exit(void)
        vdbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_FAN,
                    "cancelling any pending fan watchdog tasks\n");
 
-       /* FIXME: can we really do this unconditionally? */
-       sysfs_remove_group(&tpacpi_hwmon->kobj, &fan_attr_group);
-       driver_remove_file(&tpacpi_hwmon_pdriver.driver,
-                          &driver_attr_fan_watchdog);
-
        cancel_delayed_work(&fan_watchdog_task);
        flush_workqueue(tpacpi_wq);
 }
@@ -9326,6 +9230,10 @@ static struct ibm_struct mute_led_driver_data = {
 #define SET_START      "BCCS"
 #define GET_STOP       "BCSG"
 #define SET_STOP       "BCSS"
+#define GET_DISCHARGE  "BDSG"
+#define SET_DISCHARGE  "BDSS"
+#define GET_INHIBIT    "BICG"
+#define SET_INHIBIT    "BICS"
 
 enum {
        BAT_ANY = 0,
@@ -9342,6 +9250,8 @@ enum {
        /* This is used in the get/set helpers */
        THRESHOLD_START,
        THRESHOLD_STOP,
+       FORCE_DISCHARGE,
+       INHIBIT_CHARGE,
 };
 
 struct tpacpi_battery_data {
@@ -9349,6 +9259,7 @@ struct tpacpi_battery_data {
        int start_support;
        int charge_stop;
        int stop_support;
+       unsigned int charge_behaviours;
 };
 
 struct tpacpi_battery_driver_data {
@@ -9406,6 +9317,18 @@ static int tpacpi_battery_get(int what, int battery, int *ret)
                if (*ret == 0)
                        *ret = 100;
                return 0;
+       case FORCE_DISCHARGE:
+               if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, ret, battery))
+                       return -ENODEV;
+               /* The force discharge status is in bit 0 */
+               *ret = *ret & 0x01;
+               return 0;
+       case INHIBIT_CHARGE:
+               if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_INHIBIT, ret, battery))
+                       return -ENODEV;
+               /* The inhibit charge status is in bit 0 */
+               *ret = *ret & 0x01;
+               return 0;
        default:
                pr_crit("wrong parameter: %d", what);
                return -EINVAL;
@@ -9434,12 +9357,65 @@ static int tpacpi_battery_set(int what, int battery, int value)
                        return -ENODEV;
                }
                return 0;
+       case FORCE_DISCHARGE:
+               /* Force discharge is in bit 0,
+                * break on AC attach is in bit 1 (won't work on some ThinkPads),
+                * battery ID is in bits 8-9, 2 bits.
+                */
+               if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_DISCHARGE, &ret, param))) {
+                       pr_err("failed to set force discharge on %d", battery);
+                       return -ENODEV;
+               }
+               return 0;
+       case INHIBIT_CHARGE:
+               /* When setting inhibit charge, we set a default value of
+                * always breaking on AC detach and the effective time is set to
+                * be permanent.
+                * The battery ID is in bits 4-5, 2 bits,
+                * the effective time is in bits 8-23, 2 bytes.
+                * A time of FFFF indicates forever.
+                */
+               param = value;
+               param |= battery << 4;
+               param |= 0xFFFF << 8;
+               if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_INHIBIT, &ret, param))) {
+                       pr_err("failed to set inhibit charge on %d", battery);
+                       return -ENODEV;
+               }
+               return 0;
        default:
                pr_crit("wrong parameter: %d", what);
                return -EINVAL;
        }
 }
 
+static int tpacpi_battery_set_validate(int what, int battery, int value)
+{
+       int ret, v;
+
+       ret = tpacpi_battery_set(what, battery, value);
+       if (ret < 0)
+               return ret;
+
+       ret = tpacpi_battery_get(what, battery, &v);
+       if (ret < 0)
+               return ret;
+
+       if (v == value)
+               return 0;
+
+       msleep(500);
+
+       ret = tpacpi_battery_get(what, battery, &v);
+       if (ret < 0)
+               return ret;
+
+       if (v == value)
+               return 0;
+
+       return -EIO;
+}
+
 static int tpacpi_battery_probe(int battery)
 {
        int ret = 0;
@@ -9452,6 +9428,10 @@ static int tpacpi_battery_probe(int battery)
         * 2) Check for support
         * 3) Get the current stop threshold
         * 4) Check for support
+        * 5) Get the current force discharge status
+        * 6) Check for support
+        * 7) Get the current inhibit charge status
+        * 8) Check for support
         */
        if (acpi_has_method(hkey_handle, GET_START)) {
                if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) {
@@ -9488,10 +9468,35 @@ static int tpacpi_battery_probe(int battery)
                        return -ENODEV;
                }
        }
-       pr_info("battery %d registered (start %d, stop %d)",
-                       battery,
-                       battery_info.batteries[battery].charge_start,
-                       battery_info.batteries[battery].charge_stop);
+       if (acpi_has_method(hkey_handle, GET_DISCHARGE)) {
+               if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, &ret, battery))) {
+                       pr_err("Error probing battery discharge; %d\n", battery);
+                       return -ENODEV;
+               }
+               /* Support is marked in bit 8 */
+               if (ret & BIT(8))
+                       battery_info.batteries[battery].charge_behaviours |=
+                               BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE);
+       }
+       if (acpi_has_method(hkey_handle, GET_INHIBIT)) {
+               if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_INHIBIT, &ret, battery))) {
+                       pr_err("Error probing battery inhibit charge; %d\n", battery);
+                       return -ENODEV;
+               }
+               /* Support is marked in bit 5 */
+               if (ret & BIT(5))
+                       battery_info.batteries[battery].charge_behaviours |=
+                               BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE);
+       }
+
+       battery_info.batteries[battery].charge_behaviours |=
+               BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO);
+
+       pr_info("battery %d registered (start %d, stop %d, behaviours: 0x%x)\n",
+               battery,
+               battery_info.batteries[battery].charge_start,
+               battery_info.batteries[battery].charge_stop,
+               battery_info.batteries[battery].charge_behaviours);
 
        return 0;
 }
@@ -9626,12 +9631,46 @@ static ssize_t charge_control_end_threshold_show(struct device *device,
        return tpacpi_battery_show(THRESHOLD_STOP, device, buf);
 }
 
-static ssize_t charge_control_start_threshold_store(struct device *dev,
-                               struct device_attribute *attr,
-                               const char *buf, size_t count)
+static ssize_t charge_behaviour_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
 {
-       return tpacpi_battery_store(THRESHOLD_START, dev, buf, count);
-}
+       enum power_supply_charge_behaviour active = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
+       struct power_supply *supply = to_power_supply(dev);
+       unsigned int available;
+       int ret, battery;
+
+       battery = tpacpi_battery_get_id(supply->desc->name);
+       available = battery_info.batteries[battery].charge_behaviours;
+
+       if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) {
+               if (tpacpi_battery_get(FORCE_DISCHARGE, battery, &ret))
+                       return -ENODEV;
+               if (ret) {
+                       active = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
+                       goto out;
+               }
+       }
+
+       if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE)) {
+               if (tpacpi_battery_get(INHIBIT_CHARGE, battery, &ret))
+                       return -ENODEV;
+               if (ret) {
+                       active = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
+                       goto out;
+               }
+       }
+
+out:
+       return power_supply_charge_behaviour_show(dev, available, active, buf);
+}
+
+static ssize_t charge_control_start_threshold_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       return tpacpi_battery_store(THRESHOLD_START, dev, buf, count);
+}
 
 static ssize_t charge_control_end_threshold_store(struct device *dev,
                                struct device_attribute *attr,
@@ -9640,8 +9679,55 @@ static ssize_t charge_control_end_threshold_store(struct device *dev,
        return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count);
 }
 
+static ssize_t charge_behaviour_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct power_supply *supply = to_power_supply(dev);
+       int selected, battery, ret = 0;
+       unsigned int available;
+
+       battery = tpacpi_battery_get_id(supply->desc->name);
+       available = battery_info.batteries[battery].charge_behaviours;
+       selected = power_supply_charge_behaviour_parse(available, buf);
+
+       if (selected < 0)
+               return selected;
+
+       switch (selected) {
+       case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
+               if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
+                       ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0);
+               if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
+                       ret = min(ret, tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 0));
+               if (ret < 0)
+                       return ret;
+               break;
+       case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
+               if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
+                       ret = tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 0);
+               ret = min(ret, tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 1));
+               if (ret < 0)
+                       return ret;
+               break;
+       case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+               if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
+                       ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0);
+               ret = min(ret, tpacpi_battery_set_validate(INHIBIT_CHARGE, battery, 1));
+               if (ret < 0)
+                       return ret;
+               break;
+       default:
+               dev_err(dev, "Unexpected charge behaviour: %d\n", selected);
+               return -EINVAL;
+       }
+
+       return count;
+}
+
 static DEVICE_ATTR_RW(charge_control_start_threshold);
 static DEVICE_ATTR_RW(charge_control_end_threshold);
+static DEVICE_ATTR_RW(charge_behaviour);
 static struct device_attribute dev_attr_charge_start_threshold = __ATTR(
        charge_start_threshold,
        0644,
@@ -9660,6 +9746,7 @@ static struct attribute *tpacpi_battery_attrs[] = {
        &dev_attr_charge_control_end_threshold.attr,
        &dev_attr_charge_start_threshold.attr,
        &dev_attr_charge_stop_threshold.attr,
+       &dev_attr_charge_behaviour.attr,
        NULL,
 };
 
@@ -9884,33 +9971,6 @@ static int dytc_command(int command, int *output)
        return 0;
 }
 
-static int dytc_get_version(void)
-{
-       int err, output;
-
-       /* Check if we've been called before - and just return cached value */
-       if (dytc_version)
-               return dytc_version;
-
-       /* Otherwise query DYTC and extract version information */
-       err = dytc_command(DYTC_CMD_QUERY, &output);
-       /*
-        * If support isn't available (ENODEV) then don't return an error
-        * and don't create the sysfs group
-        */
-       if (err == -ENODEV)
-               return 0;
-       /* For all other errors we can flag the failure */
-       if (err)
-               return err;
-
-       /* Check DYTC is enabled and supports mode setting */
-       if (output & BIT(DYTC_QUERY_ENABLE_BIT))
-               dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
-
-       return 0;
-}
-
 static int lapsensor_get(bool *present, bool *state)
 {
        int output, err;
@@ -9993,59 +10053,55 @@ static ssize_t palmsensor_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(palmsensor);
 
+static struct attribute *proxsensor_attributes[] = {
+       &dev_attr_dytc_lapmode.attr,
+       &dev_attr_palmsensor.attr,
+       NULL
+};
+
+static umode_t proxsensor_attr_is_visible(struct kobject *kobj,
+                                         struct attribute *attr, int n)
+{
+       if (attr == &dev_attr_dytc_lapmode.attr) {
+               /*
+                * Platforms before DYTC version 5 claim to have a lap sensor,
+                * but it doesn't work, so we ignore them.
+                */
+               if (!has_lapsensor || dytc_version < 5)
+                       return 0;
+       } else if (attr == &dev_attr_palmsensor.attr) {
+               if (!has_palmsensor)
+                       return 0;
+       }
+
+       return attr->mode;
+}
+
+static const struct attribute_group proxsensor_attr_group = {
+       .is_visible = proxsensor_attr_is_visible,
+       .attrs = proxsensor_attributes,
+};
+
 static int tpacpi_proxsensor_init(struct ibm_init_struct *iibm)
 {
-       int palm_err, lap_err, err;
+       int palm_err, lap_err;
 
        palm_err = palmsensor_get(&has_palmsensor, &palm_state);
        lap_err = lapsensor_get(&has_lapsensor, &lap_state);
-       /*
-        * If support isn't available (ENODEV) for both devices then quit, but
-        * don't return an error.
-        */
+       /* If support isn't available for both devices return -ENODEV */
        if ((palm_err == -ENODEV) && (lap_err == -ENODEV))
-               return 0;
+               return -ENODEV;
        /* Otherwise, if there was an error return it */
        if (palm_err && (palm_err != -ENODEV))
                return palm_err;
        if (lap_err && (lap_err != -ENODEV))
                return lap_err;
 
-       if (has_palmsensor) {
-               err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr);
-               if (err)
-                       return err;
-       }
-
-       /* Check if we know the DYTC version, if we don't then get it */
-       if (!dytc_version) {
-               err = dytc_get_version();
-               if (err)
-                       return err;
-       }
-       /*
-        * Platforms before DYTC version 5 claim to have a lap sensor, but it doesn't work, so we
-        * ignore them
-        */
-       if (has_lapsensor && (dytc_version >= 5)) {
-               err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr);
-               if (err)
-                       return err;
-       }
        return 0;
 }
 
-static void proxsensor_exit(void)
-{
-       if (has_lapsensor)
-               sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr);
-       if (has_palmsensor)
-               sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr);
-}
-
 static struct ibm_struct proxsensor_driver_data = {
        .name = "proximity-sensor",
-       .exit = proxsensor_exit,
 };
 
 /*************************************************************************
@@ -10084,7 +10140,6 @@ static struct ibm_struct proxsensor_driver_data = {
 
 #define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1)
 
-static bool dytc_profile_available;
 static enum platform_profile_option dytc_current_profile;
 static atomic_t dytc_ignore_event = ATOMIC_INIT(0);
 static DEFINE_MUTEX(dytc_mutex);
@@ -10188,9 +10243,6 @@ static int dytc_profile_set(struct platform_profile_handler *pprof,
        int output;
        int err;
 
-       if (!dytc_profile_available)
-               return -ENODEV;
-
        err = mutex_lock_interruptible(&dytc_mutex);
        if (err)
                return err;
@@ -10261,60 +10313,47 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
        set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices);
        set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices);
 
-       dytc_profile_available = false;
        err = dytc_command(DYTC_CMD_QUERY, &output);
-       /*
-        * If support isn't available (ENODEV) then don't return an error
-        * and don't create the sysfs group
-        */
-       if (err == -ENODEV)
-               return 0;
-       /* For all other errors we can flag the failure */
        if (err)
                return err;
 
-       /* Check if we know the DYTC version, if we don't then get it */
-       if (!dytc_version) {
-               err = dytc_get_version();
-               if (err)
-                       return err;
-       }
+       if (output & BIT(DYTC_QUERY_ENABLE_BIT))
+               dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
+
        /* Check DYTC is enabled and supports mode setting */
-       if (dytc_version >= 5) {
-               dbg_printk(TPACPI_DBG_INIT,
-                               "DYTC version %d: thermal mode available\n", dytc_version);
-               /*
-                * Check if MMC_GET functionality available
-                * Version > 6 and return success from MMC_GET command
-                */
-               dytc_mmc_get_available = false;
-               if (dytc_version >= 6) {
-                       err = dytc_command(DYTC_CMD_MMC_GET, &output);
-                       if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS))
-                               dytc_mmc_get_available = true;
-               }
-               /* Create platform_profile structure and register */
-               err = platform_profile_register(&dytc_profile);
-               /*
-                * If for some reason platform_profiles aren't enabled
-                * don't quit terminally.
-                */
-               if (err)
-                       return 0;
+       if (dytc_version < 5)
+               return -ENODEV;
 
-               dytc_profile_available = true;
-               /* Ensure initial values are correct */
-               dytc_profile_refresh();
+       dbg_printk(TPACPI_DBG_INIT,
+                       "DYTC version %d: thermal mode available\n", dytc_version);
+       /*
+        * Check if MMC_GET functionality available
+        * Version > 6 and return success from MMC_GET command
+        */
+       dytc_mmc_get_available = false;
+       if (dytc_version >= 6) {
+               err = dytc_command(DYTC_CMD_MMC_GET, &output);
+               if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS))
+                       dytc_mmc_get_available = true;
        }
+       /* Create platform_profile structure and register */
+       err = platform_profile_register(&dytc_profile);
+       /*
+        * If for some reason platform_profiles aren't enabled
+        * don't quit terminally.
+        */
+       if (err)
+               return -ENODEV;
+
+       /* Ensure initial values are correct */
+       dytc_profile_refresh();
+
        return 0;
 }
 
 static void dytc_profile_exit(void)
 {
-       if (dytc_profile_available) {
-               dytc_profile_available = false;
-               platform_profile_remove();
-       }
+       platform_profile_remove();
 }
 
 static struct ibm_struct  dytc_profile_driver_data = {
@@ -10462,7 +10501,14 @@ static struct attribute *kbdlang_attributes[] = {
        NULL
 };
 
+static umode_t kbdlang_attr_is_visible(struct kobject *kobj,
+                                      struct attribute *attr, int n)
+{
+       return tp_features.kbd_lang ? attr->mode : 0;
+}
+
 static const struct attribute_group kbdlang_attr_group = {
+       .is_visible = kbdlang_attr_is_visible,
        .attrs = kbdlang_attributes,
 };
 
@@ -10471,28 +10517,12 @@ static int tpacpi_kbdlang_init(struct ibm_init_struct *iibm)
        int err, output;
 
        err = get_keyboard_lang(&output);
-       /*
-        * If support isn't available (ENODEV) then don't return an error
-        * just don't create the sysfs group.
-        */
-       if (err == -ENODEV)
-               return 0;
-
-       if (err)
-               return err;
-
-       /* Platform supports this feature - create the sysfs file */
-       return sysfs_create_group(&tpacpi_pdev->dev.kobj, &kbdlang_attr_group);
-}
-
-static void kbdlang_exit(void)
-{
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj, &kbdlang_attr_group);
+       tp_features.kbd_lang = !err;
+       return err;
 }
 
 static struct ibm_struct kbdlang_driver_data = {
        .name = "kbdlang",
-       .exit = kbdlang_exit,
 };
 
 /*************************************************************************
@@ -10563,41 +10593,134 @@ static ssize_t wwan_antenna_type_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(wwan_antenna_type);
 
-static int tpacpi_dprc_init(struct ibm_init_struct *iibm)
+static struct attribute *dprc_attributes[] = {
+       &dev_attr_wwan_antenna_type.attr,
+       NULL
+};
+
+static umode_t dprc_attr_is_visible(struct kobject *kobj,
+                                   struct attribute *attr, int n)
 {
-       int wwanantenna_err, err;
+       return has_antennatype ? attr->mode : 0;
+}
 
-       wwanantenna_err = get_wwan_antenna(&wwan_antennatype);
-       /*
-        * If support isn't available (ENODEV) then quit, but don't
-        * return an error.
-        */
-       if (wwanantenna_err == -ENODEV)
-               return 0;
+static const struct attribute_group dprc_attr_group = {
+       .is_visible = dprc_attr_is_visible,
+       .attrs = dprc_attributes,
+};
 
-       /* if there was an error return it */
-       if (wwanantenna_err && (wwanantenna_err != -ENODEV))
-               return wwanantenna_err;
-       else if (!wwanantenna_err)
-               has_antennatype = true;
+static int tpacpi_dprc_init(struct ibm_init_struct *iibm)
+{
+       int err;
 
-       if (has_antennatype) {
-               err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr);
-               if (err)
-                       return err;
-       }
+       err = get_wwan_antenna(&wwan_antennatype);
+       if (err)
+               return err;
+
+       has_antennatype = true;
        return 0;
 }
 
-static void dprc_exit(void)
+static struct ibm_struct dprc_driver_data = {
+       .name = "dprc",
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *tpacpi_driver_attributes[] = {
+       &driver_attr_debug_level.attr,
+       &driver_attr_version.attr,
+       &driver_attr_interface_version.attr,
+#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
+       &driver_attr_wlsw_emulstate.attr,
+       &driver_attr_bluetooth_emulstate.attr,
+       &driver_attr_wwan_emulstate.attr,
+       &driver_attr_uwb_emulstate.attr,
+#endif
+       NULL
+};
+
+#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
+static umode_t tpacpi_attr_is_visible(struct kobject *kobj,
+                                     struct attribute *attr, int n)
 {
-       if (has_antennatype)
-               sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr);
+       if (attr == &driver_attr_wlsw_emulstate.attr) {
+               if (!dbg_wlswemul)
+                       return 0;
+       } else if (attr == &driver_attr_bluetooth_emulstate.attr) {
+               if (!dbg_bluetoothemul)
+                       return 0;
+       } else if (attr == &driver_attr_wwan_emulstate.attr) {
+               if (!dbg_wwanemul)
+                       return 0;
+       } else if (attr == &driver_attr_uwb_emulstate.attr) {
+               if (!dbg_uwbemul)
+                       return 0;
+       }
+
+       return attr->mode;
 }
+#endif
 
-static struct ibm_struct dprc_driver_data = {
-       .name = "dprc",
-       .exit = dprc_exit,
+static const struct attribute_group tpacpi_driver_attr_group = {
+#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
+       .is_visible = tpacpi_attr_is_visible,
+#endif
+       .attrs = tpacpi_driver_attributes,
+};
+
+static const struct attribute_group *tpacpi_driver_groups[] = {
+       &tpacpi_driver_attr_group,
+       NULL,
+};
+
+static const struct attribute_group *tpacpi_groups[] = {
+       &adaptive_kbd_attr_group,
+       &hotkey_attr_group,
+       &bluetooth_attr_group,
+       &wan_attr_group,
+       &cmos_attr_group,
+       &proxsensor_attr_group,
+       &kbdlang_attr_group,
+       &dprc_attr_group,
+       NULL,
+};
+
+static const struct attribute_group *tpacpi_hwmon_groups[] = {
+       &thermal_attr_group,
+       &temp_label_attr_group,
+       &fan_attr_group,
+       NULL,
+};
+
+static const struct attribute_group *tpacpi_hwmon_driver_groups[] = {
+       &fan_driver_attr_group,
+       NULL,
+};
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Platform drivers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static struct platform_driver tpacpi_pdriver = {
+       .driver = {
+               .name = TPACPI_DRVR_NAME,
+               .pm = &tpacpi_pm,
+               .groups = tpacpi_driver_groups,
+               .dev_groups = tpacpi_groups,
+       },
+       .shutdown = tpacpi_shutdown_handler,
+};
+
+static struct platform_driver tpacpi_hwmon_pdriver = {
+       .driver = {
+               .name = TPACPI_HWMON_DRVR_NAME,
+               .groups = tpacpi_hwmon_driver_groups,
+       },
 };
 
 /****************************************************************************
@@ -10754,8 +10877,8 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
 
        if (iibm->init) {
                ret = iibm->init(iibm);
-               if (ret > 0)
-                       return 0;       /* probe failed */
+               if (ret > 0 || ret == -ENODEV)
+                       return 0; /* subdriver functionality not available */
                if (ret)
                        return ret;
 
@@ -11134,8 +11257,6 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
 
        for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
                ibm = ibms_init[i].data;
-               WARN_ON(ibm == NULL);
-
                if (!ibm || !ibm->name)
                        continue;
 
@@ -11247,6 +11368,13 @@ static void thinkpad_acpi_module_exit(void)
 
        tpacpi_lifecycle = TPACPI_LIFE_EXITING;
 
+       if (tpacpi_hwmon)
+               hwmon_device_unregister(tpacpi_hwmon);
+       if (tp_features.sensors_pdrv_registered)
+               platform_driver_unregister(&tpacpi_hwmon_pdriver);
+       if (tp_features.platform_drv_registered)
+               platform_driver_unregister(&tpacpi_pdriver);
+
        list_for_each_entry_safe_reverse(ibm, itmp,
                                         &tpacpi_all_drivers,
                                         all_drivers) {
@@ -11263,28 +11391,12 @@ static void thinkpad_acpi_module_exit(void)
                kfree(hotkey_keycode_map);
        }
 
-       if (tpacpi_hwmon)
-               hwmon_device_unregister(tpacpi_hwmon);
-
        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);
-
        if (proc_dir)
                remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
-
        if (tpacpi_wq)
                destroy_workqueue(tpacpi_wq);
 
@@ -11336,36 +11448,6 @@ static int __init thinkpad_acpi_module_init(void)
                return -ENODEV;
        }
 
-       ret = platform_driver_register(&tpacpi_pdriver);
-       if (ret) {
-               pr_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) {
-               pr_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) {
-               pr_err("unable to create sysfs driver attributes\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       tp_features.sensors_pdrv_attrs_registered = 1;
-
-
        /* Device initialization */
        tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1,
                                                        NULL, 0);
@@ -11386,17 +11468,7 @@ static int __init thinkpad_acpi_module_init(void)
                thinkpad_acpi_module_exit();
                return ret;
        }
-       tp_features.sensors_pdev_attrs_registered = 1;
-       tpacpi_hwmon = hwmon_device_register_with_groups(
-               &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, NULL);
 
-       if (IS_ERR(tpacpi_hwmon)) {
-               ret = PTR_ERR(tpacpi_hwmon);
-               tpacpi_hwmon = NULL;
-               pr_err("unable to register hwmon device\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
        mutex_init(&tpacpi_inputdev_send_mutex);
        tpacpi_inputdev = input_allocate_device();
        if (!tpacpi_inputdev) {
@@ -11429,6 +11501,32 @@ static int __init thinkpad_acpi_module_init(void)
 
        tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
 
+       ret = platform_driver_register(&tpacpi_pdriver);
+       if (ret) {
+               pr_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) {
+               pr_err("unable to register hwmon platform driver\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       tp_features.sensors_pdrv_registered = 1;
+
+       tpacpi_hwmon = hwmon_device_register_with_groups(
+               &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, tpacpi_hwmon_groups);
+       if (IS_ERR(tpacpi_hwmon)) {
+               ret = PTR_ERR(tpacpi_hwmon);
+               tpacpi_hwmon = NULL;
+               pr_err("unable to register hwmon device\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+
        ret = input_register_device(tpacpi_inputdev);
        if (ret < 0) {
                pr_err("unable to register input device\n");