diff options
Diffstat (limited to 'drivers')
50 files changed, 3116 insertions, 445 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 3c7d8942f23..b0de6312919 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -903,7 +903,7 @@ static struct acpi_driver acpi_battery_driver = { }, }; -static void acpi_battery_init_async(void *unused, async_cookie_t cookie) +static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) { if (acpi_disabled) return; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 57be6bea48e..08186ecbaf8 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -114,6 +114,7 @@ enum { board_ahci_sb700 = 5, /* for SB700 and SB800 */ board_ahci_mcp65 = 6, board_ahci_nopmp = 7, + board_ahci_yesncq = 8, /* global controller registers */ HOST_CAP = 0x00, /* host capabilities */ @@ -469,6 +470,14 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, + /* board_ahci_yesncq */ + { + AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ), + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }, }; static const struct pci_device_id ahci_pci_tbl[] = { @@ -535,30 +544,30 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 }, /* MCP65 */ { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 }, /* MCP65 */ - { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci }, /* MCP67 */ - { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci }, /* MCP73 */ - { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq }, /* MCP67 */ + { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_yesncq }, /* MCP73 */ + { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_yesncq }, /* MCP73 */ { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci }, /* MCP77 */ { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci }, /* MCP77 */ diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index e7ea77cf606..065507c4664 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6110,13 +6110,11 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) ata_port_printk(ap, KERN_INFO, "DUMMY\n"); } - /* perform each probe synchronously */ - DPRINTK("probe begin\n"); + /* perform each probe asynchronously */ for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; async_schedule(async_port_probe, ap); } - DPRINTK("probe end\n"); return 0; } diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 98e8c50703b..bdd43c7f432 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -566,7 +566,7 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) static int printed_version; unsigned int i; int rc; - struct ata_host *host; + struct ata_host *host = NULL; int board_id = (int) ent->driver_data; const unsigned *bar_sizes; diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c index f68e5f8e23e..6318f7ddc1d 100644 --- a/drivers/i2c/algos/i2c-algo-pca.c +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -190,7 +190,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap, int completed = 1; unsigned long timeout = jiffies + i2c_adap->timeout; - while (pca_status(adap) != 0xf8) { + while ((state = pca_status(adap)) != 0xf8) { if (time_before(jiffies, timeout)) { msleep(10); } else { diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 94eae5c3cbc..a48c8aee021 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -604,12 +604,14 @@ comment "Graphics adapter I2C/DDC channel drivers" depends on PCI config I2C_VOODOO3 - tristate "Voodoo 3" + tristate "Voodoo 3 (DEPRECATED)" depends on PCI select I2C_ALGOBIT help If you say yes to this option, support will be included for the - Voodoo 3 I2C interface. + Voodoo 3 I2C interface. This driver is deprecated and you should + use the tdfxfb driver instead, which additionally provides + framebuffer support. This driver can also be built as a module. If so, the module will be called i2c-voodoo3. diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index b6f3a0de6ca..85e2e919d1c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -716,8 +716,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) /* new style driver methods can't mix with legacy ones */ if (is_newstyle_driver(driver)) { - if (driver->attach_adapter || driver->detach_adapter - || driver->detach_client) { + if (driver->detach_adapter || driver->detach_client) { printk(KERN_WARNING "i2c-core: driver [%s] is confused\n", driver->driver.name); diff --git a/drivers/input/input.c b/drivers/input/input.c index ec3db3ade11..d44065d2e66 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -132,6 +132,11 @@ static void input_start_autorepeat(struct input_dev *dev, int code) } } +static void input_stop_autorepeat(struct input_dev *dev) +{ + del_timer(&dev->timer); +} + #define INPUT_IGNORE_EVENT 0 #define INPUT_PASS_TO_HANDLERS 1 #define INPUT_PASS_TO_DEVICE 2 @@ -167,6 +172,8 @@ static void input_handle_event(struct input_dev *dev, __change_bit(code, dev->key); if (value) input_start_autorepeat(dev, code); + else + input_stop_autorepeat(dev); } disposition = INPUT_PASS_TO_HANDLERS; @@ -737,11 +744,11 @@ static inline void input_wakeup_procfs_readers(void) static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) { - int state = input_devices_state; - poll_wait(file, &input_devices_poll_wait, wait); - if (state != input_devices_state) + if (file->f_version != input_devices_state) { + file->f_version = input_devices_state; return POLLIN | POLLRDNORM; + } return 0; } diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 45470f18d7e..f999dc60c3b 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -229,7 +229,8 @@ struct atkbd { /* * System-specific ketymap fixup routine */ -static void (*atkbd_platform_fixup)(struct atkbd *); +static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); +static void *atkbd_platform_fixup_data; static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, ssize_t (*handler)(struct atkbd *, char *)); @@ -834,87 +835,64 @@ static void atkbd_disconnect(struct serio *serio) } /* - * Most special keys (Fn+F?) on Dell laptops do not generate release - * events so we have to do it ourselves. + * generate release events for the keycodes given in data */ -static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd) +static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd, + const void *data) { - static const unsigned int forced_release_keys[] = { - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, - }; - int i; + const unsigned int *keys = data; + unsigned int i; if (atkbd->set == 2) - for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) - __set_bit(forced_release_keys[i], - atkbd->force_release_mask); + for (i = 0; keys[i] != -1U; i++) + __set_bit(keys[i], atkbd->force_release_mask); } /* + * Most special keys (Fn+F?) on Dell laptops do not generate release + * events so we have to do it ourselves. + */ +static unsigned int atkbd_dell_laptop_forced_release_keys[] = { + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, -1U +}; + +/* * Perform fixup for HP system that doesn't generate release * for its video switch */ -static void atkbd_hp_keymap_fixup(struct atkbd *atkbd) -{ - static const unsigned int forced_release_keys[] = { - 0x94, - }; - int i; - - if (atkbd->set == 2) - for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) - __set_bit(forced_release_keys[i], - atkbd->force_release_mask); -} +static unsigned int atkbd_hp_forced_release_keys[] = { + 0x94, -1U +}; /* * Inventec system with broken key release on volume keys */ -static void atkbd_inventec_keymap_fixup(struct atkbd *atkbd) -{ - const unsigned int forced_release_keys[] = { - 0xae, 0xb0, - }; - int i; - - if (atkbd->set == 2) - for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) - __set_bit(forced_release_keys[i], - atkbd->force_release_mask); -} +static unsigned int atkbd_inventec_forced_release_keys[] = { + 0xae, 0xb0, -1U +}; /* * Perform fixup for HP Pavilion ZV6100 laptop that doesn't generate release * for its volume buttons */ -static void atkbd_hp_zv6100_keymap_fixup(struct atkbd *atkbd) -{ - const unsigned int forced_release_keys[] = { - 0xae, 0xb0, - }; - int i; - - if (atkbd->set == 2) - for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) - __set_bit(forced_release_keys[i], - atkbd->force_release_mask); -} +static unsigned int atkbd_hp_zv6100_forced_release_keys[] = { + 0xae, 0xb0, -1U +}; /* * Samsung NC10 with Fn+F? key release not working */ -static void atkbd_samsung_keymap_fixup(struct atkbd *atkbd) -{ - const unsigned int forced_release_keys[] = { - 0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, - }; - int i; +static unsigned int atkbd_samsung_forced_release_keys[] = { + 0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, -1U +}; - if (atkbd->set == 2) - for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) - __set_bit(forced_release_keys[i], - atkbd->force_release_mask); -} +/* + * The volume up and volume down special keys on a Fujitsu Amilo PA 1510 laptop + * do not generate release events so we have to do it ourselves. + */ +static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = { + 0xb0, 0xae, -1U +}; /* * atkbd_set_keycode_table() initializes keyboard's keycode table @@ -967,7 +945,7 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd) * Perform additional fixups */ if (atkbd_platform_fixup) - atkbd_platform_fixup(atkbd); + atkbd_platform_fixup(atkbd, atkbd_platform_fixup_data); } /* @@ -1492,9 +1470,11 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf) return sprintf(buf, "%lu\n", atkbd->err_count); } -static int __init atkbd_setup_fixup(const struct dmi_system_id *id) +static int __init atkbd_setup_forced_release(const struct dmi_system_id *id) { - atkbd_platform_fixup = id->driver_data; + atkbd_platform_fixup = atkbd_apply_forced_release_keylist; + atkbd_platform_fixup_data = id->driver_data; + return 0; } @@ -1505,8 +1485,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, - .callback = atkbd_setup_fixup, - .driver_data = atkbd_dell_laptop_keymap_fixup, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_dell_laptop_forced_release_keys, }, { .ident = "Dell Laptop", @@ -1514,8 +1494,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, - .callback = atkbd_setup_fixup, - .driver_data = atkbd_dell_laptop_keymap_fixup, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_dell_laptop_forced_release_keys, }, { .ident = "HP 2133", @@ -1523,8 +1503,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"), }, - .callback = atkbd_setup_fixup, - .driver_data = atkbd_hp_keymap_fixup, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_hp_forced_release_keys, }, { .ident = "HP Pavilion ZV6100", @@ -1532,8 +1512,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"), }, - .callback = atkbd_setup_fixup, - .driver_data = atkbd_hp_zv6100_keymap_fixup, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_hp_zv6100_forced_release_keys, }, { .ident = "Inventec Symphony", @@ -1541,8 +1521,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), }, - .callback = atkbd_setup_fixup, - .driver_data = atkbd_inventec_keymap_fixup, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_inventec_forced_release_keys, }, { .ident = "Samsung NC10", @@ -1550,8 +1530,17 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), }, - .callback = atkbd_setup_fixup, - .driver_data = atkbd_samsung_keymap_fixup, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_samsung_forced_release_keys, + }, + { + .ident = "Fujitsu Amilo PA 1510", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"), + }, + .callback = atkbd_setup_forced_release, + .driver_data = atkbd_amilo_pa1510_forced_release_keys, }, { } }; diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index ee855c5202e..e94b7d735ac 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -211,8 +211,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT || !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) { - printk(KERN_ERR DRV_NAME - ": Invalid Debounce/Columdrive Time from pdata\n"); + printk(KERN_WARNING DRV_NAME + ": Invalid Debounce/Columndrive Time in platform data\n"); bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */ } else { bfin_write_KPAD_MSEL( diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index aacf71f3cd4..e9d639ec283 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -198,45 +198,28 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) } -/* initialise HIL */ -static int __init -hil_keyb_init(void) +/* initialize HIL */ +static int __devinit hil_keyb_init(void) { unsigned char c; unsigned int i, kbid; wait_queue_head_t hil_wait; int err; - if (hil_dev.dev) { + if (hil_dev.dev) return -ENODEV; /* already initialized */ - } + init_waitqueue_head(&hil_wait); spin_lock_init(&hil_dev.lock); + hil_dev.dev = input_allocate_device(); if (!hil_dev.dev) return -ENOMEM; -#if defined(CONFIG_HP300) - if (!MACH_IS_HP300) { - err = -ENODEV; - goto err1; - } - if (!hwreg_present((void *)(HILBASE + HIL_DATA))) { - printk(KERN_ERR "HIL: hardware register was not found\n"); - err = -ENODEV; - goto err1; - } - if (!request_region(HILBASE + HIL_DATA, 2, "hil")) { - printk(KERN_ERR "HIL: IOPORT region already used\n"); - err = -EIO; - goto err1; - } -#endif - err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id); if (err) { printk(KERN_ERR "HIL: Can't get IRQ\n"); - goto err2; + goto err1; } /* Turn on interrupts */ @@ -246,11 +229,9 @@ hil_keyb_init(void) hil_dev.valid = 0; /* clear any pending data */ hil_do(HIL_READKBDSADR, NULL, 0); - init_waitqueue_head(&hil_wait); - wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ); - if (!hil_dev.valid) { + wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ); + if (!hil_dev.valid) printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n"); - } c = hil_dev.c; hil_dev.valid = 0; @@ -268,7 +249,7 @@ hil_keyb_init(void) for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++) if (hphilkeyb_keycode[i] != KEY_RESERVED) - set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit); + __set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit); hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | @@ -287,34 +268,45 @@ hil_keyb_init(void) err = input_register_device(hil_dev.dev); if (err) { printk(KERN_ERR "HIL: Can't register device\n"); - goto err3; + goto err2; } + printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n", hil_dev.dev->name, kbid, HILBASE, HIL_IRQ); return 0; -err3: +err2: hil_do(HIL_INTOFF, NULL, 0); - disable_irq(HIL_IRQ); free_irq(HIL_IRQ, hil_dev.dev_id); -err2: -#if defined(CONFIG_HP300) - release_region(HILBASE + HIL_DATA, 2); err1: -#endif input_free_device(hil_dev.dev); hil_dev.dev = NULL; return err; } +static void __devexit hil_keyb_exit(void) +{ + if (HIL_IRQ) + free_irq(HIL_IRQ, hil_dev.dev_id); + + /* Turn off interrupts */ + hil_do(HIL_INTOFF, NULL, 0); + + input_unregister_device(hil_dev.dev); + hil_dev.dev = NULL; +} #if defined(CONFIG_PARISC) -static int __init -hil_init_chip(struct parisc_device *dev) +static int __devinit hil_probe_chip(struct parisc_device *dev) { + /* Only allow one HIL keyboard */ + if (hil_dev.dev) + return -ENODEV; + if (!dev->irq) { - printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa.start); + printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n", + (void *)dev->hpa.start); return -ENODEV; } @@ -327,51 +319,79 @@ hil_init_chip(struct parisc_device *dev) return hil_keyb_init(); } +static int __devexit hil_remove_chip(struct parisc_device *dev) +{ + hil_keyb_exit(); + + return 0; +} + static struct parisc_device_id hil_tbl[] = { { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 }, { 0, } }; +#if 0 +/* Disabled to avoid conflicts with the HP SDC HIL drivers */ MODULE_DEVICE_TABLE(parisc, hil_tbl); +#endif static struct parisc_driver hil_driver = { - .name = "hil", - .id_table = hil_tbl, - .probe = hil_init_chip, + .name = "hil", + .id_table = hil_tbl, + .probe = hil_probe_chip, + .remove = __devexit_p(hil_remove_chip), }; -#endif /* CONFIG_PARISC */ - static int __init hil_init(void) { -#if defined(CONFIG_PARISC) return register_parisc_driver(&hil_driver); -#else - return hil_keyb_init(); -#endif } - static void __exit hil_exit(void) { - if (HIL_IRQ) { - disable_irq(HIL_IRQ); - free_irq(HIL_IRQ, hil_dev.dev_id); + unregister_parisc_driver(&hil_driver); +} + +#else /* !CONFIG_PARISC */ + +static int __init hil_init(void) +{ + int error; + + /* Only allow one HIL keyboard */ + if (hil_dev.dev) + return -EBUSY; + + if (!MACH_IS_HP300) + return -ENODEV; + + if (!hwreg_present((void *)(HILBASE + HIL_DATA))) { + printk(KERN_ERR "HIL: hardware register was not found\n"); + return -ENODEV; } - /* Turn off interrupts */ - hil_do(HIL_INTOFF, NULL, 0); + if (!request_region(HILBASE + HIL_DATA, 2, "hil")) { + printk(KERN_ERR "HIL: IOPORT region already used\n"); + return -EIO; + } - input_unregister_device(hil_dev.dev); + error = hil_keyb_init(); + if (error) { + release_region(HILBASE + HIL_DATA, 2); + return error; + } - hil_dev.dev = NULL; + return 0; +} -#if defined(CONFIG_PARISC) - unregister_parisc_driver(&hil_driver); -#else - release_region(HILBASE+HIL_DATA, 2); -#endif +static void __exit hil_exit(void) +{ + hil_keyb_exit(); + release_region(HILBASE + HIL_DATA, 2); } +#endif /* CONFIG_PARISC */ + module_init(hil_init); module_exit(hil_exit); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 67e5553f699..203abac1e23 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -227,4 +227,27 @@ config INPUT_PCF50633_PMU Say Y to include support for delivering PMU events via input layer on NXP PCF50633. +config INPUT_GPIO_ROTARY_ENCODER + tristate "Rotary encoders connected to GPIO pins" + depends on GPIOLIB && GENERIC_GPIO + help + Say Y here to add support for rotary encoders connected to GPIO lines. + Check file:Documentation/incput/rotary_encoder.txt for more + information. + + To compile this driver as a module, choose M here: the + module will be called rotary_encoder. + +config INPUT_RB532_BUTTON + tristate "Mikrotik Routerboard 532 button interface" + depends on MIKROTIK_RB532 + depends on GPIOLIB && GENERIC_GPIO + select INPUT_POLLDEV + help + Say Y here if you want support for the S1 button built into + Mikrotik's Routerboard 532. + + To compile this driver as a module, choose M here: the + module will be called rb532_button. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index bb62e6efacf..eb3f407baed 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -4,21 +4,23 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o -obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o -obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o -obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o -obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o -obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o -obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o +obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o -obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o -obj-$(CONFIG_INPUT_POWERMATE) += powermate.o -obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_CM109) += cm109.o +obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o -obj-$(CONFIG_INPUT_UINPUT) += uinput.o -obj-$(CONFIG_INPUT_APANEL) += apanel.o -obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o +obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o +obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o +obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o +obj-$(CONFIG_INPUT_POWERMATE) += powermate.o +obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o +obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o +obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o +obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_UINPUT) += uinput.o +obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o +obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 3c9988dc0e9..922c0514158 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -31,12 +31,73 @@ MODULE_LICENSE("GPL"); * newly configured "channel". */ -static unsigned int channel_mask = 0xFFFF; -module_param(channel_mask, uint, 0644); +enum { + ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF, + ATI_REMOTE2_MAX_MODE_MASK = 0x1F, +}; + +static int ati_remote2_set_mask(const char *val, + struct kernel_param *kp, unsigned int max) +{ + unsigned long mask; + int ret; + + if (!val) + return -EINVAL; + + ret = strict_strtoul(val, 0, &mask); + if (ret) + return ret; + + if (mask & ~max) + return -EINVAL; + + *(unsigned int *)kp->arg = mask; + + return 0; +} + +static int ati_remote2_set_channel_mask(const char *val, + struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK); +} + +static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg); +} + +static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK); +} + +static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp) +{ + pr_debug("%s()\n", __func__); + + return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg); +} + +static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; +#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) +#define param_set_channel_mask ati_remote2_set_channel_mask +#define param_get_channel_mask ati_remote2_get_channel_mask +module_param(channel_mask, channel_mask, 0644); MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); -static unsigned int mode_mask = 0x1F; -module_param(mode_mask, uint, 0644); +static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; +#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) +#define param_set_mode_mask ati_remote2_set_mode_mask +#define param_get_mode_mask ati_remote2_get_mode_mask +module_param(mode_mask, mode_mask, 0644); MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); static struct usb_device_id ati_remote2_id_table[] = { @@ -133,12 +194,18 @@ struct ati_remote2 { u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)]; unsigned int flags; + + unsigned int channel_mask; + unsigned int mode_mask; }; static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); static void ati_remote2_disconnect(struct usb_interface *interface); static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message); static int ati_remote2_resume(struct usb_interface *interface); +static int ati_remote2_reset_resume(struct usb_interface *interface); +static int ati_remote2_pre_reset(struct usb_interface *interface); +static int ati_remote2_post_reset(struct usb_interface *interface); static struct usb_driver ati_remote2_driver = { .name = "ati_remote2", @@ -147,6 +214,9 @@ static struct usb_driver ati_remote2_driver = { .id_table = ati_remote2_id_table, .suspend = ati_remote2_suspend, .resume = ati_remote2_resume, + .reset_resume = ati_remote2_reset_resume, + .pre_reset = ati_remote2_pre_reset, + .post_reset = ati_remote2_post_reset, .supports_autosuspend = 1, }; @@ -238,7 +308,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2) channel = data[0] >> 4; - if (!((1 << channel) & channel_mask)) + if (!((1 << channel) & ar2->channel_mask)) return; mode = data[0] & 0x0F; @@ -250,7 +320,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2) return; } - if (!((1 << mode) & mode_mask)) + if (!((1 << mode) & ar2->mode_mask)) return; input_event(idev, EV_REL, REL_X, (s8) data[1]); @@ -277,7 +347,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) channel = data[0] >> 4; - if (!((1 << channel) & channel_mask)) + if (!((1 << channel) & ar2->channel_mask)) return; mode = data[0] & 0x0F; @@ -305,7 +375,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) ar2->mode = mode; } - if (!((1 << mode) & mode_mask)) + if (!((1 << mode) & ar2->mode_mask)) return; index = ati_remote2_lookup(hw_code); @@ -410,7 +480,7 @@ static int ati_remote2_getkeycode(struct input_dev *idev, int index, mode; mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask)) + if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) return -EINVAL; index = ati_remote2_lookup(scancode & 0xFF); @@ -427,7 +497,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc int index, mode, old_keycode; mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask)) + if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) return -EINVAL; index = ati_remote2_lookup(scancode & 0xFF); @@ -550,7 +620,7 @@ static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2) } } -static int ati_remote2_setup(struct ati_remote2 *ar2) +static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask) { int r, i, channel; @@ -565,8 +635,8 @@ static int ati_remote2_setup(struct ati_remote2 *ar2) channel = 0; for (i = 0; i < 16; i++) { - if ((1 << i) & channel_mask) { - if (!(~(1 << i) & 0xFFFF & channel_mask)) + if ((1 << i) & ch_mask) { + if (!(~(1 << i) & ch_mask)) channel = i + 1; break; } @@ -585,6 +655,99 @@ static int ati_remote2_setup(struct ati_remote2 *ar2) return 0; } +static ssize_t ati_remote2_show_channel_mask(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + + return sprintf(buf, "0x%04x\n", ar2->channel_mask); +} + +static ssize_t ati_remote2_store_channel_mask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + unsigned long mask; + int r; + + if (strict_strtoul(buf, 0, &mask)) + return -EINVAL; + + if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK) + return -EINVAL; + + r = usb_autopm_get_interface(ar2->intf[0]); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s(): usb_autopm_get_interface() = %d\n", __func__, r); + return r; + } + + mutex_lock(&ati_remote2_mutex); + + if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask)) + ar2->channel_mask = mask; + + mutex_unlock(&ati_remote2_mutex); + + usb_autopm_put_interface(ar2->intf[0]); + + return count; +} + +static ssize_t ati_remote2_show_mode_mask(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + + return sprintf(buf, "0x%02x\n", ar2->mode_mask); +} + +static ssize_t ati_remote2_store_mode_mask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + struct usb_interface *intf = usb_ifnum_to_if(udev, 0); + struct ati_remote2 *ar2 = usb_get_intfdata(intf); + unsigned long mask; + + if (strict_strtoul(buf, 0, &mask)) + return -EINVAL; + + if (mask & ~ATI_REMOTE2_MAX_MODE_MASK) + return -EINVAL; + + ar2->mode_mask = mask; + + return count; +} + +static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask, + ati_remote2_store_channel_mask); + +static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask, + ati_remote2_store_mode_mask); + +static struct attribute *ati_remote2_attrs[] = { + &dev_attr_channel_mask.attr, + &dev_attr_mode_mask.attr, + NULL, +}; + +static struct attribute_group ati_remote2_attr_group = { + .attrs = ati_remote2_attrs, +}; + static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); @@ -615,7 +778,10 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d if (r) goto fail2; - r = ati_remote2_setup(ar2); + ar2->channel_mask = channel_mask; + ar2->mode_mask = mode_mask; + + r = ati_remote2_setup(ar2, ar2->channel_mask); if (r) goto fail2; @@ -624,19 +790,24 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name)); - r = ati_remote2_input_init(ar2); + r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group); if (r) goto fail2; + r = ati_remote2_input_init(ar2); + if (r) + goto fail3; + usb_set_intfdata(interface, ar2); interface->needs_remote_wakeup = 1; return 0; + fail3: + sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group); fail2: ati_remote2_urb_cleanup(ar2); - usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); fail1: kfree(ar2); @@ -657,6 +828,8 @@ static void ati_remote2_disconnect(struct usb_interface *interface) input_unregister_device(ar2->idev); + sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group); + ati_remote2_urb_cleanup(ar2); usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); @@ -715,6 +888,78 @@ static int ati_remote2_resume(struct usb_interface *interface) return r; } +static int ati_remote2_reset_resume(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + int r = 0; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + r = ati_remote2_setup(ar2, ar2->channel_mask); + if (r) + goto out; + + if (ar2->flags & ATI_REMOTE2_OPENED) + r = ati_remote2_submit_urbs(ar2); + + if (!r) + ar2->flags &= ~ATI_REMOTE2_SUSPENDED; + + out: + mutex_unlock(&ati_remote2_mutex); + + return r; +} + +static int ati_remote2_pre_reset(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (ar2->flags == ATI_REMOTE2_OPENED) + ati_remote2_kill_urbs(ar2); + + return 0; +} + +static int ati_remote2_post_reset(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + int r = 0; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + if (ar2->flags == ATI_REMOTE2_OPENED) + r = ati_remote2_submit_urbs(ar2); + + mutex_unlock(&ati_remote2_mutex); + + return r; +} + static int __init ati_remote2_init(void) { int r; diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c new file mode 100644 index 00000000000..e2c7f622a0b --- /dev/null +++ b/drivers/input/misc/rb532_button.c @@ -0,0 +1,120 @@ +/* + * Support for the S1 button on Routerboard 532 + * + * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org> + */ + +#include <linux/input-polldev.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/mach-rc32434/gpio.h> +#include <asm/mach-rc32434/rb.h> + +#define DRV_NAME "rb532-button" + +#define RB532_BTN_RATE 100 /* msec */ +#define RB532_BTN_KSYM BTN_0 + +/* The S1 button state is provided by GPIO pin 1. But as this + * pin is also used for uart input as alternate function, the + * operational modes must be switched first: + * 1) disable uart using set_latch_u5() + * 2) turn off alternate function implicitly through + * gpio_direction_input() + * 3) read the GPIO's current value + * 4) undo step 2 by enabling alternate function (in this + * mode the GPIO direction is fixed, so no change needed) + * 5) turn on uart again + * The GPIO value occurs to be inverted, so pin high means + * button is not pressed. + */ +static bool rb532_button_pressed(void) +{ + int val; + + set_latch_u5(0, LO_FOFF); + gpio_direction_input(GPIO_BTN_S1); + + val = gpio_get_value(GPIO_BTN_S1); + + rb532_gpio_set_func(GPIO_BTN_S1); + set_latch_u5(LO_FOFF, 0); + + return !val; +} + +static void rb532_button_poll(struct input_polled_dev *poll_dev) +{ + input_report_key(poll_dev->input, RB532_BTN_KSYM, + rb532_button_pressed()); + input_sync(poll_dev->input); +} + +static int __devinit rb532_button_probe(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev; + int error; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) + return -ENOMEM; + + poll_dev->poll = rb532_button_poll; + poll_dev->poll_interval = RB532_BTN_RATE; + + poll_dev->input->name = "rb532 button"; + poll_dev->input->phys = "rb532/button0"; + poll_dev->input->id.bustype = BUS_HOST; + poll_dev->input->dev.parent = &pdev->dev; + + dev_set_drvdata(&pdev->dev, poll_dev); + + input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM); + + error = input_register_polled_device(poll_dev); + if (error) { + input_free_polled_device(poll_dev); + return error; + } + + return 0; +} + +static int __devexit rb532_button_remove(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev); + + input_unregister_polled_device(poll_dev); + input_free_polled_device(poll_dev); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static struct platform_driver rb532_button_driver = { + .probe = rb532_button_probe, + .remove = __devexit_p(rb532_button_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init rb532_button_init(void) +{ + return platform_driver_register(&rb532_button_driver); +} + +static void __exit rb532_button_exit(void) +{ + platform_driver_unregister(&rb532_button_driver); +} + +module_init(rb532_button_init); +module_exit(rb532_button_exit); + +MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Support for S1 button on Routerboard 532"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c new file mode 100644 index 00000000000..5bb3ab51b8c --- /dev/null +++ b/drivers/input/misc/rotary_encoder.c @@ -0,0 +1,221 @@ +/* + * rotary_encoder.c + * + * (c) 2009 Daniel Mack <daniel@caiaq.de> + * + * state machine code inspired by code from Tim Ruetz + * + * A generic driver for rotary encoders connected to GPIO lines. + * See file:Documentation/input/rotary_encoder.txt for more information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/rotary_encoder.h> + +#define DRV_NAME "rotary-encoder" + +struct rotary_encoder { + unsigned int irq_a; + unsigned int irq_b; + unsigned int pos; + unsigned int armed; + unsigned int dir; + struct input_dev *input; + struct rotary_encoder_platform_data *pdata; +}; + +static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) +{ + struct rotary_encoder *encoder = dev_id; + struct rotary_encoder_platform_data *pdata = encoder->pdata; + int a = !!gpio_get_value(pdata->gpio_a); + int b = !!gpio_get_value(pdata->gpio_b); + int state; + + a ^= pdata->inverted_a; + b ^= pdata->inverted_b; + state = (a << 1) | b; + + switch (state) { + + case 0x0: + if (!encoder->armed) + break; + + if (encoder->dir) { + /* turning counter-clockwise */ + encoder->pos += pdata->steps; + encoder->pos--; + encoder->pos %= pdata->steps; + } else { + /* turning clockwise */ + encoder->pos++; + encoder->pos %= pdata->steps; + } + + input_report_abs(encoder->input, pdata->axis, encoder->pos); + input_sync(encoder->input); + + encoder->armed = 0; + break; + + case 0x1: + case 0x2: + if (encoder->armed) + encoder->dir = state - 1; + break; + + case 0x3: + encoder->armed = 1; + break; + } + + return IRQ_HANDLED; +} + +static int __devinit rotary_encoder_probe(struct platform_device *pdev) +{ + struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + struct rotary_encoder *encoder; + struct input_dev *input; + int err; + + if (!pdata || !pdata->steps) { + dev_err(&pdev->dev, "invalid platform data\n"); + return -ENOENT; + } + + encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); + input = input_allocate_device(); + if (!encoder || !input) { + dev_err(&pdev->dev, "failed to allocate memory for device\n"); + err = -ENOMEM; + goto exit_free_mem; + } + + encoder->input = input; + encoder->pdata = pdata; + encoder->irq_a = gpio_to_irq(pdata->gpio_a); + encoder->irq_b = gpio_to_irq(pdata->gpio_b); + + /* create and register the input driver */ + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + input->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(encoder->input, + pdata->axis, 0, pdata->steps, 0, 1); + + err = input_register_device(input); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto exit_free_mem; + } + + /* request the GPIOs */ + err = gpio_request(pdata->gpio_a, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "unable to request GPIO %d\n", + pdata->gpio_a); + goto exit_unregister_input; + } + + err = gpio_request(pdata->gpio_b, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "unable to request GPIO %d\n", + pdata->gpio_b); + goto exit_free_gpio_a; + } + + /* request the IRQs */ + err = request_irq(encoder->irq_a, &rotary_encoder_irq, + IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, + DRV_NAME, encoder); + if (err) { + dev_err(&pdev->dev, "unable to request IRQ %d\n", + encoder->irq_a); + goto exit_free_gpio_b; + } + + err = request_irq(encoder->irq_b, &rotary_encoder_irq, + IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, + DRV_NAME, encoder); + if (err) { + dev_err(&pdev->dev, "unable to request IRQ %d\n", + encoder->irq_b); + goto exit_free_irq_a; + } + + platform_set_drvdata(pdev, encoder); + + return 0; + +exit_free_irq_a: + free_irq(encoder->irq_a, encoder); +exit_free_gpio_b: + gpio_free(pdata->gpio_b); +exit_free_gpio_a: + gpio_free(pdata->gpio_a); +exit_unregister_input: + input_unregister_device(input); + input = NULL; /* so we don't try to free it */ +exit_free_mem: + input_free_device(input); + kfree(encoder); + return err; +} + +static int __devexit rotary_encoder_remove(struct platform_device *pdev) +{ + struct rotary_encoder *encoder = platform_get_drvdata(pdev); + struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + + free_irq(encoder->irq_a, encoder); + free_irq(encoder->irq_b, encoder); + gpio_free(pdata->gpio_a); + gpio_free(pdata->gpio_b); + input_unregister_device(encoder->input); + platform_set_drvdata(pdev, NULL); + kfree(encoder); + + return 0; +} + +static struct platform_driver rotary_encoder_driver = { + .probe = rotary_encoder_probe, + .remove = __devexit_p(rotary_encoder_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + } +}; + +static int __init rotary_encoder_init(void) +{ + return platform_driver_register(&rotary_encoder_driver); +} + +static void __exit rotary_encoder_exit(void) +{ + platform_driver_unregister(&rotary_encoder_driver); +} + +module_init(rotary_encoder_init); +module_exit(rotary_encoder_exit); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DESCRIPTION("GPIO rotary encoder driver"); +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 4f38e6f7dfd..c66cc3d08c2 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -292,4 +292,15 @@ config MOUSE_PXA930_TRKBALL help Say Y here to support PXA930 Trackball mouse. +config MOUSE_MAPLE + tristate "Maple mouse (for the Dreamcast)" + depends on MAPLE + help + This driver supports the Maple mouse on the SEGA Dreamcast. + + Most Dreamcast users, who have a mouse, will say Y here. + + To compile this driver as a module choose M here: the module will be + called maplemouse. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 8c8a1f236e2..472189468d6 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -6,18 +6,19 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o -obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o -obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o +obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o +obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o +obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o +obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o +obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o -obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o -obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o psmouse-objs := psmouse-base.o synaptics.o diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 55cd0fa6833..a1ad2f1a7bb 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -472,7 +472,7 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) return -EIO; } - hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]); + hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]); /* HGPK signature: 0x67, 0x00, 0x<model> */ if (param[0] != 0x67 || param[1] != 0x00) diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c new file mode 100644 index 00000000000..d196abfb68b --- /dev/null +++ b/drivers/input/mouse/maplemouse.c @@ -0,0 +1,147 @@ +/* + * SEGA Dreamcast mouse driver + * Based on drivers/usb/usbmouse.c + * + * Copyright Yaegashi Takeshi, 2001 + * Adrian McMenamin, 2008 + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/maple.h> + +MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>"); +MODULE_DESCRIPTION("SEGA Dreamcast mouse driver"); +MODULE_LICENSE("GPL"); + +struct dc_mouse { + struct input_dev *dev; + struct maple_device *mdev; +}; + +static void dc_mouse_callback(struct mapleq *mq) +{ + int buttons, relx, rely, relz; + struct maple_device *mapledev = mq->dev; + struct dc_mouse *mse = maple_get_drvdata(mapledev); + struct input_dev *dev = mse->dev; + unsigned char *res = mq->recvbuf; + + buttons = ~res[8]; + relx = *(unsigned short *)(res + 12) - 512; + rely = *(unsigned short *)(res + 14) - 512; + relz = *(unsigned short *)(res + 16) - 512; + + input_report_key(dev, BTN_LEFT, buttons & 4); + input_report_key(dev, BTN_MIDDLE, buttons & 9); + input_report_key(dev, BTN_RIGHT, buttons & 2); + input_report_rel(dev, REL_X, relx); + input_report_rel(dev, REL_Y, rely); + input_report_rel(dev, REL_WHEEL, relz); + input_sync(dev); +} + +static int dc_mouse_open(struct input_dev *dev) +{ + struct dc_mouse *mse = dev->dev.platform_data; + + maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50, + MAPLE_FUNC_MOUSE); + + return 0; +} + +static void dc_mouse_close(struct input_dev *dev) +{ + struct dc_mouse *mse = dev->dev.platform_data; + + maple_getcond_callback(mse->mdev, dc_mouse_callback, 0, + MAPLE_FUNC_MOUSE); +} + + +static int __devinit probe_maple_mouse(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct maple_driver *mdrv = to_maple_driver(dev->driver); + struct input_dev *input_dev; + struct dc_mouse *mse; + int error; + + mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!mse || !input_dev) { + error = -ENOMEM; + goto fail; + } + + mse->dev = input_dev; + mse->mdev = mdev; + + input_set_drvdata(input_dev, mse); + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); + input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | + BIT_MASK(REL_WHEEL); + input_dev->name = mdev->product_name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = dc_mouse_open; + input_dev->close = dc_mouse_close; + + mdev->driver = mdrv; + maple_set_drvdata(mdev, mse); + + error = input_register_device(input_dev); + if (error) + goto fail; + + return 0; + +fail: + input_free_device(input_dev); + maple_set_drvdata(mdev, NULL); + kfree(mse); + mdev->driver = NULL; + return error; +} + +static int __devexit remove_maple_mouse(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct dc_mouse *mse = maple_get_drvdata(mdev); + + mdev->callback = NULL; + input_unregister_device(mse->dev); + maple_set_drvdata(mdev, NULL); + kfree(mse); + + return 0; +} + +static struct maple_driver dc_mouse_driver = { + .function = MAPLE_FUNC_MOUSE, + .drv = { + .name = "Dreamcast_mouse", + .probe = probe_maple_mouse, + .remove = __devexit_p(remove_maple_mouse), + }, +}; + +static int __init dc_mouse_init(void) +{ + return maple_driver_register(&dc_mouse_driver); +} + +static void __exit dc_mouse_exit(void) +{ + maple_driver_unregister(&dc_mouse_driver); +} + +module_init(dc_mouse_init); +module_exit(dc_mouse_exit); diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index fd09c8df81f..f63995f854f 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -111,11 +111,8 @@ static int __init pc110pad_init(void) struct pci_dev *dev; int err; - dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - pci_dev_put(dev); + if (!no_pci_devices()) return -ENODEV; - } if (!request_region(pc110pad_io, 4, "pc110pad")) { printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 6fa2deff744..83ed2d56b92 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -151,6 +151,14 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "01"), }, }, + { + .ident = "HP DV9700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + }, + }, { } }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index bb6486a8c07..b01fd61dadc 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -29,6 +29,51 @@ config TOUCHSCREEN_ADS7846 To compile this driver as a module, choose M here: the module will be called ads7846. +config TOUCHSCREEN_AD7877 + tristate "AD7877 based touchscreens" + depends on SPI_MASTER + help + Say Y here if you have a touchscreen interface using the + AD7877 controller, and your board-specific initialization + code includes that in its table of SPI devices. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7877. + +config TOUCHSCREEN_AD7879_I2C + tristate "AD7879 based touchscreens: AD7879-1 I2C Interface" + depends on I2C + select TOUCHSCREEN_AD7879 + help + Say Y here if you have a touchscreen interface using the + AD7879-1 controller, and your board-specific initialization + code includes that in its table of I2C devices. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7879. + +config TOUCHSCREEN_AD7879_SPI + tristate "AD7879 based touchscreens: AD7879 SPI Interface" + depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n + select TOUCHSCREEN_AD7879 + help + Say Y here if you have a touchscreen interface using the + AD7879 controller, and your board-specific initialization + code includes that in its table of SPI devices. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7879. + +config TOUCHSCREEN_AD7879 + tristate + default n + config TOUCHSCREEN_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" depends on SA1100_BITSY @@ -308,6 +353,19 @@ config TOUCHSCREEN_WM97XX_MAINSTONE To compile this driver as a module, choose M here: the module will be called mainstone-wm97xx. +config TOUCHSCREEN_WM97XX_ZYLONITE + tristate "Zylonite accelerated touch" + depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE + select TOUCHSCREEN_WM9713 + help + Say Y here for support for streaming mode with the touchscreen + on Zylonite systems. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called zylonite-wm97xx. + config TOUCHSCREEN_USB_COMPOSITE tristate "USB Touchscreen Driver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d3375aff46f..6700f7b9d16 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -6,6 +6,8 @@ wm97xx-ts-y := wm97xx-core.o +obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o +obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o @@ -34,3 +36,4 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c new file mode 100644 index 00000000000..e4728a28f49 --- /dev/null +++ b/drivers/input/touchscreen/ad7877.c @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc. + * + * Description: AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver + * Based on: ads7846.c + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * History: + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * Various changes: Imre Deak <imre.deak@nokia.com> + * + * Using code from: + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + */ + + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/spi/ad7877.h> +#include <asm/irq.h> + +#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50) + +#define MAX_SPI_FREQ_HZ 20000000 +#define MAX_12BIT ((1<<12)-1) + +#define AD7877_REG_ZEROS 0 +#define AD7877_REG_CTRL1 1 +#define AD7877_REG_CTRL2 2 +#define AD7877_REG_ALERT 3 +#define AD7877_REG_AUX1HIGH 4 +#define AD7877_REG_AUX1LOW 5 +#define AD7877_REG_BAT1HIGH 6 +#define AD7877_REG_BAT1LOW 7 +#define AD7877_REG_BAT2HIGH 8 +#define AD7877_REG_BAT2LOW 9 +#define AD7877_REG_TEMP1HIGH 10 +#define AD7877_REG_TEMP1LOW 11 +#define AD7877_REG_SEQ0 12 +#define AD7877_REG_SEQ1 13 +#define AD7877_REG_DAC 14 +#define AD7877_REG_NONE1 15 +#define AD7877_REG_EXTWRITE 15 +#define AD7877_REG_XPLUS 16 +#define AD7877_REG_YPLUS 17 +#define AD7877_REG_Z2 18 +#define AD7877_REG_aux1 19 +#define AD7877_REG_aux2 20 +#define AD7877_REG_aux3 21 +#define AD7877_REG_bat1 22 +#define AD7877_REG_bat2 23 +#define AD7877_REG_temp1 24 +#define AD7877_REG_temp2 25 +#define AD7877_REG_Z1 26 +#define AD7877_REG_GPIOCTRL1 27 +#define AD7877_REG_GPIOCTRL2 28 +#define AD7877_REG_GPIODATA 29 +#define AD7877_REG_NONE2 30 +#define AD7877_REG_NONE3 31 + +#define AD7877_SEQ_YPLUS_BIT (1<<11) +#define AD7877_SEQ_XPLUS_BIT (1<<10) +#define AD7877_SEQ_Z2_BIT (1<<9) +#define AD7877_SEQ_AUX1_BIT (1<<8) +#define AD7877_SEQ_AUX2_BIT (1<<7) +#define AD7877_SEQ_AUX3_BIT (1<<6) +#define AD7877_SEQ_BAT1_BIT (1<<5) +#define AD7877_SEQ_BAT2_BIT (1<<4) +#define AD7877_SEQ_TEMP1_BIT (1<<3) +#define AD7877_SEQ_TEMP2_BIT (1<<2) +#define AD7877_SEQ_Z1_BIT (1<<1) + +enum { + AD7877_SEQ_YPOS = 0, + AD7877_SEQ_XPOS = 1, + AD7877_SEQ_Z2 = 2, + AD7877_SEQ_AUX1 = 3, + AD7877_SEQ_AUX2 = 4, + AD7877_SEQ_AUX3 = 5, + AD7877_SEQ_BAT1 = 6, + AD7877_SEQ_BAT2 = 7, + AD7877_SEQ_TEMP1 = 8, + AD7877_SEQ_TEMP2 = 9, + AD7877_SEQ_Z1 = 10, + AD7877_NR_SENSE = 11, +}; + +/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */ +#define AD7877_DAC_CONF 0x1 + +/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */ +#define AD7877_EXTW_GPIO_3_CONF 0x1C4 +#define AD7877_EXTW_GPIO_DATA 0x200 + +/* Control REG 2 */ +#define AD7877_TMR(x) ((x & 0x3) << 0) +#define AD7877_REF(x) ((x & 0x1) << 2) +#define AD7877_POL(x) ((x & 0x1) << 3) +#define AD7877_FCD(x) ((x & 0x3) << 4) +#define AD7877_PM(x) ((x & 0x3) << 6) +#define AD7877_ACQ(x) ((x & 0x3) << 8) +#define AD7877_AVG(x) ((x & 0x3) << 10) + +/* Control REG 1 */ +#define AD7877_SER (1 << 11) /* non-differential */ +#define AD7877_DFR (0 << 11) /* differential */ + +#define AD7877_MODE_NOC (0) /* Do not convert */ +#define AD7877_MODE_SCC (1) /* Single channel conversion */ +#define AD7877_MODE_SEQ0 (2) /* Sequence 0 in Slave Mode */ +#define AD7877_MODE_SEQ1 (3) /* Sequence 1 in Master Mode */ + +#define AD7877_CHANADD(x) ((x&0xF)<<7) +#define AD7877_READADD(x) ((x)<<2) +#define AD7877_WRITEADD(x) ((x)<<12) + +#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \ + AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \ + AD7877_READADD(AD7877_REG_ ## x)) + +#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \ + AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT) + +/* + * Non-touchscreen sensors only use single-ended conversions. + */ + +struct ser_req { + u16 reset; + u16 ref_on; + u16 command; + u16 sample; + struct spi_message msg; + struct spi_transfer xfer[6]; +}; + +struct ad7877 { + struct input_dev *input; + char phys[32]; + + struct spi_device *spi; + u16 model; + u16 vref_delay_usecs; + u16 x_plate_ohms; + u16 pressure_max; + + u16 cmd_crtl1; + u16 cmd_crtl2; + u16 cmd_dummy; + u16 dac; + + u8 stopacq_polarity; + u8 first_conversion_delay; + u8 acquisition_time; + u8 averaging; + u8 pen_down_acc_interval; + + u16 conversion_data[AD7877_NR_SENSE]; + + struct spi_transfer xfer[AD7877_NR_SENSE + 2]; + struct spi_message msg; + + struct mutex mutex; + unsigned disabled:1; /* P: mutex */ + unsigned gpio3:1; /* P: mutex */ + unsigned gpio4:1; /* P: mutex */ + + spinlock_t lock; + struct timer_list timer; /* P: lock */ + unsigned pending:1; /* P: lock */ +}; + +static int gpio3; +module_param(gpio3, int, 0); +MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3"); + +/* + * ad7877_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done using spi_async() in the interrupt handler. + */ + +static int ad7877_read(struct spi_device *spi, u16 reg) +{ + struct ser_req *req; + int status, ret; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) | + AD7877_READADD(reg)); + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 2; + + req->xfer[1].rx_buf = &req->sample; + req->xfer[1].len = 2; + + spi_message_add_tail(&req->xfer[0], &req->msg); + spi_message_add_tail(&req->xfer[1], &req->msg); + + status = spi_sync(spi, &req->msg); + ret = status ? : req->sample; + + kfree(req); + + return ret; +} + +static int ad7877_write(struct spi_device *spi, u16 reg, u16 val) +{ + struct ser_req *req; + int status; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command = (u16) (AD7877_WRITEADD(reg) | (val & MAX_12BIT)); + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 2; + + spi_message_add_tail(&req->xfer[0], &req->msg); + + status = spi_sync(spi, &req->msg); + + kfree(req); + + return status; +} + +static int ad7877_read_adc(struct spi_device *spi, unsigned command) +{ + struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ser_req *req; + int status; + int sample; + int i; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + /* activate reference, so it has time to settle; */ + req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) | + AD7877_POL(ts->stopacq_polarity) | + AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) | + AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0); + + req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC; + + req->command = (u16) command; + + req->xfer[0].tx_buf = &req->reset; + req->xfer[0].len = 2; + + req->xfer[1].tx_buf = &req->ref_on; + req->xfer[1].len = 2; + req->xfer[1].delay_usecs = ts->vref_delay_usecs; + + req->xfer[2].tx_buf = &req->command; + req->xfer[2].len = 2; + req->xfer[2].delay_usecs = ts->vref_delay_usecs; + + req->xfer[3].rx_buf = &req->sample; + req->xfer[3].len = 2; + + req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/ + req->xfer[4].len = 2; + + req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/ + req->xfer[5].len = 2; + + /* group all the transfers together, so we can't interfere with + * reading touchscreen state; disable penirq while sampling + */ + for (i = 0; i < 6; i++) + spi_message_add_tail(&req->xfer[i], &req->msg); + + status = spi_sync(spi, &req->msg); + sample = req->sample; + + kfree(req); + + return status ? : sample; +} + +static void ad7877_rx(struct ad7877 *ts) +{ + struct input_dev *input_dev = ts->input; + unsigned Rt; + u16 x, y, z1, z2; + + x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT; + y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT; + z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT; + z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT; + + /* + * The samples processed here are already preprocessed by the AD7877. + * The preprocessing function consists of an averaging filter. + * The combination of 'first conversion delay' and averaging provides a robust solution, + * discarding the spurious noise in the signal and keeping only the data of interest. + * The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.h) + * Other user-programmable conversion controls include variable acquisition time, + * and first conversion delay. Up to 16 averages can be taken per conversion. + */ + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + Rt = (z2 - z1) * x * ts->x_plate_ohms; + Rt /= z1; + Rt = (Rt + 2047) >> 12; + + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, Rt); + input_sync(input_dev); + } +} + +static inline void ad7877_ts_event_release(struct ad7877 *ts) +{ + struct input_dev *input_dev = ts->input; + + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); +} + +static void ad7877_timer(unsigned long handle) +{ + struct ad7877 *ts = (void *)handle; + + ad7877_ts_event_release(ts); +} + +static irqreturn_t ad7877_irq(int irq, void *handle) +{ + struct ad7877 *ts = handle; + unsigned long flags; + int status; + + /* + * The repeated conversion sequencer controlled by TMR kicked off + * too fast. We ignore the last and process the sample sequence + * currently in the queue. It can't be older than 9.4ms, and we + * need to avoid that ts->msg doesn't get issued twice while in work. + */ + + spin_lock_irqsave(&ts->lock, flags); + if (!ts->pending) { + ts->pending = 1; + + status = spi_async(ts->spi, &ts->msg); + if (status) + dev_err(&ts->spi->dev, "spi_sync --> %d\n", status); + } + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + +static void ad7877_callback(void *_ts) +{ + struct ad7877 *ts = _ts; + + spin_lock_irq(&ts->lock); + + ad7877_rx(ts); + ts->pending = 0; + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); + + spin_unlock_irq(&ts->lock); +} + +static void ad7877_disable(struct ad7877 *ts) +{ + mutex_lock(&ts->mutex); + + if (!ts->disabled) { + ts->disabled = 1; + disable_irq(ts->spi->irq); + + /* Wait for spi_async callback */ + while (ts->pending) + msleep(1); + + if (del_timer_sync(&ts->timer)) + ad7877_ts_event_release(ts); + } + + /* we know the chip's in lowpower mode since we always + * leave it that way after every request + */ + + mutex_unlock(&ts->mutex); +} + +static void ad7877_enable(struct ad7877 *ts) +{ + mutex_lock(&ts->mutex); + + if (ts->disabled) { + ts->disabled = 0; + enable_irq(ts->spi->irq); + } + + mutex_unlock(&ts->mutex); +} + +#define SHOW(name) static ssize_t \ +name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct ad7877 *ts = dev_get_drvdata(dev); \ + ssize_t v = ad7877_read_adc(ts->spi, \ + AD7877_READ_CHAN(name)); \ + if (v < 0) \ + return v; \ + return sprintf(buf, "%u\n", (unsigned) v); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); + +SHOW(aux1) +SHOW(aux2) +SHOW(aux3) +SHOW(bat1) +SHOW(bat2) +SHOW(temp1) +SHOW(temp2) + +static ssize_t ad7877_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t ad7877_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + if (val) + ad7877_disable(ts); + else + ad7877_enable(ts); + + return count; +} + +static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store); + +static ssize_t ad7877_dac_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->dac); +} + +static ssize_t ad7877_dac_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->dac = val & 0xFF; + ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF); + mutex_unlock(&ts->mutex); + + return count; +} + +static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store); + +static ssize_t ad7877_gpio3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->gpio3); +} + +static ssize_t ad7877_gpio3_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->gpio3 = !!val; + ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | + (ts->gpio4 << 4) | (ts->gpio3 << 5)); + mutex_unlock(&ts->mutex); + + return count; +} + +static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store); + +static ssize_t ad7877_gpio4_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->gpio4); +} + +static ssize_t ad7877_gpio4_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7877 *ts = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->gpio4 = !!val; + ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA | + (ts->gpio4 << 4) | (ts->gpio3 << 5)); + mutex_unlock(&ts->mutex); + + return count; +} + +static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store); + +static struct attribute *ad7877_attributes[] = { + &dev_attr_temp1.attr, + &dev_attr_temp2.attr, + &dev_attr_aux1.attr, + &dev_attr_aux2.attr, + &dev_attr_bat1.attr, + &dev_attr_bat2.attr, + &dev_attr_disable.attr, + &dev_attr_dac.attr, + &dev_attr_gpio4.attr, + NULL +}; + +static const struct attribute_group ad7877_attr_group = { + .attrs = ad7877_attributes, +}; + +static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) +{ + struct spi_message *m; + int i; + + ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) | + AD7877_POL(ts->stopacq_polarity) | + AD7877_AVG(ts->averaging) | AD7877_PM(1) | + AD7877_TMR(ts->pen_down_acc_interval) | + AD7877_ACQ(ts->acquisition_time) | + AD7877_FCD(ts->first_conversion_delay); + + ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2); + + ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) | + AD7877_READADD(AD7877_REG_XPLUS-1) | + AD7877_MODE_SEQ1 | AD7877_DFR; + + ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1); + + ts->cmd_dummy = 0; + + m = &ts->msg; + + spi_message_init(m); + + m->complete = ad7877_callback; + m->context = ts; + + ts->xfer[0].tx_buf = &ts->cmd_crtl1; + ts->xfer[0].len = 2; + + spi_message_add_tail(&ts->xfer[0], m); + + ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */ + ts->xfer[1].len = 2; + + spi_message_add_tail(&ts->xfer[1], m); + + for (i = 0; i < 11; i++) { + ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i]; + ts->xfer[i + 2].len = 2; + spi_message_add_tail(&ts->xfer[i + 2], m); + } +} + +static int __devinit ad7877_probe(struct spi_device *spi) +{ + struct ad7877 *ts; + struct input_dev *input_dev; + struct ad7877_platform_data *pdata = spi->dev.platform_data; + int err; + u16 verify; + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified SPI CLK frequency */ + if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { + dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz); + return -EINVAL; + } + + ts = kzalloc(sizeof(struct ad7877), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + ts->spi = spi; + ts->input = input_dev; + + setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts); + mutex_init(&ts->mutex); + spin_lock_init(&ts->lock); + + ts->model = pdata->model ? : 7877; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + ts->stopacq_polarity = pdata->stopacq_polarity; + ts->first_conversion_delay = pdata->first_conversion_delay; + ts->acquisition_time = pdata->acquisition_time; + ts->averaging = pdata->averaging; + ts->pen_down_acc_interval = pdata->pen_down_acc_interval; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + + input_dev->name = "AD7877 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); + + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE); + + verify = ad7877_read(spi, AD7877_REG_SEQ1); + + if (verify != AD7877_MM_SEQUENCE){ + dev_err(&spi->dev, "%s: Failed to probe %s\n", + dev_name(&spi->dev), input_dev->name); + err = -ENODEV; + goto err_free_mem; + } + + if (gpio3) + ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF); + + ad7877_setup_ts_def_msg(spi, ts); + + /* Request AD7877 /DAV GPIO interrupt */ + + err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING | + IRQF_SAMPLE_RANDOM, spi->dev.driver->name, ts); + if (err) { + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + goto err_free_mem; + } + + err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group); + if (err) + goto err_free_irq; + + err = device_create_file(&spi->dev, + gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); + if (err) + goto err_remove_attr_group; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr; + + return 0; + +err_remove_attr: + device_remove_file(&spi->dev, + gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); +err_remove_attr_group: + sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); +err_free_irq: + free_irq(spi->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + dev_set_drvdata(&spi->dev, NULL); + return err; +} + +static int __devexit ad7877_remove(struct spi_device *spi) +{ + struct ad7877 *ts = dev_get_drvdata(&spi->dev); + + sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); + device_remove_file(&spi->dev, + gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); + + ad7877_disable(ts); + free_irq(ts->spi->irq, ts); + + input_unregister_device(ts->input); + kfree(ts); + + dev_dbg(&spi->dev, "unregistered touchscreen\n"); + dev_set_drvdata(&spi->dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int ad7877_suspend(struct spi_device *spi, pm_message_t message) +{ + struct ad7877 *ts = dev_get_drvdata(&spi->dev); + + ad7877_disable(ts); + + return 0; +} + +static int ad7877_resume(struct spi_device *spi) +{ + struct ad7877 *ts = dev_get_drvdata(&spi->dev); + + ad7877_enable(ts); + + return 0; +} +#else +#define ad7877_suspend NULL +#define ad7877_resume NULL +#endif + +static struct spi_driver ad7877_driver = { + .driver = { + .name = "ad7877", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad7877_probe, + .remove = __devexit_p(ad7877_remove), + .suspend = ad7877_suspend, + .resume = ad7877_resume, +}; + +static int __init ad7877_init(void) +{ + return spi_register_driver(&ad7877_driver); +} +module_init(ad7877_init); + +static void __exit ad7877_exit(void) +{ + spi_unregister_driver(&ad7877_driver); +} +module_exit(ad7877_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("AD7877 touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c new file mode 100644 index 00000000000..ea4c61d6868 --- /dev/null +++ b/drivers/input/touchscreen/ad7879.c @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2008 Michael Hennerich, Analog Devices Inc. + * + * Description: AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface) + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * History: + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * Various changes: Imre Deak <imre.deak@nokia.com> + * + * Using code from: + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * - ad7877.c + * Copyright (C) 2006-2008 Analog Devices Inc. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/spi/spi.h> +#include <linux/i2c.h> + +#include <linux/spi/ad7879.h> + +#define AD7879_REG_ZEROS 0 +#define AD7879_REG_CTRL1 1 +#define AD7879_REG_CTRL2 2 +#define AD7879_REG_CTRL3 3 +#define AD7879_REG_AUX1HIGH 4 +#define AD7879_REG_AUX1LOW 5 +#define AD7879_REG_TEMP1HIGH 6 +#define AD7879_REG_TEMP1LOW 7 +#define AD7879_REG_XPLUS 8 +#define AD7879_REG_YPLUS 9 +#define AD7879_REG_Z1 10 +#define AD7879_REG_Z2 11 +#define AD7879_REG_AUXVBAT 12 +#define AD7879_REG_TEMP 13 +#define AD7879_REG_REVID 14 + +/* Control REG 1 */ +#define AD7879_TMR(x) ((x & 0xFF) << 0) +#define AD7879_ACQ(x) ((x & 0x3) << 8) +#define AD7879_MODE_NOC (0 << 10) /* Do not convert */ +#define AD7879_MODE_SCC (1 << 10) /* Single channel conversion */ +#define AD7879_MODE_SEQ0 (2 << 10) /* Sequence 0 in Slave Mode */ +#define AD7879_MODE_SEQ1 (3 << 10) /* Sequence 1 in Master Mode */ +#define AD7879_MODE_INT (1 << 15) /* PENIRQ disabled INT enabled */ + +/* Control REG 2 */ +#define AD7879_FCD(x) ((x & 0x3) << 0) +#define AD7879_RESET (1 << 4) +#define AD7879_MFS(x) ((x & 0x3) << 5) +#define AD7879_AVG(x) ((x & 0x3) << 7) +#define AD7879_SER (1 << 9) /* non-differential */ +#define AD7879_DFR (0 << 9) /* differential */ +#define AD7879_GPIOPOL (1 << 10) +#define AD7879_GPIODIR (1 << 11) +#define AD7879_GPIO_DATA (1 << 12) +#define AD7879_GPIO_EN (1 << 13) +#define AD7879_PM(x) ((x & 0x3) << 14) +#define AD7879_PM_SHUTDOWN (0) +#define AD7879_PM_DYN (1) +#define AD7879_PM_FULLON (2) + +/* Control REG 3 */ +#define AD7879_TEMPMASK_BIT (1<<15) +#define AD7879_AUXVBATMASK_BIT (1<<14) +#define AD7879_INTMODE_BIT (1<<13) +#define AD7879_GPIOALERTMASK_BIT (1<<12) +#define AD7879_AUXLOW_BIT (1<<11) +#define AD7879_AUXHIGH_BIT (1<<10) +#define AD7879_TEMPLOW_BIT (1<<9) +#define AD7879_TEMPHIGH_BIT (1<<8) +#define AD7879_YPLUS_BIT (1<<7) +#define AD7879_XPLUS_BIT (1<<6) +#define AD7879_Z1_BIT (1<<5) +#define AD7879_Z2_BIT (1<<4) +#define AD7879_AUX_BIT (1<<3) +#define AD7879_VBAT_BIT (1<<2) +#define AD7879_TEMP_BIT (1<<1) + +enum { + AD7879_SEQ_XPOS = 0, + AD7879_SEQ_YPOS = 1, + AD7879_SEQ_Z1 = 2, + AD7879_SEQ_Z2 = 3, + AD7879_NR_SENSE = 4, +}; + +#define MAX_12BIT ((1<<12)-1) +#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50) + +#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) +#define AD7879_DEVID 0x7A +typedef struct spi_device bus_device; +#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) +#define AD7879_DEVID 0x79 +typedef struct i2c_client bus_device; +#endif + +struct ad7879 { + bus_device *bus; + struct input_dev *input; + struct work_struct work; + struct timer_list timer; + + struct mutex mutex; + unsigned disabled:1; /* P: mutex */ + +#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) + struct spi_message msg; + struct spi_transfer xfer[AD7879_NR_SENSE + 1]; + u16 cmd; +#endif + u16 conversion_data[AD7879_NR_SENSE]; + char phys[32]; + u8 first_conversion_delay; + u8 acquisition_time; + u8 averaging; + u8 pen_down_acc_interval; + u8 median; + u16 x_plate_ohms; + u16 pressure_max; + u16 gpio_init; + u16 cmd_crtl1; + u16 cmd_crtl2; + u16 cmd_crtl3; + unsigned gpio:1; +}; + +static int ad7879_read(bus_device *, u8); +static int ad7879_write(bus_device *, u8, u16); +static void ad7879_collect(struct ad7879 *); + +static void ad7879_report(struct ad7879 *ts) +{ + struct input_dev *input_dev = ts->input; + unsigned Rt; + u16 x, y, z1, z2; + + x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT; + y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT; + z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; + z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; + + /* + * The samples processed here are already preprocessed by the AD7879. + * The preprocessing function consists of a median and an averaging filter. + * The combination of these two techniques provides a robust solution, + * discarding the spurious noise in the signal and keeping only the data of interest. + * The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h) + * Other user-programmable conversion controls include variable acquisition time, + * and first conversion delay. Up to 16 averages can be taken per conversion. + */ + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + Rt = (z2 - z1) * x * ts->x_plate_ohms; + Rt /= z1; + Rt = (Rt + 2047) >> 12; + + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, Rt); + input_sync(input_dev); + } +} + +static void ad7879_work(struct work_struct *work) +{ + struct ad7879 *ts = container_of(work, struct ad7879, work); + + /* use keventd context to read the result registers */ + ad7879_collect(ts); + ad7879_report(ts); + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); +} + +static void ad7879_ts_event_release(struct ad7879 *ts) +{ + struct input_dev *input_dev = ts->input; + + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); +} + +static void ad7879_timer(unsigned long handle) +{ + struct ad7879 *ts = (void *)handle; + + ad7879_ts_event_release(ts); +} + +static irqreturn_t ad7879_irq(int irq, void *handle) +{ + struct ad7879 *ts = handle; + + /* The repeated conversion sequencer controlled by TMR kicked off too fast. + * We ignore the last and process the sample sequence currently in the queue. + * It can't be older than 9.4ms + */ + + if (!work_pending(&ts->work)) + schedule_work(&ts->work); + + return IRQ_HANDLED; +} + +static void ad7879_setup(struct ad7879 *ts) +{ + ts->cmd_crtl3 = AD7879_YPLUS_BIT | + AD7879_XPLUS_BIT | + AD7879_Z2_BIT | + AD7879_Z1_BIT | + AD7879_TEMPMASK_BIT | + AD7879_AUXVBATMASK_BIT | + AD7879_GPIOALERTMASK_BIT; + + ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR | + AD7879_AVG(ts->averaging) | + AD7879_MFS(ts->median) | + AD7879_FCD(ts->first_conversion_delay) | + ts->gpio_init; + + ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 | + AD7879_ACQ(ts->acquisition_time) | + AD7879_TMR(ts->pen_down_acc_interval); + + ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3); + ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1); +} + +static void ad7879_disable(struct ad7879 *ts) +{ + mutex_lock(&ts->mutex); + + if (!ts->disabled) { + + ts->disabled = 1; + disable_irq(ts->bus->irq); + + cancel_work_sync(&ts->work); + + if (del_timer_sync(&ts->timer)) + ad7879_ts_event_release(ts); + + ad7879_write(ts->bus, AD7879_REG_CTRL2, + AD7879_PM(AD7879_PM_SHUTDOWN)); + } + + mutex_unlock(&ts->mutex); +} + +static void ad7879_enable(struct ad7879 *ts) +{ + mutex_lock(&ts->mutex); + + if (ts->disabled) { + ad7879_setup(ts); + ts->disabled = 0; + enable_irq(ts->bus->irq); + } + + mutex_unlock(&ts->mutex); +} + +static ssize_t ad7879_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t ad7879_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + if (val) + ad7879_disable(ts); + else + ad7879_enable(ts); + + return count; +} + +static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store); + +static ssize_t ad7879_gpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->gpio); +} + +static ssize_t ad7879_gpio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ad7879 *ts = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ts->mutex); + ts->gpio = !!val; + error = ad7879_write(ts->bus, AD7879_REG_CTRL2, + ts->gpio ? + ts->cmd_crtl2 & ~AD7879_GPIO_DATA : + ts->cmd_crtl2 | AD7879_GPIO_DATA); + mutex_unlock(&ts->mutex); + + return error ? : count; +} + +static DEVICE_ATTR(gpio, 0664, ad7879_gpio_show, ad7879_gpio_store); + +static struct attribute *ad7879_attributes[] = { + &dev_attr_disable.attr, + &dev_attr_gpio.attr, + NULL +}; + +static const struct attribute_group ad7879_attr_group = { + .attrs = ad7879_attributes, +}; + +static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) +{ + struct input_dev *input_dev; + struct ad7879_platform_data *pdata = bus->dev.platform_data; + int err; + u16 revid; + + if (!bus->irq) { + dev_err(&bus->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_err(&bus->dev, "no platform data?\n"); + return -ENODEV; + } + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + ts->input = input_dev; + + setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); + INIT_WORK(&ts->work, ad7879_work); + mutex_init(&ts->mutex); + + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + ts->first_conversion_delay = pdata->first_conversion_delay; + ts->acquisition_time = pdata->acquisition_time; + ts->averaging = pdata->averaging; + ts->pen_down_acc_interval = pdata->pen_down_acc_interval; + ts->median = pdata->median; + + if (pdata->gpio_output) + ts->gpio_init = AD7879_GPIO_EN | + (pdata->gpio_default ? 0 : AD7879_GPIO_DATA); + else + ts->gpio_init = AD7879_GPIO_EN | AD7879_GPIODIR; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev)); + + input_dev->name = "AD7879 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->dev.parent = &bus->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); + + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + err = ad7879_write(bus, AD7879_REG_CTRL2, AD7879_RESET); + + if (err < 0) { + dev_err(&bus->dev, "Failed to write %s\n", input_dev->name); + goto err_free_mem; + } + + revid = ad7879_read(bus, AD7879_REG_REVID); + + if ((revid & 0xFF) != AD7879_DEVID) { + dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name); + err = -ENODEV; + goto err_free_mem; + } + + ad7879_setup(ts); + + err = request_irq(bus->irq, ad7879_irq, + IRQF_TRIGGER_FALLING | IRQF_SAMPLE_RANDOM, + bus->dev.driver->name, ts); + + if (err) { + dev_err(&bus->dev, "irq %d busy?\n", bus->irq); + goto err_free_mem; + } + + err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group); + if (err) + goto err_free_irq; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr; + + dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n", + revid >> 8, bus->irq); + + return 0; + +err_remove_attr: + sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group); +err_free_irq: + free_irq(bus->irq, ts); +err_free_mem: + input_free_device(input_dev); + + return err; +} + +static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts) +{ + ad7879_disable(ts); + sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group); + free_irq(ts->bus->irq, ts); + input_unregister_device(ts->input); + dev_dbg(&bus->dev, "unregistered touchscreen\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int ad7879_suspend(bus_device *bus, pm_message_t message) +{ + struct ad7879 *ts = dev_get_drvdata(&bus->dev); + + ad7879_disable(ts); + + return 0; +} + +static int ad7879_resume(bus_device *bus) +{ + struct ad7879 *ts = dev_get_drvdata(&bus->dev); + + ad7879_enable(ts); + + return 0; +} +#else +#define ad7879_suspend NULL +#define ad7879_resume NULL +#endif + +#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE) +#define MAX_SPI_FREQ_HZ 5000000 +#define AD7879_CMD_MAGIC 0xE000 +#define AD7879_CMD_READ (1 << 10) +#define AD7879_WRITECMD(reg) (AD7879_CMD_MAGIC | (reg & 0xF)) +#define AD7879_READCMD(reg) (AD7879_CMD_MAGIC | AD7879_CMD_READ | (reg & 0xF)) + +struct ser_req { + u16 command; + u16 data; + struct spi_message msg; + struct spi_transfer xfer[2]; +}; + +/* + * ad7879_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done in ad7879_collect(). + */ + +static int ad7879_read(struct spi_device *spi, u8 reg) +{ + struct ser_req *req; + int status, ret; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command = (u16) AD7879_READCMD(reg); + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 2; + + req->xfer[1].rx_buf = &req->data; + req->xfer[1].len = 2; + + spi_message_add_tail(&req->xfer[0], &req->msg); + spi_message_add_tail(&req->xfer[1], &req->msg); + + status = spi_sync(spi, &req->msg); + ret = status ? : req->data; + + kfree(req); + + return ret; +} + +static int ad7879_write(struct spi_device *spi, u8 reg, u16 val) +{ + struct ser_req *req; + int status; + + req = kzalloc(sizeof *req, GFP_KERNEL); + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command = (u16) AD7879_WRITECMD(reg); + req->xfer[0].tx_buf = &req->command; + req->xfer[0].len = 2; + + req->data = val; + req->xfer[1].tx_buf = &req->data; + req->xfer[1].len = 2; + + spi_message_add_tail(&req->xfer[0], &req->msg); + spi_message_add_tail(&req->xfer[1], &req->msg); + + status = spi_sync(spi, &req->msg); + + kfree(req); + + return status; +} + +static void ad7879_collect(struct ad7879 *ts) +{ + int status = spi_sync(ts->bus, &ts->msg); + + if (status) + dev_err(&ts->bus->dev, "spi_sync --> %d\n", status); +} + +static void ad7879_setup_ts_def_msg(struct ad7879 *ts) +{ + struct spi_message *m; + int i; + + ts->cmd = (u16) AD7879_READCMD(AD7879_REG_XPLUS); + + m = &ts->msg; + spi_message_init(m); + ts->xfer[0].tx_buf = &ts->cmd; + ts->xfer[0].len = 2; + + spi_message_add_tail(&ts->xfer[0], m); + + for (i = 0; i < AD7879_NR_SENSE; i++) { + ts->xfer[i + 1].rx_buf = &ts->conversion_data[i]; + ts->xfer[i + 1].len = 2; + spi_message_add_tail(&ts->xfer[i + 1], m); + } +} + +static int __devinit ad7879_probe(struct spi_device *spi) +{ + struct ad7879 *ts; + int error; + + /* don't exceed max specified SPI CLK frequency */ + if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) { + dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz); + return -EINVAL; + } + + ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, ts); + ts->bus = spi; + + ad7879_setup_ts_def_msg(ts); + + error = ad7879_construct(spi, ts); + if (error) { + dev_set_drvdata(&spi->dev, NULL); + kfree(ts); + } + + return 0; +} + +static int __devexit ad7879_remove(struct spi_device *spi) +{ + struct ad7879 *ts = dev_get_drvdata(&spi->dev); + + ad7879_destroy(spi, ts); + dev_set_drvdata(&spi->dev, NULL); + kfree(ts); + + return 0; +} + +static struct spi_driver ad7879_driver = { + .driver = { + .name = "ad7879", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad7879_probe, + .remove = __devexit_p(ad7879_remove), + .suspend = ad7879_suspend, + .resume = ad7879_resume, +}; + +static int __init ad7879_init(void) +{ + return spi_register_driver(&ad7879_driver); +} +module_init(ad7879_init); + +static void __exit ad7879_exit(void) +{ + spi_unregister_driver(&ad7879_driver); +} +module_exit(ad7879_exit); + +#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE) + +/* All registers are word-sized. + * AD7879 uses a high-byte first convention. + */ +static int ad7879_read(struct i2c_client *client, u8 reg) +{ + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +static int ad7879_write(struct i2c_client *client, u8 reg, u16 val) +{ + return i2c_smbus_write_word_data(client, reg, swab16(val)); +} + +static void ad7879_collect(struct ad7879 *ts) +{ + int i; + + for (i = 0; i < AD7879_NR_SENSE; i++) + ts->conversion_data[i] = ad7879_read(ts->bus, + AD7879_REG_XPLUS + i); +} + +static int __devinit ad7879_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7879 *ts; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "SMBUS Word Data not Supported\n"); + return -EIO; + } + + ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + i2c_set_clientdata(client, ts); + ts->bus = client; + + error = ad7879_construct(client, ts); + if (error) { + i2c_set_clientdata(client, NULL); + kfree(ts); + } + + return 0; +} + +static int __devexit ad7879_remove(struct i2c_client *client) +{ + struct ad7879 *ts = dev_get_drvdata(&client->dev); + + ad7879_destroy(client, ts); + i2c_set_clientdata(client, NULL); + kfree(ts); + + return 0; +} + +static const struct i2c_device_id ad7879_id[] = { + { "ad7879", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad7879_id); + +static struct i2c_driver ad7879_driver = { + .driver = { + .name = "ad7879", + .owner = THIS_MODULE, + }, + .probe = ad7879_probe, + .remove = __devexit_p(ad7879_remove), + .suspend = ad7879_suspend, + .resume = ad7879_resume, + .id_table = ad7879_id, +}; + +static int __init ad7879_init(void) +{ + return i2c_add_driver(&ad7879_driver); +} +module_init(ad7879_init); + +static void __exit ad7879_exit(void) +{ + i2c_del_driver(&ad7879_driver); +} +module_exit(ad7879_exit); +#endif + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index 1d11e2be9ef..dfa6a84ab50 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -162,6 +162,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm) input_report_abs(wm->input_dev, ABS_X, x & 0xfff); input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); + input_report_key(wm->input_dev, BTN_TOUCH, (p != 0)); input_sync(wm->input_dev); reads++; } while (reads < cinfo[sp_idx].reads); @@ -245,7 +246,7 @@ static void wm97xx_irq_enable(struct wm97xx *wm, int enable) if (enable) enable_irq(wm->pen_irq); else - disable_irq(wm->pen_irq); + disable_irq_nosync(wm->pen_irq); } static struct wm97xx_mach_ops mainstone_mach_ops = { diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 54986627def..e868264fe79 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -151,12 +151,14 @@ static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 input_report_abs(idev, ABS_X, x); input_report_abs(idev, ABS_Y, y); input_report_abs(idev, ABS_PRESSURE, pressure); + input_report_key(idev, BTN_TOUCH, 1); input_sync(idev); } static void ucb1400_ts_event_release(struct input_dev *idev) { input_report_abs(idev, ABS_PRESSURE, 0); + input_report_key(idev, BTN_TOUCH, 0); input_sync(idev); } @@ -377,7 +379,8 @@ static int ucb1400_ts_probe(struct platform_device *dev) ucb->ts_idev->id.product = ucb->id; ucb->ts_idev->open = ucb1400_ts_open; ucb->ts_idev->close = ucb1400_ts_close; - ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS); + ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); ucb1400_adc_enable(ucb->ac97); x_res = ucb1400_ts_read_xres(ucb); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index d15aa11d705..cec480bffe3 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -409,6 +409,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) wm->pen_is_down = 0; dev_dbg(wm->dev, "pen up\n"); input_report_abs(wm->input_dev, ABS_PRESSURE, 0); + input_report_key(wm->input_dev, BTN_TOUCH, 0); input_sync(wm->input_dev); } else if (!(rc & RC_AGAIN)) { /* We need high frequency updates only while @@ -433,6 +434,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); + input_report_key(wm->input_dev, BTN_TOUCH, 1); input_sync(wm->input_dev); wm->pen_is_down = 1; wm->ts_reader_interval = wm->ts_reader_min_interval; @@ -628,18 +630,21 @@ static int wm97xx_probe(struct device *dev) wm->input_dev->phys = "wm97xx"; wm->input_dev->open = wm97xx_ts_input_open; wm->input_dev->close = wm97xx_ts_input_close; - set_bit(EV_ABS, wm->input_dev->evbit); - set_bit(ABS_X, wm->input_dev->absbit); - set_bit(ABS_Y, wm->input_dev->absbit); - set_bit(ABS_PRESSURE, wm->input_dev->absbit); + + __set_bit(EV_ABS, wm->input_dev->evbit); + __set_bit(EV_KEY, wm->input_dev->evbit); + __set_bit(BTN_TOUCH, wm->input_dev->keybit); + input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], abs_x[2], 0); input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], abs_y[2], 0); input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], abs_p[2], 0); + input_set_drvdata(wm->input_dev, wm); wm->input_dev->dev.parent = dev; + ret = input_register_device(wm->input_dev); if (ret < 0) goto dev_alloc_err; diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c new file mode 100644 index 00000000000..41e4359c277 --- /dev/null +++ b/drivers/input/touchscreen/zylonite-wm97xx.c @@ -0,0 +1,240 @@ +/* + * zylonite-wm97xx.c -- Zylonite Continuous Touch screen driver + * + * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * Parts Copyright : Ian Molton <spyro@f2s.com> + * Andrew Zabolotny <zap@homelink.ru> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Notes: + * This is a wm97xx extended touch driver supporting interrupt driven + * and continuous operation on Marvell Zylonite development systems + * (which have a WM9713 on board). + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/wm97xx.h> + +#include <mach/hardware.h> +#include <mach/mfp.h> +#include <mach/regs-ac97.h> + +struct continuous { + u16 id; /* codec id */ + u8 code; /* continuous code */ + u8 reads; /* number of coord reads per read cycle */ + u32 speed; /* number of coords per second */ +}; + +#define WM_READS(sp) ((sp / HZ) + 1) + +static const struct continuous cinfo[] = { + { WM9713_ID2, 0, WM_READS(94), 94 }, + { WM9713_ID2, 1, WM_READS(120), 120 }, + { WM9713_ID2, 2, WM_READS(154), 154 }, + { WM9713_ID2, 3, WM_READS(188), 188 }, +}; + +/* continuous speed index */ +static int sp_idx; + +/* + * Pen sampling frequency (Hz) in continuous mode. + */ +static int cont_rate = 200; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); + +/* + * Pressure readback. + * + * Set to 1 to read back pen down pressure + */ +static int pressure; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); + +/* + * AC97 touch data slot. + * + * Touch screen readback data ac97 slot + */ +static int ac97_touch_slot = 5; +module_param(ac97_touch_slot, int, 0); +MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); + + +/* flush AC97 slot 5 FIFO machines */ +static void wm97xx_acc_pen_up(struct wm97xx *wm) +{ + int i; + + msleep(1); + + for (i = 0; i < 16; i++) + MODR; +} + +static int wm97xx_acc_pen_down(struct wm97xx *wm) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int reads = 0; + static u16 last, tries; + + /* When the AC97 queue has been drained we need to allow time + * to buffer up samples otherwise we end up spinning polling + * for samples. The controller can't have a suitably low + * threashold set to use the notifications it gives. + */ + msleep(1); + + if (tries > 5) { + tries = 0; + return RC_PENUP; + } + + x = MODR; + if (x == last) { + tries++; + return RC_AGAIN; + } + last = x; + do { + if (reads) + x = MODR; + y = MODR; + if (pressure) + p = MODR; + + /* are samples valid */ + if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X || + (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y || + (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES) + goto up; + + /* coordinate is good */ + tries = 0; + input_report_abs(wm->input_dev, ABS_X, x & 0xfff); + input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); + input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); + input_report_key(wm->input_dev, BTN_TOUCH, (p != 0)); + input_sync(wm->input_dev); + reads++; + } while (reads < cinfo[sp_idx].reads); +up: + return RC_PENDOWN | RC_AGAIN; +} + +static int wm97xx_acc_startup(struct wm97xx *wm) +{ + int idx; + + /* check we have a codec */ + if (wm->ac97 == NULL) + return -ENODEV; + + /* Go you big red fire engine */ + for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { + if (wm->id != cinfo[idx].id) + continue; + sp_idx = idx; + if (cont_rate <= cinfo[idx].speed) + break; + } + wm->acc_rate = cinfo[sp_idx].code; + wm->acc_slot = ac97_touch_slot; + dev_info(wm->dev, + "zylonite accelerated touchscreen driver, %d samples/sec\n", + cinfo[sp_idx].speed); + + return 0; +} + +static void wm97xx_irq_enable(struct wm97xx *wm, int enable) +{ + if (enable) + enable_irq(wm->pen_irq); + else + disable_irq_nosync(wm->pen_irq); +} + +static struct wm97xx_mach_ops zylonite_mach_ops = { + .acc_enabled = 1, + .acc_pen_up = wm97xx_acc_pen_up, + .acc_pen_down = wm97xx_acc_pen_down, + .acc_startup = wm97xx_acc_startup, + .irq_enable = wm97xx_irq_enable, + .irq_gpio = WM97XX_GPIO_2, +}; + +static int zylonite_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + int gpio_touch_irq; + + if (cpu_is_pxa320()) + gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15); + else + gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26); + + wm->pen_irq = IRQ_GPIO(gpio_touch_irq); + set_irq_type(IRQ_GPIO(gpio_touch_irq), IRQ_TYPE_EDGE_BOTH); + + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + + return wm97xx_register_mach_ops(wm, &zylonite_mach_ops); +} + +static int zylonite_wm97xx_remove(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + wm97xx_unregister_mach_ops(wm); + + return 0; +} + +static struct platform_driver zylonite_wm97xx_driver = { + .probe = zylonite_wm97xx_probe, + .remove = zylonite_wm97xx_remove, + .driver = { + .name = "wm97xx-touch", + }, +}; + +static int __init zylonite_wm97xx_init(void) +{ + return platform_driver_register(&zylonite_wm97xx_driver); +} + +static void __exit zylonite_wm97xx_exit(void) +{ + platform_driver_unregister(&zylonite_wm97xx_driver); +} + +module_init(zylonite_wm97xx_init); +module_exit(zylonite_wm97xx_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 102ef4a14c5..d2109054de8 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -82,7 +82,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template, if (!gpio_is_valid(template->gpio)) { printk(KERN_INFO "Skipping unavilable LED gpio %d (%s)\n", template->gpio, template->name); - return; + return 0; } ret = gpio_request(template->gpio, template->name); diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 772990415f9..68eb4493f99 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -28,25 +28,12 @@ config DVB_AV7110 download/extract it, and then copy it to /usr/lib/hotplug/firmware or /lib/firmware (depending on configuration of firmware hotplug). - Say Y if you own such a card and want to use it. - -config DVB_AV7110_FIRMWARE - bool "Compile AV7110 firmware into the driver" - depends on DVB_AV7110 && !STANDALONE - default y if DVB_AV7110=y - help - The AV7110 firmware is normally loaded by the firmware hotplug manager. - If you want to compile the firmware into the driver you need to say - Y here and provide the correct path of the firmware. You need this - option if you want to compile the whole driver statically into the - kernel. + Alternatively, you can download the file and use the kernel's + EXTRA_FIRMWARE configuration option to build it into your + kernel image by adding the filename to the EXTRA_FIRMWARE + configuration option string. - All other people say N. - -config DVB_AV7110_FIRMWARE_FILE - string "Full pathname of av7110 firmware file" - depends on DVB_AV7110_FIRMWARE - default "/usr/lib/hotplug/firmware/dvb-ttpci-01.fw" + Say Y if you own such a card and want to use it. config DVB_AV7110_OSD bool "AV7110 OSD support" diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile index 71451237294..8a4d5bb20a5 100644 --- a/drivers/media/dvb/ttpci/Makefile +++ b/drivers/media/dvb/ttpci/Makefile @@ -19,12 +19,3 @@ obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ EXTRA_CFLAGS += -Idrivers/media/common/tuners - -hostprogs-y := fdump - -ifeq ($(CONFIG_DVB_AV7110_FIRMWARE),y) -$(obj)/av7110.o: $(obj)/av7110_firm.h - -$(obj)/av7110_firm.h: $(obj)/fdump - $(obj)/fdump $(CONFIG_DVB_AV7110_FIRMWARE_FILE) dvb_ttpci_fw $@ -endif diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 4624cee93e7..d1d959ed37b 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -1518,20 +1518,6 @@ static int check_firmware(struct av7110* av7110) return 0; } -#ifdef CONFIG_DVB_AV7110_FIRMWARE_FILE -#include "av7110_firm.h" -static void put_firmware(struct av7110* av7110) -{ - av7110->bin_fw = NULL; -} - -static inline int get_firmware(struct av7110* av7110) -{ - av7110->bin_fw = dvb_ttpci_fw; - av7110->size_fw = sizeof(dvb_ttpci_fw); - return check_firmware(av7110); -} -#else static void put_firmware(struct av7110* av7110) { vfree(av7110->bin_fw); @@ -1580,8 +1566,6 @@ static int get_firmware(struct av7110* av7110) release_firmware(fw); return ret; } -#endif - static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 3a3f5279e92..5e3f88911a1 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -198,29 +198,10 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) /* we cannot write av7110 DRAM directly, so load a bootloader into * the DPRAM which implements a simple boot protocol */ -static u8 bootcode[] = { - 0xea, 0x00, 0x00, 0x0e, 0xe1, 0xb0, 0xf0, 0x0e, 0xe2, 0x5e, 0xf0, 0x04, - 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x08, 0xe2, 0x5e, 0xf0, 0x04, - 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04, 0x2c, 0x00, 0x00, 0x24, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x34, - 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0x5a, 0x5a, 0x00, 0x1f, 0x15, 0x55, - 0x00, 0x00, 0x00, 0x09, 0xe5, 0x9f, 0xd0, 0x7c, 0xe5, 0x9f, 0x40, 0x74, - 0xe3, 0xa0, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x04, - 0xe5, 0x9f, 0x10, 0x70, 0xe5, 0x9f, 0x20, 0x70, 0xe5, 0x9f, 0x30, 0x64, - 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe1, 0x51, 0x00, 0x02, - 0xda, 0xff, 0xff, 0xfb, 0xe5, 0x9f, 0xf0, 0x50, 0xe1, 0xd4, 0x10, 0xb0, - 0xe3, 0x51, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xfc, 0xe1, 0xa0, 0x10, 0x0d, - 0xe5, 0x94, 0x30, 0x04, 0xe1, 0xd4, 0x20, 0xb2, 0xe2, 0x82, 0x20, 0x3f, - 0xe1, 0xb0, 0x23, 0x22, 0x03, 0xa0, 0x00, 0x02, 0xe1, 0xc4, 0x00, 0xb0, - 0x0a, 0xff, 0xff, 0xf4, 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, - 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe2, 0x52, 0x20, 0x01, - 0x1a, 0xff, 0xff, 0xf9, 0xe2, 0x2d, 0xdb, 0x05, 0xea, 0xff, 0xff, 0xec, - 0x2c, 0x00, 0x03, 0xf8, 0x2c, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x08, 0x00, - 0x2c, 0x00, 0x00, 0x74, 0x2c, 0x00, 0x00, 0xc0 -}; - int av7110_bootarm(struct av7110 *av7110) { + const struct firmware *fw; + const char *fw_name = "av7110/bootcode.bin"; struct saa7146_dev *dev = av7110->dev; u32 ret; int i; @@ -261,7 +242,15 @@ int av7110_bootarm(struct av7110 *av7110) //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); - mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode)); + ret = request_firmware(&fw, fw_name, &dev->pci->dev); + if (ret) { + printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", + fw_name); + return ret; + } + + mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); + release_firmware(fw); iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); if (saa7146_wait_for_debi_done(av7110->dev, 1)) { @@ -302,7 +291,7 @@ int av7110_bootarm(struct av7110 *av7110) av7110->arm_ready = 1; return 0; } - +MODULE_FIRMWARE("av7110/bootcode.bin"); /**************************************************************************** * DEBI command polling diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index ca99e5c1fc8..1634aba5cb8 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -390,7 +390,8 @@ static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, } /* buffer writes */ -static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, u8 *val, int count) +static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, + const u8 *val, int count) { memcpy(av7110->debi_virt, val, count); av7110_debiwrite(av7110, config, addr, 0, count); diff --git a/drivers/media/dvb/ttpci/fdump.c b/drivers/media/dvb/ttpci/fdump.c deleted file mode 100644 index c90001d35e7..00000000000 --- a/drivers/media/dvb/ttpci/fdump.c +++ /dev/null @@ -1,44 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -int main(int argc, char **argv) -{ - unsigned char buf[8]; - unsigned int i, count, bytes = 0; - FILE *fd_in, *fd_out; - - if (argc != 4) { - fprintf(stderr, "\n\tusage: %s <ucode.bin> <array_name> <output_name>\n\n", argv[0]); - return -1; - } - - fd_in = fopen(argv[1], "rb"); - if (fd_in == NULL) { - fprintf(stderr, "firmware file '%s' not found\n", argv[1]); - return -1; - } - - fd_out = fopen(argv[3], "w+"); - if (fd_out == NULL) { - fprintf(stderr, "cannot create output file '%s'\n", argv[3]); - return -1; - } - - fprintf(fd_out, "\n#include <asm/types.h>\n\nu8 %s [] = {", argv[2]); - - while ((count = fread(buf, 1, 8, fd_in)) > 0) { - fprintf(fd_out, "\n\t"); - for (i = 0; i < count; i++, bytes++) - fprintf(fd_out, "0x%02x, ", buf[i]); - } - - fprintf(fd_out, "\n};\n\n"); - - fclose(fd_in); - fclose(fd_out); - - return 0; -} diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 25a00ce4f24..fa3a11365ec 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -173,12 +173,21 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) struct dmar_drhd_unit *dmaru; int ret = 0; + drhd = (struct acpi_dmar_hardware_unit *)header; + if (!drhd->address) { + /* Promote an attitude of violence to a BIOS engineer today */ + WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); + return -ENODEV; + } dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); if (!dmaru) return -ENOMEM; dmaru->hdr = header; - drhd = (struct acpi_dmar_hardware_unit *)header; dmaru->reg_base_addr = drhd->address; dmaru->segment = drhd->segment; dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index fb3a3f3fca7..001b328adf8 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -733,8 +733,8 @@ static void dma_pte_clear_range(struct dmar_domain *domain, u64 start, u64 end) start &= (((u64)1) << addr_width) - 1; end &= (((u64)1) << addr_width) - 1; /* in case it's partial page */ - start = PAGE_ALIGN(start); - end &= PAGE_MASK; + start &= PAGE_MASK; + end = PAGE_ALIGN(end); npages = (end - start) / VTD_PAGE_SIZE; /* we don't need lock here, nobody else touches the iova range */ diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 687dcf2d015..5defe5ea5ed 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -1663,7 +1663,7 @@ qla1280_load_firmware_pio(struct scsi_qla_host *ha) /* Load RISC code. */ risc_address = ha->fwstart; - fw_data = (const __le16 *)&fw->data[4]; + fw_data = (const __le16 *)&fw->data[6]; risc_code_size = (fw->size - 6) / 2; for (i = 0; i < risc_code_size; i++) { @@ -1722,7 +1722,7 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha) /* Load RISC code. */ risc_address = ha->fwstart; - fw_data = (const __le16 *)&fw->data[4]; + fw_data = (const __le16 *)&fw->data[6]; risc_code_size = (fw->size - 6) / 2; dprintk(1, "%s: DMA RISC code (%i) words\n", diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index e1850904ff7..fbc83bebdd8 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -38,9 +38,6 @@ static inline void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) { }; #endif -/* scsi_scan.c */ -int scsi_complete_async_scans(void); - /* scsi_devinfo.c */ extern int scsi_get_device_flags(struct scsi_device *sdev, const unsigned char *vendor, diff --git a/drivers/scsi/scsi_wait_scan.c b/drivers/scsi/scsi_wait_scan.c index 8a636103083..2f21af21269 100644 --- a/drivers/scsi/scsi_wait_scan.c +++ b/drivers/scsi/scsi_wait_scan.c @@ -11,7 +11,7 @@ */ #include <linux/module.h> -#include "scsi_priv.h" +#include <scsi/scsi_scan.h> static int __init wait_scan_init(void) { diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 0328fd4006e..343e3a35b6a 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -854,7 +854,7 @@ config SERIAL_IMX_CONSOLE config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" - depends on PPC32 + depends on PPC32 || MICROBLAZE select SERIAL_CORE help Say Y here if you want to use the Xilinx uartlite serial controller. @@ -1340,7 +1340,7 @@ config SERIAL_NETX_CONSOLE config SERIAL_OF_PLATFORM tristate "Serial port on Open Firmware platform bus" - depends on PPC_OF + depends on PPC_OF || MICROBLAZE depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL help If you have a PowerPC based system that has serial ports diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 63024145215..5eb8f21da82 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -240,8 +240,6 @@ config ORION5X_WATCHDOG To compile this driver as a module, choose M here: the module will be called orion5x_wdt. -# ARM26 Architecture - # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 806b3eb0853..7f8c56b14f5 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -42,8 +42,6 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o -# ARM26 Architecture - # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index e35d5458923..29e52c237a3 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -197,7 +197,7 @@ static struct miscdevice at91wdt_miscdev = { .fops = &at91wdt_fops, }; -static int __init at91wdt_probe(struct platform_device *pdev) +static int __devinit at91wdt_probe(struct platform_device *pdev) { int res; @@ -214,7 +214,7 @@ static int __init at91wdt_probe(struct platform_device *pdev) return 0; } -static int __exit at91wdt_remove(struct platform_device *pdev) +static int __devexit at91wdt_remove(struct platform_device *pdev) { int res; @@ -252,7 +252,7 @@ static int at91wdt_resume(struct platform_device *pdev) static struct platform_driver at91wdt_driver = { .probe = at91wdt_probe, - .remove = __exit_p(at91wdt_remove), + .remove = __devexit_p(at91wdt_remove), .shutdown = at91wdt_shutdown, .suspend = at91wdt_suspend, .resume = at91wdt_resume, diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index 2dbe83570d6..7ba0b11ec52 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -52,10 +52,10 @@ #define ESB_LOCK_REG 0x68 /* WDT lock register */ /* Memory mapped registers */ -#define ESB_TIMER1_REG BASEADDR + 0x00 /* Timer1 value after each reset */ -#define ESB_TIMER2_REG BASEADDR + 0x04 /* Timer2 value after each reset */ -#define ESB_GINTSR_REG BASEADDR + 0x08 /* General Interrupt Status Register */ -#define ESB_RELOAD_REG BASEADDR + 0x0c /* Reload register */ +#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset */ +#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset */ +#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */ +#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register */ /* Lock register bits */ #define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */ @@ -68,6 +68,7 @@ #define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */ /* Reload register bits */ +#define ESB_WDT_TIMEOUT (0x01 << 9) /* Watchdog timed out */ #define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */ /* Magic constants */ @@ -87,7 +88,6 @@ static struct platform_device *esb_platform_device; /* 30 sec default heartbeat (1 < heartbeat < 2*1023) */ #define WATCHDOG_HEARTBEAT 30 static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ - module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<heartbeat<2046, default=" @@ -123,7 +123,7 @@ static int esb_timer_start(void) esb_unlock_registers(); writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); /* Enable or Enable + Lock? */ - val = 0x02 | (nowayout ? 0x01 : 0x00); + val = ESB_WDT_ENABLE | (nowayout ? ESB_WDT_LOCK : 0x00); pci_write_config_byte(esb_pci, ESB_LOCK_REG, val); spin_unlock(&esb_lock); return 0; @@ -143,7 +143,7 @@ static int esb_timer_stop(void) spin_unlock(&esb_lock); /* Returns 0 if the timer was disabled, non-zero otherwise */ - return (val & 0x01); + return val & ESB_WDT_ENABLE; } static void esb_timer_keepalive(void) @@ -190,18 +190,6 @@ static int esb_timer_set_heartbeat(int time) return 0; } -static int esb_timer_read(void) -{ - u32 count; - - /* This isn't documented, and doesn't take into - * acount which stage is running, but it looks - * like a 20 bit count down, so we might as well report it. - */ - pci_read_config_dword(esb_pci, 0x64, &count); - return (int)count; -} - /* * /dev/watchdog handling */ @@ -282,7 +270,7 @@ static long esb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) sizeof(ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: - return put_user(esb_timer_read(), p); + return put_user(0, p); case WDIOC_GETBOOTSTATUS: return put_user(triggered, p); @@ -362,8 +350,6 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl); static unsigned char __devinit esb_getdevice(void) { - u8 val1; - unsigned short val2; /* * Find the PCI device */ @@ -371,66 +357,79 @@ static unsigned char __devinit esb_getdevice(void) esb_pci = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9, NULL); - if (esb_pci) { - if (pci_enable_device(esb_pci)) { - printk(KERN_ERR PFX "failed to enable device\n"); - goto err_devput; - } + if (!esb_pci) + return 0; - if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) { - printk(KERN_ERR PFX "failed to request region\n"); - goto err_disable; - } + if (pci_enable_device(esb_pci)) { + printk(KERN_ERR PFX "failed to enable device\n"); + goto err_devput; + } - BASEADDR = pci_ioremap_bar(esb_pci, 0); - if (BASEADDR == NULL) { - /* Something's wrong here, BASEADDR has to be set */ - printk(KERN_ERR PFX "failed to get BASEADDR\n"); - goto err_release; - } + if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) { + printk(KERN_ERR PFX "failed to request region\n"); + goto err_disable; + } - /* - * The watchdog has two timers, it can be setup so that the - * expiry of timer1 results in an interrupt and the expiry of - * timer2 results in a reboot. We set it to not generate - * any interrupts as there is not much we can do with it - * right now. - * - * We also enable reboots and set the timer frequency to - * the PCI clock divided by 2^15 (approx 1KHz). - */ - pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); - - /* Check that the WDT isn't already locked */ - pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); - if (val1 & ESB_WDT_LOCK) - printk(KERN_WARNING PFX "nowayout already set\n"); - - /* Set the timer to watchdog mode and disable it for now */ - pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); - - /* Check if the watchdog was previously triggered */ - esb_unlock_registers(); - val2 = readw(ESB_RELOAD_REG); - triggered = (val2 & (0x01 << 9) >> 9); - - /* Reset trigger flag and timers */ - esb_unlock_registers(); - writew((0x11 << 8), ESB_RELOAD_REG); - - /* Done */ - return 1; + BASEADDR = pci_ioremap_bar(esb_pci, 0); + if (BASEADDR == NULL) { + /* Something's wrong here, BASEADDR has to be set */ + printk(KERN_ERR PFX "failed to get BASEADDR\n"); + goto err_release; + } + + /* Done */ + return 1; err_release: - pci_release_region(esb_pci, 0); + pci_release_region(esb_pci, 0); err_disable: - pci_disable_device(esb_pci); + pci_disable_device(esb_pci); err_devput: - pci_dev_put(esb_pci); - } + pci_dev_put(esb_pci); return 0; } +static void __devinit esb_initdevice(void) +{ + u8 val1; + u16 val2; + + /* + * Config register: + * Bit 5 : 0 = Enable WDT_OUTPUT + * Bit 2 : 0 = set the timer frequency to the PCI clock + * divided by 2^15 (approx 1KHz). + * Bits 1:0 : 11 = WDT_INT_TYPE Disabled. + * The watchdog has two timers, it can be setup so that the + * expiry of timer1 results in an interrupt and the expiry of + * timer2 results in a reboot. We set it to not generate + * any interrupts as there is not much we can do with it + * right now. + */ + pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); + + /* Check that the WDT isn't already locked */ + pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); + if (val1 & ESB_WDT_LOCK) + printk(KERN_WARNING PFX "nowayout already set\n"); + + /* Set the timer to watchdog mode and disable it for now */ + pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); + + /* Check if the watchdog was previously triggered */ + esb_unlock_registers(); + val2 = readw(ESB_RELOAD_REG); + if (val2 & ESB_WDT_TIMEOUT) + triggered = WDIOF_CARDRESET; + + /* Reset WDT_TIMEOUT flag and timers */ + esb_unlock_registers(); + writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG); + + /* And set the correct timeout value */ + esb_timer_set_heartbeat(heartbeat); +} + static int __devinit esb_probe(struct platform_device *dev) { int ret; @@ -441,13 +440,17 @@ static int __devinit esb_probe(struct platform_device *dev) /* Check that the heartbeat value is within it's range; if not reset to the default */ - if (esb_timer_set_heartbeat(heartbeat)) { - esb_timer_set_heartbeat(WATCHDOG_HEARTBEAT); + if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) { + heartbeat = WATCHDOG_HEARTBEAT; printk(KERN_INFO PFX "heartbeat value must be 1<heartbeat<2046, using %d\n", heartbeat); } + /* Initialize the watchdog and make sure it does not run */ + esb_initdevice(); + + /* Register the watchdog so that userspace has access to it */ ret = misc_register(&esb_miscdev); if (ret != 0) { printk(KERN_ERR PFX @@ -455,7 +458,6 @@ static int __devinit esb_probe(struct platform_device *dev) WATCHDOG_MINOR, ret); goto err_unmap; } - esb_timer_stop(); printk(KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n", BASEADDR, heartbeat, nowayout); @@ -463,11 +465,8 @@ static int __devinit esb_probe(struct platform_device *dev) err_unmap: iounmap(BASEADDR); -/* err_release: */ pci_release_region(esb_pci, 0); -/* err_disable: */ pci_disable_device(esb_pci); -/* err_devput: */ pci_dev_put(esb_pci); return ret; } diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c index 74c92d38411..ae3832110ac 100644 --- a/drivers/watchdog/ks8695_wdt.c +++ b/drivers/watchdog/ks8695_wdt.c @@ -221,7 +221,7 @@ static struct miscdevice ks8695wdt_miscdev = { .fops = &ks8695wdt_fops, }; -static int __init ks8695wdt_probe(struct platform_device *pdev) +static int __devinit ks8695wdt_probe(struct platform_device *pdev) { int res; @@ -238,7 +238,7 @@ static int __init ks8695wdt_probe(struct platform_device *pdev) return 0; } -static int __exit ks8695wdt_remove(struct platform_device *pdev) +static int __devexit ks8695wdt_remove(struct platform_device *pdev) { int res; @@ -276,7 +276,7 @@ static int ks8695wdt_resume(struct platform_device *pdev) static struct platform_driver ks8695wdt_driver = { .probe = ks8695wdt_probe, - .remove = __exit_p(ks8695wdt_remove), + .remove = __devexit_p(ks8695wdt_remove), .shutdown = ks8695wdt_shutdown, .suspend = ks8695wdt_suspend, .resume = ks8695wdt_resume, diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index aa5ad6e33f0..f2713851aaa 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -258,7 +258,7 @@ static const struct file_operations omap_wdt_fops = { .release = omap_wdt_release, }; -static int __init omap_wdt_probe(struct platform_device *pdev) +static int __devinit omap_wdt_probe(struct platform_device *pdev) { struct resource *res, *mem; struct omap_wdt_dev *wdev; @@ -367,7 +367,7 @@ static void omap_wdt_shutdown(struct platform_device *pdev) omap_wdt_disable(wdev); } -static int omap_wdt_remove(struct platform_device *pdev) +static int __devexit omap_wdt_remove(struct platform_device *pdev) { struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -426,7 +426,7 @@ static int omap_wdt_resume(struct platform_device *pdev) static struct platform_driver omap_wdt_driver = { .probe = omap_wdt_probe, - .remove = omap_wdt_remove, + .remove = __devexit_p(omap_wdt_remove), .shutdown = omap_wdt_shutdown, .suspend = omap_wdt_suspend, .resume = omap_wdt_resume, diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c index e81441f103d..7529616739d 100644 --- a/drivers/watchdog/orion5x_wdt.c +++ b/drivers/watchdog/orion5x_wdt.c @@ -42,7 +42,17 @@ static unsigned int wdt_tclk; static unsigned long wdt_status; static spinlock_t wdt_lock; -static void wdt_enable(void) +static void orion5x_wdt_ping(void) +{ + spin_lock(&wdt_lock); + + /* Reload watchdog duration */ + writel(wdt_tclk * heartbeat, WDT_VAL); + + spin_unlock(&wdt_lock); +} + +static void orion5x_wdt_enable(void) { u32 reg; @@ -69,7 +79,7 @@ static void wdt_enable(void) spin_unlock(&wdt_lock); } -static void wdt_disable(void) +static void orion5x_wdt_disable(void) { u32 reg; @@ -101,7 +111,7 @@ static int orion5x_wdt_open(struct inode *inode, struct file *file) if (test_and_set_bit(WDT_IN_USE, &wdt_status)) return -EBUSY; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - wdt_enable(); + orion5x_wdt_enable(); return nonseekable_open(inode, file); } @@ -122,18 +132,28 @@ static ssize_t orion5x_wdt_write(struct file *file, const char *data, set_bit(WDT_OK_TO_CLOSE, &wdt_status); } } - wdt_enable(); + orion5x_wdt_ping(); } return len; } -static struct watchdog_info ident = { +static int orion5x_wdt_settimeout(int new_time) +{ + if ((new_time <= 0) || (new_time > wdt_max_duration)) + return -EINVAL; + + /* Set new watchdog time to be used when + * orion5x_wdt_enable() or orion5x_wdt_ping() is called. */ + heartbeat = new_time; + return 0; +} + +static const struct watchdog_info ident = { .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "Orion5x Watchdog", }; - static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -152,7 +172,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, break; case WDIOC_KEEPALIVE: - wdt_enable(); + orion5x_wdt_ping(); ret = 0; break; @@ -161,12 +181,11 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, if (ret) break; - if (time <= 0 || time > wdt_max_duration) { + if (orion5x_wdt_settimeout(time)) { ret = -EINVAL; break; } - heartbeat = time; - wdt_enable(); + orion5x_wdt_ping(); /* Fall through */ case WDIOC_GETTIMEOUT: @@ -187,7 +206,7 @@ static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, static int orion5x_wdt_release(struct inode *inode, struct file *file) { if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - wdt_disable(); + orion5x_wdt_disable(); else printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " "timer will not stop\n"); @@ -230,7 +249,7 @@ static int __devinit orion5x_wdt_probe(struct platform_device *pdev) orion5x_wdt_miscdev.parent = &pdev->dev; wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; - if (heartbeat <= 0 || heartbeat > wdt_max_duration) + if (orion5x_wdt_settimeout(heartbeat)) heartbeat = wdt_max_duration; ret = misc_register(&orion5x_wdt_miscdev); @@ -247,7 +266,7 @@ static int __devexit orion5x_wdt_remove(struct platform_device *pdev) int ret; if (test_bit(WDT_IN_USE, &wdt_status)) { - wdt_disable(); + orion5x_wdt_disable(); clear_bit(WDT_IN_USE, &wdt_status); } @@ -258,9 +277,16 @@ static int __devexit orion5x_wdt_remove(struct platform_device *pdev) return ret; } +static void orion5x_wdt_shutdown(struct platform_device *pdev) +{ + if (test_bit(WDT_IN_USE, &wdt_status)) + orion5x_wdt_disable(); +} + static struct platform_driver orion5x_wdt_driver = { .probe = orion5x_wdt_probe, .remove = __devexit_p(orion5x_wdt_remove), + .shutdown = orion5x_wdt_shutdown, .driver = { .owner = THIS_MODULE, .name = "orion5x_wdt", @@ -285,10 +311,11 @@ MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); MODULE_DESCRIPTION("Orion5x Processor Watchdog"); module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds"); +MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |