diff options
Diffstat (limited to 'drivers/platform/x86/toshiba_acpi.c')
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 641 |
1 files changed, 338 insertions, 303 deletions
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index cb009b2629ee..13ef8c37471d 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -47,7 +47,6 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/backlight.h> -#include <linux/platform_device.h> #include <linux/rfkill.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> @@ -63,11 +62,7 @@ MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); MODULE_LICENSE("GPL"); /* Toshiba ACPI method paths */ -#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" -#define TOSH_INTERFACE_1 "\\_SB_.VALD" -#define TOSH_INTERFACE_2 "\\_SB_.VALZ" #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" -#define GHCI_METHOD ".GHCI" /* Toshiba HCI interface definitions * @@ -111,6 +106,25 @@ MODULE_LICENSE("GPL"); #define HCI_WIRELESS_BT_ATTACH 0x40 #define HCI_WIRELESS_BT_POWER 0x80 +struct toshiba_acpi_dev { + struct acpi_device *acpi_dev; + const char *method_hci; + struct rfkill *bt_rfk; + struct input_dev *hotkey_dev; + struct backlight_device *backlight_dev; + struct led_classdev led_dev; + + int force_fan; + int last_key_event; + int key_event_valid; + + int illumination_supported:1; + int video_supported:1; + int fan_supported:1; + + struct mutex mutex; +}; + static const struct acpi_device_id toshiba_device_ids[] = { {"TOS6200", 0}, {"TOS6208", 0}, @@ -119,7 +133,7 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); -static const struct key_entry toshiba_acpi_keymap[] __initconst = { +static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { { KE_KEY, 0x101, { KEY_MUTE } }, { KE_KEY, 0x102, { KEY_ZOOMOUT } }, { KE_KEY, 0x103, { KEY_ZOOMIN } }, @@ -155,15 +169,6 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value) /* acpi interface wrappers */ -static int is_valid_acpi_path(const char *methodName) -{ - acpi_handle handle; - acpi_status status; - - status = acpi_get_handle(NULL, (char *)methodName, &handle); - return !ACPI_FAILURE(status); -} - static int write_acpi_int(const char *methodName, int val) { struct acpi_object_list params; @@ -176,32 +181,14 @@ static int write_acpi_int(const char *methodName, int val) in_objs[0].integer.value = val; status = acpi_evaluate_object(NULL, (char *)methodName, ¶ms, NULL); - return (status == AE_OK); -} - -#if 0 -static int read_acpi_int(const char *methodName, int *pVal) -{ - struct acpi_buffer results; - union acpi_object out_objs[1]; - acpi_status status; - - results.length = sizeof(out_objs); - results.pointer = out_objs; - - status = acpi_evaluate_object(0, (char *)methodName, 0, &results); - *pVal = out_objs[0].integer.value; - - return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER); + return (status == AE_OK) ? 0 : -EIO; } -#endif - -static const char *method_hci /*= 0*/ ; /* Perform a raw HCI call. Here we don't care about input or output buffer * format. */ -static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) +static acpi_status hci_raw(struct toshiba_acpi_dev *dev, + const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) { struct acpi_object_list params; union acpi_object in_objs[HCI_WORDS]; @@ -220,7 +207,8 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) results.length = sizeof(out_objs); results.pointer = out_objs; - status = acpi_evaluate_object(NULL, (char *)method_hci, ¶ms, + status = acpi_evaluate_object(dev->acpi_dev->handle, + (char *)dev->method_hci, ¶ms, &results); if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { for (i = 0; i < out_objs->package.count; ++i) { @@ -237,85 +225,79 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) * may be useful (such as "not supported"). */ -static acpi_status hci_write1(u32 reg, u32 in1, u32 * result) +static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, + u32 in1, u32 *result) { u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result) +static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, + u32 *out1, u32 *result) { u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *out1 = out[2]; *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result) +static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, + u32 in1, u32 in2, u32 *result) { u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) +static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, + u32 *out1, u32 *out2, u32 *result) { u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *out1 = out[2]; *out2 = out[3]; *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -struct toshiba_acpi_dev { - struct platform_device *p_dev; - struct rfkill *bt_rfk; - struct input_dev *hotkey_dev; - int illumination_installed; - acpi_handle handle; - - const char *bt_name; - - struct mutex mutex; -}; - /* Illumination support */ -static int toshiba_illumination_available(void) +static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) { u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; in[0] = 0xf100; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("Illumination device not available\n"); return 0; } in[0] = 0xf400; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); return 1; } static void toshiba_illumination_set(struct led_classdev *cdev, enum led_brightness brightness) { + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, led_dev); u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; /* First request : initialize communication. */ in[0] = 0xf100; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("Illumination device not available\n"); return; @@ -326,7 +308,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, in[0] = 0xf400; in[1] = 0x14e; in[2] = 1; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("ACPI call for illumination failed\n"); return; @@ -336,7 +318,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, in[0] = 0xf400; in[1] = 0x14e; in[2] = 0; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("ACPI call for illumination failed.\n"); return; @@ -347,11 +329,13 @@ static void toshiba_illumination_set(struct led_classdev *cdev, in[0] = 0xf200; in[1] = 0; in[2] = 0; - hci_raw(in, out); + hci_raw(dev, in, out); } static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) { + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, led_dev); u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; @@ -359,7 +343,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) /* First request : initialize communication. */ in[0] = 0xf100; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("Illumination device not available\n"); return LED_OFF; @@ -368,7 +352,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) /* Check the illumination */ in[0] = 0xf300; in[1] = 0x14e; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("ACPI call for illumination failed.\n"); return LED_OFF; @@ -380,46 +364,35 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) in[0] = 0xf200; in[1] = 0; in[2] = 0; - hci_raw(in, out); + hci_raw(dev, in, out); return result; } -static struct led_classdev toshiba_led = { - .name = "toshiba::illumination", - .max_brightness = 1, - .brightness_set = toshiba_illumination_set, - .brightness_get = toshiba_illumination_get, -}; - -static struct toshiba_acpi_dev toshiba_acpi = { - .bt_name = "Toshiba Bluetooth", -}; - /* Bluetooth rfkill handlers */ -static u32 hci_get_bt_present(bool *present) +static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) { u32 hci_result; u32 value, value2; value = 0; value2 = 0; - hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); + hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); if (hci_result == HCI_SUCCESS) *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; return hci_result; } -static u32 hci_get_radio_state(bool *radio_state) +static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) { u32 hci_result; u32 value, value2; value = 0; value2 = 0x0001; - hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); + hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); *radio_state = value & HCI_WIRELESS_KILL_SWITCH; return hci_result; @@ -436,8 +409,8 @@ static int bt_rfkill_set_block(void *data, bool blocked) value = (blocked == false); mutex_lock(&dev->mutex); - if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) { - err = -EBUSY; + if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { + err = -EIO; goto out; } @@ -446,11 +419,11 @@ static int bt_rfkill_set_block(void *data, bool blocked) goto out; } - hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); - hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); + hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); + hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) - err = -EBUSY; + err = -EIO; else err = 0; out: @@ -467,7 +440,7 @@ static void bt_rfkill_poll(struct rfkill *rfkill, void *data) mutex_lock(&dev->mutex); - hci_result = hci_get_radio_state(&value); + hci_result = hci_get_radio_state(dev, &value); if (hci_result != HCI_SUCCESS) { /* Can't do anything useful */ mutex_unlock(&dev->mutex); @@ -488,63 +461,64 @@ static const struct rfkill_ops toshiba_rfk_ops = { }; static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; -static struct backlight_device *toshiba_backlight_device; -static int force_fan; -static int last_key_event; -static int key_event_valid; static int get_lcd(struct backlight_device *bd) { + struct toshiba_acpi_dev *dev = bl_get_data(bd); u32 hci_result; u32 value; - hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); + if (hci_result == HCI_SUCCESS) return (value >> HCI_LCD_BRIGHTNESS_SHIFT); - } else - return -EFAULT; + + return -EIO; } static int lcd_proc_show(struct seq_file *m, void *v) { - int value = get_lcd(NULL); + struct toshiba_acpi_dev *dev = m->private; + int value; + + if (!dev->backlight_dev) + return -ENODEV; + value = get_lcd(dev->backlight_dev); if (value >= 0) { seq_printf(m, "brightness: %d\n", value); seq_printf(m, "brightness_levels: %d\n", HCI_LCD_BRIGHTNESS_LEVELS); - } else { - pr_err("Error reading LCD brightness\n"); + return 0; } - return 0; + pr_err("Error reading LCD brightness\n"); + return -EIO; } static int lcd_proc_open(struct inode *inode, struct file *file) { - return single_open(file, lcd_proc_show, NULL); + return single_open(file, lcd_proc_show, PDE(inode)->data); } -static int set_lcd(int value) +static int set_lcd(struct toshiba_acpi_dev *dev, int value) { u32 hci_result; value = value << HCI_LCD_BRIGHTNESS_SHIFT; - hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); - if (hci_result != HCI_SUCCESS) - return -EFAULT; - - return 0; + hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; } static int set_lcd_status(struct backlight_device *bd) { - return set_lcd(bd->props.brightness); + struct toshiba_acpi_dev *dev = bl_get_data(bd); + return set_lcd(dev, bd->props.brightness); } static ssize_t lcd_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char cmd[42]; size_t len; int value; @@ -557,7 +531,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, if (sscanf(cmd, " brightness : %i", &value) == 1 && value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { - ret = set_lcd(value); + ret = set_lcd(dev, value); if (ret == 0) ret = count; } else { @@ -575,41 +549,49 @@ static const struct file_operations lcd_proc_fops = { .write = lcd_proc_write, }; -static int video_proc_show(struct seq_file *m, void *v) +static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) { u32 hci_result; + + hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + +static int video_proc_show(struct seq_file *m, void *v) +{ + struct toshiba_acpi_dev *dev = m->private; u32 value; + int ret; - hci_read1(HCI_VIDEO_OUT, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + ret = get_video_status(dev, &value); + if (!ret) { int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; seq_printf(m, "lcd_out: %d\n", is_lcd); seq_printf(m, "crt_out: %d\n", is_crt); seq_printf(m, "tv_out: %d\n", is_tv); - } else { - pr_err("Error reading video out status\n"); } - return 0; + return ret; } static int video_proc_open(struct inode *inode, struct file *file) { - return single_open(file, video_proc_show, NULL); + return single_open(file, video_proc_show, PDE(inode)->data); } static ssize_t video_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char *cmd, *buffer; + int ret; int value; int remain = count; int lcd_out = -1; int crt_out = -1; int tv_out = -1; - u32 hci_result; u32 video_out; cmd = kmalloc(count + 1, GFP_KERNEL); @@ -644,8 +626,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, kfree(cmd); - hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); - if (hci_result == HCI_SUCCESS) { + ret = get_video_status(dev, &video_out); + if (!ret) { unsigned int new_video_out = video_out; if (lcd_out != -1) _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); @@ -656,12 +638,10 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, /* To avoid unnecessary video disruption, only write the new * video setting if something changed. */ if (new_video_out != video_out) - write_acpi_int(METHOD_VIDEO_OUT, new_video_out); - } else { - return -EFAULT; + ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); } - return count; + return ret ? ret : count; } static const struct file_operations video_proc_fops = { @@ -673,30 +653,38 @@ static const struct file_operations video_proc_fops = { .write = video_proc_write, }; -static int fan_proc_show(struct seq_file *m, void *v) +static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) { u32 hci_result; + + hci_read1(dev, HCI_FAN, status, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + +static int fan_proc_show(struct seq_file *m, void *v) +{ + struct toshiba_acpi_dev *dev = m->private; + int ret; u32 value; - hci_read1(HCI_FAN, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + ret = get_fan_status(dev, &value); + if (!ret) { seq_printf(m, "running: %d\n", (value > 0)); - seq_printf(m, "force_on: %d\n", force_fan); - } else { - pr_err("Error reading fan status\n"); + seq_printf(m, "force_on: %d\n", dev->force_fan); } - return 0; + return ret; } static int fan_proc_open(struct inode *inode, struct file *file) { - return single_open(file, fan_proc_show, NULL); + return single_open(file, fan_proc_show, PDE(inode)->data); } static ssize_t fan_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char cmd[42]; size_t len; int value; @@ -709,11 +697,11 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf, if (sscanf(cmd, " force_on : %i", &value) == 1 && value >= 0 && value <= 1) { - hci_write1(HCI_FAN, value, &hci_result); + hci_write1(dev, HCI_FAN, value, &hci_result); if (hci_result != HCI_SUCCESS) - return -EFAULT; + return -EIO; else - force_fan = value; + dev->force_fan = value; } else { return -EINVAL; } @@ -732,42 +720,43 @@ static const struct file_operations fan_proc_fops = { static int keys_proc_show(struct seq_file *m, void *v) { + struct toshiba_acpi_dev *dev = m->private; u32 hci_result; u32 value; - if (!key_event_valid) { - hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + if (!dev->key_event_valid) { + hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); if (hci_result == HCI_SUCCESS) { - key_event_valid = 1; - last_key_event = value; + dev->key_event_valid = 1; + dev->last_key_event = value; } else if (hci_result == HCI_EMPTY) { /* better luck next time */ } else if (hci_result == HCI_NOT_SUPPORTED) { /* This is a workaround for an unresolved issue on * some machines where system events sporadically * become disabled. */ - hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); pr_notice("Re-enabled hotkeys\n"); } else { pr_err("Error reading hotkey status\n"); - goto end; + return -EIO; } } - seq_printf(m, "hotkey_ready: %d\n", key_event_valid); - seq_printf(m, "hotkey: 0x%04x\n", last_key_event); -end: + seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); + seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); return 0; } static int keys_proc_open(struct inode *inode, struct file *file) { - return single_open(file, keys_proc_show, NULL); + return single_open(file, keys_proc_show, PDE(inode)->data); } static ssize_t keys_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char cmd[42]; size_t len; int value; @@ -778,7 +767,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf, cmd[len] = '\0'; if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { - key_event_valid = 0; + dev->key_event_valid = 0; } else { return -EINVAL; } @@ -820,21 +809,35 @@ static const struct file_operations version_proc_fops = { #define PROC_TOSHIBA "toshiba" -static void __init create_toshiba_proc_entries(void) +static void __devinit +create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) { - proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); - proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); - proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); - proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); - proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); + if (dev->backlight_dev) + proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &lcd_proc_fops, dev); + if (dev->video_supported) + proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &video_proc_fops, dev); + if (dev->fan_supported) + proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &fan_proc_fops, dev); + if (dev->hotkey_dev) + proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &keys_proc_fops, dev); + proc_create_data("version", S_IRUGO, toshiba_proc_dir, + &version_proc_fops, dev); } -static void remove_toshiba_proc_entries(void) +static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) { - remove_proc_entry("lcd", toshiba_proc_dir); - remove_proc_entry("video", toshiba_proc_dir); - remove_proc_entry("fan", toshiba_proc_dir); - remove_proc_entry("keys", toshiba_proc_dir); + if (dev->backlight_dev) + remove_proc_entry("lcd", toshiba_proc_dir); + if (dev->video_supported) + remove_proc_entry("video", toshiba_proc_dir); + if (dev->fan_supported) + remove_proc_entry("fan", toshiba_proc_dir); + if (dev->hotkey_dev) + remove_proc_entry("keys", toshiba_proc_dir); remove_proc_entry("version", toshiba_proc_dir); } @@ -843,224 +846,256 @@ static const struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; -static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) -{ - u32 hci_result, value; - - if (event != 0x80) - return; - do { - hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { - if (value == 0x100) - continue; - /* act on key press; ignore key release */ - if (value & 0x80) - continue; - - if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, - value, 1, true)) { - pr_info("Unknown key %x\n", - value); - } - } else if (hci_result == HCI_NOT_SUPPORTED) { - /* This is a workaround for an unresolved issue on - * some machines where system events sporadically - * become disabled. */ - hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); - pr_notice("Re-enabled hotkeys\n"); - } - } while (hci_result != HCI_EMPTY); -} - -static int __init toshiba_acpi_setup_keyboard(char *device) +static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) { acpi_status status; int error; - status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); - if (ACPI_FAILURE(status)) { - pr_info("Unable to get notification device\n"); - return -ENODEV; - } - - toshiba_acpi.hotkey_dev = input_allocate_device(); - if (!toshiba_acpi.hotkey_dev) { + dev->hotkey_dev = input_allocate_device(); + if (!dev->hotkey_dev) { pr_info("Unable to register input device\n"); return -ENOMEM; } - toshiba_acpi.hotkey_dev->name = "Toshiba input device"; - toshiba_acpi.hotkey_dev->phys = device; - toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; + dev->hotkey_dev->name = "Toshiba input device"; + dev->hotkey_dev->phys = "toshiba_acpi/input0"; + dev->hotkey_dev->id.bustype = BUS_HOST; - error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, - toshiba_acpi_keymap, NULL); + error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); if (error) goto err_free_dev; - status = acpi_install_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); - if (ACPI_FAILURE(status)) { - pr_info("Unable to install hotkey notification\n"); - error = -ENODEV; - goto err_free_keymap; - } - - status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); + status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); if (ACPI_FAILURE(status)) { pr_info("Unable to enable hotkeys\n"); error = -ENODEV; - goto err_remove_notify; + goto err_free_keymap; } - error = input_register_device(toshiba_acpi.hotkey_dev); + error = input_register_device(dev->hotkey_dev); if (error) { pr_info("Unable to register input device\n"); - goto err_remove_notify; + goto err_free_keymap; } return 0; - err_remove_notify: - acpi_remove_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); err_free_keymap: - sparse_keymap_free(toshiba_acpi.hotkey_dev); + sparse_keymap_free(dev->hotkey_dev); err_free_dev: - input_free_device(toshiba_acpi.hotkey_dev); - toshiba_acpi.hotkey_dev = NULL; + input_free_device(dev->hotkey_dev); + dev->hotkey_dev = NULL; return error; } -static void toshiba_acpi_exit(void) +static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) { - if (toshiba_acpi.hotkey_dev) { - acpi_remove_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); - sparse_keymap_free(toshiba_acpi.hotkey_dev); - input_unregister_device(toshiba_acpi.hotkey_dev); + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + + remove_toshiba_proc_entries(dev); + + if (dev->hotkey_dev) { + input_unregister_device(dev->hotkey_dev); + sparse_keymap_free(dev->hotkey_dev); } - if (toshiba_acpi.bt_rfk) { - rfkill_unregister(toshiba_acpi.bt_rfk); - rfkill_destroy(toshiba_acpi.bt_rfk); + if (dev->bt_rfk) { + rfkill_unregister(dev->bt_rfk); + rfkill_destroy(dev->bt_rfk); } - if (toshiba_backlight_device) - backlight_device_unregister(toshiba_backlight_device); + if (dev->backlight_dev) + backlight_device_unregister(dev->backlight_dev); - remove_toshiba_proc_entries(); + if (dev->illumination_supported) + led_classdev_unregister(&dev->led_dev); - if (toshiba_proc_dir) - remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + kfree(dev); + + return 0; +} + +static const char * __devinit find_hci_method(acpi_handle handle) +{ + acpi_status status; + acpi_handle hci_handle; - if (toshiba_acpi.illumination_installed) - led_classdev_unregister(&toshiba_led); + status = acpi_get_handle(handle, "GHCI", &hci_handle); + if (ACPI_SUCCESS(status)) + return "GHCI"; - platform_device_unregister(toshiba_acpi.p_dev); + status = acpi_get_handle(handle, "SPFC", &hci_handle); + if (ACPI_SUCCESS(status)) + return "SPFC"; - return; + return NULL; } -static int __init toshiba_acpi_init(void) +static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) { + struct toshiba_acpi_dev *dev; + const char *hci_method; u32 hci_result; + u32 dummy; bool bt_present; int ret = 0; struct backlight_properties props; - if (acpi_disabled) - return -ENODEV; - - /* simple device detection: look for HCI method */ - if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { - method_hci = TOSH_INTERFACE_1 GHCI_METHOD; - if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) - pr_info("Unable to activate hotkeys\n"); - } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { - method_hci = TOSH_INTERFACE_2 GHCI_METHOD; - if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) - pr_info("Unable to activate hotkeys\n"); - } else - return -ENODEV; - pr_info("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION); - pr_info(" HCI method: %s\n", method_hci); - - mutex_init(&toshiba_acpi.mutex); - - toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi", - -1, NULL, 0); - if (IS_ERR(toshiba_acpi.p_dev)) { - ret = PTR_ERR(toshiba_acpi.p_dev); - pr_err("unable to register platform device\n"); - toshiba_acpi.p_dev = NULL; - toshiba_acpi_exit(); - return ret; + + hci_method = find_hci_method(acpi_dev->handle); + if (!hci_method) { + pr_err("HCI interface not found\n"); + return -ENODEV; } - force_fan = 0; - key_event_valid = 0; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->acpi_dev = acpi_dev; + dev->method_hci = hci_method; + acpi_dev->driver_data = dev; - /* enable event fifo */ - hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + if (toshiba_acpi_setup_keyboard(dev)) + pr_info("Unable to activate hotkeys\n"); - toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); - if (!toshiba_proc_dir) { - toshiba_acpi_exit(); - return -ENODEV; - } else { - create_toshiba_proc_entries(); - } + mutex_init(&dev->mutex); + + /* enable event fifo */ + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); props.type = BACKLIGHT_PLATFORM; props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; - toshiba_backlight_device = backlight_device_register("toshiba", - &toshiba_acpi.p_dev->dev, - NULL, - &toshiba_backlight_data, - &props); - if (IS_ERR(toshiba_backlight_device)) { - ret = PTR_ERR(toshiba_backlight_device); + dev->backlight_dev = backlight_device_register("toshiba", + &acpi_dev->dev, + dev, + &toshiba_backlight_data, + &props); + if (IS_ERR(dev->backlight_dev)) { + ret = PTR_ERR(dev->backlight_dev); pr_err("Could not register toshiba backlight device\n"); - toshiba_backlight_device = NULL; - toshiba_acpi_exit(); - return ret; + dev->backlight_dev = NULL; + goto error; } + dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev); /* Register rfkill switch for Bluetooth */ - if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { - toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name, - &toshiba_acpi.p_dev->dev, - RFKILL_TYPE_BLUETOOTH, - &toshiba_rfk_ops, - &toshiba_acpi); - if (!toshiba_acpi.bt_rfk) { + if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { + dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", + &acpi_dev->dev, + RFKILL_TYPE_BLUETOOTH, + &toshiba_rfk_ops, + dev); + if (!dev->bt_rfk) { pr_err("unable to allocate rfkill device\n"); - toshiba_acpi_exit(); - return -ENOMEM; + ret = -ENOMEM; + goto error; } - ret = rfkill_register(toshiba_acpi.bt_rfk); + ret = rfkill_register(dev->bt_rfk); if (ret) { pr_err("unable to register rfkill device\n"); - rfkill_destroy(toshiba_acpi.bt_rfk); - toshiba_acpi_exit(); - return ret; + rfkill_destroy(dev->bt_rfk); + goto error; } } - toshiba_acpi.illumination_installed = 0; - if (toshiba_illumination_available()) { - if (!led_classdev_register(&(toshiba_acpi.p_dev->dev), - &toshiba_led)) - toshiba_acpi.illumination_installed = 1; + if (toshiba_illumination_available(dev)) { + dev->led_dev.name = "toshiba::illumination"; + dev->led_dev.max_brightness = 1; + dev->led_dev.brightness_set = toshiba_illumination_set; + dev->led_dev.brightness_get = toshiba_illumination_get; + if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) + dev->illumination_supported = 1; } + /* Determine whether or not BIOS supports fan and video interfaces */ + + ret = get_video_status(dev, &dummy); + dev->video_supported = !ret; + + ret = get_fan_status(dev, &dummy); + dev->fan_supported = !ret; + + create_toshiba_proc_entries(dev); + return 0; + +error: + toshiba_acpi_remove(acpi_dev, 0); + return ret; +} + +static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) +{ + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + u32 hci_result, value; + + if (event != 0x80) + return; + do { + hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + if (value == 0x100) + continue; + /* act on key press; ignore key release */ + if (value & 0x80) + continue; + + if (!sparse_keymap_report_event(dev->hotkey_dev, + value, 1, true)) { + pr_info("Unknown key %x\n", + value); + } + } else if (hci_result == HCI_NOT_SUPPORTED) { + /* This is a workaround for an unresolved issue on + * some machines where system events sporadically + * become disabled. */ + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); + pr_notice("Re-enabled hotkeys\n"); + } + } while (hci_result != HCI_EMPTY); +} + + +static struct acpi_driver toshiba_acpi_driver = { + .name = "Toshiba ACPI driver", + .owner = THIS_MODULE, + .ids = toshiba_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = toshiba_acpi_add, + .remove = toshiba_acpi_remove, + .notify = toshiba_acpi_notify, + }, +}; + +static int __init toshiba_acpi_init(void) +{ + int ret; + + toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); + if (!toshiba_proc_dir) { + pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); + return -ENODEV; + } + + ret = acpi_bus_register_driver(&toshiba_acpi_driver); + if (ret) { + pr_err("Failed to register ACPI driver: %d\n", ret); + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + } + + return ret; +} + +static void __exit toshiba_acpi_exit(void) +{ + acpi_bus_unregister_driver(&toshiba_acpi_driver); + if (toshiba_proc_dir) + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } module_init(toshiba_acpi_init); |