diff options
Diffstat (limited to 'drivers')
101 files changed, 7439 insertions, 3350 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 31d6f535a79d..01dde80597f7 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -754,6 +754,11 @@ static struct kobj_type ktype_cpufreq = { .release = cpufreq_sysfs_release, }; +static struct kobj_type ktype_empty_cpufreq = { + .sysfs_ops = &sysfs_ops, + .release = cpufreq_sysfs_release, +}; + /** * cpufreq_add_dev - add a CPU device @@ -822,8 +827,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) dprintk("initialization failed\n"); goto err_out; } - policy->user_policy.min = policy->cpuinfo.min_freq; - policy->user_policy.max = policy->cpuinfo.max_freq; + policy->user_policy.min = policy->min; + policy->user_policy.max = policy->max; blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_START, policy); @@ -876,26 +881,36 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); /* prepare interface data */ - ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &sys_dev->kobj, - "cpufreq"); - if (ret) - goto err_out_driver_exit; - - /* set up files for this cpu device */ - drv_attr = cpufreq_driver->attr; - while ((drv_attr) && (*drv_attr)) { - ret = sysfs_create_file(&policy->kobj, &((*drv_attr)->attr)); + if (!cpufreq_driver->hide_interface) { + ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, + &sys_dev->kobj, "cpufreq"); if (ret) goto err_out_driver_exit; - drv_attr++; - } - if (cpufreq_driver->get) { - ret = sysfs_create_file(&policy->kobj, &cpuinfo_cur_freq.attr); - if (ret) - goto err_out_driver_exit; - } - if (cpufreq_driver->target) { - ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr); + + /* set up files for this cpu device */ + drv_attr = cpufreq_driver->attr; + while ((drv_attr) && (*drv_attr)) { + ret = sysfs_create_file(&policy->kobj, + &((*drv_attr)->attr)); + if (ret) + goto err_out_driver_exit; + drv_attr++; + } + if (cpufreq_driver->get) { + ret = sysfs_create_file(&policy->kobj, + &cpuinfo_cur_freq.attr); + if (ret) + goto err_out_driver_exit; + } + if (cpufreq_driver->target) { + ret = sysfs_create_file(&policy->kobj, + &scaling_cur_freq.attr); + if (ret) + goto err_out_driver_exit; + } + } else { + ret = kobject_init_and_add(&policy->kobj, &ktype_empty_cpufreq, + &sys_dev->kobj, "cpufreq"); if (ret) goto err_out_driver_exit; } diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index b4fd8ca701a4..e85c8fe9ffcf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -85,14 +85,14 @@ config HID_COMPAT config HID_A4TECH tristate "A4 tech" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. config HID_APPLE tristate "Apple" if EMBEDDED depends on (USB_HID || BT_HIDP) - default y + default !EMBEDDED ---help--- Support for some Apple devices which less or more break HID specification. @@ -103,64 +103,49 @@ config HID_APPLE config HID_BELKIN tristate "Belkin" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Belkin Flip KVM and Wireless keyboard. -config HID_BRIGHT - tristate "Bright" if EMBEDDED - depends on USB_HID - default y - ---help--- - Support for Bright ABNT-2 keyboard. - config HID_CHERRY tristate "Cherry" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Cherry Cymotion keyboard. config HID_CHICONY tristate "Chicony" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Chicony Tactical pad. config HID_CYPRESS tristate "Cypress" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for cypress mouse and barcode readers. -config HID_DELL - tristate "Dell" if EMBEDDED - depends on USB_HID - default y - ---help--- - Support for quirky Dell HID hardware that require - special LED handling (W7658 and SK8115 models) - config HID_EZKEY tristate "Ezkey" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Ezkey BTC 8193 keyboard. config HID_GYRATION tristate "Gyration" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Gyration remote control. config HID_LOGITECH tristate "Logitech" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Logitech devices that are not fully compliant with HID standard. @@ -191,21 +176,28 @@ config LOGIRUMBLEPAD2_FF config HID_MICROSOFT tristate "Microsoft" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Microsoft devices that are not fully compliant with HID standard. config HID_MONTEREY tristate "Monterey" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Monterey Genius KB29E. +config HID_NTRIG + tristate "NTrig" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Support for N-Trig touch screen. + config HID_PANTHERLORD tristate "Pantherlord devices support" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for PantherLord/GreenAsia based device support. @@ -220,31 +212,47 @@ config PANTHERLORD_FF config HID_PETALYNX tristate "Petalynx" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Petalynx Maxter remote control. config HID_SAMSUNG tristate "Samsung" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Samsung InfraRed remote control. config HID_SONY tristate "Sony" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Sony PS3 controller. config HID_SUNPLUS tristate "Sunplus" if EMBEDDED depends on USB_HID - default y + default !EMBEDDED ---help--- Support for Sunplus wireless desktop. +config GREENASIA_FF + tristate "GreenAsia (Product ID 0x12) force feedback support" + depends on USB_HID + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you have a GreenAsia (Product ID 0x12) based game controller + (like MANTA Warior MM816 and SpeedLink Strike2 SL-6635) or adapter + and want to enable force feedback support for it. + +config HID_TOPSEED + tristate "TopSeed Cyberlink remote control support" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Say Y if you have a TopSeed Cyberlink remote control. + config THRUSTMASTER_FF tristate "ThrustMaster devices support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b09e43e7413e..fbd021f153f1 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -23,22 +23,23 @@ endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o -obj-$(CONFIG_HID_BRIGHT) += hid-bright.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o -obj-$(CONFIG_HID_DELL) += hid-dell.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o +obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SONY) += hid-sony.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o +obj-$(CONFIG_GREENASIA_FF) += hid-gaff.o obj-$(CONFIG_THRUSTMASTER_FF) += hid-tmff.o +obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_ZEROPLUS_FF) += hid-zpff.o obj-$(CONFIG_USB_HID) += usbhid/ diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c deleted file mode 100644 index 38517a117dfd..000000000000 --- a/drivers/hid/hid-bright.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * HID driver for some bright "special" devices - * - * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com> - * - * Based on hid-dell driver - */ - -/* - * 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. - */ - -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/module.h> - -#include "hid-ids.h" - -static int bright_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - dev_err(&hdev->dev, "parse failed\n"); - goto err_free; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - dev_err(&hdev->dev, "hw start failed\n"); - goto err_free; - } - - usbhid_set_leds(hdev); - - return 0; -err_free: - return ret; -} - -static const struct hid_device_id bright_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) }, - { } -}; -MODULE_DEVICE_TABLE(hid, bright_devices); - -static struct hid_driver bright_driver = { - .name = "bright", - .id_table = bright_devices, - .probe = bright_probe, -}; - -static int bright_init(void) -{ - return hid_register_driver(&bright_driver); -} - -static void bright_exit(void) -{ - hid_unregister_driver(&bright_driver); -} - -module_init(bright_init); -module_exit(bright_exit); -MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(bright); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 40df3e1b4bd1..5d7640e49dc5 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1256,19 +1256,16 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, - { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERIC_13BA, USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, @@ -1279,7 +1276,6 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, @@ -1297,23 +1293,105 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { } }; +struct hid_dynid { + struct list_head list; + struct hid_device_id id; +}; + +/** + * store_new_id - add a new HID device ID to this driver and re-probe devices + * @driver: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Adds a new dynamic hid device ID to this driver, + * and causes the driver to probe for all devices again. + */ +static ssize_t store_new_id(struct device_driver *drv, const char *buf, + size_t count) +{ + struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); + struct hid_dynid *dynid; + __u32 bus, vendor, product; + unsigned long driver_data = 0; + int ret; + + ret = sscanf(buf, "%x %x %x %lx", + &bus, &vendor, &product, &driver_data); + if (ret < 3) + return -EINVAL; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.bus = bus; + dynid->id.vendor = vendor; + dynid->id.product = product; + dynid->id.driver_data = driver_data; + + spin_lock(&hdrv->dyn_lock); + list_add_tail(&dynid->list, &hdrv->dyn_list); + spin_unlock(&hdrv->dyn_lock); + + ret = 0; + if (get_driver(&hdrv->driver)) { + ret = driver_attach(&hdrv->driver); + put_driver(&hdrv->driver); + } + + return ret ? : count; +} +static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); + +static void hid_free_dynids(struct hid_driver *hdrv) +{ + struct hid_dynid *dynid, *n; + + spin_lock(&hdrv->dyn_lock); + list_for_each_entry_safe(dynid, n, &hdrv->dyn_list, list) { + list_del(&dynid->list); + kfree(dynid); + } + spin_unlock(&hdrv->dyn_lock); +} + +static const struct hid_device_id *hid_match_device(struct hid_device *hdev, + struct hid_driver *hdrv) +{ + struct hid_dynid *dynid; + + spin_lock(&hdrv->dyn_lock); + list_for_each_entry(dynid, &hdrv->dyn_list, list) { + if (hid_match_one_id(hdev, &dynid->id)) { + spin_unlock(&hdrv->dyn_lock); + return &dynid->id; + } + } + spin_unlock(&hdrv->dyn_lock); + + return hid_match_id(hdev, hdrv->id_table); +} + static int hid_bus_match(struct device *dev, struct device_driver *drv) { struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); struct hid_device *hdev = container_of(dev, struct hid_device, dev); - if (!hid_match_id(hdev, hdrv->id_table)) + if (!hid_match_device(hdev, hdrv)) return 0; /* generic wants all non-blacklisted */ @@ -1332,7 +1410,7 @@ static int hid_device_probe(struct device *dev) int ret = 0; if (!hdev->driver) { - id = hid_match_id(hdev, hdrv->id_table); + id = hid_match_device(hdev, hdrv); if (id == NULL) return -ENODEV; @@ -1420,6 +1498,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, @@ -1577,6 +1656,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } @@ -1618,9 +1700,10 @@ int hid_add_device(struct hid_device *hdev) if (hid_ignore(hdev)) return -ENODEV; - /* XXX hack, any other cleaner solution < 20 bus_id bytes? */ - sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus, - hdev->vendor, hdev->product, atomic_inc_return(&id)); + /* XXX hack, any other cleaner solution after the driver core + * is converted to allow more than 20 bytes as the device name? */ + dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, + hdev->vendor, hdev->product, atomic_inc_return(&id)); ret = device_add(&hdev->dev); if (!ret) @@ -1695,18 +1778,33 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, const char *mod_name) { + int ret; + hdrv->driver.name = hdrv->name; hdrv->driver.bus = &hid_bus_type; hdrv->driver.owner = owner; hdrv->driver.mod_name = mod_name; - return driver_register(&hdrv->driver); + INIT_LIST_HEAD(&hdrv->dyn_list); + spin_lock_init(&hdrv->dyn_lock); + + ret = driver_register(&hdrv->driver); + if (ret) + return ret; + + ret = driver_create_file(&hdrv->driver, &driver_attr_new_id); + if (ret) + driver_unregister(&hdrv->driver); + + return ret; } EXPORT_SYMBOL_GPL(__hid_register_driver); void hid_unregister_driver(struct hid_driver *hdrv) { + driver_remove_file(&hdrv->driver, &driver_attr_new_id); driver_unregister(&hdrv->driver); + hid_free_dynids(hdrv); } EXPORT_SYMBOL_GPL(hid_unregister_driver); diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c deleted file mode 100644 index f5474300b83a..000000000000 --- a/drivers/hid/hid-dell.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * HID driver for some dell "special" devices - * - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina - * Copyright (c) 2007 Paul Walmsley - * Copyright (c) 2008 Jiri Slaby - */ - -/* - * 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. - */ - -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/module.h> - -#include "hid-ids.h" - -static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - dev_err(&hdev->dev, "parse failed\n"); - goto err_free; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - dev_err(&hdev->dev, "hw start failed\n"); - goto err_free; - } - - usbhid_set_leds(hdev); - - return 0; -err_free: - return ret; -} - -static const struct hid_device_id dell_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERIC_13BA, USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE) }, - { } -}; -MODULE_DEVICE_TABLE(hid, dell_devices); - -static struct hid_driver dell_driver = { - .name = "dell", - .id_table = dell_devices, - .probe = dell_probe, -}; - -static int dell_init(void) -{ - return hid_register_driver(&dell_driver); -} - -static void dell_exit(void) -{ - hid_unregister_driver(&dell_driver); -} - -module_init(dell_init); -module_exit(dell_exit); -MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(dell); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index e148f86fb58e..b4cc0f743d63 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -43,6 +43,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_MONTEREY_MODULE HID_COMPAT_CALL_DRIVER(monterey); #endif +#ifdef CONFIG_HID_NTRIG_MODULE + HID_COMPAT_CALL_DRIVER(ntrig); +#endif #ifdef CONFIG_HID_PANTHERLORD_MODULE HID_COMPAT_CALL_DRIVER(pantherlord); #endif @@ -58,6 +61,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_SUNPLUS_MODULE HID_COMPAT_CALL_DRIVER(sunplus); #endif +#ifdef CONFIG_GREENASIA_FF_MODULE + HID_COMPAT_CALL_DRIVER(greenasia); +#endif #ifdef CONFIG_THRUSTMASTER_FF_MODULE HID_COMPAT_CALL_DRIVER(thrustmaster); #endif diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c new file mode 100644 index 000000000000..71211f6a4f02 --- /dev/null +++ b/drivers/hid/hid-gaff.c @@ -0,0 +1,185 @@ +/* + * Force feedback support for GreenAsia (Product ID 0x12) based devices + * + * The devices are distributed under various names and the same USB device ID + * can be used in many game controllers. + * + * + * 0e8f:0012 "GreenAsia Inc. USB Joystick " + * - tested with MANTA Warior MM816 and SpeedLink Strike2 SL-6635. + * + * Copyright (c) 2008 Lukasz Lubojanski <lukasz@lubojanski.info> + */ + +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/hid.h> +#include "hid-ids.h" +#include "usbhid/usbhid.h" + +struct gaff_device { + struct hid_report *report; +}; + +static int hid_gaff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct gaff_device *gaff = data; + int left, right; + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + + dbg_hid("called with 0x%04x 0x%04x", left, right); + + left = left * 0xfe / 0xffff; + right = right * 0xfe / 0xffff; + + gaff->report->field[0]->value[0] = 0x51; + gaff->report->field[0]->value[1] = 0x0; + gaff->report->field[0]->value[2] = right; + gaff->report->field[0]->value[3] = 0; + gaff->report->field[0]->value[4] = left; + gaff->report->field[0]->value[5] = 0; + dbg_hid("running with 0x%02x 0x%02x", left, right); + usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + + gaff->report->field[0]->value[0] = 0xfa; + gaff->report->field[0]->value[1] = 0xfe; + gaff->report->field[0]->value[2] = 0x0; + gaff->report->field[0]->value[4] = 0x0; + + usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + + return 0; +} + +static int gaff_init(struct hid_device *hid) +{ + struct gaff_device *gaff; + struct hid_report *report; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct list_head *report_ptr = report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + dev_err(&hid->dev, "no output reports found\n"); + return -ENODEV; + } + + report_ptr = report_ptr->next; + + report = list_entry(report_ptr, struct hid_report, list); + if (report->maxfield < 1) { + dev_err(&hid->dev, "no fields in the report\n"); + return -ENODEV; + } + + if (report->field[0]->report_count < 6) { + dev_err(&hid->dev, "not enough values in the field\n"); + return -ENODEV; + } + + gaff = kzalloc(sizeof(struct gaff_device), GFP_KERNEL); + if (!gaff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, gaff, hid_gaff_play); + if (error) { + kfree(gaff); + return error; + } + + gaff->report = report; + gaff->report->field[0]->value[0] = 0x51; + gaff->report->field[0]->value[1] = 0x00; + gaff->report->field[0]->value[2] = 0x00; + gaff->report->field[0]->value[3] = 0x00; + usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + + gaff->report->field[0]->value[0] = 0xfa; + gaff->report->field[0]->value[1] = 0xfe; + + usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + + dev_info(&hid->dev, "Force Feedback for GreenAsia 0x12" + " devices by Lukasz Lubojanski <lukasz@lubojanski.info>\n"); + + return 0; +} + +static int ga_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + dev_dbg(&hdev->dev, "Greenasia HID hardware probe..."); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + gaff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id ga_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012), }, + { } +}; +MODULE_DEVICE_TABLE(hid, ga_devices); + +static struct hid_driver ga_driver = { + .name = "greenasia", + .id_table = ga_devices, + .probe = ga_probe, +}; + +static int __init ga_init(void) +{ + return hid_register_driver(&ga_driver); +} + +static void __exit ga_exit(void) +{ + hid_unregister_driver(&ga_driver); +} + +module_init(ga_init); +module_exit(ga_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(greenasia); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 39289699c32f..acc1abc834a4 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -107,9 +107,6 @@ #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 -#define USB_VENDOR_ID_BRIGHT 0x1241 -#define USB_DEVICE_ID_BRIGHT_ABNT2 0x1503 - #define USB_VENDOR_ID_BERKSHIRE 0x0c98 #define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 @@ -141,9 +138,8 @@ #define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 #define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 -#define USB_VENDOR_ID_DELL 0x413c -#define USB_DEVICE_ID_DELL_W7658 0x2005 -#define USB_DEVICE_ID_DELL_SK8115 0x2105 +#define USB_VENDOR_ID_DEALEXTREAME 0x10c5 +#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a #define USB_VENDOR_ID_DELORME 0x1163 #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 @@ -167,9 +163,6 @@ #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc -#define USB_VENDOR_ID_GENERIC_13BA 0x13ba -#define USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE 0x0017 - #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 #define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 @@ -292,7 +285,6 @@ #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a -#define USB_DEVICE_ID_LOGITECH_KBD 0xc311 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 @@ -339,6 +331,9 @@ #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 +#define USB_VENDOR_ID_NTRIG 0x1b96 +#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 + #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 @@ -383,9 +378,15 @@ #define USB_VENDOR_ID_TOPMAX 0x0663 #define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 +#define USB_VENDOR_ID_TOPSEED 0x0766 +#define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 + #define USB_VENDOR_ID_TURBOX 0x062a #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_VENDOR_ID_UCLOGIC 0x5543 +#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 + #define USB_VENDOR_ID_VERNIER 0x08f7 #define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 2bae340eafe2..83e07c9f4144 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -26,7 +26,6 @@ #define LG_RDESC 0x001 #define LG_BAD_RELATIVE_KEYS 0x002 #define LG_DUPLICATE_USAGES 0x004 -#define LG_RESET_LEDS 0x008 #define LG_EXPANDED_KEYMAP 0x010 #define LG_IGNORE_DOUBLED_WHEEL 0x020 #define LG_WIRELESS 0x040 @@ -248,9 +247,6 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - if (quirks & LG_RESET_LEDS) - usbhid_set_leds(hdev); - if (quirks & LG_FF) lgff_init(hdev); if (quirks & LG_FF2) @@ -279,9 +275,6 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), .driver_data = LG_DUPLICATE_USAGES }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD), - .driver_data = LG_RESET_LEDS }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c new file mode 100644 index 000000000000..db44fbd7bdf6 --- /dev/null +++ b/drivers/hid/hid-ntrig.c @@ -0,0 +1,82 @@ +/* + * HID driver for some ntrig "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + * Copyright (c) 2008 Rafi Rubin + * + */ + +/* + * 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. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define NTRIG_DUPLICATE_USAGES 0x001 + +#define nt_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) + +static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_DIGITIZER && + (usage->hid & 0xff) == 0x47) { + nt_map_key_clear(BTN_TOOL_DOUBLETAP); + return 1; + } + return 0; +} + +static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->type == EV_KEY || usage->type == EV_REL + || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} +static const struct hid_device_id ntrig_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN), + .driver_data = NTRIG_DUPLICATE_USAGES }, + { } +}; +MODULE_DEVICE_TABLE(hid, ntrig_devices); + +static struct hid_driver ntrig_driver = { + .name = "ntrig", + .id_table = ntrig_devices, + .input_mapping = ntrig_input_mapping, + .input_mapped = ntrig_input_mapped, +}; + +static int ntrig_init(void) +{ + return hid_register_driver(&ntrig_driver); +} + +static void ntrig_exit(void) +{ + hid_unregister_driver(&ntrig_driver); +} + +module_init(ntrig_init); +module_exit(ntrig_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(ntrig); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 86e563b8d644..dd5a3979a4de 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -102,7 +102,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) } ret = sony_set_operational(hdev); - if (ret) + if (ret < 0) goto err_stop; return 0; diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c new file mode 100644 index 000000000000..cca64a0564a9 --- /dev/null +++ b/drivers/hid/hid-topseed.c @@ -0,0 +1,77 @@ +/* + * HID driver for TopSeed Cyberlink remote + * + * Copyright (c) 2008 Lev Babiev + * based on hid-cherry driver + */ + +/* + * 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. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define ts_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x00d: ts_map_key_clear(KEY_HOME); break; + case 0x024: ts_map_key_clear(KEY_MENU); break; + case 0x025: ts_map_key_clear(KEY_TV); break; + case 0x048: ts_map_key_clear(KEY_RED); break; + case 0x047: ts_map_key_clear(KEY_GREEN); break; + case 0x049: ts_map_key_clear(KEY_YELLOW); break; + case 0x04a: ts_map_key_clear(KEY_BLUE); break; + case 0x04b: ts_map_key_clear(KEY_ANGLE); break; + case 0x04c: ts_map_key_clear(KEY_LANGUAGE); break; + case 0x04d: ts_map_key_clear(KEY_SUBTITLE); break; + case 0x031: ts_map_key_clear(KEY_AUDIO); break; + case 0x032: ts_map_key_clear(KEY_TEXT); break; + case 0x033: ts_map_key_clear(KEY_CHANNEL); break; + default: + return 0; + } + + return 1; +} + +static const struct hid_device_id ts_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ts_devices); + +static struct hid_driver ts_driver = { + .name = "topseed", + .id_table = ts_devices, + .input_mapping = ts_input_mapping, +}; + +static int ts_init(void) +{ + return hid_register_driver(&ts_driver); +} + +static void ts_exit(void) +{ + hid_unregister_driver(&ts_driver); +} + +module_init(ts_init); +module_exit(ts_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(topseed); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 7685ae6808c4..732449628971 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -208,7 +208,7 @@ static int hidraw_release(struct inode * inode, struct file * file) list_del(&list->node); dev = hidraw_table[minor]; - if (!dev->open--) { + if (!--dev->open) { if (list->hidraw->exist) dev->hid->ll_driver->close(dev->hid); else @@ -265,6 +265,34 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, break; } default: + { + struct hid_device *hid = dev->hid; + if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { + int len; + if (!hid->name) + return 0; + len = strlen(hid->name) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->name, len) ? + -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { + int len; + if (!hid->phys) + return 0; + len = strlen(hid->phys) + 1; + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + return copy_to_user(user_arg, hid->phys, len) ? + -EFAULT : len; + } + } + ret = -ENOTTY; } unlock_kernel(); @@ -329,7 +357,7 @@ int hidraw_connect(struct hid_device *hid) goto out; } - dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor), + dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor), NULL, "%s%d", "hidraw", minor); if (IS_ERR(dev->dev)) { diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 5d9aa95fc3ef..4edb3bef94a6 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -45,7 +45,7 @@ config USB_HIDDEV If unsure, say Y. menu "USB HID Boot Protocol drivers" - depends on USB!=n && USB_HID!=y + depends on USB!=n && USB_HID!=y && EMBEDDED config USB_KBD tristate "USB HIDBP Keyboard (simple Boot) support" diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 606369ea24ca..03cb494af1c5 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2006-2008 Jiri Kosina */ /* @@ -641,9 +641,7 @@ static void hid_find_max_report(struct hid_device *hid, unsigned int type, unsigned int size; list_for_each_entry(report, &hid->report_enum[type].report_list, list) { - size = ((report->size - 1) >> 3) + 1; - if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered) - size++; + size = ((report->size - 1) >> 3) + 1 + hid->report_enum[type].numbered; if (*max < size) *max = size; } @@ -653,13 +651,16 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma))) - return -1; - if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma))) - return -1; - if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma))) - return -1; - if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma))) + usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL, + &usbhid->inbuf_dma); + usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL, + &usbhid->outbuf_dma); + usbhid->cr = usb_buffer_alloc(dev, sizeof(*usbhid->cr), GFP_KERNEL, + &usbhid->cr_dma); + usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL, + &usbhid->ctrlbuf_dma); + if (!usbhid->inbuf || !usbhid->outbuf || !usbhid->cr || + !usbhid->ctrlbuf) return -1; return 0; @@ -807,7 +808,7 @@ static int usbhid_start(struct hid_device *hid) int interval; endpoint = &interface->endpoint[n].desc; - if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ + if (!usb_endpoint_xfer_int(endpoint)) continue; interval = endpoint->bInterval; @@ -876,6 +877,15 @@ static int usbhid_start(struct hid_device *hid) set_bit(HID_STARTED, &usbhid->iofl); + /* Some keyboards don't work until their LEDs have been set. + * Since BIOSes do set the LEDs, it must be safe for any device + * that supports the keyboard boot protocol. + */ + if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT && + interface->desc.bInterfaceProtocol == + USB_INTERFACE_PROTOCOL_KEYBOARD) + usbhid_set_leds(hid); + return 0; fail: diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 47ebe045f9b5..4391717d2519 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -54,6 +54,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 83e851a5ed30..6a98f9f572b0 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -49,6 +49,7 @@ struct hiddev { int exist; int open; + struct mutex existancelock; wait_queue_head_t wait; struct hid_device *hid; struct list_head list; @@ -63,6 +64,7 @@ struct hiddev_list { struct fasync_struct *fasync; struct hiddev *hiddev; struct list_head node; + struct mutex thread_lock; }; static struct hiddev *hiddev_table[HIDDEV_MINORS]; @@ -264,29 +266,48 @@ static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; - unsigned long flags; + int res; int i = iminor(inode) - HIDDEV_MINOR_BASE; - if (i >= HIDDEV_MINORS || !hiddev_table[i]) + if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i]) return -ENODEV; if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) return -ENOMEM; + mutex_init(&list->thread_lock); list->hiddev = hiddev_table[i]; - spin_lock_irqsave(&list->hiddev->list_lock, flags); - list_add_tail(&list->node, &hiddev_table[i]->list); - spin_unlock_irqrestore(&list->hiddev->list_lock, flags); file->private_data = list; - if (!list->hiddev->open++) - if (list->hiddev->exist) - usbhid_open(hiddev_table[i]->hid); + /* + * no need for locking because the USB major number + * is shared which usbcore guards against disconnect + */ + if (list->hiddev->exist) { + if (!list->hiddev->open++) { + res = usbhid_open(hiddev_table[i]->hid); + if (res < 0) { + res = -EIO; + goto bail; + } + } + } else { + res = -ENODEV; + goto bail; + } + + spin_lock_irq(&list->hiddev->list_lock); + list_add_tail(&list->node, &hiddev_table[i]->list); + spin_unlock_irq(&list->hiddev->list_lock); return 0; +bail: + file->private_data = NULL; + kfree(list->hiddev); + return res; } /* @@ -305,7 +326,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun DECLARE_WAITQUEUE(wait, current); struct hiddev_list *list = file->private_data; int event_size; - int retval = 0; + int retval; event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); @@ -313,10 +334,14 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun if (count < event_size) return 0; + /* lock against other threads */ + retval = mutex_lock_interruptible(&list->thread_lock); + if (retval) + return -ERESTARTSYS; + while (retval == 0) { if (list->head == list->tail) { - add_wait_queue(&list->hiddev->wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); + prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE); while (list->head == list->tail) { if (file->f_flags & O_NONBLOCK) { @@ -332,35 +357,45 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun break; } + /* let O_NONBLOCK tasks run */ + mutex_unlock(&list->thread_lock); schedule(); + if (mutex_lock_interruptible(&list->thread_lock)) + return -EINTR; set_current_state(TASK_INTERRUPTIBLE); } + finish_wait(&list->hiddev->wait, &wait); - set_current_state(TASK_RUNNING); - remove_wait_queue(&list->hiddev->wait, &wait); } - if (retval) + if (retval) { + mutex_unlock(&list->thread_lock); return retval; + } while (list->head != list->tail && retval + event_size <= count) { if ((list->flags & HIDDEV_FLAG_UREF) == 0) { - if (list->buffer[list->tail].field_index != - HID_FIELD_INDEX_NONE) { + if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) { struct hiddev_event event; + event.hid = list->buffer[list->tail].usage_code; event.value = list->buffer[list->tail].value; - if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) + if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) { + mutex_unlock(&list->thread_lock); return -EFAULT; + } retval += sizeof(struct hiddev_event); } } else { if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { - if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) + + if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) { + mutex_unlock(&list->thread_lock); return -EFAULT; + } retval += sizeof(struct hiddev_usage_ref); } } @@ -368,6 +403,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun } } + mutex_unlock(&list->thread_lock); return retval; } @@ -555,7 +591,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct hid_field *field; struct usbhid_device *usbhid = hid->driver_data; void __user *user_arg = (void __user *)arg; - int i; + int i, r; /* Called without BKL by compat methods so no BKL taken */ @@ -619,10 +655,22 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } case HIDIOCGSTRING: - return hiddev_ioctl_string(hiddev, cmd, user_arg); + mutex_lock(&hiddev->existancelock); + if (!hiddev->exist) + r = hiddev_ioctl_string(hiddev, cmd, user_arg); + else + r = -ENODEV; + mutex_unlock(&hiddev->existancelock); + return r; case HIDIOCINITREPORT: + mutex_lock(&hiddev->existancelock); + if (!hiddev->exist) { + mutex_unlock(&hiddev->existancelock); + return -ENODEV; + } usbhid_init_reports(hid); + mutex_unlock(&hiddev->existancelock); return 0; @@ -636,8 +684,12 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) return -EINVAL; - usbhid_submit_report(hid, report, USB_DIR_IN); - usbhid_wait_io(hid); + mutex_lock(&hiddev->existancelock); + if (hiddev->exist) { + usbhid_submit_report(hid, report, USB_DIR_IN); + usbhid_wait_io(hid); + } + mutex_unlock(&hiddev->existancelock); return 0; @@ -651,8 +703,12 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) return -EINVAL; - usbhid_submit_report(hid, report, USB_DIR_OUT); - usbhid_wait_io(hid); + mutex_lock(&hiddev->existancelock); + if (hiddev->exist) { + usbhid_submit_report(hid, report, USB_DIR_OUT); + usbhid_wait_io(hid); + } + mutex_unlock(&hiddev->existancelock); return 0; @@ -710,7 +766,13 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case HIDIOCGUSAGES: case HIDIOCSUSAGES: case HIDIOCGCOLLECTIONINDEX: - return hiddev_ioctl_usage(hiddev, cmd, user_arg); + mutex_lock(&hiddev->existancelock); + if (hiddev->exist) + r = hiddev_ioctl_usage(hiddev, cmd, user_arg); + else + r = -ENODEV; + mutex_unlock(&hiddev->existancelock); + return r; case HIDIOCGCOLLECTIONINFO: if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) @@ -808,23 +870,22 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) return -1; - retval = usb_register_dev(usbhid->intf, &hiddev_class); - if (retval) { - err_hid("Not able to get a minor for this device."); - kfree(hiddev); - return -1; - } - init_waitqueue_head(&hiddev->wait); INIT_LIST_HEAD(&hiddev->list); spin_lock_init(&hiddev->list_lock); + mutex_init(&hiddev->existancelock); hiddev->hid = hid; hiddev->exist = 1; - hid->minor = usbhid->intf->minor; - hid->hiddev = hiddev; - - hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + retval = usb_register_dev(usbhid->intf, &hiddev_class); + if (retval) { + err_hid("Not able to get a minor for this device."); + kfree(hiddev); + return -1; + } else { + hid->minor = usbhid->intf->minor; + hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + } return 0; } @@ -839,7 +900,9 @@ void hiddev_disconnect(struct hid_device *hid) struct hiddev *hiddev = hid->hiddev; struct usbhid_device *usbhid = hid->driver_data; + mutex_lock(&hiddev->existancelock); hiddev->exist = 0; + mutex_unlock(&hiddev->existancelock); hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; usb_deregister_dev(usbhid->intf, &hiddev_class); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 332abcdf9956..9eb30564be9c 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -40,6 +40,16 @@ int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); +/* iofl flags */ +#define HID_CTRL_RUNNING 1 +#define HID_OUT_RUNNING 2 +#define HID_IN_RUNNING 3 +#define HID_RESET_PENDING 4 +#define HID_SUSPENDED 5 +#define HID_CLEAR_HALT 6 +#define HID_DISCONNECTED 7 +#define HID_STARTED 8 + /* * USB-specific HID struct, to be pointed to * from struct hid_device->driver_data diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 608038d64f81..be8ee2cac8bb 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -2,13 +2,16 @@ * TI OMAP I2C master mode driver * * Copyright (C) 2003 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * - * Updated to work with multiple I2C interfaces on 24xx by - * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com> * Copyright (C) 2005 Nokia Corporation + * Copyright (C) 2004 - 2007 Texas Instruments. * - * Cleaned up by Juha Yrjölä <juha.yrjola@nokia.com> + * Originally written by MontaVista Software, Inc. + * Additional contributions by: + * Tony Lindgren <tony@atomide.com> + * Imre Deak <imre.deak@nokia.com> + * Juha Yrjölä <juha.yrjola@solidboot.com> + * Syed Khasim <x0khasim@ti.com> + * Nishant Menon <nm@ti.com> * * 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 @@ -33,8 +36,14 @@ #include <linux/completion.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/io.h> + +/* I2C controller revisions */ +#define OMAP_I2C_REV_2 0x20 -#include <asm/io.h> +/* I2C controller revisions present on specific hardware */ +#define OMAP_I2C_REV_ON_2430 0x36 +#define OMAP_I2C_REV_ON_3430 0x3C /* timeout waiting for the controller to respond */ #define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) @@ -43,6 +52,8 @@ #define OMAP_I2C_IE_REG 0x04 #define OMAP_I2C_STAT_REG 0x08 #define OMAP_I2C_IV_REG 0x0c +/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */ +#define OMAP_I2C_WE_REG 0x0c #define OMAP_I2C_SYSS_REG 0x10 #define OMAP_I2C_BUF_REG 0x14 #define OMAP_I2C_CNT_REG 0x18 @@ -55,8 +66,11 @@ #define OMAP_I2C_SCLL_REG 0x34 #define OMAP_I2C_SCLH_REG 0x38 #define OMAP_I2C_SYSTEST_REG 0x3c +#define OMAP_I2C_BUFSTAT_REG 0x40 /* I2C Interrupt Enable Register (OMAP_I2C_IE): */ +#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drain int enable */ +#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer drain int enable */ #define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */ #define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */ #define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */ @@ -64,7 +78,8 @@ #define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */ /* I2C Status Register (OMAP_I2C_STAT): */ -#define OMAP_I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define OMAP_I2C_STAT_XDR (1 << 14) /* TX Buffer draining */ +#define OMAP_I2C_STAT_RDR (1 << 13) /* RX Buffer draining */ #define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */ #define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ #define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ @@ -76,13 +91,34 @@ #define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */ #define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */ +/* I2C WE wakeup enable register */ +#define OMAP_I2C_WE_XDR_WE (1 << 14) /* TX drain wakup */ +#define OMAP_I2C_WE_RDR_WE (1 << 13) /* RX drain wakeup */ +#define OMAP_I2C_WE_AAS_WE (1 << 9) /* Address as slave wakeup*/ +#define OMAP_I2C_WE_BF_WE (1 << 8) /* Bus free wakeup */ +#define OMAP_I2C_WE_STC_WE (1 << 6) /* Start condition wakeup */ +#define OMAP_I2C_WE_GC_WE (1 << 5) /* General call wakeup */ +#define OMAP_I2C_WE_DRDY_WE (1 << 3) /* TX/RX data ready wakeup */ +#define OMAP_I2C_WE_ARDY_WE (1 << 2) /* Reg access ready wakeup */ +#define OMAP_I2C_WE_NACK_WE (1 << 1) /* No acknowledgment wakeup */ +#define OMAP_I2C_WE_AL_WE (1 << 0) /* Arbitration lost wakeup */ + +#define OMAP_I2C_WE_ALL (OMAP_I2C_WE_XDR_WE | OMAP_I2C_WE_RDR_WE | \ + OMAP_I2C_WE_AAS_WE | OMAP_I2C_WE_BF_WE | \ + OMAP_I2C_WE_STC_WE | OMAP_I2C_WE_GC_WE | \ + OMAP_I2C_WE_DRDY_WE | OMAP_I2C_WE_ARDY_WE | \ + OMAP_I2C_WE_NACK_WE | OMAP_I2C_WE_AL_WE) + /* I2C Buffer Configuration Register (OMAP_I2C_BUF): */ #define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */ +#define OMAP_I2C_BUF_RXFIF_CLR (1 << 14) /* RX FIFO Clear */ #define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */ +#define OMAP_I2C_BUF_TXFIF_CLR (1 << 6) /* TX FIFO Clear */ /* I2C Configuration Register (OMAP_I2C_CON): */ #define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */ #define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */ +#define OMAP_I2C_CON_OPMODE_HS (1 << 12) /* High Speed support */ #define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */ #define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */ #define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */ @@ -91,6 +127,10 @@ #define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */ #define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */ +/* I2C SCL time value when Master */ +#define OMAP_I2C_SCLL_HSSCLL 8 +#define OMAP_I2C_SCLH_HSSCLH 8 + /* I2C System Test Register (OMAP_I2C_SYSTEST): */ #ifdef DEBUG #define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ @@ -103,17 +143,19 @@ #define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */ #endif -/* I2C System Status register (OMAP_I2C_SYSS): */ -#define OMAP_I2C_SYSS_RDONE (1 << 0) /* Reset Done */ +/* OCP_SYSSTATUS bit definitions */ +#define SYSS_RESETDONE_MASK (1 << 0) + +/* OCP_SYSCONFIG bit definitions */ +#define SYSC_CLOCKACTIVITY_MASK (0x3 << 8) +#define SYSC_SIDLEMODE_MASK (0x3 << 3) +#define SYSC_ENAWAKEUP_MASK (1 << 2) +#define SYSC_SOFTRESET_MASK (1 << 1) +#define SYSC_AUTOIDLE_MASK (1 << 0) -/* I2C System Configuration Register (OMAP_I2C_SYSC): */ -#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */ +#define SYSC_IDLEMODE_SMART 0x2 +#define SYSC_CLOCKACTIVITY_FCLK 0x2 -/* REVISIT: Use platform_data instead of module parameters */ -/* Fast Mode = 400 kHz, Standard = 100 kHz */ -static int clock = 100; /* Default: 100 kHz */ -module_param(clock, int, 0); -MODULE_PARM_DESC(clock, "Set I2C clock in kHz: 400=fast mode (default == 100)"); struct omap_i2c_dev { struct device *dev; @@ -123,11 +165,17 @@ struct omap_i2c_dev { struct clk *fclk; /* Functional clock */ struct completion cmd_complete; struct resource *ioarea; + u32 speed; /* Speed of bus in Khz */ u16 cmd_err; u8 *buf; size_t buf_len; struct i2c_adapter adapter; - unsigned rev1:1; + u8 fifo_size; /* use as flag and value + * fifo_size==0 implies no fifo + * if set, should be trsh+1 + */ + u8 rev; + unsigned b_hw:1; /* bad h/w fixes */ unsigned idle:1; u16 iestate; /* Saved interrupt register */ }; @@ -143,9 +191,9 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) return __raw_readw(i2c_dev->base + reg); } -static int omap_i2c_get_clocks(struct omap_i2c_dev *dev) +static int __init omap_i2c_get_clocks(struct omap_i2c_dev *dev) { - if (cpu_is_omap16xx() || cpu_is_omap24xx()) { + if (cpu_is_omap16xx() || cpu_class_is_omap2()) { dev->iclk = clk_get(dev->dev, "i2c_ick"); if (IS_ERR(dev->iclk)) { dev->iclk = NULL; @@ -178,25 +226,33 @@ static void omap_i2c_put_clocks(struct omap_i2c_dev *dev) static void omap_i2c_unidle(struct omap_i2c_dev *dev) { + WARN_ON(!dev->idle); + if (dev->iclk != NULL) clk_enable(dev->iclk); clk_enable(dev->fclk); + dev->idle = 0; if (dev->iestate) omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate); - dev->idle = 0; } static void omap_i2c_idle(struct omap_i2c_dev *dev) { u16 iv; - dev->idle = 1; + WARN_ON(dev->idle); + dev->iestate = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0); - if (dev->rev1) - iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */ - else + if (dev->rev < OMAP_I2C_REV_2) { + iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); /* Read clears */ + } else { omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, dev->iestate); + + /* Flush posted write before the dev->idle store occurs */ + omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + } + dev->idle = 1; clk_disable(dev->fclk); if (dev->iclk != NULL) clk_disable(dev->iclk); @@ -204,18 +260,20 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev) static int omap_i2c_init(struct omap_i2c_dev *dev) { - u16 psc = 0; + u16 psc = 0, scll = 0, sclh = 0; + u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0; unsigned long fclk_rate = 12000000; unsigned long timeout; + unsigned long internal_clk = 0; - if (!dev->rev1) { - omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST); + if (dev->rev >= OMAP_I2C_REV_2) { + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK); /* For some reason we need to set the EN bit before the * reset done bit gets set. */ timeout = jiffies + OMAP_I2C_TIMEOUT; omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) & - OMAP_I2C_SYSS_RDONE)) { + SYSS_RESETDONE_MASK)) { if (time_after(jiffies, timeout)) { dev_warn(dev->dev, "timeout waiting " "for controller reset\n"); @@ -223,6 +281,33 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) } msleep(1); } + + /* SYSC register is cleared by the reset; rewrite it */ + if (dev->rev == OMAP_I2C_REV_ON_2430) { + + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, + SYSC_AUTOIDLE_MASK); + + } else if (dev->rev >= OMAP_I2C_REV_ON_3430) { + u32 v; + + v = SYSC_AUTOIDLE_MASK; + v |= SYSC_ENAWAKEUP_MASK; + v |= (SYSC_IDLEMODE_SMART << + __ffs(SYSC_SIDLEMODE_MASK)); + v |= (SYSC_CLOCKACTIVITY_FCLK << + __ffs(SYSC_CLOCKACTIVITY_MASK)); + + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v); + /* + * Enabling all wakup sources to stop I2C freezing on + * WFI instruction. + * REVISIT: Some wkup sources might not be needed. + */ + omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, + OMAP_I2C_WE_ALL); + + } } omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); @@ -249,27 +334,65 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) psc = fclk_rate / 12000000; } + if (cpu_is_omap2430() || cpu_is_omap34xx()) { + + /* HSI2C controller internal clk rate should be 19.2 Mhz */ + internal_clk = 19200; + fclk_rate = clk_get_rate(dev->fclk) / 1000; + + /* Compute prescaler divisor */ + psc = fclk_rate / internal_clk; + psc = psc - 1; + + /* If configured for High Speed */ + if (dev->speed > 400) { + /* For first phase of HS mode */ + fsscll = internal_clk / (400 * 2) - 6; + fssclh = internal_clk / (400 * 2) - 6; + + /* For second phase of HS mode */ + hsscll = fclk_rate / (dev->speed * 2) - 6; + hssclh = fclk_rate / (dev->speed * 2) - 6; + } else { + /* To handle F/S modes */ + fsscll = internal_clk / (dev->speed * 2) - 6; + fssclh = internal_clk / (dev->speed * 2) - 6; + } + scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll; + sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh; + } else { + /* Program desired operating rate */ + fclk_rate /= (psc + 1) * 1000; + if (psc > 2) + psc = 2; + scll = fclk_rate / (dev->speed * 2) - 7 + psc; + sclh = fclk_rate / (dev->speed * 2) - 7 + psc; + } + /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */ omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc); - /* Program desired operating rate */ - fclk_rate /= (psc + 1) * 1000; - if (psc > 2) - psc = 2; + /* SCL low and high time values */ + omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll); + omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh); - omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, - fclk_rate / (clock * 2) - 7 + psc); - omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, - fclk_rate / (clock * 2) - 7 + psc); + if (dev->fifo_size) + /* Note: setup required fifo size - 1 */ + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, + (dev->fifo_size - 1) << 8 | /* RTRSH */ + OMAP_I2C_BUF_RXFIF_CLR | + (dev->fifo_size - 1) | /* XTRSH */ + OMAP_I2C_BUF_TXFIF_CLR); /* Take the I2C module out of reset: */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); /* Enable interrupts */ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, - (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | - OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | - OMAP_I2C_IE_AL)); + (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | + OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | + OMAP_I2C_IE_AL) | ((dev->fifo_size) ? + (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0)); return 0; } @@ -316,20 +439,59 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); + /* Clear the FIFO Buffers */ + w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG); + w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w); + init_completion(&dev->cmd_complete); dev->cmd_err = 0; w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; + + /* High speed configuration */ + if (dev->speed > 400) + w |= OMAP_I2C_CON_OPMODE_HS; + if (msg->flags & I2C_M_TEN) w |= OMAP_I2C_CON_XA; if (!(msg->flags & I2C_M_RD)) w |= OMAP_I2C_CON_TRX; - if (stop) + + if (!dev->b_hw && stop) w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); - r = wait_for_completion_interruptible_timeout(&dev->cmd_complete, - OMAP_I2C_TIMEOUT); + /* + * Don't write stt and stp together on some hardware. + */ + if (dev->b_hw && stop) { + unsigned long delay = jiffies + OMAP_I2C_TIMEOUT; + u16 con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + while (con & OMAP_I2C_CON_STT) { + con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + + /* Let the user know if i2c is in a bad state */ + if (time_after(jiffies, delay)) { + dev_err(dev->dev, "controller timed out " + "waiting for start condition to finish\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + w |= OMAP_I2C_CON_STP; + w &= ~OMAP_I2C_CON_STT; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + } + + /* + * REVISIT: We should abort the transfer on signals, but the bus goes + * into arbitration and we're currently unable to recover from it. + */ + r = wait_for_completion_timeout(&dev->cmd_complete, + OMAP_I2C_TIMEOUT); dev->buf_len = 0; if (r < 0) return r; @@ -376,7 +538,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) omap_i2c_unidle(dev); - if ((r = omap_i2c_wait_for_bb(dev)) < 0) + r = omap_i2c_wait_for_bb(dev); + if (r < 0) goto out; for (i = 0; i < num; i++) { @@ -411,6 +574,9 @@ omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat) omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); } +/* rev1 devices are apparently only on some 15xx */ +#ifdef CONFIG_ARCH_OMAP15XX + static irqreturn_t omap_i2c_rev1_isr(int this_irq, void *dev_id) { @@ -465,6 +631,9 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id) return IRQ_HANDLED; } +#else +#define omap_i2c_rev1_isr NULL +#endif static irqreturn_t omap_i2c_isr(int this_irq, void *dev_id) @@ -472,7 +641,7 @@ omap_i2c_isr(int this_irq, void *dev_id) struct omap_i2c_dev *dev = dev_id; u16 bits; u16 stat, w; - int count = 0; + int err, count = 0; if (dev->idle) return IRQ_NONE; @@ -487,39 +656,96 @@ omap_i2c_isr(int this_irq, void *dev_id) omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); - if (stat & OMAP_I2C_STAT_ARDY) { - omap_i2c_complete_cmd(dev, 0); - continue; + err = 0; + if (stat & OMAP_I2C_STAT_NACK) { + err |= OMAP_I2C_STAT_NACK; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, + OMAP_I2C_CON_STP); } - if (stat & OMAP_I2C_STAT_RRDY) { - w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); - if (dev->buf_len) { - *dev->buf++ = w; - dev->buf_len--; + if (stat & OMAP_I2C_STAT_AL) { + dev_err(dev->dev, "Arbitration lost\n"); + err |= OMAP_I2C_STAT_AL; + } + if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | + OMAP_I2C_STAT_AL)) + omap_i2c_complete_cmd(dev, err); + if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) { + u8 num_bytes = 1; + if (dev->fifo_size) { + if (stat & OMAP_I2C_STAT_RRDY) + num_bytes = dev->fifo_size; + else + num_bytes = omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG); + } + while (num_bytes) { + num_bytes--; + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); if (dev->buf_len) { - *dev->buf++ = w >> 8; + *dev->buf++ = w; dev->buf_len--; + /* Data reg from 2430 is 8 bit wide */ + if (!cpu_is_omap2430() && + !cpu_is_omap34xx()) { + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } + } else { + if (stat & OMAP_I2C_STAT_RRDY) + dev_err(dev->dev, + "RRDY IRQ while no data" + " requested\n"); + if (stat & OMAP_I2C_STAT_RDR) + dev_err(dev->dev, + "RDR IRQ while no data" + " requested\n"); + break; } - } else - dev_err(dev->dev, "RRDY IRQ while no data " - "requested\n"); - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY); + } + omap_i2c_ack_stat(dev, + stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)); continue; } - if (stat & OMAP_I2C_STAT_XRDY) { - w = 0; - if (dev->buf_len) { - w = *dev->buf++; - dev->buf_len--; + if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) { + u8 num_bytes = 1; + if (dev->fifo_size) { + if (stat & OMAP_I2C_STAT_XRDY) + num_bytes = dev->fifo_size; + else + num_bytes = omap_i2c_read_reg(dev, + OMAP_I2C_BUFSTAT_REG); + } + while (num_bytes) { + num_bytes--; + w = 0; if (dev->buf_len) { - w |= *dev->buf++ << 8; + w = *dev->buf++; dev->buf_len--; + /* Data reg from 2430 is 8 bit wide */ + if (!cpu_is_omap2430() && + !cpu_is_omap34xx()) { + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + } + } else { + if (stat & OMAP_I2C_STAT_XRDY) + dev_err(dev->dev, + "XRDY IRQ while no " + "data to send\n"); + if (stat & OMAP_I2C_STAT_XDR) + dev_err(dev->dev, + "XDR IRQ while no " + "data to send\n"); + break; } - } else - dev_err(dev->dev, "XRDY IRQ while no " - "data to send\n"); - omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY); + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + } + omap_i2c_ack_stat(dev, + stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); continue; } if (stat & OMAP_I2C_STAT_ROVR) { @@ -527,18 +753,9 @@ omap_i2c_isr(int this_irq, void *dev_id) dev->cmd_err |= OMAP_I2C_STAT_ROVR; } if (stat & OMAP_I2C_STAT_XUDF) { - dev_err(dev->dev, "Transmit overflow\n"); + dev_err(dev->dev, "Transmit underflow\n"); dev->cmd_err |= OMAP_I2C_STAT_XUDF; } - if (stat & OMAP_I2C_STAT_NACK) { - omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK); - omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, - OMAP_I2C_CON_STP); - } - if (stat & OMAP_I2C_STAT_AL) { - dev_err(dev->dev, "Arbitration lost\n"); - omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL); - } } return count ? IRQ_HANDLED : IRQ_NONE; @@ -549,13 +766,15 @@ static const struct i2c_algorithm omap_i2c_algo = { .functionality = omap_i2c_func, }; -static int +static int __init omap_i2c_probe(struct platform_device *pdev) { struct omap_i2c_dev *dev; struct i2c_adapter *adap; struct resource *mem, *irq, *ioarea; + irq_handler_t isr; int r; + u32 speed = 0; /* NOTE: driver uses the static register mapping */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -576,17 +795,19 @@ omap_i2c_probe(struct platform_device *pdev) return -EBUSY; } - if (clock > 200) - clock = 400; /* Fast mode */ - else - clock = 100; /* Standard mode */ - dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); if (!dev) { r = -ENOMEM; goto err_release_region; } + if (pdev->dev.platform_data != NULL) + speed = *(u32 *)pdev->dev.platform_data; + else + speed = 100; /* Defualt speed */ + + dev->speed = speed; + dev->idle = 1; dev->dev = &pdev->dev; dev->irq = irq->start; dev->base = ioremap(mem->start, mem->end - mem->start + 1); @@ -602,22 +823,39 @@ omap_i2c_probe(struct platform_device *pdev) omap_i2c_unidle(dev); - if (cpu_is_omap15xx()) - dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20; + dev->rev = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; + + if (cpu_is_omap2430() || cpu_is_omap34xx()) { + u16 s; + + /* Set up the fifo size - Get total size */ + s = (omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3; + dev->fifo_size = 0x8 << s; + + /* + * Set up notification threshold as half the total available + * size. This is to ensure that we can handle the status on int + * call back latencies. + */ + dev->fifo_size = (dev->fifo_size / 2); + dev->b_hw = 1; /* Enable hardware fixes */ + } /* reset ASAP, clearing any IRQs */ omap_i2c_init(dev); - r = request_irq(dev->irq, dev->rev1 ? omap_i2c_rev1_isr : omap_i2c_isr, - 0, pdev->name, dev); + isr = (dev->rev < OMAP_I2C_REV_2) ? omap_i2c_rev1_isr : omap_i2c_isr; + r = request_irq(dev->irq, isr, 0, pdev->name, dev); if (r) { dev_err(dev->dev, "failure requesting irq %i\n", dev->irq); goto err_unuse_clocks; } - r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; + dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n", - pdev->id, r >> 4, r & 0xf, clock); + pdev->id, dev->rev >> 4, dev->rev & 0xf, dev->speed); + + omap_i2c_idle(dev); adap = &dev->adapter; i2c_set_adapdata(adap, dev); @@ -635,8 +873,6 @@ omap_i2c_probe(struct platform_device *pdev) goto err_free_irq; } - omap_i2c_idle(dev); - return 0; err_free_irq: diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index c39079f9c73f..f69f91ffb469 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -35,11 +35,9 @@ #include <linux/clk.h> #include <linux/cpufreq.h> -#include <mach/hardware.h> #include <asm/irq.h> #include <asm/io.h> -#include <mach/regs-gpio.h> #include <plat/regs-iic.h> #include <plat/iic.h> @@ -64,6 +62,7 @@ struct s3c24xx_i2c { unsigned int msg_ptr; unsigned int tx_setup; + unsigned int irq; enum s3c24xx_i2c_state state; unsigned long clkrate; @@ -71,7 +70,6 @@ struct s3c24xx_i2c { void __iomem *regs; struct clk *clk; struct device *dev; - struct resource *irq; struct resource *ioarea; struct i2c_adapter adap; @@ -80,16 +78,7 @@ struct s3c24xx_i2c { #endif }; -/* default platform data to use if not supplied in the platform_device -*/ - -static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = { - .flags = 0, - .slave_addr = 0x10, - .bus_freq = 100*1000, - .max_freq = 400*1000, - .sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON, -}; +/* default platform data removed, dev should always carry data. */ /* s3c24xx_i2c_is2440() * @@ -103,21 +92,6 @@ static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c) return !strcmp(pdev->name, "s3c2440-i2c"); } - -/* s3c24xx_i2c_get_platformdata - * - * get the platform data associated with the given device, or return - * the default if there is none -*/ - -static inline struct s3c2410_platform_i2c *s3c24xx_i2c_get_platformdata(struct device *dev) -{ - if (dev->platform_data != NULL) - return (struct s3c2410_platform_i2c *)dev->platform_data; - - return &s3c24xx_i2c_default_platform; -} - /* s3c24xx_i2c_master_complete * * complete the message and wake up the caller, using the given return code, @@ -130,7 +104,7 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) i2c->msg_ptr = 0; i2c->msg = NULL; - i2c->msg_idx ++; + i2c->msg_idx++; i2c->msg_num = 0; if (ret) i2c->msg_idx = ret; @@ -141,19 +115,17 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) { unsigned long tmp; - + tmp = readl(i2c->regs + S3C2410_IICCON); writel(tmp & ~S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); - } static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c) { unsigned long tmp; - + tmp = readl(i2c->regs + S3C2410_IICCON); writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); - } /* irq enable/disable functions */ @@ -161,7 +133,7 @@ static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c) static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c) { unsigned long tmp; - + tmp = readl(i2c->regs + S3C2410_IICCON); writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); } @@ -169,7 +141,7 @@ static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c) static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) { unsigned long tmp; - + tmp = readl(i2c->regs + S3C2410_IICCON); writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); } @@ -177,10 +149,10 @@ static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) /* s3c24xx_i2c_message_start * - * put the start of a message onto the bus + * put the start of a message onto the bus */ -static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, +static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr & 0x7f) << 1; @@ -199,15 +171,15 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, if (msg->flags & I2C_M_REV_DIR_ADDR) addr ^= 1; - // todo - check for wether ack wanted or not + /* todo - check for wether ack wanted or not */ s3c24xx_i2c_enable_ack(i2c); iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT); - + dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); - + /* delay here to ensure the data byte has gotten onto the bus * before the transaction is started */ @@ -215,8 +187,8 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); - - stat |= S3C2410_IICSTAT_START; + + stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); } @@ -227,11 +199,11 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) dev_dbg(i2c->dev, "STOP\n"); /* stop the transfer */ - iicstat &= ~ S3C2410_IICSTAT_START; + iicstat &= ~S3C2410_IICSTAT_START; writel(iicstat, i2c->regs + S3C2410_IICSTAT); - + i2c->state = STATE_STOP; - + s3c24xx_i2c_master_complete(i2c, ret); s3c24xx_i2c_disable_irq(i2c); } @@ -241,7 +213,7 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) /* is_lastmsg() * - * returns TRUE if the current message is the last in the set + * returns TRUE if the current message is the last in the set */ static inline int is_lastmsg(struct s3c24xx_i2c *i2c) @@ -289,14 +261,14 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) case STATE_STOP: dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); - s3c24xx_i2c_disable_irq(i2c); + s3c24xx_i2c_disable_irq(i2c); goto out_ack; case STATE_START: /* last thing we did was send a start condition on the * bus, or started a new i2c message */ - + if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ @@ -322,7 +294,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) if (i2c->state == STATE_READ) goto prepare_read; - /* fall through to the write state, as we will need to + /* fall through to the write state, as we will need to * send a byte as well */ case STATE_WRITE: @@ -339,7 +311,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) } } - retry_write: + retry_write: if (!is_msgend(i2c)) { byte = i2c->msg->buf[i2c->msg_ptr++]; @@ -359,9 +331,9 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) dev_dbg(i2c->dev, "WRITE: Next Message\n"); i2c->msg_ptr = 0; - i2c->msg_idx ++; + i2c->msg_idx++; i2c->msg++; - + /* check to see if we need to do another message */ if (i2c->msg->flags & I2C_M_NOSTART) { @@ -375,7 +347,6 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) goto retry_write; } else { - /* send the new start */ s3c24xx_i2c_message_start(i2c, i2c->msg); i2c->state = STATE_START; @@ -389,7 +360,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) break; case STATE_READ: - /* we have a byte of data in the data register, do + /* we have a byte of data in the data register, do * something with it, and then work out wether we are * going to do any more read/write */ @@ -397,13 +368,13 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; - prepare_read: + prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ if (is_lastmsg(i2c)) s3c24xx_i2c_disable_ack(i2c); - + } else if (is_msgend(i2c)) { /* ok, we've read the entire buffer, see if there * is anything else we need to do */ @@ -429,7 +400,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) /* acknowlegde the IRQ and get back on with the work */ out_ack: - tmp = readl(i2c->regs + S3C2410_IICCON); + tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); out: @@ -450,19 +421,19 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) status = readl(i2c->regs + S3C2410_IICSTAT); if (status & S3C2410_IICSTAT_ARBITR) { - // deal with arbitration loss + /* deal with arbitration loss */ dev_err(i2c->dev, "deal with arbitration loss\n"); } if (i2c->state == STATE_IDLE) { dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n"); - tmp = readl(i2c->regs + S3C2410_IICCON); + tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); goto out; } - + /* pretty much this leaves us with the fact that we've * transmitted or received whatever byte we last sent */ @@ -485,16 +456,13 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) while (timeout-- > 0) { iicstat = readl(i2c->regs + S3C2410_IICSTAT); - + if (!(iicstat & S3C2410_IICSTAT_BUSBUSY)) return 0; msleep(1); } - dev_dbg(i2c->dev, "timeout: GPEDAT is %08x\n", - __raw_readl(S3C2410_GPEDAT)); - return -ETIMEDOUT; } @@ -503,7 +471,8 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) * this starts an i2c transfer */ -static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) +static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, + struct i2c_msg *msgs, int num) { unsigned long timeout; int ret; @@ -529,12 +498,12 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); spin_unlock_irq(&i2c->lock); - + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; - /* having these next two as dev_err() makes life very + /* having these next two as dev_err() makes life very * noisy when doing an i2cdetect */ if (timeout == 0) @@ -591,19 +560,6 @@ static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .functionality = s3c24xx_i2c_func, }; -static struct s3c24xx_i2c s3c24xx_i2c = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock), - .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait), - .tx_setup = 50, - .adap = { - .name = "s3c2410-i2c", - .owner = THIS_MODULE, - .algo = &s3c24xx_i2c_algorithm, - .retries = 2, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, - }, -}; - /* s3c24xx_i2c_calcdivisor * * return the divisor settings for a given frequency @@ -643,7 +599,7 @@ static inline int freq_acceptable(unsigned int freq, unsigned int wanted) { int diff = freq - wanted; - return (diff >= -2 && diff <= 2); + return diff >= -2 && diff <= 2; } /* s3c24xx_i2c_clockrate @@ -655,7 +611,7 @@ static inline int freq_acceptable(unsigned int freq, unsigned int wanted) static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) { - struct s3c2410_platform_i2c *pdata; + struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data; unsigned long clkin = clk_get_rate(i2c->clk); unsigned int divs, div1; u32 iiccon; @@ -663,10 +619,8 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) int start, end; i2c->clkrate = clkin; - - pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent); clkin /= 1000; /* clkin now in KHz */ - + dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n", pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq); @@ -774,7 +728,7 @@ static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) /* s3c24xx_i2c_init * - * initialise the controller, set the IO lines and frequency + * initialise the controller, set the IO lines and frequency */ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) @@ -785,15 +739,15 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) /* get the plafrom data */ - pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent); + pdata = i2c->dev->platform_data; /* inititalise the gpio */ - s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA); - s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL); + if (pdata->cfg_gpio) + pdata->cfg_gpio(to_platform_device(i2c->dev)); /* write slave address */ - + writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD); dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); @@ -831,12 +785,32 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) static int s3c24xx_i2c_probe(struct platform_device *pdev) { - struct s3c24xx_i2c *i2c = &s3c24xx_i2c; + struct s3c24xx_i2c *i2c; struct s3c2410_platform_i2c *pdata; struct resource *res; int ret; - pdata = s3c24xx_i2c_get_platformdata(&pdev->dev); + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); + if (!i2c) { + dev_err(&pdev->dev, "no memory for state\n"); + return -ENOMEM; + } + + strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &s3c24xx_i2c_algorithm; + i2c->adap.retries = 2; + i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + i2c->tx_setup = 50; + + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); /* find the clock and enable it */ @@ -878,7 +852,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) goto err_ioarea; } - dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); + dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", + i2c->regs, i2c->ioarea, res); /* setup info block for the i2c core */ @@ -892,29 +867,23 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to - * ensure no current IRQs pending + * ensure no current IRQs pending */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { dev_err(&pdev->dev, "cannot find IRQ\n"); - ret = -ENOENT; goto err_iomap; } - ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, - pdev->name, i2c); + ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, + dev_name(&pdev->dev), i2c); if (ret != 0) { - dev_err(&pdev->dev, "cannot claim IRQ\n"); + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); goto err_iomap; } - i2c->irq = res; - - dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res, - (unsigned long)res->start); - ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); @@ -944,7 +913,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) s3c24xx_i2c_deregister_cpufreq(i2c); err_irq: - free_irq(i2c->irq->start, i2c); + free_irq(i2c->irq, i2c); err_iomap: iounmap(i2c->regs); @@ -958,6 +927,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) clk_put(i2c->clk); err_noclk: + kfree(i2c); return ret; } @@ -973,7 +943,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) s3c24xx_i2c_deregister_cpufreq(i2c); i2c_del_adapter(&i2c->adap); - free_irq(i2c->irq->start, i2c); + free_irq(i2c->irq, i2c); clk_disable(i2c->clk); clk_put(i2c->clk); @@ -982,6 +952,7 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) release_resource(i2c->ioarea); kfree(i2c->ioarea); + kfree(i2c); return 0; } diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 4c35702830ce..864ac561fdbb 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -126,19 +126,6 @@ config ISP1301_OMAP This driver can also be built as a module. If so, the module will be called isp1301_omap. -config TPS65010 - tristate "TPS6501x Power Management chips" - depends on GPIOLIB - default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK - help - If you say yes here you get support for the TPS6501x series of - Power Management chips. These include voltage regulators, - lithium ion/polymer battery charging, and other features that - are often used in portable devices like cell phones and cameras. - - This driver can also be built as a module. If so, the module - will be called tps65010. - config SENSORS_MAX6875 tristate "Maxim MAX6875 Power supply supervisor" depends on EXPERIMENTAL @@ -164,16 +151,6 @@ config SENSORS_TSL2550 This driver can also be built as a module. If so, the module will be called tsl2550. -config MENELAUS - bool "TWL92330/Menelaus PM chip" - depends on I2C=y && ARCH_OMAP24XX - help - If you say yes here you get support for the Texas Instruments - TWL92330/Menelaus Power Management chip. This include voltage - regulators, Dual slot memory card tranceivers, real-time clock - and other features that are often used in portable devices like - cell phones and PDAs. - config MCU_MPC8349EMITX tristate "MPC8349E-mITX MCU driver" depends on I2C && PPC_83xx diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 23d2a31b0a64..8b95f41a5001 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -19,8 +19,6 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o -obj-$(CONFIG_TPS65010) += tps65010.o -obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c index 53912c327bfe..8dc2bb781605 100644 --- a/drivers/infiniband/hw/ipath/ipath_fs.c +++ b/drivers/infiniband/hw/ipath/ipath_fs.c @@ -57,9 +57,6 @@ static int ipathfs_mknod(struct inode *dir, struct dentry *dentry, } inode->i_mode = mode; - inode->i_uid = 0; - inode->i_gid = 0; - inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_private = data; if ((mode & S_IFMT) == S_IFDIR) { diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c index d5b4cc357a3c..650120261abf 100644 --- a/drivers/isdn/capi/capidrv.c +++ b/drivers/isdn/capi/capidrv.c @@ -1519,7 +1519,7 @@ static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) int digit2 = 0; if (!isdigit(*s)) return -3; while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; } - if (digit1 <= 0 && digit1 > 30) return -4; + if (digit1 <= 0 || digit1 > 30) return -4; if (*s == 0 || *s == ',' || *s == ' ') { bmask |= (1 << digit1); digit1 = 0; @@ -1530,7 +1530,7 @@ static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) s++; if (!isdigit(*s)) return -3; while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; } - if (digit2 <= 0 && digit2 > 30) return -4; + if (digit2 <= 0 || digit2 > 30) return -4; if (*s == 0 || *s == ',' || *s == ' ') { if (digit1 > digit2) for (i = digit2; i <= digit1 ; i++) diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c index 0aa66ec4cbdd..b129409925af 100644 --- a/drivers/isdn/capi/capifs.c +++ b/drivers/isdn/capi/capifs.c @@ -111,8 +111,6 @@ capifs_fill_super(struct super_block *s, void *data, int silent) goto fail; inode->i_ino = 1; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_blocks = 0; - inode->i_uid = inode->i_gid = 0; inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 1c615804ea76..72880b7e28d9 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -3,9 +3,10 @@ # dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ - dm-ioctl.o dm-io.o dm-kcopyd.o + dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-multipath-objs := dm-path-selector.o dm-mpath.o -dm-snapshot-objs := dm-snap.o dm-exception-store.o +dm-snapshot-objs := dm-snap.o dm-exception-store.o dm-snap-transient.o \ + dm-snap-persistent.o dm-mirror-objs := dm-raid1.o md-mod-objs := md.o bitmap.o raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \ diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 3326750ec02c..35bda49796fb 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1322,11 +1322,7 @@ static int __init dm_crypt_init(void) static void __exit dm_crypt_exit(void) { - int r = dm_unregister_target(&crypt_target); - - if (r < 0) - DMERR("unregister failed %d", r); - + dm_unregister_target(&crypt_target); kmem_cache_destroy(_crypt_io_pool); } diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index 848b381f1173..59ee1b015d2d 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -364,11 +364,7 @@ bad_queue: static void __exit dm_delay_exit(void) { - int r = dm_unregister_target(&delay_target); - - if (r < 0) - DMERR("unregister failed %d", r); - + dm_unregister_target(&delay_target); kmem_cache_destroy(delayed_cache); destroy_workqueue(kdelayd_wq); } diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 01590f3e0009..dccbfb0e010f 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -1,756 +1,45 @@ /* - * dm-exception-store.c - * * Copyright (C) 2001-2002 Sistina Software (UK) Limited. - * Copyright (C) 2006 Red Hat GmbH + * Copyright (C) 2006-2008 Red Hat GmbH * * This file is released under the GPL. */ -#include "dm-snap.h" +#include "dm-exception-store.h" #include <linux/mm.h> #include <linux/pagemap.h> #include <linux/vmalloc.h> #include <linux/slab.h> -#include <linux/dm-io.h> -#include <linux/dm-kcopyd.h> - -#define DM_MSG_PREFIX "snapshots" -#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */ - -/*----------------------------------------------------------------- - * Persistent snapshots, by persistent we mean that the snapshot - * will survive a reboot. - *---------------------------------------------------------------*/ - -/* - * We need to store a record of which parts of the origin have - * been copied to the snapshot device. The snapshot code - * requires that we copy exception chunks to chunk aligned areas - * of the COW store. It makes sense therefore, to store the - * metadata in chunk size blocks. - * - * There is no backward or forward compatibility implemented, - * snapshots with different disk versions than the kernel will - * not be usable. It is expected that "lvcreate" will blank out - * the start of a fresh COW device before calling the snapshot - * constructor. - * - * The first chunk of the COW device just contains the header. - * After this there is a chunk filled with exception metadata, - * followed by as many exception chunks as can fit in the - * metadata areas. - * - * All on disk structures are in little-endian format. The end - * of the exceptions info is indicated by an exception with a - * new_chunk of 0, which is invalid since it would point to the - * header chunk. - */ - -/* - * Magic for persistent snapshots: "SnAp" - Feeble isn't it. - */ -#define SNAP_MAGIC 0x70416e53 - -/* - * The on-disk version of the metadata. - */ -#define SNAPSHOT_DISK_VERSION 1 - -struct disk_header { - uint32_t magic; - - /* - * Is this snapshot valid. There is no way of recovering - * an invalid snapshot. - */ - uint32_t valid; - - /* - * Simple, incrementing version. no backward - * compatibility. - */ - uint32_t version; - - /* In sectors */ - uint32_t chunk_size; -}; - -struct disk_exception { - uint64_t old_chunk; - uint64_t new_chunk; -}; - -struct commit_callback { - void (*callback)(void *, int success); - void *context; -}; - -/* - * The top level structure for a persistent exception store. - */ -struct pstore { - struct dm_snapshot *snap; /* up pointer to my snapshot */ - int version; - int valid; - uint32_t exceptions_per_area; - - /* - * Now that we have an asynchronous kcopyd there is no - * need for large chunk sizes, so it wont hurt to have a - * whole chunks worth of metadata in memory at once. - */ - void *area; - - /* - * An area of zeros used to clear the next area. - */ - void *zero_area; - - /* - * Used to keep track of which metadata area the data in - * 'chunk' refers to. - */ - chunk_t current_area; - - /* - * The next free chunk for an exception. - */ - chunk_t next_free; - - /* - * The index of next free exception in the current - * metadata area. - */ - uint32_t current_committed; - - atomic_t pending_count; - uint32_t callback_count; - struct commit_callback *callbacks; - struct dm_io_client *io_client; - - struct workqueue_struct *metadata_wq; -}; - -static unsigned sectors_to_pages(unsigned sectors) -{ - return DIV_ROUND_UP(sectors, PAGE_SIZE >> 9); -} - -static int alloc_area(struct pstore *ps) -{ - int r = -ENOMEM; - size_t len; - - len = ps->snap->chunk_size << SECTOR_SHIFT; - - /* - * Allocate the chunk_size block of memory that will hold - * a single metadata area. - */ - ps->area = vmalloc(len); - if (!ps->area) - return r; - - ps->zero_area = vmalloc(len); - if (!ps->zero_area) { - vfree(ps->area); - return r; - } - memset(ps->zero_area, 0, len); - - return 0; -} - -static void free_area(struct pstore *ps) -{ - vfree(ps->area); - ps->area = NULL; - vfree(ps->zero_area); - ps->zero_area = NULL; -} - -struct mdata_req { - struct dm_io_region *where; - struct dm_io_request *io_req; - struct work_struct work; - int result; -}; - -static void do_metadata(struct work_struct *work) -{ - struct mdata_req *req = container_of(work, struct mdata_req, work); - - req->result = dm_io(req->io_req, 1, req->where, NULL); -} - -/* - * Read or write a chunk aligned and sized block of data from a device. - */ -static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) -{ - struct dm_io_region where = { - .bdev = ps->snap->cow->bdev, - .sector = ps->snap->chunk_size * chunk, - .count = ps->snap->chunk_size, - }; - struct dm_io_request io_req = { - .bi_rw = rw, - .mem.type = DM_IO_VMA, - .mem.ptr.vma = ps->area, - .client = ps->io_client, - .notify.fn = NULL, - }; - struct mdata_req req; - - if (!metadata) - return dm_io(&io_req, 1, &where, NULL); - - req.where = &where; - req.io_req = &io_req; - - /* - * Issue the synchronous I/O from a different thread - * to avoid generic_make_request recursion. - */ - INIT_WORK(&req.work, do_metadata); - queue_work(ps->metadata_wq, &req.work); - flush_workqueue(ps->metadata_wq); - - return req.result; -} - -/* - * Convert a metadata area index to a chunk index. - */ -static chunk_t area_location(struct pstore *ps, chunk_t area) -{ - return 1 + ((ps->exceptions_per_area + 1) * area); -} - -/* - * Read or write a metadata area. Remembering to skip the first - * chunk which holds the header. - */ -static int area_io(struct pstore *ps, int rw) -{ - int r; - chunk_t chunk; - - chunk = area_location(ps, ps->current_area); - - r = chunk_io(ps, chunk, rw, 0); - if (r) - return r; - - return 0; -} - -static void zero_memory_area(struct pstore *ps) -{ - memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); -} - -static int zero_disk_area(struct pstore *ps, chunk_t area) -{ - struct dm_io_region where = { - .bdev = ps->snap->cow->bdev, - .sector = ps->snap->chunk_size * area_location(ps, area), - .count = ps->snap->chunk_size, - }; - struct dm_io_request io_req = { - .bi_rw = WRITE, - .mem.type = DM_IO_VMA, - .mem.ptr.vma = ps->zero_area, - .client = ps->io_client, - .notify.fn = NULL, - }; - - return dm_io(&io_req, 1, &where, NULL); -} - -static int read_header(struct pstore *ps, int *new_snapshot) -{ - int r; - struct disk_header *dh; - chunk_t chunk_size; - int chunk_size_supplied = 1; - - /* - * Use default chunk size (or hardsect_size, if larger) if none supplied - */ - if (!ps->snap->chunk_size) { - ps->snap->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, - bdev_hardsect_size(ps->snap->cow->bdev) >> 9); - ps->snap->chunk_mask = ps->snap->chunk_size - 1; - ps->snap->chunk_shift = ffs(ps->snap->chunk_size) - 1; - chunk_size_supplied = 0; - } - - ps->io_client = dm_io_client_create(sectors_to_pages(ps->snap-> - chunk_size)); - if (IS_ERR(ps->io_client)) - return PTR_ERR(ps->io_client); - - r = alloc_area(ps); - if (r) - return r; - - r = chunk_io(ps, 0, READ, 1); - if (r) - goto bad; - - dh = (struct disk_header *) ps->area; - - if (le32_to_cpu(dh->magic) == 0) { - *new_snapshot = 1; - return 0; - } - - if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { - DMWARN("Invalid or corrupt snapshot"); - r = -ENXIO; - goto bad; - } - - *new_snapshot = 0; - ps->valid = le32_to_cpu(dh->valid); - ps->version = le32_to_cpu(dh->version); - chunk_size = le32_to_cpu(dh->chunk_size); - - if (!chunk_size_supplied || ps->snap->chunk_size == chunk_size) - return 0; - - DMWARN("chunk size %llu in device metadata overrides " - "table chunk size of %llu.", - (unsigned long long)chunk_size, - (unsigned long long)ps->snap->chunk_size); - - /* We had a bogus chunk_size. Fix stuff up. */ - free_area(ps); - - ps->snap->chunk_size = chunk_size; - ps->snap->chunk_mask = chunk_size - 1; - ps->snap->chunk_shift = ffs(chunk_size) - 1; - - r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), - ps->io_client); - if (r) - return r; - - r = alloc_area(ps); - return r; - -bad: - free_area(ps); - return r; -} - -static int write_header(struct pstore *ps) -{ - struct disk_header *dh; - - memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); - - dh = (struct disk_header *) ps->area; - dh->magic = cpu_to_le32(SNAP_MAGIC); - dh->valid = cpu_to_le32(ps->valid); - dh->version = cpu_to_le32(ps->version); - dh->chunk_size = cpu_to_le32(ps->snap->chunk_size); - - return chunk_io(ps, 0, WRITE, 1); -} - -/* - * Access functions for the disk exceptions, these do the endian conversions. - */ -static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) -{ - BUG_ON(index >= ps->exceptions_per_area); - - return ((struct disk_exception *) ps->area) + index; -} -static void read_exception(struct pstore *ps, - uint32_t index, struct disk_exception *result) -{ - struct disk_exception *e = get_exception(ps, index); - - /* copy it */ - result->old_chunk = le64_to_cpu(e->old_chunk); - result->new_chunk = le64_to_cpu(e->new_chunk); -} - -static void write_exception(struct pstore *ps, - uint32_t index, struct disk_exception *de) -{ - struct disk_exception *e = get_exception(ps, index); - - /* copy it */ - e->old_chunk = cpu_to_le64(de->old_chunk); - e->new_chunk = cpu_to_le64(de->new_chunk); -} +#define DM_MSG_PREFIX "snapshot exception stores" -/* - * Registers the exceptions that are present in the current area. - * 'full' is filled in to indicate if the area has been - * filled. - */ -static int insert_exceptions(struct pstore *ps, int *full) +int dm_exception_store_init(void) { int r; - unsigned int i; - struct disk_exception de; - - /* presume the area is full */ - *full = 1; - - for (i = 0; i < ps->exceptions_per_area; i++) { - read_exception(ps, i, &de); - - /* - * If the new_chunk is pointing at the start of - * the COW device, where the first metadata area - * is we know that we've hit the end of the - * exceptions. Therefore the area is not full. - */ - if (de.new_chunk == 0LL) { - ps->current_committed = i; - *full = 0; - break; - } - - /* - * Keep track of the start of the free chunks. - */ - if (ps->next_free <= de.new_chunk) - ps->next_free = de.new_chunk + 1; - - /* - * Otherwise we add the exception to the snapshot. - */ - r = dm_add_exception(ps->snap, de.old_chunk, de.new_chunk); - if (r) - return r; - } - - return 0; -} - -static int read_exceptions(struct pstore *ps) -{ - int r, full = 1; - - /* - * Keeping reading chunks and inserting exceptions until - * we find a partially full area. - */ - for (ps->current_area = 0; full; ps->current_area++) { - r = area_io(ps, READ); - if (r) - return r; - r = insert_exceptions(ps, &full); - if (r) - return r; + r = dm_transient_snapshot_init(); + if (r) { + DMERR("Unable to register transient exception store type."); + goto transient_fail; } - ps->current_area--; - - return 0; -} - -static struct pstore *get_info(struct exception_store *store) -{ - return (struct pstore *) store->context; -} - -static void persistent_fraction_full(struct exception_store *store, - sector_t *numerator, sector_t *denominator) -{ - *numerator = get_info(store)->next_free * store->snap->chunk_size; - *denominator = get_dev_size(store->snap->cow->bdev); -} - -static void persistent_destroy(struct exception_store *store) -{ - struct pstore *ps = get_info(store); - - destroy_workqueue(ps->metadata_wq); - dm_io_client_destroy(ps->io_client); - vfree(ps->callbacks); - free_area(ps); - kfree(ps); -} - -static int persistent_read_metadata(struct exception_store *store) -{ - int r, uninitialized_var(new_snapshot); - struct pstore *ps = get_info(store); - - /* - * Read the snapshot header. - */ - r = read_header(ps, &new_snapshot); - if (r) - return r; - - /* - * Now we know correct chunk_size, complete the initialisation. - */ - ps->exceptions_per_area = (ps->snap->chunk_size << SECTOR_SHIFT) / - sizeof(struct disk_exception); - ps->callbacks = dm_vcalloc(ps->exceptions_per_area, - sizeof(*ps->callbacks)); - if (!ps->callbacks) - return -ENOMEM; - - /* - * Do we need to setup a new snapshot ? - */ - if (new_snapshot) { - r = write_header(ps); - if (r) { - DMWARN("write_header failed"); - return r; - } - - ps->current_area = 0; - zero_memory_area(ps); - r = zero_disk_area(ps, 0); - if (r) { - DMWARN("zero_disk_area(0) failed"); - return r; - } - } else { - /* - * Sanity checks. - */ - if (ps->version != SNAPSHOT_DISK_VERSION) { - DMWARN("unable to handle snapshot disk version %d", - ps->version); - return -EINVAL; - } - - /* - * Metadata are valid, but snapshot is invalidated - */ - if (!ps->valid) - return 1; - - /* - * Read the metadata. - */ - r = read_exceptions(ps); - if (r) - return r; + r = dm_persistent_snapshot_init(); + if (r) { + DMERR("Unable to register persistent exception store type"); + goto persistent_fail; } return 0; -} - -static int persistent_prepare(struct exception_store *store, - struct dm_snap_exception *e) -{ - struct pstore *ps = get_info(store); - uint32_t stride; - chunk_t next_free; - sector_t size = get_dev_size(store->snap->cow->bdev); - - /* Is there enough room ? */ - if (size < ((ps->next_free + 1) * store->snap->chunk_size)) - return -ENOSPC; - e->new_chunk = ps->next_free; - - /* - * Move onto the next free pending, making sure to take - * into account the location of the metadata chunks. - */ - stride = (ps->exceptions_per_area + 1); - next_free = ++ps->next_free; - if (sector_div(next_free, stride) == 1) - ps->next_free++; - - atomic_inc(&ps->pending_count); - return 0; -} - -static void persistent_commit(struct exception_store *store, - struct dm_snap_exception *e, - void (*callback) (void *, int success), - void *callback_context) -{ - unsigned int i; - struct pstore *ps = get_info(store); - struct disk_exception de; - struct commit_callback *cb; - - de.old_chunk = e->old_chunk; - de.new_chunk = e->new_chunk; - write_exception(ps, ps->current_committed++, &de); - - /* - * Add the callback to the back of the array. This code - * is the only place where the callback array is - * manipulated, and we know that it will never be called - * multiple times concurrently. - */ - cb = ps->callbacks + ps->callback_count++; - cb->callback = callback; - cb->context = callback_context; - - /* - * If there are exceptions in flight and we have not yet - * filled this metadata area there's nothing more to do. - */ - if (!atomic_dec_and_test(&ps->pending_count) && - (ps->current_committed != ps->exceptions_per_area)) - return; - - /* - * If we completely filled the current area, then wipe the next one. - */ - if ((ps->current_committed == ps->exceptions_per_area) && - zero_disk_area(ps, ps->current_area + 1)) - ps->valid = 0; - - /* - * Commit exceptions to disk. - */ - if (ps->valid && area_io(ps, WRITE)) - ps->valid = 0; - - /* - * Advance to the next area if this one is full. - */ - if (ps->current_committed == ps->exceptions_per_area) { - ps->current_committed = 0; - ps->current_area++; - zero_memory_area(ps); - } - - for (i = 0; i < ps->callback_count; i++) { - cb = ps->callbacks + i; - cb->callback(cb->context, ps->valid); - } - - ps->callback_count = 0; -} - -static void persistent_drop(struct exception_store *store) -{ - struct pstore *ps = get_info(store); - - ps->valid = 0; - if (write_header(ps)) - DMWARN("write header failed"); -} - -int dm_create_persistent(struct exception_store *store) -{ - struct pstore *ps; - - /* allocate the pstore */ - ps = kmalloc(sizeof(*ps), GFP_KERNEL); - if (!ps) - return -ENOMEM; - - ps->snap = store->snap; - ps->valid = 1; - ps->version = SNAPSHOT_DISK_VERSION; - ps->area = NULL; - ps->next_free = 2; /* skipping the header and first area */ - ps->current_committed = 0; - - ps->callback_count = 0; - atomic_set(&ps->pending_count, 0); - ps->callbacks = NULL; - - ps->metadata_wq = create_singlethread_workqueue("ksnaphd"); - if (!ps->metadata_wq) { - kfree(ps); - DMERR("couldn't start header metadata update thread"); - return -ENOMEM; - } - - store->destroy = persistent_destroy; - store->read_metadata = persistent_read_metadata; - store->prepare_exception = persistent_prepare; - store->commit_exception = persistent_commit; - store->drop_snapshot = persistent_drop; - store->fraction_full = persistent_fraction_full; - store->context = ps; - - return 0; -} - -/*----------------------------------------------------------------- - * Implementation of the store for non-persistent snapshots. - *---------------------------------------------------------------*/ -struct transient_c { - sector_t next_free; -}; - -static void transient_destroy(struct exception_store *store) -{ - kfree(store->context); -} - -static int transient_read_metadata(struct exception_store *store) -{ - return 0; -} - -static int transient_prepare(struct exception_store *store, - struct dm_snap_exception *e) -{ - struct transient_c *tc = (struct transient_c *) store->context; - sector_t size = get_dev_size(store->snap->cow->bdev); - - if (size < (tc->next_free + store->snap->chunk_size)) - return -1; - - e->new_chunk = sector_to_chunk(store->snap, tc->next_free); - tc->next_free += store->snap->chunk_size; - - return 0; -} - -static void transient_commit(struct exception_store *store, - struct dm_snap_exception *e, - void (*callback) (void *, int success), - void *callback_context) -{ - /* Just succeed */ - callback(callback_context, 1); -} - -static void transient_fraction_full(struct exception_store *store, - sector_t *numerator, sector_t *denominator) -{ - *numerator = ((struct transient_c *) store->context)->next_free; - *denominator = get_dev_size(store->snap->cow->bdev); +persistent_fail: + dm_persistent_snapshot_exit(); +transient_fail: + return r; } -int dm_create_transient(struct exception_store *store) +void dm_exception_store_exit(void) { - struct transient_c *tc; - - store->destroy = transient_destroy; - store->read_metadata = transient_read_metadata; - store->prepare_exception = transient_prepare; - store->commit_exception = transient_commit; - store->drop_snapshot = NULL; - store->fraction_full = transient_fraction_full; - - tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); - if (!tc) - return -ENOMEM; - - tc->next_free = 0; - store->context = tc; - - return 0; + dm_persistent_snapshot_exit(); + dm_transient_snapshot_exit(); } diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h new file mode 100644 index 000000000000..bb9f33d5daa2 --- /dev/null +++ b/drivers/md/dm-exception-store.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * Copyright (C) 2008 Red Hat, Inc. All rights reserved. + * + * Device-mapper snapshot exception store. + * + * This file is released under the GPL. + */ + +#ifndef _LINUX_DM_EXCEPTION_STORE +#define _LINUX_DM_EXCEPTION_STORE + +#include <linux/blkdev.h> +#include <linux/device-mapper.h> + +/* + * The snapshot code deals with largish chunks of the disk at a + * time. Typically 32k - 512k. + */ +typedef sector_t chunk_t; + +/* + * An exception is used where an old chunk of data has been + * replaced by a new one. + * If chunk_t is 64 bits in size, the top 8 bits of new_chunk hold the number + * of chunks that follow contiguously. Remaining bits hold the number of the + * chunk within the device. + */ +struct dm_snap_exception { + struct list_head hash_list; + + chunk_t old_chunk; + chunk_t new_chunk; +}; + +/* + * Abstraction to handle the meta/layout of exception stores (the + * COW device). + */ +struct dm_exception_store { + /* + * Destroys this object when you've finished with it. + */ + void (*destroy) (struct dm_exception_store *store); + + /* + * The target shouldn't read the COW device until this is + * called. As exceptions are read from the COW, they are + * reported back via the callback. + */ + int (*read_metadata) (struct dm_exception_store *store, + int (*callback)(void *callback_context, + chunk_t old, chunk_t new), + void *callback_context); + + /* + * Find somewhere to store the next exception. + */ + int (*prepare_exception) (struct dm_exception_store *store, + struct dm_snap_exception *e); + + /* + * Update the metadata with this exception. + */ + void (*commit_exception) (struct dm_exception_store *store, + struct dm_snap_exception *e, + void (*callback) (void *, int success), + void *callback_context); + + /* + * The snapshot is invalid, note this in the metadata. + */ + void (*drop_snapshot) (struct dm_exception_store *store); + + int (*status) (struct dm_exception_store *store, status_type_t status, + char *result, unsigned int maxlen); + + /* + * Return how full the snapshot is. + */ + void (*fraction_full) (struct dm_exception_store *store, + sector_t *numerator, + sector_t *denominator); + + struct dm_snapshot *snap; + void *context; +}; + +/* + * Funtions to manipulate consecutive chunks + */ +# if defined(CONFIG_LBD) || (BITS_PER_LONG == 64) +# define DM_CHUNK_CONSECUTIVE_BITS 8 +# define DM_CHUNK_NUMBER_BITS 56 + +static inline chunk_t dm_chunk_number(chunk_t chunk) +{ + return chunk & (chunk_t)((1ULL << DM_CHUNK_NUMBER_BITS) - 1ULL); +} + +static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e) +{ + return e->new_chunk >> DM_CHUNK_NUMBER_BITS; +} + +static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e) +{ + e->new_chunk += (1ULL << DM_CHUNK_NUMBER_BITS); + + BUG_ON(!dm_consecutive_chunk_count(e)); +} + +# else +# define DM_CHUNK_CONSECUTIVE_BITS 0 + +static inline chunk_t dm_chunk_number(chunk_t chunk) +{ + return chunk; +} + +static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e) +{ + return 0; +} + +static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e) +{ +} + +# endif + +int dm_exception_store_init(void); +void dm_exception_store_exit(void); + +/* + * Two exception store implementations. + */ +int dm_persistent_snapshot_init(void); +void dm_persistent_snapshot_exit(void); + +int dm_transient_snapshot_init(void); +void dm_transient_snapshot_exit(void); + +int dm_create_persistent(struct dm_exception_store *store); + +int dm_create_transient(struct dm_exception_store *store); + +#endif /* _LINUX_DM_EXCEPTION_STORE */ diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 777c948180f9..54d0588fc1f6 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -233,7 +233,7 @@ static void __hash_remove(struct hash_cell *hc) } if (hc->new_map) - dm_table_put(hc->new_map); + dm_table_destroy(hc->new_map); dm_put(hc->md); free_cell(hc); } @@ -827,8 +827,8 @@ static int do_resume(struct dm_ioctl *param) r = dm_swap_table(md, new_map); if (r) { + dm_table_destroy(new_map); dm_put(md); - dm_table_put(new_map); return r; } @@ -836,8 +836,6 @@ static int do_resume(struct dm_ioctl *param) set_disk_ro(dm_disk(md), 0); else set_disk_ro(dm_disk(md), 1); - - dm_table_put(new_map); } if (dm_suspended(md)) @@ -1080,7 +1078,7 @@ static int table_load(struct dm_ioctl *param, size_t param_size) } if (hc->new_map) - dm_table_put(hc->new_map); + dm_table_destroy(hc->new_map); hc->new_map = t; up_write(&_hash_lock); @@ -1109,7 +1107,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) } if (hc->new_map) { - dm_table_put(hc->new_map); + dm_table_destroy(hc->new_map); hc->new_map = NULL; } @@ -1550,8 +1548,10 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) goto out; } - strcpy(name, hc->name); - strcpy(uuid, hc->uuid ? : ""); + if (name) + strcpy(name, hc->name); + if (uuid) + strcpy(uuid, hc->uuid ? : ""); out: up_read(&_hash_lock); diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 44042becad8a..bfa107f59d96 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -142,6 +142,7 @@ static struct target_type linear_target = { .status = linear_status, .ioctl = linear_ioctl, .merge = linear_merge, + .features = DM_TARGET_SUPPORTS_BARRIERS, }; int __init dm_linear_init(void) @@ -156,8 +157,5 @@ int __init dm_linear_init(void) void dm_linear_exit(void) { - int r = dm_unregister_target(&linear_target); - - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&linear_target); } diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index a8c0fc79ca78..737961f275c1 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -326,8 +326,6 @@ static void header_from_disk(struct log_header *core, struct log_header *disk) static int rw_header(struct log_c *lc, int rw) { lc->io_req.bi_rw = rw; - lc->io_req.mem.ptr.vma = lc->disk_header; - lc->io_req.notify.fn = NULL; return dm_io(&lc->io_req, 1, &lc->header_location, NULL); } @@ -362,10 +360,15 @@ static int read_header(struct log_c *log) return 0; } -static inline int write_header(struct log_c *log) +static int _check_region_size(struct dm_target *ti, uint32_t region_size) { - header_to_disk(&log->header, log->disk_header); - return rw_header(log, WRITE); + if (region_size < 2 || region_size > ti->len) + return 0; + + if (!is_power_of_2(region_size)) + return 0; + + return 1; } /*---------------------------------------------------------------- @@ -403,8 +406,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, } } - if (sscanf(argv[0], "%u", ®ion_size) != 1) { - DMWARN("invalid region size string"); + if (sscanf(argv[0], "%u", ®ion_size) != 1 || + !_check_region_size(ti, region_size)) { + DMWARN("invalid region size %s", argv[0]); return -EINVAL; } @@ -453,8 +457,18 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, */ buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + bitset_size, ti->limits.hardsect_size); + + if (buf_size > dev->bdev->bd_inode->i_size) { + DMWARN("log device %s too small: need %llu bytes", + dev->name, (unsigned long long)buf_size); + kfree(lc); + return -EINVAL; + } + lc->header_location.count = buf_size >> SECTOR_SHIFT; + lc->io_req.mem.type = DM_IO_VMA; + lc->io_req.notify.fn = NULL; lc->io_req.client = dm_io_client_create(dm_div_up(buf_size, PAGE_SIZE)); if (IS_ERR(lc->io_req.client)) { @@ -467,10 +481,12 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, lc->disk_header = vmalloc(buf_size); if (!lc->disk_header) { DMWARN("couldn't allocate disk log buffer"); + dm_io_client_destroy(lc->io_req.client); kfree(lc); return -ENOMEM; } + lc->io_req.mem.ptr.vma = lc->disk_header; lc->clean_bits = (void *)lc->disk_header + (LOG_OFFSET << SECTOR_SHIFT); } @@ -482,6 +498,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, DMWARN("couldn't allocate sync bitset"); if (!dev) vfree(lc->clean_bits); + else + dm_io_client_destroy(lc->io_req.client); vfree(lc->disk_header); kfree(lc); return -ENOMEM; @@ -495,6 +513,8 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, vfree(lc->sync_bits); if (!dev) vfree(lc->clean_bits); + else + dm_io_client_destroy(lc->io_req.client); vfree(lc->disk_header); kfree(lc); return -ENOMEM; @@ -631,8 +651,10 @@ static int disk_resume(struct dm_dirty_log *log) /* set the correct number of regions in the header */ lc->header.nr_regions = lc->region_count; + header_to_disk(&lc->header, lc->disk_header); + /* write the new header */ - r = write_header(lc); + r = rw_header(lc, WRITE); if (r) { DMWARN("%s: Failed to write header on dirty region log device", lc->log_dev->name); @@ -682,7 +704,7 @@ static int disk_flush(struct dm_dirty_log *log) if (!lc->touched) return 0; - r = write_header(lc); + r = rw_header(lc, WRITE); if (r) fail_log_device(lc); else diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 3d7f4923cd13..095f77bf9681 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -889,7 +889,7 @@ static int fail_path(struct pgpath *pgpath) dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti, pgpath->path.dev->name, m->nr_valid_paths); - queue_work(kmultipathd, &m->trigger_event); + schedule_work(&m->trigger_event); queue_work(kmultipathd, &pgpath->deactivate_path); out: @@ -932,7 +932,7 @@ static int reinstate_path(struct pgpath *pgpath) dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti, pgpath->path.dev->name, m->nr_valid_paths); - queue_work(kmultipathd, &m->trigger_event); + schedule_work(&m->trigger_event); out: spin_unlock_irqrestore(&m->lock, flags); @@ -976,7 +976,7 @@ static void bypass_pg(struct multipath *m, struct priority_group *pg, spin_unlock_irqrestore(&m->lock, flags); - queue_work(kmultipathd, &m->trigger_event); + schedule_work(&m->trigger_event); } /* @@ -1006,7 +1006,7 @@ static int switch_pg_num(struct multipath *m, const char *pgstr) } spin_unlock_irqrestore(&m->lock, flags); - queue_work(kmultipathd, &m->trigger_event); + schedule_work(&m->trigger_event); return 0; } @@ -1495,14 +1495,10 @@ static int __init dm_multipath_init(void) static void __exit dm_multipath_exit(void) { - int r; - destroy_workqueue(kmpath_handlerd); destroy_workqueue(kmultipathd); - r = dm_unregister_target(&multipath_target); - if (r < 0) - DMERR("target unregister failed %d", r); + dm_unregister_target(&multipath_target); kmem_cache_destroy(_mpio_cache); } diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index ec43f9fa4b2a..4d6bc101962e 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -197,9 +197,6 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type) struct mirror_set *ms = m->ms; struct mirror *new; - if (!errors_handled(ms)) - return; - /* * error_count is used for nothing more than a * simple way to tell if a device has encountered @@ -210,6 +207,9 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type) if (test_and_set_bit(error_type, &m->error_type)) return; + if (!errors_handled(ms)) + return; + if (m != get_default_mirror(ms)) goto out; @@ -808,12 +808,6 @@ static void free_context(struct mirror_set *ms, struct dm_target *ti, kfree(ms); } -static inline int _check_region_size(struct dm_target *ti, uint32_t size) -{ - return !(size % (PAGE_SIZE >> 9) || !is_power_of_2(size) || - size > ti->len); -} - static int get_mirror(struct mirror_set *ms, struct dm_target *ti, unsigned int mirror, char **argv) { @@ -872,12 +866,6 @@ static struct dm_dirty_log *create_dirty_log(struct dm_target *ti, return NULL; } - if (!_check_region_size(ti, dl->type->get_region_size(dl))) { - ti->error = "Invalid region size"; - dm_dirty_log_destroy(dl); - return NULL; - } - return dl; } @@ -1300,11 +1288,7 @@ static int __init dm_mirror_init(void) static void __exit dm_mirror_exit(void) { - int r; - - r = dm_unregister_target(&mirror_target); - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&mirror_target); } /* Module hooks */ diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c new file mode 100644 index 000000000000..936b34e0959f --- /dev/null +++ b/drivers/md/dm-snap-persistent.c @@ -0,0 +1,704 @@ +/* + * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * Copyright (C) 2006-2008 Red Hat GmbH + * + * This file is released under the GPL. + */ + +#include "dm-exception-store.h" +#include "dm-snap.h" + +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/dm-io.h> + +#define DM_MSG_PREFIX "persistent snapshot" +#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */ + +/*----------------------------------------------------------------- + * Persistent snapshots, by persistent we mean that the snapshot + * will survive a reboot. + *---------------------------------------------------------------*/ + +/* + * We need to store a record of which parts of the origin have + * been copied to the snapshot device. The snapshot code + * requires that we copy exception chunks to chunk aligned areas + * of the COW store. It makes sense therefore, to store the + * metadata in chunk size blocks. + * + * There is no backward or forward compatibility implemented, + * snapshots with different disk versions than the kernel will + * not be usable. It is expected that "lvcreate" will blank out + * the start of a fresh COW device before calling the snapshot + * constructor. + * + * The first chunk of the COW device just contains the header. + * After this there is a chunk filled with exception metadata, + * followed by as many exception chunks as can fit in the + * metadata areas. + * + * All on disk structures are in little-endian format. The end + * of the exceptions info is indicated by an exception with a + * new_chunk of 0, which is invalid since it would point to the + * header chunk. + */ + +/* + * Magic for persistent snapshots: "SnAp" - Feeble isn't it. + */ +#define SNAP_MAGIC 0x70416e53 + +/* + * The on-disk version of the metadata. + */ +#define SNAPSHOT_DISK_VERSION 1 + +struct disk_header { + uint32_t magic; + + /* + * Is this snapshot valid. There is no way of recovering + * an invalid snapshot. + */ + uint32_t valid; + + /* + * Simple, incrementing version. no backward + * compatibility. + */ + uint32_t version; + + /* In sectors */ + uint32_t chunk_size; +}; + +struct disk_exception { + uint64_t old_chunk; + uint64_t new_chunk; +}; + +struct commit_callback { + void (*callback)(void *, int success); + void *context; +}; + +/* + * The top level structure for a persistent exception store. + */ +struct pstore { + struct dm_snapshot *snap; /* up pointer to my snapshot */ + int version; + int valid; + uint32_t exceptions_per_area; + + /* + * Now that we have an asynchronous kcopyd there is no + * need for large chunk sizes, so it wont hurt to have a + * whole chunks worth of metadata in memory at once. + */ + void *area; + + /* + * An area of zeros used to clear the next area. + */ + void *zero_area; + + /* + * Used to keep track of which metadata area the data in + * 'chunk' refers to. + */ + chunk_t current_area; + + /* + * The next free chunk for an exception. + */ + chunk_t next_free; + + /* + * The index of next free exception in the current + * metadata area. + */ + uint32_t current_committed; + + atomic_t pending_count; + uint32_t callback_count; + struct commit_callback *callbacks; + struct dm_io_client *io_client; + + struct workqueue_struct *metadata_wq; +}; + +static unsigned sectors_to_pages(unsigned sectors) +{ + return DIV_ROUND_UP(sectors, PAGE_SIZE >> 9); +} + +static int alloc_area(struct pstore *ps) +{ + int r = -ENOMEM; + size_t len; + + len = ps->snap->chunk_size << SECTOR_SHIFT; + + /* + * Allocate the chunk_size block of memory that will hold + * a single metadata area. + */ + ps->area = vmalloc(len); + if (!ps->area) + return r; + + ps->zero_area = vmalloc(len); + if (!ps->zero_area) { + vfree(ps->area); + return r; + } + memset(ps->zero_area, 0, len); + + return 0; +} + +static void free_area(struct pstore *ps) +{ + vfree(ps->area); + ps->area = NULL; + vfree(ps->zero_area); + ps->zero_area = NULL; +} + +struct mdata_req { + struct dm_io_region *where; + struct dm_io_request *io_req; + struct work_struct work; + int result; +}; + +static void do_metadata(struct work_struct *work) +{ + struct mdata_req *req = container_of(work, struct mdata_req, work); + + req->result = dm_io(req->io_req, 1, req->where, NULL); +} + +/* + * Read or write a chunk aligned and sized block of data from a device. + */ +static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) +{ + struct dm_io_region where = { + .bdev = ps->snap->cow->bdev, + .sector = ps->snap->chunk_size * chunk, + .count = ps->snap->chunk_size, + }; + struct dm_io_request io_req = { + .bi_rw = rw, + .mem.type = DM_IO_VMA, + .mem.ptr.vma = ps->area, + .client = ps->io_client, + .notify.fn = NULL, + }; + struct mdata_req req; + + if (!metadata) + return dm_io(&io_req, 1, &where, NULL); + + req.where = &where; + req.io_req = &io_req; + + /* + * Issue the synchronous I/O from a different thread + * to avoid generic_make_request recursion. + */ + INIT_WORK(&req.work, do_metadata); + queue_work(ps->metadata_wq, &req.work); + flush_workqueue(ps->metadata_wq); + + return req.result; +} + +/* + * Convert a metadata area index to a chunk index. + */ +static chunk_t area_location(struct pstore *ps, chunk_t area) +{ + return 1 + ((ps->exceptions_per_area + 1) * area); +} + +/* + * Read or write a metadata area. Remembering to skip the first + * chunk which holds the header. + */ +static int area_io(struct pstore *ps, int rw) +{ + int r; + chunk_t chunk; + + chunk = area_location(ps, ps->current_area); + + r = chunk_io(ps, chunk, rw, 0); + if (r) + return r; + + return 0; +} + +static void zero_memory_area(struct pstore *ps) +{ + memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); +} + +static int zero_disk_area(struct pstore *ps, chunk_t area) +{ + struct dm_io_region where = { + .bdev = ps->snap->cow->bdev, + .sector = ps->snap->chunk_size * area_location(ps, area), + .count = ps->snap->chunk_size, + }; + struct dm_io_request io_req = { + .bi_rw = WRITE, + .mem.type = DM_IO_VMA, + .mem.ptr.vma = ps->zero_area, + .client = ps->io_client, + .notify.fn = NULL, + }; + + return dm_io(&io_req, 1, &where, NULL); +} + +static int read_header(struct pstore *ps, int *new_snapshot) +{ + int r; + struct disk_header *dh; + chunk_t chunk_size; + int chunk_size_supplied = 1; + + /* + * Use default chunk size (or hardsect_size, if larger) if none supplied + */ + if (!ps->snap->chunk_size) { + ps->snap->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, + bdev_hardsect_size(ps->snap->cow->bdev) >> 9); + ps->snap->chunk_mask = ps->snap->chunk_size - 1; + ps->snap->chunk_shift = ffs(ps->snap->chunk_size) - 1; + chunk_size_supplied = 0; + } + + ps->io_client = dm_io_client_create(sectors_to_pages(ps->snap-> + chunk_size)); + if (IS_ERR(ps->io_client)) + return PTR_ERR(ps->io_client); + + r = alloc_area(ps); + if (r) + return r; + + r = chunk_io(ps, 0, READ, 1); + if (r) + goto bad; + + dh = (struct disk_header *) ps->area; + + if (le32_to_cpu(dh->magic) == 0) { + *new_snapshot = 1; + return 0; + } + + if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { + DMWARN("Invalid or corrupt snapshot"); + r = -ENXIO; + goto bad; + } + + *new_snapshot = 0; + ps->valid = le32_to_cpu(dh->valid); + ps->version = le32_to_cpu(dh->version); + chunk_size = le32_to_cpu(dh->chunk_size); + + if (!chunk_size_supplied || ps->snap->chunk_size == chunk_size) + return 0; + + DMWARN("chunk size %llu in device metadata overrides " + "table chunk size of %llu.", + (unsigned long long)chunk_size, + (unsigned long long)ps->snap->chunk_size); + + /* We had a bogus chunk_size. Fix stuff up. */ + free_area(ps); + + ps->snap->chunk_size = chunk_size; + ps->snap->chunk_mask = chunk_size - 1; + ps->snap->chunk_shift = ffs(chunk_size) - 1; + + r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), + ps->io_client); + if (r) + return r; + + r = alloc_area(ps); + return r; + +bad: + free_area(ps); + return r; +} + +static int write_header(struct pstore *ps) +{ + struct disk_header *dh; + + memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); + + dh = (struct disk_header *) ps->area; + dh->magic = cpu_to_le32(SNAP_MAGIC); + dh->valid = cpu_to_le32(ps->valid); + dh->version = cpu_to_le32(ps->version); + dh->chunk_size = cpu_to_le32(ps->snap->chunk_size); + + return chunk_io(ps, 0, WRITE, 1); +} + +/* + * Access functions for the disk exceptions, these do the endian conversions. + */ +static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) +{ + BUG_ON(index >= ps->exceptions_per_area); + + return ((struct disk_exception *) ps->area) + index; +} + +static void read_exception(struct pstore *ps, + uint32_t index, struct disk_exception *result) +{ + struct disk_exception *e = get_exception(ps, index); + + /* copy it */ + result->old_chunk = le64_to_cpu(e->old_chunk); + result->new_chunk = le64_to_cpu(e->new_chunk); +} + +static void write_exception(struct pstore *ps, + uint32_t index, struct disk_exception *de) +{ + struct disk_exception *e = get_exception(ps, index); + + /* copy it */ + e->old_chunk = cpu_to_le64(de->old_chunk); + e->new_chunk = cpu_to_le64(de->new_chunk); +} + +/* + * Registers the exceptions that are present in the current area. + * 'full' is filled in to indicate if the area has been + * filled. + */ +static int insert_exceptions(struct pstore *ps, + int (*callback)(void *callback_context, + chunk_t old, chunk_t new), + void *callback_context, + int *full) +{ + int r; + unsigned int i; + struct disk_exception de; + + /* presume the area is full */ + *full = 1; + + for (i = 0; i < ps->exceptions_per_area; i++) { + read_exception(ps, i, &de); + + /* + * If the new_chunk is pointing at the start of + * the COW device, where the first metadata area + * is we know that we've hit the end of the + * exceptions. Therefore the area is not full. + */ + if (de.new_chunk == 0LL) { + ps->current_committed = i; + *full = 0; + break; + } + + /* + * Keep track of the start of the free chunks. + */ + if (ps->next_free <= de.new_chunk) + ps->next_free = de.new_chunk + 1; + + /* + * Otherwise we add the exception to the snapshot. + */ + r = callback(callback_context, de.old_chunk, de.new_chunk); + if (r) + return r; + } + + return 0; +} + +static int read_exceptions(struct pstore *ps, + int (*callback)(void *callback_context, chunk_t old, + chunk_t new), + void *callback_context) +{ + int r, full = 1; + + /* + * Keeping reading chunks and inserting exceptions until + * we find a partially full area. + */ + for (ps->current_area = 0; full; ps->current_area++) { + r = area_io(ps, READ); + if (r) + return r; + + r = insert_exceptions(ps, callback, callback_context, &full); + if (r) + return r; + } + + ps->current_area--; + + return 0; +} + +static struct pstore *get_info(struct dm_exception_store *store) +{ + return (struct pstore *) store->context; +} + +static void persistent_fraction_full(struct dm_exception_store *store, + sector_t *numerator, sector_t *denominator) +{ + *numerator = get_info(store)->next_free * store->snap->chunk_size; + *denominator = get_dev_size(store->snap->cow->bdev); +} + +static void persistent_destroy(struct dm_exception_store *store) +{ + struct pstore *ps = get_info(store); + + destroy_workqueue(ps->metadata_wq); + dm_io_client_destroy(ps->io_client); + vfree(ps->callbacks); + free_area(ps); + kfree(ps); +} + +static int persistent_read_metadata(struct dm_exception_store *store, + int (*callback)(void *callback_context, + chunk_t old, chunk_t new), + void *callback_context) +{ + int r, uninitialized_var(new_snapshot); + struct pstore *ps = get_info(store); + + /* + * Read the snapshot header. + */ + r = read_header(ps, &new_snapshot); + if (r) + return r; + + /* + * Now we know correct chunk_size, complete the initialisation. + */ + ps->exceptions_per_area = (ps->snap->chunk_size << SECTOR_SHIFT) / + sizeof(struct disk_exception); + ps->callbacks = dm_vcalloc(ps->exceptions_per_area, + sizeof(*ps->callbacks)); + if (!ps->callbacks) + return -ENOMEM; + + /* + * Do we need to setup a new snapshot ? + */ + if (new_snapshot) { + r = write_header(ps); + if (r) { + DMWARN("write_header failed"); + return r; + } + + ps->current_area = 0; + zero_memory_area(ps); + r = zero_disk_area(ps, 0); + if (r) { + DMWARN("zero_disk_area(0) failed"); + return r; + } + } else { + /* + * Sanity checks. + */ + if (ps->version != SNAPSHOT_DISK_VERSION) { + DMWARN("unable to handle snapshot disk version %d", + ps->version); + return -EINVAL; + } + + /* + * Metadata are valid, but snapshot is invalidated + */ + if (!ps->valid) + return 1; + + /* + * Read the metadata. + */ + r = read_exceptions(ps, callback, callback_context); + if (r) + return r; + } + + return 0; +} + +static int persistent_prepare_exception(struct dm_exception_store *store, + struct dm_snap_exception *e) +{ + struct pstore *ps = get_info(store); + uint32_t stride; + chunk_t next_free; + sector_t size = get_dev_size(store->snap->cow->bdev); + + /* Is there enough room ? */ + if (size < ((ps->next_free + 1) * store->snap->chunk_size)) + return -ENOSPC; + + e->new_chunk = ps->next_free; + + /* + * Move onto the next free pending, making sure to take + * into account the location of the metadata chunks. + */ + stride = (ps->exceptions_per_area + 1); + next_free = ++ps->next_free; + if (sector_div(next_free, stride) == 1) + ps->next_free++; + + atomic_inc(&ps->pending_count); + return 0; +} + +static void persistent_commit_exception(struct dm_exception_store *store, + struct dm_snap_exception *e, + void (*callback) (void *, int success), + void *callback_context) +{ + unsigned int i; + struct pstore *ps = get_info(store); + struct disk_exception de; + struct commit_callback *cb; + + de.old_chunk = e->old_chunk; + de.new_chunk = e->new_chunk; + write_exception(ps, ps->current_committed++, &de); + + /* + * Add the callback to the back of the array. This code + * is the only place where the callback array is + * manipulated, and we know that it will never be called + * multiple times concurrently. + */ + cb = ps->callbacks + ps->callback_count++; + cb->callback = callback; + cb->context = callback_context; + + /* + * If there are exceptions in flight and we have not yet + * filled this metadata area there's nothing more to do. + */ + if (!atomic_dec_and_test(&ps->pending_count) && + (ps->current_committed != ps->exceptions_per_area)) + return; + + /* + * If we completely filled the current area, then wipe the next one. + */ + if ((ps->current_committed == ps->exceptions_per_area) && + zero_disk_area(ps, ps->current_area + 1)) + ps->valid = 0; + + /* + * Commit exceptions to disk. + */ + if (ps->valid && area_io(ps, WRITE)) + ps->valid = 0; + + /* + * Advance to the next area if this one is full. + */ + if (ps->current_committed == ps->exceptions_per_area) { + ps->current_committed = 0; + ps->current_area++; + zero_memory_area(ps); + } + + for (i = 0; i < ps->callback_count; i++) { + cb = ps->callbacks + i; + cb->callback(cb->context, ps->valid); + } + + ps->callback_count = 0; +} + +static void persistent_drop_snapshot(struct dm_exception_store *store) +{ + struct pstore *ps = get_info(store); + + ps->valid = 0; + if (write_header(ps)) + DMWARN("write header failed"); +} + +int dm_create_persistent(struct dm_exception_store *store) +{ + struct pstore *ps; + + /* allocate the pstore */ + ps = kmalloc(sizeof(*ps), GFP_KERNEL); + if (!ps) + return -ENOMEM; + + ps->snap = store->snap; + ps->valid = 1; + ps->version = SNAPSHOT_DISK_VERSION; + ps->area = NULL; + ps->next_free = 2; /* skipping the header and first area */ + ps->current_committed = 0; + + ps->callback_count = 0; + atomic_set(&ps->pending_count, 0); + ps->callbacks = NULL; + + ps->metadata_wq = create_singlethread_workqueue("ksnaphd"); + if (!ps->metadata_wq) { + kfree(ps); + DMERR("couldn't start header metadata update thread"); + return -ENOMEM; + } + + store->destroy = persistent_destroy; + store->read_metadata = persistent_read_metadata; + store->prepare_exception = persistent_prepare_exception; + store->commit_exception = persistent_commit_exception; + store->drop_snapshot = persistent_drop_snapshot; + store->fraction_full = persistent_fraction_full; + store->context = ps; + + return 0; +} + +int dm_persistent_snapshot_init(void) +{ + return 0; +} + +void dm_persistent_snapshot_exit(void) +{ +} diff --git a/drivers/md/dm-snap-transient.c b/drivers/md/dm-snap-transient.c new file mode 100644 index 000000000000..7f6e2e6dcb0d --- /dev/null +++ b/drivers/md/dm-snap-transient.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * Copyright (C) 2006-2008 Red Hat GmbH + * + * This file is released under the GPL. + */ + +#include "dm-exception-store.h" +#include "dm-snap.h" + +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/dm-io.h> + +#define DM_MSG_PREFIX "transient snapshot" + +/*----------------------------------------------------------------- + * Implementation of the store for non-persistent snapshots. + *---------------------------------------------------------------*/ +struct transient_c { + sector_t next_free; +}; + +static void transient_destroy(struct dm_exception_store *store) +{ + kfree(store->context); +} + +static int transient_read_metadata(struct dm_exception_store *store, + int (*callback)(void *callback_context, + chunk_t old, chunk_t new), + void *callback_context) +{ + return 0; +} + +static int transient_prepare_exception(struct dm_exception_store *store, + struct dm_snap_exception *e) +{ + struct transient_c *tc = (struct transient_c *) store->context; + sector_t size = get_dev_size(store->snap->cow->bdev); + + if (size < (tc->next_free + store->snap->chunk_size)) + return -1; + + e->new_chunk = sector_to_chunk(store->snap, tc->next_free); + tc->next_free += store->snap->chunk_size; + + return 0; +} + +static void transient_commit_exception(struct dm_exception_store *store, + struct dm_snap_exception *e, + void (*callback) (void *, int success), + void *callback_context) +{ + /* Just succeed */ + callback(callback_context, 1); +} + +static void transient_fraction_full(struct dm_exception_store *store, + sector_t *numerator, sector_t *denominator) +{ + *numerator = ((struct transient_c *) store->context)->next_free; + *denominator = get_dev_size(store->snap->cow->bdev); +} + +int dm_create_transient(struct dm_exception_store *store) +{ + struct transient_c *tc; + + store->destroy = transient_destroy; + store->read_metadata = transient_read_metadata; + store->prepare_exception = transient_prepare_exception; + store->commit_exception = transient_commit_exception; + store->drop_snapshot = NULL; + store->fraction_full = transient_fraction_full; + + tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); + if (!tc) + return -ENOMEM; + + tc->next_free = 0; + store->context = tc; + + return 0; +} + +int dm_transient_snapshot_init(void) +{ + return 0; +} + +void dm_transient_snapshot_exit(void) +{ +} diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 6c96db26b87c..65ff82ff124e 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -9,6 +9,7 @@ #include <linux/blkdev.h> #include <linux/ctype.h> #include <linux/device-mapper.h> +#include <linux/delay.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kdev_t.h> @@ -20,6 +21,7 @@ #include <linux/log2.h> #include <linux/dm-kcopyd.h> +#include "dm-exception-store.h" #include "dm-snap.h" #include "dm-bio-list.h" @@ -428,8 +430,13 @@ out: list_add(&new_e->hash_list, e ? &e->hash_list : l); } -int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new) +/* + * Callback used by the exception stores to load exceptions when + * initialising. + */ +static int dm_add_exception(void *context, chunk_t old, chunk_t new) { + struct dm_snapshot *s = context; struct dm_snap_exception *e; e = alloc_exception(); @@ -658,7 +665,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) spin_lock_init(&s->tracked_chunk_lock); /* Metadata must only be loaded into one table at once */ - r = s->store.read_metadata(&s->store); + r = s->store.read_metadata(&s->store, dm_add_exception, (void *)s); if (r < 0) { ti->error = "Failed to read snapshot metadata"; goto bad_load_and_register; @@ -735,7 +742,7 @@ static void snapshot_dtr(struct dm_target *ti) unregister_snapshot(s); while (atomic_read(&s->pending_exceptions_count)) - yield(); + msleep(1); /* * Ensure instructions in mempool_destroy aren't reordered * before atomic_read. @@ -888,10 +895,10 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) /* * Check for conflicting reads. This is extremely improbable, - * so yield() is sufficient and there is no need for a wait queue. + * so msleep(1) is sufficient and there is no need for a wait queue. */ while (__chunk_is_tracked(s, pe->e.old_chunk)) - yield(); + msleep(1); /* * Add a proper exception, and remove the @@ -1404,6 +1411,12 @@ static int __init dm_snapshot_init(void) { int r; + r = dm_exception_store_init(); + if (r) { + DMERR("Failed to initialize exception stores"); + return r; + } + r = dm_register_target(&snapshot_target); if (r) { DMERR("snapshot target register failed %d", r); @@ -1452,39 +1465,34 @@ static int __init dm_snapshot_init(void) return 0; - bad_pending_pool: +bad_pending_pool: kmem_cache_destroy(tracked_chunk_cache); - bad5: +bad5: kmem_cache_destroy(pending_cache); - bad4: +bad4: kmem_cache_destroy(exception_cache); - bad3: +bad3: exit_origin_hash(); - bad2: +bad2: dm_unregister_target(&origin_target); - bad1: +bad1: dm_unregister_target(&snapshot_target); return r; } static void __exit dm_snapshot_exit(void) { - int r; - destroy_workqueue(ksnapd); - r = dm_unregister_target(&snapshot_target); - if (r) - DMERR("snapshot unregister failed %d", r); - - r = dm_unregister_target(&origin_target); - if (r) - DMERR("origin unregister failed %d", r); + dm_unregister_target(&snapshot_target); + dm_unregister_target(&origin_target); exit_origin_hash(); kmem_cache_destroy(pending_cache); kmem_cache_destroy(exception_cache); kmem_cache_destroy(tracked_chunk_cache); + + dm_exception_store_exit(); } /* Module hooks */ diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h index 99c0106ede2d..d9e62b43cf85 100644 --- a/drivers/md/dm-snap.h +++ b/drivers/md/dm-snap.h @@ -1,6 +1,4 @@ /* - * dm-snapshot.c - * * Copyright (C) 2001-2002 Sistina Software (UK) Limited. * * This file is released under the GPL. @@ -10,6 +8,7 @@ #define DM_SNAPSHOT_H #include <linux/device-mapper.h> +#include "dm-exception-store.h" #include "dm-bio-list.h" #include <linux/blkdev.h> #include <linux/workqueue.h> @@ -20,116 +19,6 @@ struct exception_table { struct list_head *table; }; -/* - * The snapshot code deals with largish chunks of the disk at a - * time. Typically 32k - 512k. - */ -typedef sector_t chunk_t; - -/* - * An exception is used where an old chunk of data has been - * replaced by a new one. - * If chunk_t is 64 bits in size, the top 8 bits of new_chunk hold the number - * of chunks that follow contiguously. Remaining bits hold the number of the - * chunk within the device. - */ -struct dm_snap_exception { - struct list_head hash_list; - - chunk_t old_chunk; - chunk_t new_chunk; -}; - -/* - * Funtions to manipulate consecutive chunks - */ -# if defined(CONFIG_LBD) || (BITS_PER_LONG == 64) -# define DM_CHUNK_CONSECUTIVE_BITS 8 -# define DM_CHUNK_NUMBER_BITS 56 - -static inline chunk_t dm_chunk_number(chunk_t chunk) -{ - return chunk & (chunk_t)((1ULL << DM_CHUNK_NUMBER_BITS) - 1ULL); -} - -static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e) -{ - return e->new_chunk >> DM_CHUNK_NUMBER_BITS; -} - -static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e) -{ - e->new_chunk += (1ULL << DM_CHUNK_NUMBER_BITS); - - BUG_ON(!dm_consecutive_chunk_count(e)); -} - -# else -# define DM_CHUNK_CONSECUTIVE_BITS 0 - -static inline chunk_t dm_chunk_number(chunk_t chunk) -{ - return chunk; -} - -static inline unsigned dm_consecutive_chunk_count(struct dm_snap_exception *e) -{ - return 0; -} - -static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e) -{ -} - -# endif - -/* - * Abstraction to handle the meta/layout of exception stores (the - * COW device). - */ -struct exception_store { - - /* - * Destroys this object when you've finished with it. - */ - void (*destroy) (struct exception_store *store); - - /* - * The target shouldn't read the COW device until this is - * called. - */ - int (*read_metadata) (struct exception_store *store); - - /* - * Find somewhere to store the next exception. - */ - int (*prepare_exception) (struct exception_store *store, - struct dm_snap_exception *e); - - /* - * Update the metadata with this exception. - */ - void (*commit_exception) (struct exception_store *store, - struct dm_snap_exception *e, - void (*callback) (void *, int success), - void *callback_context); - - /* - * The snapshot is invalid, note this in the metadata. - */ - void (*drop_snapshot) (struct exception_store *store); - - /* - * Return how full the snapshot is. - */ - void (*fraction_full) (struct exception_store *store, - sector_t *numerator, - sector_t *denominator); - - struct dm_snapshot *snap; - void *context; -}; - #define DM_TRACKED_CHUNK_HASH_SIZE 16 #define DM_TRACKED_CHUNK_HASH(x) ((unsigned long)(x) & \ (DM_TRACKED_CHUNK_HASH_SIZE - 1)) @@ -172,7 +61,7 @@ struct dm_snapshot { spinlock_t pe_lock; /* The on disk metadata handler */ - struct exception_store store; + struct dm_exception_store store; struct dm_kcopyd_client *kcopyd_client; @@ -187,20 +76,6 @@ struct dm_snapshot { }; /* - * Used by the exception stores to load exceptions hen - * initialising. - */ -int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new); - -/* - * Constructor and destructor for the default persistent - * store. - */ -int dm_create_persistent(struct exception_store *store); - -int dm_create_transient(struct exception_store *store); - -/* * Return the number of sectors in the device. */ static inline sector_t get_dev_size(struct block_device *bdev) diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 9e4ef88d421e..41569bc60abc 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -337,9 +337,7 @@ int __init dm_stripe_init(void) void dm_stripe_exit(void) { - if (dm_unregister_target(&stripe_target)) - DMWARN("target unregistration failed"); - + dm_unregister_target(&stripe_target); destroy_workqueue(kstriped); return; diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c new file mode 100644 index 000000000000..a2a45e6c7c8b --- /dev/null +++ b/drivers/md/dm-sysfs.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 Red Hat, Inc. All rights reserved. + * + * This file is released under the GPL. + */ + +#include <linux/sysfs.h> +#include <linux/dm-ioctl.h> +#include "dm.h" + +struct dm_sysfs_attr { + struct attribute attr; + ssize_t (*show)(struct mapped_device *, char *); + ssize_t (*store)(struct mapped_device *, char *); +}; + +#define DM_ATTR_RO(_name) \ +struct dm_sysfs_attr dm_attr_##_name = \ + __ATTR(_name, S_IRUGO, dm_attr_##_name##_show, NULL) + +static ssize_t dm_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct dm_sysfs_attr *dm_attr; + struct mapped_device *md; + ssize_t ret; + + dm_attr = container_of(attr, struct dm_sysfs_attr, attr); + if (!dm_attr->show) + return -EIO; + + md = dm_get_from_kobject(kobj); + if (!md) + return -EINVAL; + + ret = dm_attr->show(md, page); + dm_put(md); + + return ret; +} + +static ssize_t dm_attr_name_show(struct mapped_device *md, char *buf) +{ + if (dm_copy_name_and_uuid(md, buf, NULL)) + return -EIO; + + strcat(buf, "\n"); + return strlen(buf); +} + +static ssize_t dm_attr_uuid_show(struct mapped_device *md, char *buf) +{ + if (dm_copy_name_and_uuid(md, NULL, buf)) + return -EIO; + + strcat(buf, "\n"); + return strlen(buf); +} + +static DM_ATTR_RO(name); +static DM_ATTR_RO(uuid); + +static struct attribute *dm_attrs[] = { + &dm_attr_name.attr, + &dm_attr_uuid.attr, + NULL, +}; + +static struct sysfs_ops dm_sysfs_ops = { + .show = dm_attr_show, +}; + +/* + * dm kobject is embedded in mapped_device structure + * no need to define release function here + */ +static struct kobj_type dm_ktype = { + .sysfs_ops = &dm_sysfs_ops, + .default_attrs = dm_attrs, +}; + +/* + * Initialize kobj + * because nobody using md yet, no need to call explicit dm_get/put + */ +int dm_sysfs_init(struct mapped_device *md) +{ + return kobject_init_and_add(dm_kobject(md), &dm_ktype, + &disk_to_dev(dm_disk(md))->kobj, + "%s", "dm"); +} + +/* + * Remove kobj, called after all references removed + */ +void dm_sysfs_exit(struct mapped_device *md) +{ + kobject_put(dm_kobject(md)); +} diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 04e5fd742c2c..2fd66c30f7f8 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001 Sistina Software (UK) Limited. - * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. */ @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/mutex.h> +#include <linux/delay.h> #include <asm/atomic.h> #define DM_MSG_PREFIX "table" @@ -24,6 +25,19 @@ #define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t)) #define CHILDREN_PER_NODE (KEYS_PER_NODE + 1) +/* + * The table has always exactly one reference from either mapped_device->map + * or hash_cell->new_map. This reference is not counted in table->holders. + * A pair of dm_create_table/dm_destroy_table functions is used for table + * creation/destruction. + * + * Temporary references from the other code increase table->holders. A pair + * of dm_table_get/dm_table_put functions is used to manipulate it. + * + * When the table is about to be destroyed, we wait for table->holders to + * drop to zero. + */ + struct dm_table { struct mapped_device *md; atomic_t holders; @@ -38,6 +52,8 @@ struct dm_table { sector_t *highs; struct dm_target *targets; + unsigned barriers_supported:1; + /* * Indicates the rw permissions for the new logical * device. This should be a combination of FMODE_READ @@ -226,7 +242,8 @@ int dm_table_create(struct dm_table **result, fmode_t mode, return -ENOMEM; INIT_LIST_HEAD(&t->devices); - atomic_set(&t->holders, 1); + atomic_set(&t->holders, 0); + t->barriers_supported = 1; if (!num_targets) num_targets = KEYS_PER_NODE; @@ -256,10 +273,14 @@ static void free_devices(struct list_head *devices) } } -static void table_destroy(struct dm_table *t) +void dm_table_destroy(struct dm_table *t) { unsigned int i; + while (atomic_read(&t->holders)) + msleep(1); + smp_mb(); + /* free the indexes (see dm_table_complete) */ if (t->depth >= 2) vfree(t->index[t->depth - 2]); @@ -297,8 +318,8 @@ void dm_table_put(struct dm_table *t) if (!t) return; - if (atomic_dec_and_test(&t->holders)) - table_destroy(t); + smp_mb__before_atomic_dec(); + atomic_dec(&t->holders); } /* @@ -728,6 +749,10 @@ int dm_table_add_target(struct dm_table *t, const char *type, /* FIXME: the plan is to combine high here and then have * the merge fn apply the target level restrictions. */ combine_restrictions_low(&t->limits, &tgt->limits); + + if (!(tgt->type->features & DM_TARGET_SUPPORTS_BARRIERS)) + t->barriers_supported = 0; + return 0; bad: @@ -772,6 +797,12 @@ int dm_table_complete(struct dm_table *t) check_for_valid_limits(&t->limits); + /* + * We only support barriers if there is exactly one underlying device. + */ + if (!list_is_singular(&t->devices)) + t->barriers_supported = 0; + /* how many indexes will the btree have ? */ leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); @@ -986,6 +1017,12 @@ struct mapped_device *dm_table_get_md(struct dm_table *t) return t->md; } +int dm_table_barrier_ok(struct dm_table *t) +{ + return t->barriers_supported; +} +EXPORT_SYMBOL(dm_table_barrier_ok); + EXPORT_SYMBOL(dm_vcalloc); EXPORT_SYMBOL(dm_get_device); EXPORT_SYMBOL(dm_put_device); diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c index 835cf95b857f..7decf10006e4 100644 --- a/drivers/md/dm-target.c +++ b/drivers/md/dm-target.c @@ -130,26 +130,26 @@ int dm_register_target(struct target_type *t) return rv; } -int dm_unregister_target(struct target_type *t) +void dm_unregister_target(struct target_type *t) { struct tt_internal *ti; down_write(&_lock); if (!(ti = __find_target_type(t->name))) { - up_write(&_lock); - return -EINVAL; + DMCRIT("Unregistering unrecognised target: %s", t->name); + BUG(); } if (ti->use) { - up_write(&_lock); - return -ETXTBSY; + DMCRIT("Attempt to unregister target still in use: %s", + t->name); + BUG(); } list_del(&ti->list); kfree(ti); up_write(&_lock); - return 0; } /* @@ -187,8 +187,7 @@ int __init dm_target_init(void) void dm_target_exit(void) { - if (dm_unregister_target(&error_target)) - DMWARN("error target unregistration failed"); + dm_unregister_target(&error_target); } EXPORT_SYMBOL(dm_register_target); diff --git a/drivers/md/dm-zero.c b/drivers/md/dm-zero.c index cdbf126ec106..bbc97030c0c2 100644 --- a/drivers/md/dm-zero.c +++ b/drivers/md/dm-zero.c @@ -69,10 +69,7 @@ static int __init dm_zero_init(void) static void __exit dm_zero_exit(void) { - int r = dm_unregister_target(&zero_target); - - if (r < 0) - DMERR("unregister failed %d", r); + dm_unregister_target(&zero_target); } module_init(dm_zero_init) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 421c9f02d8ca..51ba1db4b3e7 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. - * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. */ @@ -32,6 +32,7 @@ static unsigned int _major = 0; static DEFINE_SPINLOCK(_minor_lock); /* + * For bio-based dm. * One of these is allocated per bio. */ struct dm_io { @@ -43,6 +44,7 @@ struct dm_io { }; /* + * For bio-based dm. * One of these is allocated per target within a bio. Hopefully * this will be simplified out one day. */ @@ -54,6 +56,27 @@ struct dm_target_io { DEFINE_TRACE(block_bio_complete); +/* + * For request-based dm. + * One of these is allocated per request. + */ +struct dm_rq_target_io { + struct mapped_device *md; + struct dm_target *ti; + struct request *orig, clone; + int error; + union map_info info; +}; + +/* + * For request-based dm. + * One of these is allocated per bio. + */ +struct dm_rq_clone_bio_info { + struct bio *orig; + struct request *rq; +}; + union map_info *dm_get_mapinfo(struct bio *bio) { if (bio && bio->bi_private) @@ -144,11 +167,16 @@ struct mapped_device { /* forced geometry settings */ struct hd_geometry geometry; + + /* sysfs handle */ + struct kobject kobj; }; #define MIN_IOS 256 static struct kmem_cache *_io_cache; static struct kmem_cache *_tio_cache; +static struct kmem_cache *_rq_tio_cache; +static struct kmem_cache *_rq_bio_info_cache; static int __init local_init(void) { @@ -164,9 +192,17 @@ static int __init local_init(void) if (!_tio_cache) goto out_free_io_cache; + _rq_tio_cache = KMEM_CACHE(dm_rq_target_io, 0); + if (!_rq_tio_cache) + goto out_free_tio_cache; + + _rq_bio_info_cache = KMEM_CACHE(dm_rq_clone_bio_info, 0); + if (!_rq_bio_info_cache) + goto out_free_rq_tio_cache; + r = dm_uevent_init(); if (r) - goto out_free_tio_cache; + goto out_free_rq_bio_info_cache; _major = major; r = register_blkdev(_major, _name); @@ -180,6 +216,10 @@ static int __init local_init(void) out_uevent_exit: dm_uevent_exit(); +out_free_rq_bio_info_cache: + kmem_cache_destroy(_rq_bio_info_cache); +out_free_rq_tio_cache: + kmem_cache_destroy(_rq_tio_cache); out_free_tio_cache: kmem_cache_destroy(_tio_cache); out_free_io_cache: @@ -190,6 +230,8 @@ out_free_io_cache: static void local_exit(void) { + kmem_cache_destroy(_rq_bio_info_cache); + kmem_cache_destroy(_rq_tio_cache); kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); unregister_blkdev(_major, _name); @@ -796,7 +838,11 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) ci.map = dm_get_table(md); if (unlikely(!ci.map)) return -EIO; - + if (unlikely(bio_barrier(bio) && !dm_table_barrier_ok(ci.map))) { + dm_table_put(ci.map); + bio_endio(bio, -EOPNOTSUPP); + return 0; + } ci.md = md; ci.bio = bio; ci.io = alloc_io(md); @@ -880,15 +926,6 @@ static int dm_request(struct request_queue *q, struct bio *bio) struct mapped_device *md = q->queuedata; int cpu; - /* - * There is no use in forwarding any barrier request since we can't - * guarantee it is (or can be) handled by the targets correctly. - */ - if (unlikely(bio_barrier(bio))) { - bio_endio(bio, -EOPNOTSUPP); - return 0; - } - down_read(&md->io_lock); cpu = part_stat_lock(); @@ -943,8 +980,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits) struct mapped_device *md = congested_data; struct dm_table *map; - atomic_inc(&md->pending); - if (!test_bit(DMF_BLOCK_IO, &md->flags)) { map = dm_get_table(md); if (map) { @@ -953,10 +988,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits) } } - if (!atomic_dec_return(&md->pending)) - /* nudge anyone waiting on suspend queue */ - wake_up(&md->wait); - return r; } @@ -1216,10 +1247,12 @@ static int __bind(struct mapped_device *md, struct dm_table *t) if (md->suspended_bdev) __set_size(md, size); - if (size == 0) + + if (!size) { + dm_table_destroy(t); return 0; + } - dm_table_get(t); dm_table_event_callback(t, event_callback, md); write_lock(&md->map_lock); @@ -1241,7 +1274,7 @@ static void __unbind(struct mapped_device *md) write_lock(&md->map_lock); md->map = NULL; write_unlock(&md->map_lock); - dm_table_put(map); + dm_table_destroy(map); } /* @@ -1255,6 +1288,8 @@ int dm_create(int minor, struct mapped_device **result) if (!md) return -ENXIO; + dm_sysfs_init(md); + *result = md; return 0; } @@ -1330,8 +1365,9 @@ void dm_put(struct mapped_device *md) dm_table_presuspend_targets(map); dm_table_postsuspend_targets(map); } - __unbind(md); + dm_sysfs_exit(md); dm_table_put(map); + __unbind(md); free_dev(md); } } @@ -1669,6 +1705,27 @@ struct gendisk *dm_disk(struct mapped_device *md) return md->disk; } +struct kobject *dm_kobject(struct mapped_device *md) +{ + return &md->kobj; +} + +/* + * struct mapped_device should not be exported outside of dm.c + * so use this check to verify that kobj is part of md structure + */ +struct mapped_device *dm_get_from_kobject(struct kobject *kobj) +{ + struct mapped_device *md; + + md = container_of(kobj, struct mapped_device, kobj); + if (&md->kobj != kobj) + return NULL; + + dm_get(md); + return md; +} + int dm_suspended(struct mapped_device *md) { return test_bit(DMF_SUSPENDED, &md->flags); diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 0ade60cdef42..20194e000c5a 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -36,6 +36,7 @@ struct dm_table; /*----------------------------------------------------------------- * Internal table functions. *---------------------------------------------------------------*/ +void dm_table_destroy(struct dm_table *t); void dm_table_event_callback(struct dm_table *t, void (*fn)(void *), void *context); struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index); @@ -51,6 +52,7 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits); * To check the return value from dm_table_find_target(). */ #define dm_target_is_valid(t) ((t)->table) +int dm_table_barrier_ok(struct dm_table *t); /*----------------------------------------------------------------- * A registry of target types. @@ -72,6 +74,14 @@ int dm_interface_init(void); void dm_interface_exit(void); /* + * sysfs interface + */ +int dm_sysfs_init(struct mapped_device *md); +void dm_sysfs_exit(struct mapped_device *md); +struct kobject *dm_kobject(struct mapped_device *md); +struct mapped_device *dm_get_from_kobject(struct kobject *kobj); + +/* * Targets for linear and striped mappings */ int dm_linear_init(void); diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c index 56faef1a1d55..06c655c55587 100644 --- a/drivers/message/i2o/exec-osm.c +++ b/drivers/message/i2o/exec-osm.c @@ -19,7 +19,7 @@ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI> * Deepak Saxena <deepak@plexity.net> * Boji T Kannanthanam <boji.t.kannanthanam@intel.com> - * Alan Cox <alan@redhat.com>: + * Alan Cox <alan@lxorguk.ukuu.org.uk>: * Ported to Linux 2.5. * Markus Lidel <Markus.Lidel@shadowconnect.com>: * Minor fixes for 2.6. diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c index f3384c32b9a1..efba7021948a 100644 --- a/drivers/message/i2o/i2o_config.c +++ b/drivers/message/i2o/i2o_config.c @@ -19,7 +19,7 @@ * Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel() * Deepak Saxena (11/18/1999): * Added event managmenet support - * Alan Cox <alan@redhat.com>: + * Alan Cox <alan@lxorguk.ukuu.org.uk>: * 2.4 rewrite ported to 2.5 * Markus Lidel <Markus.Lidel@shadowconnect.com>: * Added pass-thru support for Adaptec's raidutils diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index 6e53a30bfd38..35c67d1f255e 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -19,7 +19,7 @@ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI> * Deepak Saxena <deepak@plexity.net> * Boji T Kannanthanam <boji.t.kannanthanam@intel.com> - * Alan Cox <alan@redhat.com>: + * Alan Cox <alan@lxorguk.ukuu.org.uk>: * Ported to Linux 2.5. * Markus Lidel <Markus.Lidel@shadowconnect.com>: * Minor fixes for 2.6. diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c index 610ef1204e68..25d6f2341983 100644 --- a/drivers/message/i2o/pci.c +++ b/drivers/message/i2o/pci.c @@ -19,7 +19,7 @@ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI> * Deepak Saxena <deepak@plexity.net> * Boji T Kannanthanam <boji.t.kannanthanam@intel.com> - * Alan Cox <alan@redhat.com>: + * Alan Cox <alan@lxorguk.ukuu.org.uk>: * Ported to Linux 2.5. * Markus Lidel <Markus.Lidel@shadowconnect.com>: * Minor fixes for 2.6. diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 257277394f8c..416f9e7286ba 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -34,6 +34,14 @@ config MFD_ASIC3 This driver supports the ASIC3 multifunction chip found on many PDAs (mainly iPAQ and HTC based ones) +config MFD_DM355EVM_MSP + bool "DaVinci DM355 EVM microcontroller" + depends on I2C && MACH_DAVINCI_DM355_EVM + help + This driver supports the MSP430 microcontroller used on these + boards. MSP430 firmware manages resets and power sequencing, + inputs from buttons and the IR remote, LEDs, an RTC, and more. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM @@ -61,9 +69,32 @@ config UCB1400_CORE To compile this driver as a module, choose M here: the module will be called ucb1400_core. +config TPS65010 + tristate "TPS6501x Power Management chips" + depends on I2C && GPIOLIB + default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK + help + If you say yes here you get support for the TPS6501x series of + Power Management chips. These include voltage regulators, + lithium ion/polymer battery charging, and other features that + are often used in portable devices like cell phones and cameras. + + This driver can also be built as a module. If so, the module + will be called tps65010. + +config MENELAUS + bool "Texas Instruments TWL92330/Menelaus PM chip" + depends on I2C=y && ARCH_OMAP24XX + help + If you say yes here you get support for the Texas Instruments + TWL92330/Menelaus Power Management chip. This include voltage + regulators, Dual slot memory card tranceivers, real-time clock + and other features that are often used in portable devices like + cell phones and PDAs. + config TWL4030_CORE bool "Texas Instruments TWL4030/TPS659x0 Support" - depends on I2C=y && GENERIC_HARDIRQS && (ARCH_OMAP2 || ARCH_OMAP3) + depends on I2C=y && GENERIC_HARDIRQS help Say yes here if you have TWL4030 family chip on your board. This core driver provides register access and IRQ handling @@ -116,6 +147,7 @@ config PMIC_DA903X config MFD_WM8400 tristate "Support Wolfson Microelectronics WM8400" + select MFD_CORE depends on I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio @@ -142,6 +174,38 @@ config MFD_WM8350_CONFIG_MODE_3 bool depends on MFD_WM8350 +config MFD_WM8351_CONFIG_MODE_0 + bool + depends on MFD_WM8350 + +config MFD_WM8351_CONFIG_MODE_1 + bool + depends on MFD_WM8350 + +config MFD_WM8351_CONFIG_MODE_2 + bool + depends on MFD_WM8350 + +config MFD_WM8351_CONFIG_MODE_3 + bool + depends on MFD_WM8350 + +config MFD_WM8352_CONFIG_MODE_0 + bool + depends on MFD_WM8350 + +config MFD_WM8352_CONFIG_MODE_1 + bool + depends on MFD_WM8350 + +config MFD_WM8352_CONFIG_MODE_2 + bool + depends on MFD_WM8350 + +config MFD_WM8352_CONFIG_MODE_3 + bool + depends on MFD_WM8350 + config MFD_WM8350_I2C tristate "Support Wolfson Microelectronics WM8350 with I2C" select MFD_WM8350 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9a5ad8af9116..0c9418b36c26 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o +obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o + obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o @@ -17,6 +19,9 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o +obj-$(CONFIG_TPS65010) += tps65010.o +obj-$(CONFIG_MENELAUS) += menelaus.o + obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o obj-$(CONFIG_MFD_CORE) += mfd-core.o @@ -31,4 +36,4 @@ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o endif obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o -obj-$(CONFIG_PMIC_DA903X) += da903x.o
\ No newline at end of file +obj-$(CONFIG_PMIC_DA903X) += da903x.o diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index 0b5bd85dfcec..99f8dcfe3d98 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -151,12 +151,24 @@ int da903x_write(struct device *dev, int reg, uint8_t val) } EXPORT_SYMBOL_GPL(da903x_write); +int da903x_writes(struct device *dev, int reg, int len, uint8_t *val) +{ + return __da903x_writes(to_i2c_client(dev), reg, len, val); +} +EXPORT_SYMBOL_GPL(da903x_writes); + int da903x_read(struct device *dev, int reg, uint8_t *val) { return __da903x_read(to_i2c_client(dev), reg, val); } EXPORT_SYMBOL_GPL(da903x_read); +int da903x_reads(struct device *dev, int reg, int len, uint8_t *val) +{ + return __da903x_reads(to_i2c_client(dev), reg, len, val); +} +EXPORT_SYMBOL_GPL(da903x_reads); + int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask) { struct da903x_chip *chip = dev_get_drvdata(dev); @@ -435,13 +447,13 @@ static const struct i2c_device_id da903x_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, da903x_id_table); -static int __devexit __remove_subdev(struct device *dev, void *unused) +static int __remove_subdev(struct device *dev, void *unused) { platform_device_unregister(to_platform_device(dev)); return 0; } -static int __devexit da903x_remove_subdevs(struct da903x_chip *chip) +static int da903x_remove_subdevs(struct da903x_chip *chip) { return device_for_each_child(chip->dev, NULL, __remove_subdev); } diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c new file mode 100644 index 000000000000..4214b3f72426 --- /dev/null +++ b/drivers/mfd/dm355evm_msp.c @@ -0,0 +1,420 @@ +/* + * dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board + * + * Copyright (C) 2008 David Brownell + * + * 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. + */ + +#include <linux/init.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/leds.h> +#include <linux/i2c.h> +#include <linux/i2c/dm355evm_msp.h> + + +/* + * The DM355 is a DaVinci chip with video support but no C64+ DSP. Its + * EVM board has an MSP430 programmed with firmware for various board + * support functions. This driver exposes some of them directly, and + * supports other drivers (e.g. RTC, input) for more complex access. + * + * Because this firmware is entirely board-specific, this file embeds + * knowledge that would be passed as platform_data in a generic driver. + * + * This driver was tested with firmware revision A4. + */ + +#if defined(CONFIG_KEYBOARD_DM355EVM) \ + || defined(CONFIG_KEYBOARD_DM355EVM_MODULE) +#define msp_has_keyboard() true +#else +#define msp_has_keyboard() false +#endif + +#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) +#define msp_has_leds() true +#else +#define msp_has_leds() false +#endif + +#if defined(CONFIG_RTC_DRV_DM355EVM) || defined(CONFIG_RTC_DRV_DM355EVM_MODULE) +#define msp_has_rtc() true +#else +#define msp_has_rtc() false +#endif + +#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE) +#define msp_has_tvp() true +#else +#define msp_has_tvp() false +#endif + + +/*----------------------------------------------------------------------*/ + +/* REVISIT for paranoia's sake, retry reads/writes on error */ + +static struct i2c_client *msp430; + +/** + * dm355evm_msp_write - Writes a register in dm355evm_msp + * @value: the value to be written + * @reg: register address + * + * Returns result of operation - 0 is success, else negative errno + */ +int dm355evm_msp_write(u8 value, u8 reg) +{ + return i2c_smbus_write_byte_data(msp430, reg, value); +} +EXPORT_SYMBOL(dm355evm_msp_write); + +/** + * dm355evm_msp_read - Reads a register from dm355evm_msp + * @reg: register address + * + * Returns result of operation - value, or negative errno + */ +int dm355evm_msp_read(u8 reg) +{ + return i2c_smbus_read_byte_data(msp430, reg); +} +EXPORT_SYMBOL(dm355evm_msp_read); + +/*----------------------------------------------------------------------*/ + +/* + * Many of the msp430 pins are just used as fixed-direction GPIOs. + * We could export a few more of them this way, if we wanted. + */ +#define MSP_GPIO(bit,reg) ((DM355EVM_MSP_ ## reg) << 3 | (bit)) + +static const u8 msp_gpios[] = { + /* eight leds */ + MSP_GPIO(0, LED), MSP_GPIO(1, LED), + MSP_GPIO(2, LED), MSP_GPIO(3, LED), + MSP_GPIO(4, LED), MSP_GPIO(5, LED), + MSP_GPIO(6, LED), MSP_GPIO(7, LED), + /* SW6 and the NTSC/nPAL jumper */ + MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1), + MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1), + MSP_GPIO(4, SWITCH1), +}; + +#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3) +#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07) + +static int msp_gpio_in(struct gpio_chip *chip, unsigned offset) +{ + switch (MSP_GPIO_REG(offset)) { + case DM355EVM_MSP_SWITCH1: + case DM355EVM_MSP_SWITCH2: + case DM355EVM_MSP_SDMMC: + return 0; + default: + return -EINVAL; + } +} + +static u8 msp_led_cache; + +static int msp_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + int reg, status; + + reg = MSP_GPIO_REG(offset); + status = dm355evm_msp_read(reg); + if (status < 0) + return status; + if (reg == DM355EVM_MSP_LED) + msp_led_cache = status; + return status & MSP_GPIO_MASK(offset); +} + +static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value) +{ + int mask, bits; + + /* NOTE: there are some other signals that could be + * packaged as output GPIOs, but they aren't as useful + * as the LEDs ... so for now we don't. + */ + if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED) + return -EINVAL; + + mask = MSP_GPIO_MASK(offset); + bits = msp_led_cache; + + bits &= ~mask; + if (value) + bits |= mask; + msp_led_cache = bits; + + return dm355evm_msp_write(bits, DM355EVM_MSP_LED); +} + +static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + msp_gpio_out(chip, offset, value); +} + +static struct gpio_chip dm355evm_msp_gpio = { + .label = "dm355evm_msp", + .owner = THIS_MODULE, + .direction_input = msp_gpio_in, + .get = msp_gpio_get, + .direction_output = msp_gpio_out, + .set = msp_gpio_set, + .base = -EINVAL, /* dynamic assignment */ + .ngpio = ARRAY_SIZE(msp_gpios), + .can_sleep = true, +}; + +/*----------------------------------------------------------------------*/ + +static struct device *add_child(struct i2c_client *client, const char *name, + void *pdata, unsigned pdata_len, + bool can_wakeup, int irq) +{ + struct platform_device *pdev; + int status; + + pdev = platform_device_alloc(name, -1); + if (!pdev) { + dev_dbg(&client->dev, "can't alloc dev\n"); + status = -ENOMEM; + goto err; + } + + device_init_wakeup(&pdev->dev, can_wakeup); + pdev->dev.parent = &client->dev; + + if (pdata) { + status = platform_device_add_data(pdev, pdata, pdata_len); + if (status < 0) { + dev_dbg(&pdev->dev, "can't add platform_data\n"); + goto err; + } + } + + if (irq) { + struct resource r = { + .start = irq, + .flags = IORESOURCE_IRQ, + }; + + status = platform_device_add_resources(pdev, &r, 1); + if (status < 0) { + dev_dbg(&pdev->dev, "can't add irq\n"); + goto err; + } + } + + status = platform_device_add(pdev); + +err: + if (status < 0) { + platform_device_put(pdev); + dev_err(&client->dev, "can't add %s dev\n", name); + return ERR_PTR(status); + } + return &pdev->dev; +} + +static int add_children(struct i2c_client *client) +{ + static const struct { + int offset; + char *label; + } config_inputs[] = { + /* 8 == right after the LEDs */ + { 8 + 0, "sw6_1", }, + { 8 + 1, "sw6_2", }, + { 8 + 2, "sw6_3", }, + { 8 + 3, "sw6_4", }, + { 8 + 4, "NTSC/nPAL", }, + }; + + struct device *child; + int status; + int i; + + /* GPIO-ish stuff */ + dm355evm_msp_gpio.dev = &client->dev; + status = gpiochip_add(&dm355evm_msp_gpio); + if (status < 0) + return status; + + /* LED output */ + if (msp_has_leds()) { +#define GPIO_LED(l) .name = l, .active_low = true + static struct gpio_led evm_leds[] = { + { GPIO_LED("dm355evm::ds14"), + .default_trigger = "heartbeat", }, + { GPIO_LED("dm355evm::ds15"), + .default_trigger = "mmc0", }, + { GPIO_LED("dm355evm::ds16"), + /* could also be a CE-ATA drive */ + .default_trigger = "mmc1", }, + { GPIO_LED("dm355evm::ds17"), + .default_trigger = "nand-disk", }, + { GPIO_LED("dm355evm::ds18"), }, + { GPIO_LED("dm355evm::ds19"), }, + { GPIO_LED("dm355evm::ds20"), }, + { GPIO_LED("dm355evm::ds21"), }, + }; +#undef GPIO_LED + + struct gpio_led_platform_data evm_led_data = { + .num_leds = ARRAY_SIZE(evm_leds), + .leds = evm_leds, + }; + + for (i = 0; i < ARRAY_SIZE(evm_leds); i++) + evm_leds[i].gpio = i + dm355evm_msp_gpio.base; + + /* NOTE: these are the only fully programmable LEDs + * on the board, since GPIO-61/ds22 (and many signals + * going to DC7) must be used for AEMIF address lines + * unless the top 1 GB of NAND is unused... + */ + child = add_child(client, "leds-gpio", + &evm_led_data, sizeof(evm_led_data), + false, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + /* configuration inputs */ + for (i = 0; i < ARRAY_SIZE(config_inputs); i++) { + int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset; + + gpio_request(gpio, config_inputs[i].label); + gpio_direction_input(gpio); + + /* make it easy for userspace to see these */ + gpio_export(gpio, false); + } + + /* RTC is a 32 bit counter, no alarm */ + if (msp_has_rtc()) { + child = add_child(client, "rtc-dm355evm", + NULL, 0, false, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + /* input from buttons and IR remote (uses the IRQ) */ + if (msp_has_keyboard()) { + child = add_child(client, "dm355evm_keys", + NULL, 0, true, client->irq); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + return 0; +} + +/*----------------------------------------------------------------------*/ + +static void dm355evm_command(unsigned command) +{ + int status; + + status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND); + if (status < 0) + dev_err(&msp430->dev, "command %d failure %d\n", + command, status); +} + +static void dm355evm_power_off(void) +{ + dm355evm_command(MSP_COMMAND_POWEROFF); +} + +static int dm355evm_msp_remove(struct i2c_client *client) +{ + pm_power_off = NULL; + msp430 = NULL; + return 0; +} + +static int +dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int status; + const char *video = msp_has_tvp() ? "TVP5146" : "imager"; + + if (msp430) + return -EBUSY; + msp430 = client; + + /* display revision status; doubles as sanity check */ + status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); + if (status < 0) + goto fail; + dev_info(&client->dev, "firmware v.%02X, %s as video-in\n", + status, video); + + /* mux video input: either tvp5146 or some external imager */ + status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER, + DM355EVM_MSP_VIDEO_IN); + if (status < 0) + dev_warn(&client->dev, "error %d muxing %s as video-in\n", + status, video); + + /* init LED cache, and turn off the LEDs */ + msp_led_cache = 0xff; + dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED); + + /* export capabilities we support */ + status = add_children(client); + if (status < 0) + goto fail; + + /* PM hookup */ + pm_power_off = dm355evm_power_off; + + return 0; + +fail: + /* FIXME remove children ... */ + dm355evm_msp_remove(client); + return status; +} + +static const struct i2c_device_id dm355evm_msp_ids[] = { + { "dm355evm_msp", 0 }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids); + +static struct i2c_driver dm355evm_msp_driver = { + .driver.name = "dm355evm_msp", + .id_table = dm355evm_msp_ids, + .probe = dm355evm_msp_probe, + .remove = dm355evm_msp_remove, +}; + +static int __init dm355evm_msp_init(void) +{ + return i2c_add_driver(&dm355evm_msp_driver); +} +subsys_initcall(dm355evm_msp_init); + +static void __exit dm355evm_msp_exit(void) +{ + i2c_del_driver(&dm355evm_msp_driver); +} +module_exit(dm355evm_msp_exit); + +MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/menelaus.c b/drivers/mfd/menelaus.c index 4b364bae6b3e..4b364bae6b3e 100644 --- a/drivers/i2c/chips/menelaus.c +++ b/drivers/mfd/menelaus.c diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 6c0d1bec4b76..54ddf3772e0c 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -34,6 +34,7 @@ static int mfd_add_device(struct device *parent, int id, goto fail_device; pdev->dev.parent = parent; + platform_set_drvdata(pdev, cell->driver_data); ret = platform_device_add_data(pdev, cell->platform_data, cell->data_size); diff --git a/drivers/i2c/chips/tps65010.c b/drivers/mfd/tps65010.c index acf8b9d5f575..acf8b9d5f575 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/mfd/tps65010.c diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index dd843c4fbcc7..b59c385cbc12 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -33,6 +33,8 @@ #include <linux/clk.h> #include <linux/err.h> +#include <linux/regulator/machine.h> + #include <linux/i2c.h> #include <linux/i2c/twl4030.h> @@ -71,6 +73,13 @@ #define twl_has_gpio() false #endif +#if defined(CONFIG_REGULATOR_TWL4030) \ + || defined(CONFIG_REGULATOR_TWL4030_MODULE) +#define twl_has_regulator() true +#else +#define twl_has_regulator() false +#endif + #if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE) #define twl_has_madc() true #else @@ -149,6 +158,10 @@ #define HIGH_PERF_SQ (1 << 3) +/* chip-specific feature flags, for i2c_device_id.driver_data */ +#define TWL4030_VAUX2 BIT(0) /* pre-5030 voltage ranges */ +#define TPS_SUBSET BIT(1) /* tps659[23]0 have fewer LDOs */ + /*----------------------------------------------------------------------*/ /* is driver active, bound to a chip? */ @@ -225,7 +238,7 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { * * Returns the result of operation - 0 is success */ -int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes) +int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { int ret; int sid; @@ -274,7 +287,7 @@ EXPORT_SYMBOL(twl4030_i2c_write); * * Returns result of operation - num_bytes is success else failure. */ -int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes) +int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { int ret; u8 val; @@ -352,258 +365,258 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8); /*----------------------------------------------------------------------*/ -/* - * NOTE: We know the first 8 IRQs after pdata->base_irq are - * for the PIH, and the next are for the PWR_INT SIH, since - * that's how twl_init_irq() sets things up. - */ - -static int add_children(struct twl4030_platform_data *pdata) +static struct device * +add_numbered_child(unsigned chip, const char *name, int num, + void *pdata, unsigned pdata_len, + bool can_wakeup, int irq0, int irq1) { - struct platform_device *pdev = NULL; - struct twl4030_client *twl = NULL; - int status = 0; + struct platform_device *pdev; + struct twl4030_client *twl = &twl4030_modules[chip]; + int status; + + pdev = platform_device_alloc(name, num); + if (!pdev) { + dev_dbg(&twl->client->dev, "can't alloc dev\n"); + status = -ENOMEM; + goto err; + } - if (twl_has_bci() && pdata->bci) { - twl = &twl4030_modules[3]; + device_init_wakeup(&pdev->dev, can_wakeup); + pdev->dev.parent = &twl->client->dev; - pdev = platform_device_alloc("twl4030_bci", -1); - if (!pdev) { - pr_debug("%s: can't alloc bci dev\n", DRIVER_NAME); - status = -ENOMEM; + if (pdata) { + status = platform_device_add_data(pdev, pdata, pdata_len); + if (status < 0) { + dev_dbg(&pdev->dev, "can't add platform_data\n"); goto err; } + } - if (status == 0) { - pdev->dev.parent = &twl->client->dev; - status = platform_device_add_data(pdev, pdata->bci, - sizeof(*pdata->bci)); - if (status < 0) { - dev_dbg(&twl->client->dev, - "can't add bci data, %d\n", - status); - goto err; - } - } - - if (status == 0) { - struct resource r = { - .start = pdata->irq_base + 8 + 1, - .flags = IORESOURCE_IRQ, - }; - - status = platform_device_add_resources(pdev, &r, 1); - } - - if (status == 0) - status = platform_device_add(pdev); + if (irq0) { + struct resource r[2] = { + { .start = irq0, .flags = IORESOURCE_IRQ, }, + { .start = irq1, .flags = IORESOURCE_IRQ, }, + }; + status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1); if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't create bci dev, %d\n", - status); + dev_dbg(&pdev->dev, "can't add irqs\n"); goto err; } } - if (twl_has_gpio() && pdata->gpio) { - twl = &twl4030_modules[1]; + status = platform_device_add(pdev); - pdev = platform_device_alloc("twl4030_gpio", -1); - if (!pdev) { - pr_debug("%s: can't alloc gpio dev\n", DRIVER_NAME); - status = -ENOMEM; - goto err; - } +err: + if (status < 0) { + platform_device_put(pdev); + dev_err(&twl->client->dev, "can't add %s dev\n", name); + return ERR_PTR(status); + } + return &pdev->dev; +} - /* more driver model init */ - if (status == 0) { - pdev->dev.parent = &twl->client->dev; - /* device_init_wakeup(&pdev->dev, 1); */ - - status = platform_device_add_data(pdev, pdata->gpio, - sizeof(*pdata->gpio)); - if (status < 0) { - dev_dbg(&twl->client->dev, - "can't add gpio data, %d\n", - status); - goto err; - } - } +static inline struct device *add_child(unsigned chip, const char *name, + void *pdata, unsigned pdata_len, + bool can_wakeup, int irq0, int irq1) +{ + return add_numbered_child(chip, name, -1, pdata, pdata_len, + can_wakeup, irq0, irq1); +} - /* GPIO module IRQ */ - if (status == 0) { - struct resource r = { - .start = pdata->irq_base + 0, - .flags = IORESOURCE_IRQ, - }; +static struct device * +add_regulator_linked(int num, struct regulator_init_data *pdata, + struct regulator_consumer_supply *consumers, + unsigned num_consumers) +{ + /* regulator framework demands init_data ... */ + if (!pdata) + return NULL; - status = platform_device_add_resources(pdev, &r, 1); - } + if (consumers) { + pdata->consumer_supplies = consumers; + pdata->num_consumer_supplies = num_consumers; + } - if (status == 0) - status = platform_device_add(pdev); + /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */ + return add_numbered_child(3, "twl4030_reg", num, + pdata, sizeof(*pdata), false, 0, 0); +} - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't create gpio dev, %d\n", - status); - goto err; - } +static struct device * +add_regulator(int num, struct regulator_init_data *pdata) +{ + return add_regulator_linked(num, pdata, NULL, 0); +} + +/* + * NOTE: We know the first 8 IRQs after pdata->base_irq are + * for the PIH, and the next are for the PWR_INT SIH, since + * that's how twl_init_irq() sets things up. + */ + +static int +add_children(struct twl4030_platform_data *pdata, unsigned long features) +{ + struct device *child; + struct device *usb_transceiver = NULL; + + if (twl_has_bci() && pdata->bci && !(features & TPS_SUBSET)) { + child = add_child(3, "twl4030_bci", + pdata->bci, sizeof(*pdata->bci), + false, + /* irq0 = CHG_PRES, irq1 = BCI */ + pdata->irq_base + 8 + 1, pdata->irq_base + 2); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + if (twl_has_gpio() && pdata->gpio) { + child = add_child(1, "twl4030_gpio", + pdata->gpio, sizeof(*pdata->gpio), + false, pdata->irq_base + 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_keypad() && pdata->keypad) { - pdev = platform_device_alloc("twl4030_keypad", -1); - if (pdev) { - twl = &twl4030_modules[2]; - pdev->dev.parent = &twl->client->dev; - device_init_wakeup(&pdev->dev, 1); - status = platform_device_add_data(pdev, pdata->keypad, - sizeof(*pdata->keypad)); - if (status < 0) { - dev_dbg(&twl->client->dev, - "can't add keypad data, %d\n", - status); - platform_device_put(pdev); - goto err; - } - status = platform_device_add(pdev); - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't create keypad dev, %d\n", - status); - goto err; - } - } else { - pr_debug("%s: can't alloc keypad dev\n", DRIVER_NAME); - status = -ENOMEM; - goto err; - } + child = add_child(2, "twl4030_keypad", + pdata->keypad, sizeof(*pdata->keypad), + true, pdata->irq_base + 1, 0); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_madc() && pdata->madc) { - pdev = platform_device_alloc("twl4030_madc", -1); - if (pdev) { - twl = &twl4030_modules[2]; - pdev->dev.parent = &twl->client->dev; - device_init_wakeup(&pdev->dev, 1); - status = platform_device_add_data(pdev, pdata->madc, - sizeof(*pdata->madc)); - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't add madc data, %d\n", - status); - goto err; - } - status = platform_device_add(pdev); - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't create madc dev, %d\n", - status); - goto err; - } - } else { - pr_debug("%s: can't alloc madc dev\n", DRIVER_NAME); - status = -ENOMEM; - goto err; - } + child = add_child(2, "twl4030_madc", + pdata->madc, sizeof(*pdata->madc), + true, pdata->irq_base + 3, 0); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_rtc()) { - twl = &twl4030_modules[3]; - - pdev = platform_device_alloc("twl4030_rtc", -1); - if (!pdev) { - pr_debug("%s: can't alloc rtc dev\n", DRIVER_NAME); - status = -ENOMEM; - } else { - pdev->dev.parent = &twl->client->dev; - device_init_wakeup(&pdev->dev, 1); - } - /* - * REVISIT platform_data here currently might use of + * REVISIT platform_data here currently might expose the * "msecure" line ... but for now we just expect board - * setup to tell the chip "we are secure" at all times. + * setup to tell the chip "it's always ok to SET_TIME". * Eventually, Linux might become more aware of such * HW security concerns, and "least privilege". */ - - /* RTC module IRQ */ - if (status == 0) { - struct resource r = { - .start = pdata->irq_base + 8 + 3, - .flags = IORESOURCE_IRQ, - }; - - status = platform_device_add_resources(pdev, &r, 1); - } - - if (status == 0) - status = platform_device_add(pdev); - - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't create rtc dev, %d\n", - status); - goto err; - } + child = add_child(3, "twl4030_rtc", + NULL, 0, + true, pdata->irq_base + 8 + 3, 0); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_usb() && pdata->usb) { - twl = &twl4030_modules[0]; - - pdev = platform_device_alloc("twl4030_usb", -1); - if (!pdev) { - pr_debug("%s: can't alloc usb dev\n", DRIVER_NAME); - status = -ENOMEM; - goto err; - } - - if (status == 0) { - pdev->dev.parent = &twl->client->dev; - device_init_wakeup(&pdev->dev, 1); - status = platform_device_add_data(pdev, pdata->usb, - sizeof(*pdata->usb)); - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't add usb data, %d\n", - status); - goto err; - } - } - - if (status == 0) { - struct resource r = { - .start = pdata->irq_base + 8 + 2, - .flags = IORESOURCE_IRQ, - }; + child = add_child(0, "twl4030_usb", + pdata->usb, sizeof(*pdata->usb), + true, + /* irq0 = USB_PRES, irq1 = USB */ + pdata->irq_base + 8 + 2, pdata->irq_base + 4); + if (IS_ERR(child)) + return PTR_ERR(child); + + /* we need to connect regulators to this transceiver */ + usb_transceiver = child; + } - status = platform_device_add_resources(pdev, &r, 1); - } + if (twl_has_regulator()) { + /* + child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); + if (IS_ERR(child)) + return PTR_ERR(child); + */ + + child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL4030_REG_VDAC, pdata->vdac); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator((features & TWL4030_VAUX2) + ? TWL4030_REG_VAUX2_4030 + : TWL4030_REG_VAUX2, + pdata->vaux2); + if (IS_ERR(child)) + return PTR_ERR(child); + } - if (status == 0) - status = platform_device_add(pdev); + if (twl_has_regulator() && usb_transceiver) { + static struct regulator_consumer_supply usb1v5 = { + .supply = "usb1v5", + }; + static struct regulator_consumer_supply usb1v8 = { + .supply = "usb1v8", + }; + static struct regulator_consumer_supply usb3v1 = { + .supply = "usb3v1", + }; + + /* this is a template that gets copied */ + struct regulator_init_data usb_fixed = { + .constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .constraints.valid_ops_mask = + REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }; + + usb1v5.dev = usb_transceiver; + usb1v8.dev = usb_transceiver; + usb3v1.dev = usb_transceiver; + + child = add_regulator_linked(TWL4030_REG_VUSB1V5, &usb_fixed, + &usb1v5, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator_linked(TWL4030_REG_VUSB1V8, &usb_fixed, + &usb1v8, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator_linked(TWL4030_REG_VUSB3V1, &usb_fixed, + &usb3v1, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + } - if (status < 0) { - platform_device_put(pdev); - dev_dbg(&twl->client->dev, - "can't create usb dev, %d\n", - status); - } + /* maybe add LDOs that are omitted on cost-reduced parts */ + if (twl_has_regulator() && !(features & TPS_SUBSET)) { + /* + child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2); + if (IS_ERR(child)) + return PTR_ERR(child); + */ + + child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL4030_REG_VSIM, pdata->vsim); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3); + if (IS_ERR(child)) + return PTR_ERR(child); + + child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4); + if (IS_ERR(child)) + return PTR_ERR(child); } -err: - if (status) - pr_err("failed to add twl4030's children (status %d)\n", status); - return status; + return 0; } /*----------------------------------------------------------------------*/ @@ -645,12 +658,7 @@ static void __init clocks_init(void) osc = clk_get(NULL, "osc_ck"); else osc = clk_get(NULL, "osc_sys_ck"); -#else - /* REVISIT for non-OMAP systems, pass the clock rate from - * board init code, using platform_data. - */ - osc = ERR_PTR(-EIO); -#endif + if (IS_ERR(osc)) { printk(KERN_WARNING "Skipping twl4030 internal clock init and " "using bootloader value (unknown osc rate)\n"); @@ -660,6 +668,18 @@ static void __init clocks_init(void) rate = clk_get_rate(osc); clk_put(osc); +#else + /* REVISIT for non-OMAP systems, pass the clock rate from + * board init code, using platform_data. + */ + osc = ERR_PTR(-EIO); + + printk(KERN_WARNING "Skipping twl4030 internal clock init and " + "using bootloader value (unknown osc rate)\n"); + + return; +#endif + switch (rate) { case 19200000: ctrl = HFCLK_FREQ_19p2_MHZ; @@ -764,7 +784,7 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id) goto fail; } - status = add_children(pdata); + status = add_children(pdata, id->driver_data); fail: if (status < 0) twl4030_remove(client); @@ -772,11 +792,11 @@ fail: } static const struct i2c_device_id twl4030_ids[] = { - { "twl4030", 0 }, /* "Triton 2" */ - { "tps65950", 0 }, /* catalog version of twl4030 */ - { "tps65930", 0 }, /* fewer LDOs and DACs; no charger */ - { "tps65920", 0 }, /* fewer LDOs; no codec or charger */ - { "twl5030", 0 }, /* T2 updated */ + { "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */ + { "twl5030", 0 }, /* T2 updated */ + { "tps65950", 0 }, /* catalog version of twl5030 */ + { "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */ + { "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */ { /* end of list */ }, }; MODULE_DEVICE_TABLE(i2c, twl4030_ids); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index fae868a8d499..b10876036983 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -180,10 +180,15 @@ static struct completion irq_event; static int twl4030_irq_thread(void *data) { long irq = (long)data; - irq_desc_t *desc = irq_desc + irq; + struct irq_desc *desc = irq_to_desc(irq); static unsigned i2c_errors; const static unsigned max_i2c_errors = 100; + if (!desc) { + pr_err("twl4030: Invalid IRQ: %ld\n", irq); + return -EINVAL; + } + current->flags |= PF_NOFREEZE; while (!kthread_should_stop()) { @@ -215,7 +220,13 @@ static int twl4030_irq_thread(void *data) pih_isr; pih_isr >>= 1, module_irq++) { if (pih_isr & 0x1) { - irq_desc_t *d = irq_desc + module_irq; + struct irq_desc *d = irq_to_desc(module_irq); + + if (!d) { + pr_err("twl4030: Invalid SIH IRQ: %d\n", + module_irq); + return -EINVAL; + } /* These can't be masked ... always warn * if we get any surprises. @@ -452,10 +463,16 @@ static void twl4030_sih_do_edge(struct work_struct *work) /* Modify only the bits we know must change */ while (edge_change) { int i = fls(edge_change) - 1; - struct irq_desc *d = irq_desc + i + agent->irq_base; + struct irq_desc *d = irq_to_desc(i + agent->irq_base); int byte = 1 + (i >> 2); int off = (i & 0x3) * 2; + if (!d) { + pr_err("twl4030: Invalid IRQ: %d\n", + i + agent->irq_base); + return; + } + bytes[byte] &= ~(0x03 << off); spin_lock_irq(&d->lock); @@ -512,9 +529,14 @@ static void twl4030_sih_unmask(unsigned irq) static int twl4030_sih_set_type(unsigned irq, unsigned trigger) { struct sih_agent *sih = get_irq_chip_data(irq); - struct irq_desc *desc = irq_desc + irq; + struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; + if (!desc) { + pr_err("twl4030: Invalid IRQ: %d\n", irq); + return -EINVAL; + } + if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 0d47fb9e4b3b..3a273ccef3f2 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -63,7 +63,6 @@ */ static DEFINE_MUTEX(io_mutex); static DEFINE_MUTEX(reg_lock_mutex); -static DEFINE_MUTEX(auxadc_mutex); /* Perform a physical read from the device. */ @@ -299,6 +298,13 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, } EXPORT_SYMBOL_GPL(wm8350_block_write); +/** + * wm8350_reg_lock() + * + * The WM8350 has a hardware lock which can be used to prevent writes to + * some registers (generally those which can cause particularly serious + * problems if misused). This function enables that lock. + */ int wm8350_reg_lock(struct wm8350 *wm8350) { u16 key = WM8350_LOCK_KEY; @@ -314,6 +320,15 @@ int wm8350_reg_lock(struct wm8350 *wm8350) } EXPORT_SYMBOL_GPL(wm8350_reg_lock); +/** + * wm8350_reg_unlock() + * + * The WM8350 has a hardware lock which can be used to prevent writes to + * some registers (generally those which can cause particularly serious + * problems if misused). This function disables that lock so updates + * can be performed. For maximum safety this should be done only when + * required. + */ int wm8350_reg_unlock(struct wm8350 *wm8350) { u16 key = WM8350_UNLOCK_KEY; @@ -1066,38 +1081,158 @@ int wm8350_unmask_irq(struct wm8350 *wm8350, int irq) } EXPORT_SYMBOL_GPL(wm8350_unmask_irq); +int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref) +{ + u16 reg, result = 0; + int tries = 5; + + if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP) + return -EINVAL; + if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP + && (scale != 0 || vref != 0)) + return -EINVAL; + + mutex_lock(&wm8350->auxadc_mutex); + + /* Turn on the ADC */ + reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA); + + if (scale || vref) { + reg = scale << 13; + reg |= vref << 12; + wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg); + } + + reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); + reg |= 1 << channel | WM8350_AUXADC_POLL; + wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg); + + do { + schedule_timeout_interruptible(1); + reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1); + } while (tries-- && (reg & WM8350_AUXADC_POLL)); + + if (!tries) + dev_err(wm8350->dev, "adc chn %d read timeout\n", channel); + else + result = wm8350_reg_read(wm8350, + WM8350_AUX1_READBACK + channel); + + /* Turn off the ADC */ + reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, + reg & ~WM8350_AUXADC_ENA); + + mutex_unlock(&wm8350->auxadc_mutex); + + return result & WM8350_AUXADC_DATA1_MASK; +} +EXPORT_SYMBOL_GPL(wm8350_read_auxadc); + /* * Cache is always host endian. */ -static int wm8350_create_cache(struct wm8350 *wm8350, int mode) +static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) { int i, ret = 0; u16 value; const u16 *reg_map; - switch (mode) { -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 + switch (type) { case 0: - reg_map = wm8350_mode0_defaults; - break; + switch (mode) { +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 + case 0: + reg_map = wm8350_mode0_defaults; + break; #endif #ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - case 1: - reg_map = wm8350_mode1_defaults; - break; + case 1: + reg_map = wm8350_mode1_defaults; + break; #endif #ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - case 2: - reg_map = wm8350_mode2_defaults; - break; + case 2: + reg_map = wm8350_mode2_defaults; + break; #endif #ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - case 3: - reg_map = wm8350_mode3_defaults; + case 3: + reg_map = wm8350_mode3_defaults; + break; +#endif + default: + dev_err(wm8350->dev, + "WM8350 configuration mode %d not supported\n", + mode); + return -EINVAL; + } + break; + + case 1: + switch (mode) { +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 + case 0: + reg_map = wm8351_mode0_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 + case 1: + reg_map = wm8351_mode1_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 + case 2: + reg_map = wm8351_mode2_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 + case 3: + reg_map = wm8351_mode3_defaults; + break; +#endif + default: + dev_err(wm8350->dev, + "WM8351 configuration mode %d not supported\n", + mode); + return -EINVAL; + } break; + + case 2: + switch (mode) { +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 + case 0: + reg_map = wm8352_mode0_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 + case 1: + reg_map = wm8352_mode1_defaults; + break; #endif +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 + case 2: + reg_map = wm8352_mode2_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 + case 3: + reg_map = wm8352_mode3_defaults; + break; +#endif + default: + dev_err(wm8350->dev, + "WM8352 configuration mode %d not supported\n", + mode); + return -EINVAL; + } + break; + default: - dev_err(wm8350->dev, "Configuration mode %d not supported\n", + dev_err(wm8350->dev, + "WM835x configuration mode %d not supported\n", mode); return -EINVAL; } @@ -1163,53 +1298,113 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { int ret = -EINVAL; - u16 id1, id2, mask, mode; + u16 id1, id2, mask_rev; + u16 cust_id, mode, chip_rev; /* get WM8350 revision and config mode */ wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), &mask_rev); id1 = be16_to_cpu(id1); id2 = be16_to_cpu(id2); + mask_rev = be16_to_cpu(mask_rev); - if (id1 == 0x6143) { - switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) { + if (id1 != 0x6143) { + dev_err(wm8350->dev, + "Device with ID %x is not a WM8350\n", id1); + ret = -ENODEV; + goto err; + } + + mode = id2 & WM8350_CONF_STS_MASK >> 10; + cust_id = id2 & WM8350_CUST_ID_MASK; + chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12; + dev_info(wm8350->dev, + "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d\n", + mode, cust_id, mask_rev, chip_rev); + + if (cust_id != 0) { + dev_err(wm8350->dev, "Unsupported CUST_ID\n"); + ret = -ENODEV; + goto err; + } + + switch (mask_rev) { + case 0: + wm8350->pmic.max_dcdc = WM8350_DCDC_6; + wm8350->pmic.max_isink = WM8350_ISINK_B; + + switch (chip_rev) { case WM8350_REV_E: - dev_info(wm8350->dev, "Found Rev E device\n"); - wm8350->rev = WM8350_REV_E; + dev_info(wm8350->dev, "WM8350 Rev E\n"); break; case WM8350_REV_F: - dev_info(wm8350->dev, "Found Rev F device\n"); - wm8350->rev = WM8350_REV_F; + dev_info(wm8350->dev, "WM8350 Rev F\n"); break; case WM8350_REV_G: - dev_info(wm8350->dev, "Found Rev G device\n"); - wm8350->rev = WM8350_REV_G; + dev_info(wm8350->dev, "WM8350 Rev G\n"); + wm8350->power.rev_g_coeff = 1; + break; + case WM8350_REV_H: + dev_info(wm8350->dev, "WM8350 Rev H\n"); + wm8350->power.rev_g_coeff = 1; break; default: /* For safety we refuse to run on unknown hardware */ - dev_info(wm8350->dev, "Found unknown rev\n"); + dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV\n"); ret = -ENODEV; goto err; } - } else { - dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n", - id1); + break; + + case 1: + wm8350->pmic.max_dcdc = WM8350_DCDC_4; + wm8350->pmic.max_isink = WM8350_ISINK_A; + + switch (chip_rev) { + case 0: + dev_info(wm8350->dev, "WM8351 Rev A\n"); + wm8350->power.rev_g_coeff = 1; + break; + + default: + dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV\n"); + ret = -ENODEV; + goto err; + } + break; + + case 2: + wm8350->pmic.max_dcdc = WM8350_DCDC_6; + wm8350->pmic.max_isink = WM8350_ISINK_B; + + switch (chip_rev) { + case 0: + dev_info(wm8350->dev, "WM8352 Rev A\n"); + wm8350->power.rev_g_coeff = 1; + break; + + default: + dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV\n"); + ret = -ENODEV; + goto err; + } + break; + + default: + dev_err(wm8350->dev, "Unknown MASK_REV\n"); ret = -ENODEV; goto err; } - mode = id2 & WM8350_CONF_STS_MASK >> 10; - mask = id2 & WM8350_CUST_ID_MASK; - dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask); - - ret = wm8350_create_cache(wm8350, mode); + ret = wm8350_create_cache(wm8350, mask_rev, mode); if (ret < 0) { - printk(KERN_ERR "wm8350: failed to create register cache\n"); + dev_err(wm8350->dev, "Failed to create register cache\n"); return ret; } - if (pdata->init) { + if (pdata && pdata->init) { ret = pdata->init(wm8350); if (ret != 0) { dev_err(wm8350->dev, "Platform init() failed: %d\n", @@ -1218,6 +1413,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, } } + mutex_init(&wm8350->auxadc_mutex); mutex_init(&wm8350->irq_mutex); INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); if (irq) { diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 3e0ce0e50ea2..8d8c93217572 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -1,8 +1,6 @@ /* * wm8350-i2c.c -- Generic I2C driver for Wolfson WM8350 PMIC * - * This driver defines and configures the WM8350 for the Freescale i.MX32ADS. - * * Copyright 2007, 2008 Wolfson Microelectronics PLC. * * Author: Liam Girdwood @@ -99,6 +97,8 @@ static int wm8350_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id wm8350_i2c_id[] = { { "wm8350", 0 }, + { "wm8351", 0 }, + { "wm8352", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id); diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index 974678db22cd..68887b817d17 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -1074,6 +1074,2102 @@ const u16 wm8350_mode3_defaults[] = { }; #endif +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8351_mode0_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0001, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0004, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x0000, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0FFC, /* R134 - GPIO Configuration (i/o) */ + 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0013, /* R140 - GPIO Function Select 1 */ + 0x0000, /* R141 - GPIO Function Select 2 */ + 0x0000, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 */ + 0x0000, /* R175 */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0000, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0000, /* R186 - DCDC3 Control */ + 0x0000, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0000, /* R189 - DCDC4 Control */ + 0x0000, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 */ + 0x0000, /* R193 */ + 0x0000, /* R194 */ + 0x0000, /* R195 */ + 0x0000, /* R196 */ + 0x0006, /* R197 */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001B, /* R203 - LDO2 Control */ + 0x0000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001B, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001B, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 - FLL Test 1 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x1000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8351_mode1_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0001, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0204, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x0000, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0CFB, /* R134 - GPIO Configuration (i/o) */ + 0x0C1F, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0300, /* R140 - GPIO Function Select 1 */ + 0x1110, /* R141 - GPIO Function Select 2 */ + 0x0013, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 */ + 0x0000, /* R175 */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0C00, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0026, /* R186 - DCDC3 Control */ + 0x0400, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0062, /* R189 - DCDC4 Control */ + 0x0800, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 */ + 0x0000, /* R193 */ + 0x0000, /* R194 */ + 0x000A, /* R195 */ + 0x1000, /* R196 */ + 0x0006, /* R197 */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x0006, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0010, /* R203 - LDO2 Control */ + 0x0C00, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001F, /* R206 - LDO3 Control */ + 0x0800, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x000A, /* R209 - LDO4 Control */ + 0x0800, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 - FLL Test 1 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x1000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x1000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8351_mode2_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0001, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0214, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x0110, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x09FA, /* R134 - GPIO Configuration (i/o) */ + 0x0DF6, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x1310, /* R140 - GPIO Function Select 1 */ + 0x0003, /* R141 - GPIO Function Select 2 */ + 0x2000, /* R142 - GPIO Function Select 3 */ + 0x0000, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 */ + 0x0000, /* R175 */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x001A, /* R180 - DCDC1 Control */ + 0x0800, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0056, /* R186 - DCDC3 Control */ + 0x0400, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0026, /* R189 - DCDC4 Control */ + 0x0C00, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 */ + 0x0000, /* R193 */ + 0x0000, /* R194 */ + 0x0026, /* R195 */ + 0x0C00, /* R196 */ + 0x0006, /* R197 */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0400, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0010, /* R203 - LDO2 Control */ + 0x0C00, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x0015, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001A, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 - FLL Test 1 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x1000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8351_mode3_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0001, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0204, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0010, /* R129 - GPIO Pin pull up Control */ + 0x0000, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0BFB, /* R134 - GPIO Configuration (i/o) */ + 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0310, /* R140 - GPIO Function Select 1 */ + 0x0001, /* R141 - GPIO Function Select 2 */ + 0x2300, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 */ + 0x0000, /* R175 */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0026, /* R186 - DCDC3 Control */ + 0x0800, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0062, /* R189 - DCDC4 Control */ + 0x1400, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 */ + 0x0000, /* R193 */ + 0x0000, /* R194 */ + 0x0026, /* R195 */ + 0x0400, /* R196 */ + 0x0006, /* R197 */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x0006, /* R200 - LDO1 Control */ + 0x0C00, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0016, /* R203 - LDO2 Control */ + 0x0000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x0019, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001A, /* R209 - LDO4 Control */ + 0x1000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 - FLL Test 1 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x1000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8352_mode0_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0002, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0004, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x0000, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0FFC, /* R134 - GPIO Configuration (i/o) */ + 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0013, /* R140 - GPIO Function Select 1 */ + 0x0000, /* R141 - GPIO Function Select 2 */ + 0x0000, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0000, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0000, /* R186 - DCDC3 Control */ + 0x0000, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0000, /* R189 - DCDC4 Control */ + 0x0000, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0000, /* R195 - DCDC6 Control */ + 0x0000, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001B, /* R203 - LDO2 Control */ + 0x0000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001B, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001B, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x5000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ + 0x5100, /* R252 */ + 0x1000, /* R253 - DCDC6 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8352_mode1_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0002, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0204, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x0000, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0BFB, /* R134 - GPIO Configuration (i/o) */ + 0x0FFF, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0300, /* R140 - GPIO Function Select 1 */ + 0x0000, /* R141 - GPIO Function Select 2 */ + 0x2300, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x0062, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0006, /* R186 - DCDC3 Control */ + 0x0800, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0006, /* R189 - DCDC4 Control */ + 0x0C00, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x1000, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x0002, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001A, /* R203 - LDO2 Control */ + 0x0000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001F, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001F, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x5000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ + 0x5100, /* R252 */ + 0x1000, /* R253 - DCDC6 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8352_mode2_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0002, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0204, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x0110, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x09DA, /* R134 - GPIO Configuration (i/o) */ + 0x0DD6, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x1310, /* R140 - GPIO Function Select 1 */ + 0x0033, /* R141 - GPIO Function Select 2 */ + 0x2000, /* R142 - GPIO Function Select 3 */ + 0x0000, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0800, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0056, /* R186 - DCDC3 Control */ + 0x1800, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x000E, /* R189 - DCDC4 Control */ + 0x1000, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0C00, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0006, /* R203 - LDO2 Control */ + 0x0400, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001C, /* R206 - LDO3 Control */ + 0x1400, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001A, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x5000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ + 0x5100, /* R252 */ + 0x1000, /* R253 - DCDC6 Test Controls */ +}; +#endif + +#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8352_mode3_defaults[] = { + 0x6143, /* R0 - Reset/ID */ + 0x0000, /* R1 - ID */ + 0x0002, /* R2 - Revision */ + 0x1C02, /* R3 - System Control 1 */ + 0x0204, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3A00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - OUT1L Volume */ + 0x00E4, /* R105 - OUT1R Volume */ + 0x00E4, /* R106 - OUT2L Volume */ + 0x02E4, /* R107 - OUT2R Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0010, /* R129 - GPIO Pin pull up Control */ + 0x0000, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0BFB, /* R134 - GPIO Configuration (i/o) */ + 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0310, /* R140 - GPIO Function Select 1 */ + 0x0001, /* R141 - GPIO Function Select 2 */ + 0x2300, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x032D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x0006, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0050, /* R186 - DCDC3 Control */ + 0x0C00, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x000E, /* R189 - DCDC4 Control */ + 0x0400, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0029, /* R195 - DCDC6 Control */ + 0x0800, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001D, /* R200 - LDO1 Control */ + 0x1000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0017, /* R203 - LDO2 Control */ + 0x1000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x0006, /* R206 - LDO3 Control */ + 0x1000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x0010, /* R209 - LDO4 Control */ + 0x1000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 - Security1 */ + 0x4000, /* R220 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 - Signal overrides */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 - Charger Overides/status */ + 0x0000, /* R227 - misc overrides */ + 0x0000, /* R228 - Supply overrides/status 1 */ + 0x0000, /* R229 - Supply overrides/status 2 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 - comparotor overrides */ + 0x0000, /* R232 */ + 0x0000, /* R233 - State Machine status */ + 0x1200, /* R234 */ + 0x0000, /* R235 */ + 0x8000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0003, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0004, /* R243 */ + 0x0300, /* R244 */ + 0x0000, /* R245 */ + 0x0200, /* R246 */ + 0x0000, /* R247 */ + 0x1000, /* R248 - DCDC1 Test Controls */ + 0x5000, /* R249 */ + 0x1000, /* R250 - DCDC3 Test Controls */ + 0x1000, /* R251 - DCDC4 Test Controls */ + 0x5100, /* R252 */ + 0x1000, /* R253 - DCDC6 Test Controls */ +}; +#endif + /* The register defaults for the config mode used must be compiled in but * due to the impact on kernel size it is possible to disable */ @@ -1307,14 +3403,14 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = { { 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */ { 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */ { 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */ - { 0xFFFF, 0xFFFF, 0xFFFF }, /* R219 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R219 - Security */ { 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */ { 0x0000, 0x0000, 0x0000 }, /* R221 */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */ { 0x0000, 0x0000, 0x0000 }, /* R224 */ { 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */ - { 0x0000, 0x0000, 0x0000 }, /* R226 */ + { 0x0000, 0x0000, 0xFFFF }, /* R226 - Charger status */ { 0x0000, 0x0000, 0xFFFF }, /* R227 */ { 0x0000, 0x0000, 0x0000 }, /* R228 */ { 0x0000, 0x0000, 0x0000 }, /* R229 */ diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 6a0cedb5bb8a..cf30d06a0104 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -15,6 +15,7 @@ #include <linux/bug.h> #include <linux/i2c.h> #include <linux/kernel.h> +#include <linux/mfd/core.h> #include <linux/mfd/wm8400-private.h> #include <linux/mfd/wm8400-audio.h> @@ -239,6 +240,16 @@ void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) } EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); +static int wm8400_register_codec(struct wm8400 *wm8400) +{ + struct mfd_cell cell = { + .name = "wm8400-codec", + .driver_data = wm8400, + }; + + return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0); +} + /* * wm8400_init - Generic initialisation * @@ -296,24 +307,32 @@ static int wm8400_init(struct wm8400 *wm8400, reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT; dev_info(wm8400->dev, "WM8400 revision %x\n", reg); + ret = wm8400_register_codec(wm8400); + if (ret != 0) { + dev_err(wm8400->dev, "Failed to register codec\n"); + goto err_children; + } + if (pdata && pdata->platform_init) { ret = pdata->platform_init(wm8400->dev); - if (ret != 0) + if (ret != 0) { dev_err(wm8400->dev, "Platform init failed: %d\n", ret); + goto err_children; + } } else dev_warn(wm8400->dev, "No platform initialisation supplied\n"); + return 0; + +err_children: + mfd_remove_devices(wm8400->dev); return ret; } static void wm8400_release(struct wm8400 *wm8400) { - int i; - - for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++) - if (wm8400->regulators[i].name) - platform_device_unregister(&wm8400->regulators[i]); + mfd_remove_devices(wm8400->dev); } #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c index 22a7e8ba211d..de966a6fb7e6 100644 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ b/drivers/misc/ibmasm/ibmasmfs.c @@ -146,8 +146,6 @@ static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode) if (ret) { ret->i_mode = mode; - ret->i_uid = ret->i_gid = 0; - ret->i_blocks = 0; ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; } return ret; diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 517fce48d94a..5b396ff6c83f 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -66,6 +66,7 @@ #include <linux/mm.h> #include <linux/highmem.h> #include <linux/sockios.h> +#include <linux/firmware.h> #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #include <linux/if_vlan.h> @@ -186,8 +187,6 @@ MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); #define MAX_RODATA_LEN 8*1024 #define MAX_DATA_LEN 2*1024 -#include "acenic_firmware.h" - #ifndef tigon2FwReleaseLocal #define tigon2FwReleaseLocal 0 #endif @@ -417,6 +416,10 @@ static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; MODULE_AUTHOR("Jes Sorensen <jes@trained-monkey.org>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver"); +#ifndef CONFIG_ACENIC_OMIT_TIGON_I +MODULE_FIRMWARE("acenic/tg1.bin"); +#endif +MODULE_FIRMWARE("acenic/tg2.bin"); module_param_array_named(link, link_state, int, NULL, 0); module_param_array(trace, int, NULL, 0); @@ -943,8 +946,8 @@ static int __devinit ace_init(struct net_device *dev) case 4: case 5: printk(KERN_INFO " Tigon I (Rev. %i), Firmware: %i.%i.%i, ", - tig_ver, tigonFwReleaseMajor, tigonFwReleaseMinor, - tigonFwReleaseFix); + tig_ver, ap->firmware_major, ap->firmware_minor, + ap->firmware_fix); writel(0, ®s->LocalCtrl); ap->version = 1; ap->tx_ring_entries = TIGON_I_TX_RING_ENTRIES; @@ -952,8 +955,8 @@ static int __devinit ace_init(struct net_device *dev) #endif case 6: printk(KERN_INFO " Tigon II (Rev. %i), Firmware: %i.%i.%i, ", - tig_ver, tigon2FwReleaseMajor, tigon2FwReleaseMinor, - tigon2FwReleaseFix); + tig_ver, ap->firmware_major, ap->firmware_minor, + ap->firmware_fix); writel(readl(®s->CpuBCtrl) | CPU_HALT, ®s->CpuBCtrl); readl(®s->CpuBCtrl); /* PCI write posting */ /* @@ -1205,7 +1208,9 @@ static int __devinit ace_init(struct net_device *dev) memset(ap->info, 0, sizeof(struct ace_info)); memset(ap->skb, 0, sizeof(struct ace_skb)); - ace_load_firmware(dev); + if (ace_load_firmware(dev)) + goto init_error; + ap->fw_running = 0; tmp_ptr = ap->info_dma; @@ -1441,10 +1446,7 @@ static int __devinit ace_init(struct net_device *dev) if (ap->version >= 2) writel(tmp, ®s->TuneFastLink); - if (ACE_IS_TIGON_I(ap)) - writel(tigonFwStartAddr, ®s->Pc); - if (ap->version == 2) - writel(tigon2FwStartAddr, ®s->Pc); + writel(ap->firmware_start, ®s->Pc); writel(0, ®s->Mb0Lo); @@ -2761,8 +2763,8 @@ static void ace_get_drvinfo(struct net_device *dev, strlcpy(info->driver, "acenic", sizeof(info->driver)); snprintf(info->version, sizeof(info->version), "%i.%i.%i", - tigonFwReleaseMajor, tigonFwReleaseMinor, - tigonFwReleaseFix); + ap->firmware_major, ap->firmware_minor, + ap->firmware_fix); if (ap->pdev) strlcpy(info->bus_info, pci_name(ap->pdev), @@ -2869,11 +2871,10 @@ static struct net_device_stats *ace_get_stats(struct net_device *dev) } -static void __devinit ace_copy(struct ace_regs __iomem *regs, void *src, - u32 dest, int size) +static void __devinit ace_copy(struct ace_regs __iomem *regs, const __be32 *src, + u32 dest, int size) { void __iomem *tdest; - u32 *wsrc; short tsize, i; if (size <= 0) @@ -2885,20 +2886,15 @@ static void __devinit ace_copy(struct ace_regs __iomem *regs, void *src, tdest = (void __iomem *) ®s->Window + (dest & (ACE_WINDOW_SIZE - 1)); writel(dest & ~(ACE_WINDOW_SIZE - 1), ®s->WinBase); - /* - * This requires byte swapping on big endian, however - * writel does that for us - */ - wsrc = src; for (i = 0; i < (tsize / 4); i++) { - writel(wsrc[i], tdest + i*4); + /* Firmware is big-endian */ + writel(be32_to_cpup(src), tdest); + src++; + tdest += 4; + dest += 4; + size -= 4; } - dest += tsize; - src += tsize; - size -= tsize; } - - return; } @@ -2937,8 +2933,13 @@ static void __devinit ace_clear(struct ace_regs __iomem *regs, u32 dest, int siz */ static int __devinit ace_load_firmware(struct net_device *dev) { + const struct firmware *fw; + const char *fw_name = "acenic/tg2.bin"; struct ace_private *ap = netdev_priv(dev); struct ace_regs __iomem *regs = ap->regs; + const __be32 *fw_data; + u32 load_addr; + int ret; if (!(readl(®s->CpuCtrl) & CPU_HALTED)) { printk(KERN_ERR "%s: trying to download firmware while the " @@ -2946,28 +2947,52 @@ static int __devinit ace_load_firmware(struct net_device *dev) return -EFAULT; } + if (ACE_IS_TIGON_I(ap)) + fw_name = "acenic/tg1.bin"; + + ret = request_firmware(&fw, fw_name, &ap->pdev->dev); + if (ret) { + printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", + ap->name, fw_name); + return ret; + } + + fw_data = (void *)fw->data; + + /* Firmware blob starts with version numbers, followed by + load and start address. Remainder is the blob to be loaded + contiguously from load address. We don't bother to represent + the BSS/SBSS sections any more, since we were clearing the + whole thing anyway. */ + ap->firmware_major = fw->data[0]; + ap->firmware_minor = fw->data[1]; + ap->firmware_fix = fw->data[2]; + + ap->firmware_start = be32_to_cpu(fw_data[1]); + if (ap->firmware_start < 0x4000 || ap->firmware_start >= 0x80000) { + printk(KERN_ERR "%s: bogus load address %08x in \"%s\"\n", + ap->name, ap->firmware_start, fw_name); + ret = -EINVAL; + goto out; + } + + load_addr = be32_to_cpu(fw_data[2]); + if (load_addr < 0x4000 || load_addr >= 0x80000) { + printk(KERN_ERR "%s: bogus load address %08x in \"%s\"\n", + ap->name, load_addr, fw_name); + ret = -EINVAL; + goto out; + } + /* - * Do not try to clear more than 512KB or we end up seeing - * funny things on NICs with only 512KB SRAM + * Do not try to clear more than 512KiB or we end up seeing + * funny things on NICs with only 512KiB SRAM */ ace_clear(regs, 0x2000, 0x80000-0x2000); - if (ACE_IS_TIGON_I(ap)) { - ace_copy(regs, tigonFwText, tigonFwTextAddr, tigonFwTextLen); - ace_copy(regs, tigonFwData, tigonFwDataAddr, tigonFwDataLen); - ace_copy(regs, tigonFwRodata, tigonFwRodataAddr, - tigonFwRodataLen); - ace_clear(regs, tigonFwBssAddr, tigonFwBssLen); - ace_clear(regs, tigonFwSbssAddr, tigonFwSbssLen); - }else if (ap->version == 2) { - ace_clear(regs, tigon2FwBssAddr, tigon2FwBssLen); - ace_clear(regs, tigon2FwSbssAddr, tigon2FwSbssLen); - ace_copy(regs, tigon2FwText, tigon2FwTextAddr,tigon2FwTextLen); - ace_copy(regs, tigon2FwRodata, tigon2FwRodataAddr, - tigon2FwRodataLen); - ace_copy(regs, tigon2FwData, tigon2FwDataAddr,tigon2FwDataLen); - } - - return 0; + ace_copy(regs, &fw_data[3], load_addr, fw->size-12); + out: + release_firmware(fw); + return ret; } diff --git a/drivers/net/acenic.h b/drivers/net/acenic.h index 4487f32759a4..c987c9b5a137 100644 --- a/drivers/net/acenic.h +++ b/drivers/net/acenic.h @@ -694,6 +694,10 @@ struct ace_private u32 last_tx, last_std_rx, last_mini_rx; #endif int pci_using_dac; + u8 firmware_major; + u8 firmware_minor; + u8 firmware_fix; + u32 firmware_start; }; diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 9f38b16ccbbd..134b2d60b479 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -658,12 +658,12 @@ static int e100_self_test(struct nic *nic) e100_disable_irq(nic); /* Check results of self-test */ - if(nic->mem->selftest.result != 0) { + if (nic->mem->selftest.result != 0) { DPRINTK(HW, ERR, "Self-test failed: result=0x%08X\n", nic->mem->selftest.result); return -ETIMEDOUT; } - if(nic->mem->selftest.signature == 0) { + if (nic->mem->selftest.signature == 0) { DPRINTK(HW, ERR, "Self-test failed: timed out\n"); return -ETIMEDOUT; } @@ -684,13 +684,13 @@ static void e100_eeprom_write(struct nic *nic, u16 addr_len, u16 addr, __le16 da cmd_addr_data[2] = op_ewds << (addr_len - 2); /* Bit-bang cmds to write word to eeprom */ - for(j = 0; j < 3; j++) { + for (j = 0; j < 3; j++) { /* Chip select */ iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); - for(i = 31; i >= 0; i--) { + for (i = 31; i >= 0; i--) { ctrl = (cmd_addr_data[j] & (1 << i)) ? eecs | eedi : eecs; iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); @@ -723,7 +723,7 @@ static __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr) e100_write_flush(nic); udelay(4); /* Bit-bang to read word from eeprom */ - for(i = 31; i >= 0; i--) { + for (i = 31; i >= 0; i--) { ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs; iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); @@ -734,7 +734,7 @@ static __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr) /* Eeprom drives a dummy zero to EEDO after receiving * complete address. Use this to adjust addr_len. */ ctrl = ioread8(&nic->csr->eeprom_ctrl_lo); - if(!(ctrl & eedo) && i > 16) { + if (!(ctrl & eedo) && i > 16) { *addr_len -= (i - 16); i = 17; } @@ -758,9 +758,9 @@ static int e100_eeprom_load(struct nic *nic) e100_eeprom_read(nic, &addr_len, 0); nic->eeprom_wc = 1 << addr_len; - for(addr = 0; addr < nic->eeprom_wc; addr++) { + for (addr = 0; addr < nic->eeprom_wc; addr++) { nic->eeprom[addr] = e100_eeprom_read(nic, &addr_len, addr); - if(addr < nic->eeprom_wc - 1) + if (addr < nic->eeprom_wc - 1) checksum += le16_to_cpu(nic->eeprom[addr]); } @@ -784,15 +784,15 @@ static int e100_eeprom_save(struct nic *nic, u16 start, u16 count) e100_eeprom_read(nic, &addr_len, 0); nic->eeprom_wc = 1 << addr_len; - if(start + count >= nic->eeprom_wc) + if (start + count >= nic->eeprom_wc) return -EINVAL; - for(addr = start; addr < start + count; addr++) + for (addr = start; addr < start + count; addr++) e100_eeprom_write(nic, addr_len, addr, nic->eeprom[addr]); /* The checksum, stored in the last word, is calculated such that * the sum of words should be 0xBABA */ - for(addr = 0; addr < nic->eeprom_wc - 1; addr++) + for (addr = 0; addr < nic->eeprom_wc - 1; addr++) checksum += le16_to_cpu(nic->eeprom[addr]); nic->eeprom[nic->eeprom_wc - 1] = cpu_to_le16(0xBABA - checksum); e100_eeprom_write(nic, addr_len, nic->eeprom_wc - 1, @@ -812,19 +812,19 @@ static int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr) spin_lock_irqsave(&nic->cmd_lock, flags); /* Previous command is accepted when SCB clears */ - for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) { - if(likely(!ioread8(&nic->csr->scb.cmd_lo))) + for (i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) { + if (likely(!ioread8(&nic->csr->scb.cmd_lo))) break; cpu_relax(); - if(unlikely(i > E100_WAIT_SCB_FAST)) + if (unlikely(i > E100_WAIT_SCB_FAST)) udelay(5); } - if(unlikely(i == E100_WAIT_SCB_TIMEOUT)) { + if (unlikely(i == E100_WAIT_SCB_TIMEOUT)) { err = -EAGAIN; goto err_unlock; } - if(unlikely(cmd != cuc_resume)) + if (unlikely(cmd != cuc_resume)) iowrite32(dma_addr, &nic->csr->scb.gen_ptr); iowrite8(cmd, &nic->csr->scb.cmd_lo); @@ -843,7 +843,7 @@ static int e100_exec_cb(struct nic *nic, struct sk_buff *skb, spin_lock_irqsave(&nic->cb_lock, flags); - if(unlikely(!nic->cbs_avail)) { + if (unlikely(!nic->cbs_avail)) { err = -ENOMEM; goto err_unlock; } @@ -853,7 +853,7 @@ static int e100_exec_cb(struct nic *nic, struct sk_buff *skb, nic->cbs_avail--; cb->skb = skb; - if(unlikely(!nic->cbs_avail)) + if (unlikely(!nic->cbs_avail)) err = -ENOSPC; cb_prepare(nic, cb, skb); @@ -864,15 +864,15 @@ static int e100_exec_cb(struct nic *nic, struct sk_buff *skb, wmb(); cb->prev->command &= cpu_to_le16(~cb_s); - while(nic->cb_to_send != nic->cb_to_use) { - if(unlikely(e100_exec_cmd(nic, nic->cuc_cmd, + while (nic->cb_to_send != nic->cb_to_use) { + if (unlikely(e100_exec_cmd(nic, nic->cuc_cmd, nic->cb_to_send->dma_addr))) { /* Ok, here's where things get sticky. It's * possible that we can't schedule the command * because the controller is too busy, so * let's just queue the command and try again * when another command is scheduled. */ - if(err == -ENOSPC) { + if (err == -ENOSPC) { //request a reset schedule_work(&nic->tx_timeout_task); } @@ -945,7 +945,7 @@ static void e100_get_defaults(struct nic *nic) /* MAC type is encoded as rev ID; exception: ICH is treated as 82559 */ nic->mac = (nic->flags & ich) ? mac_82559_D101M : nic->pdev->revision; - if(nic->mac == mac_unknown) + if (nic->mac == mac_unknown) nic->mac = mac_82557_D100_A; nic->params.rfds = rfds; @@ -1008,23 +1008,23 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb) config->adaptive_ifs = nic->adaptive_ifs; config->loopback = nic->loopback; - if(nic->mii.force_media && nic->mii.full_duplex) + if (nic->mii.force_media && nic->mii.full_duplex) config->full_duplex_force = 0x1; /* 1=force, 0=auto */ - if(nic->flags & promiscuous || nic->loopback) { + if (nic->flags & promiscuous || nic->loopback) { config->rx_save_bad_frames = 0x1; /* 1=save, 0=discard */ config->rx_discard_short_frames = 0x0; /* 1=discard, 0=save */ config->promiscuous_mode = 0x1; /* 1=on, 0=off */ } - if(nic->flags & multicast_all) + if (nic->flags & multicast_all) config->multicast_all = 0x1; /* 1=accept, 0=no */ /* disable WoL when up */ - if(netif_running(nic->netdev) || !(nic->flags & wol_magic)) + if (netif_running(nic->netdev) || !(nic->flags & wol_magic)) config->magic_packet_disable = 0x1; /* 1=off, 0=on */ - if(nic->mac >= mac_82558_D101_A4) { + if (nic->mac >= mac_82558_D101_A4) { config->fc_disable = 0x1; /* 1=Tx fc off, 0=Tx fc on */ config->mwi_enable = 0x1; /* 1=enable, 0=disable */ config->standard_tcb = 0x0; /* 1=standard, 0=extended */ @@ -1369,21 +1369,21 @@ static int e100_phy_init(struct nic *nic) u16 bmcr, stat, id_lo, id_hi, cong; /* Discover phy addr by searching addrs in order {1,0,2,..., 31} */ - for(addr = 0; addr < 32; addr++) { + for (addr = 0; addr < 32; addr++) { nic->mii.phy_id = (addr == 0) ? 1 : (addr == 1) ? 0 : addr; bmcr = mdio_read(netdev, nic->mii.phy_id, MII_BMCR); stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR); stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR); - if(!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) + if (!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) break; } DPRINTK(HW, DEBUG, "phy_addr = %d\n", nic->mii.phy_id); - if(addr == 32) + if (addr == 32) return -EAGAIN; /* Selected the phy and isolate the rest */ - for(addr = 0; addr < 32; addr++) { - if(addr != nic->mii.phy_id) { + for (addr = 0; addr < 32; addr++) { + if (addr != nic->mii.phy_id) { mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE); } else { bmcr = mdio_read(netdev, addr, MII_BMCR); @@ -1400,7 +1400,7 @@ static int e100_phy_init(struct nic *nic) /* Handle National tx phys */ #define NCS_PHY_MODEL_MASK 0xFFF0FFFF - if((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) { + if ((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) { /* Disable congestion control */ cong = mdio_read(netdev, nic->mii.phy_id, MII_NSC_CONG); cong |= NSC_CONG_TXREADY; @@ -1408,7 +1408,7 @@ static int e100_phy_init(struct nic *nic) mdio_write(netdev, nic->mii.phy_id, MII_NSC_CONG, cong); } - if((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && + if ((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000) && !(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled))) { /* enable/disable MDI/MDI-X auto-switching. */ @@ -1426,25 +1426,25 @@ static int e100_hw_init(struct nic *nic) e100_hw_reset(nic); DPRINTK(HW, ERR, "e100_hw_init\n"); - if(!in_interrupt() && (err = e100_self_test(nic))) + if (!in_interrupt() && (err = e100_self_test(nic))) return err; - if((err = e100_phy_init(nic))) + if ((err = e100_phy_init(nic))) return err; - if((err = e100_exec_cmd(nic, cuc_load_base, 0))) + if ((err = e100_exec_cmd(nic, cuc_load_base, 0))) return err; - if((err = e100_exec_cmd(nic, ruc_load_base, 0))) + if ((err = e100_exec_cmd(nic, ruc_load_base, 0))) return err; if ((err = e100_exec_cb_wait(nic, NULL, e100_setup_ucode))) return err; - if((err = e100_exec_cb(nic, NULL, e100_configure))) + if ((err = e100_exec_cb(nic, NULL, e100_configure))) return err; - if((err = e100_exec_cb(nic, NULL, e100_setup_iaaddr))) + if ((err = e100_exec_cb(nic, NULL, e100_setup_iaaddr))) return err; - if((err = e100_exec_cmd(nic, cuc_dump_addr, + if ((err = e100_exec_cmd(nic, cuc_dump_addr, nic->dma_addr + offsetof(struct mem, stats)))) return err; - if((err = e100_exec_cmd(nic, cuc_dump_reset, 0))) + if ((err = e100_exec_cmd(nic, cuc_dump_reset, 0))) return err; e100_disable_irq(nic); @@ -1460,7 +1460,7 @@ static void e100_multi(struct nic *nic, struct cb *cb, struct sk_buff *skb) cb->command = cpu_to_le16(cb_multi); cb->u.multi.count = cpu_to_le16(count * ETH_ALEN); - for(i = 0; list && i < count; i++, list = list->next) + for (i = 0; list && i < count; i++, list = list->next) memcpy(&cb->u.multi.addr[i*ETH_ALEN], &list->dmi_addr, ETH_ALEN); } @@ -1472,12 +1472,12 @@ static void e100_set_multicast_list(struct net_device *netdev) DPRINTK(HW, DEBUG, "mc_count=%d, flags=0x%04X\n", netdev->mc_count, netdev->flags); - if(netdev->flags & IFF_PROMISC) + if (netdev->flags & IFF_PROMISC) nic->flags |= promiscuous; else nic->flags &= ~promiscuous; - if(netdev->flags & IFF_ALLMULTI || + if (netdev->flags & IFF_ALLMULTI || netdev->mc_count > E100_MAX_MULTICAST_ADDRS) nic->flags |= multicast_all; else @@ -1500,7 +1500,7 @@ static void e100_update_stats(struct nic *nic) * complete, so we're always waiting for results of the * previous command. */ - if(*complete == cpu_to_le32(cuc_dump_reset_complete)) { + if (*complete == cpu_to_le32(cuc_dump_reset_complete)) { *complete = 0; nic->tx_frames = le32_to_cpu(s->tx_good_frames); nic->tx_collisions = le32_to_cpu(s->tx_total_collisions); @@ -1527,12 +1527,12 @@ static void e100_update_stats(struct nic *nic) le32_to_cpu(s->tx_single_collisions); nic->tx_multiple_collisions += le32_to_cpu(s->tx_multiple_collisions); - if(nic->mac >= mac_82558_D101_A4) { + if (nic->mac >= mac_82558_D101_A4) { nic->tx_fc_pause += le32_to_cpu(s->fc_xmt_pause); nic->rx_fc_pause += le32_to_cpu(s->fc_rcv_pause); nic->rx_fc_unsupported += le32_to_cpu(s->fc_rcv_unsupported); - if(nic->mac >= mac_82559_D101M) { + if (nic->mac >= mac_82559_D101M) { nic->tx_tco_frames += le16_to_cpu(s->xmt_tco_frames); nic->rx_tco_frames += @@ -1542,7 +1542,7 @@ static void e100_update_stats(struct nic *nic) } - if(e100_exec_cmd(nic, cuc_dump_reset, 0)) + if (e100_exec_cmd(nic, cuc_dump_reset, 0)) DPRINTK(TX_ERR, DEBUG, "exec cuc_dump_reset failed\n"); } @@ -1551,19 +1551,19 @@ static void e100_adjust_adaptive_ifs(struct nic *nic, int speed, int duplex) /* Adjust inter-frame-spacing (IFS) between two transmits if * we're getting collisions on a half-duplex connection. */ - if(duplex == DUPLEX_HALF) { + if (duplex == DUPLEX_HALF) { u32 prev = nic->adaptive_ifs; u32 min_frames = (speed == SPEED_100) ? 1000 : 100; - if((nic->tx_frames / 32 < nic->tx_collisions) && + if ((nic->tx_frames / 32 < nic->tx_collisions) && (nic->tx_frames > min_frames)) { - if(nic->adaptive_ifs < 60) + if (nic->adaptive_ifs < 60) nic->adaptive_ifs += 5; } else if (nic->tx_frames < min_frames) { - if(nic->adaptive_ifs >= 5) + if (nic->adaptive_ifs >= 5) nic->adaptive_ifs -= 5; } - if(nic->adaptive_ifs != prev) + if (nic->adaptive_ifs != prev) e100_exec_cb(nic, NULL, e100_configure); } } @@ -1579,12 +1579,12 @@ static void e100_watchdog(unsigned long data) mii_ethtool_gset(&nic->mii, &cmd); - if(mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->netdev)) { + if (mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->netdev)) { printk(KERN_INFO "e100: %s NIC Link is Up %s Mbps %s Duplex\n", nic->netdev->name, cmd.speed == SPEED_100 ? "100" : "10", cmd.duplex == DUPLEX_FULL ? "Full" : "Half"); - } else if(!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->netdev)) { + } else if (!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->netdev)) { printk(KERN_INFO "e100: %s NIC Link is Down\n", nic->netdev->name); } @@ -1604,11 +1604,11 @@ static void e100_watchdog(unsigned long data) e100_update_stats(nic); e100_adjust_adaptive_ifs(nic, cmd.speed, cmd.duplex); - if(nic->mac <= mac_82557_D100_C) + if (nic->mac <= mac_82557_D100_C) /* Issue a multicast command to workaround a 557 lock up */ e100_set_multicast_list(nic->netdev); - if(nic->flags & ich && cmd.speed==SPEED_10 && cmd.duplex==DUPLEX_HALF) + if (nic->flags & ich && cmd.speed==SPEED_10 && cmd.duplex==DUPLEX_HALF) /* Need SW workaround for ICH[x] 10Mbps/half duplex Tx hang. */ nic->flags |= ich_10h_workaround; else @@ -1623,7 +1623,7 @@ static void e100_xmit_prepare(struct nic *nic, struct cb *cb, { cb->command = nic->tx_command; /* interrupt every 16 packets regardless of delay */ - if((nic->cbs_avail & ~15) == nic->cbs_avail) + if ((nic->cbs_avail & ~15) == nic->cbs_avail) cb->command |= cpu_to_le16(cb_i); cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd); cb->u.tcb.tcb_byte_count = 0; @@ -1640,18 +1640,18 @@ static int e100_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct nic *nic = netdev_priv(netdev); int err; - if(nic->flags & ich_10h_workaround) { + if (nic->flags & ich_10h_workaround) { /* SW workaround for ICH[x] 10Mbps/half duplex Tx hang. Issue a NOP command followed by a 1us delay before issuing the Tx command. */ - if(e100_exec_cmd(nic, cuc_nop, 0)) + if (e100_exec_cmd(nic, cuc_nop, 0)) DPRINTK(TX_ERR, DEBUG, "exec cuc_nop failed\n"); udelay(1); } err = e100_exec_cb(nic, skb, e100_xmit_prepare); - switch(err) { + switch (err) { case -ENOSPC: /* We queued the skb, but now we're out of space. */ DPRINTK(TX_ERR, DEBUG, "No space for CB\n"); @@ -1677,14 +1677,14 @@ static int e100_tx_clean(struct nic *nic) spin_lock(&nic->cb_lock); /* Clean CBs marked complete */ - for(cb = nic->cb_to_clean; + for (cb = nic->cb_to_clean; cb->status & cpu_to_le16(cb_complete); cb = nic->cb_to_clean = cb->next) { DPRINTK(TX_DONE, DEBUG, "cb[%d]->status = 0x%04X\n", (int)(((void*)cb - (void*)nic->cbs)/sizeof(struct cb)), cb->status); - if(likely(cb->skb != NULL)) { + if (likely(cb->skb != NULL)) { dev->stats.tx_packets++; dev->stats.tx_bytes += cb->skb->len; @@ -1703,7 +1703,7 @@ static int e100_tx_clean(struct nic *nic) spin_unlock(&nic->cb_lock); /* Recover from running out of Tx resources in xmit_frame */ - if(unlikely(tx_cleaned && netif_queue_stopped(nic->netdev))) + if (unlikely(tx_cleaned && netif_queue_stopped(nic->netdev))) netif_wake_queue(nic->netdev); return tx_cleaned; @@ -1711,10 +1711,10 @@ static int e100_tx_clean(struct nic *nic) static void e100_clean_cbs(struct nic *nic) { - if(nic->cbs) { - while(nic->cbs_avail != nic->params.cbs.count) { + if (nic->cbs) { + while (nic->cbs_avail != nic->params.cbs.count) { struct cb *cb = nic->cb_to_clean; - if(cb->skb) { + if (cb->skb) { pci_unmap_single(nic->pdev, le32_to_cpu(cb->u.tcb.tbd.buf_addr), le16_to_cpu(cb->u.tcb.tbd.size), @@ -1746,10 +1746,10 @@ static int e100_alloc_cbs(struct nic *nic) nic->cbs = pci_alloc_consistent(nic->pdev, sizeof(struct cb) * count, &nic->cbs_dma_addr); - if(!nic->cbs) + if (!nic->cbs) return -ENOMEM; - for(cb = nic->cbs, i = 0; i < count; cb++, i++) { + for (cb = nic->cbs, i = 0; i < count; cb++, i++) { cb->next = (i + 1 < count) ? cb + 1 : nic->cbs; cb->prev = (i == 0) ? nic->cbs + count - 1 : cb - 1; @@ -1767,14 +1767,14 @@ static int e100_alloc_cbs(struct nic *nic) static inline void e100_start_receiver(struct nic *nic, struct rx *rx) { - if(!nic->rxs) return; - if(RU_SUSPENDED != nic->ru_running) return; + if (!nic->rxs) return; + if (RU_SUSPENDED != nic->ru_running) return; /* handle init time starts */ - if(!rx) rx = nic->rxs; + if (!rx) rx = nic->rxs; /* (Re)start RU if suspended or idle and RFA is non-NULL */ - if(rx->skb) { + if (rx->skb) { e100_exec_cmd(nic, ruc_start, rx->dma_addr); nic->ru_running = RU_RUNNING; } @@ -1783,7 +1783,7 @@ static inline void e100_start_receiver(struct nic *nic, struct rx *rx) #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN) static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) { - if(!(rx->skb = netdev_alloc_skb(nic->netdev, RFD_BUF_LEN + NET_IP_ALIGN))) + if (!(rx->skb = netdev_alloc_skb(nic->netdev, RFD_BUF_LEN + NET_IP_ALIGN))) return -ENOMEM; /* Align, init, and map the RFD. */ @@ -1820,7 +1820,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, struct rfd *rfd = (struct rfd *)skb->data; u16 rfd_status, actual_size; - if(unlikely(work_done && *work_done >= work_to_do)) + if (unlikely(work_done && *work_done >= work_to_do)) return -EAGAIN; /* Need to sync before taking a peek at cb_complete bit */ @@ -1847,7 +1847,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, /* Get actual data size */ actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF; - if(unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd))) + if (unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd))) actual_size = RFD_BUF_LEN - sizeof(struct rfd); /* Get data */ @@ -1872,10 +1872,10 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, skb_put(skb, actual_size); skb->protocol = eth_type_trans(skb, nic->netdev); - if(unlikely(!(rfd_status & cb_ok))) { + if (unlikely(!(rfd_status & cb_ok))) { /* Don't indicate if hardware indicates errors */ dev_kfree_skb_any(skb); - } else if(actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) { + } else if (actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) { /* Don't indicate oversized frames */ nic->rx_over_length_errors++; dev_kfree_skb_any(skb); @@ -1883,7 +1883,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, dev->stats.rx_packets++; dev->stats.rx_bytes += actual_size; netif_receive_skb(skb); - if(work_done) + if (work_done) (*work_done)++; } @@ -1901,7 +1901,7 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done, struct rfd *old_before_last_rfd, *new_before_last_rfd; /* Indicate newly arrived packets */ - for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { + for (rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { err = e100_rx_indicate(nic, rx, work_done, work_to_do); /* Hit quota or no more to clean */ if (-EAGAIN == err || -ENODATA == err) @@ -1922,8 +1922,8 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done, old_before_last_rfd = (struct rfd *)old_before_last_rx->skb->data; /* Alloc new skbs to refill list */ - for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { - if(unlikely(e100_rx_alloc_skb(nic, rx))) + for (rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { + if (unlikely(e100_rx_alloc_skb(nic, rx))) break; /* Better luck next time (see watchdog) */ } @@ -1959,11 +1959,11 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done, PCI_DMA_BIDIRECTIONAL); } - if(restart_required) { + if (restart_required) { // ack the rnr? iowrite8(stat_ack_rnr, &nic->csr->scb.stat_ack); e100_start_receiver(nic, nic->rx_to_clean); - if(work_done) + if (work_done) (*work_done)++; } } @@ -1975,9 +1975,9 @@ static void e100_rx_clean_list(struct nic *nic) nic->ru_running = RU_UNINITIALIZED; - if(nic->rxs) { - for(rx = nic->rxs, i = 0; i < count; rx++, i++) { - if(rx->skb) { + if (nic->rxs) { + for (rx = nic->rxs, i = 0; i < count; rx++, i++) { + if (rx->skb) { pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); dev_kfree_skb(rx->skb); @@ -1999,13 +1999,13 @@ static int e100_rx_alloc_list(struct nic *nic) nic->rx_to_use = nic->rx_to_clean = NULL; nic->ru_running = RU_UNINITIALIZED; - if(!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC))) + if (!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC))) return -ENOMEM; - for(rx = nic->rxs, i = 0; i < count; rx++, i++) { + for (rx = nic->rxs, i = 0; i < count; rx++, i++) { rx->next = (i + 1 < count) ? rx + 1 : nic->rxs; rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1; - if(e100_rx_alloc_skb(nic, rx)) { + if (e100_rx_alloc_skb(nic, rx)) { e100_rx_clean_list(nic); return -ENOMEM; } @@ -2038,7 +2038,7 @@ static irqreturn_t e100_intr(int irq, void *dev_id) DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack); - if(stat_ack == stat_ack_not_ours || /* Not our interrupt */ + if (stat_ack == stat_ack_not_ours || /* Not our interrupt */ stat_ack == stat_ack_not_present) /* Hardware is ejected */ return IRQ_NONE; @@ -2046,10 +2046,10 @@ static irqreturn_t e100_intr(int irq, void *dev_id) iowrite8(stat_ack, &nic->csr->scb.stat_ack); /* We hit Receive No Resource (RNR); restart RU after cleaning */ - if(stat_ack & stat_ack_rnr) + if (stat_ack & stat_ack_rnr) nic->ru_running = RU_SUSPENDED; - if(likely(netif_rx_schedule_prep(&nic->napi))) { + if (likely(netif_rx_schedule_prep(&nic->napi))) { e100_disable_irq(nic); __netif_rx_schedule(&nic->napi); } @@ -2102,7 +2102,7 @@ static int e100_set_mac_address(struct net_device *netdev, void *p) static int e100_change_mtu(struct net_device *netdev, int new_mtu) { - if(new_mtu < ETH_ZLEN || new_mtu > ETH_DATA_LEN) + if (new_mtu < ETH_ZLEN || new_mtu > ETH_DATA_LEN) return -EINVAL; netdev->mtu = new_mtu; return 0; @@ -2121,16 +2121,16 @@ static int e100_up(struct nic *nic) { int err; - if((err = e100_rx_alloc_list(nic))) + if ((err = e100_rx_alloc_list(nic))) return err; - if((err = e100_alloc_cbs(nic))) + if ((err = e100_alloc_cbs(nic))) goto err_rx_clean_list; - if((err = e100_hw_init(nic))) + if ((err = e100_hw_init(nic))) goto err_clean_cbs; e100_set_multicast_list(nic->netdev); e100_start_receiver(nic, NULL); mod_timer(&nic->watchdog, jiffies); - if((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED, + if ((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED, nic->netdev->name, nic->netdev))) goto err_no_irq; netif_wake_queue(nic->netdev); @@ -2192,26 +2192,26 @@ static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode) * in loopback mode, and the test passes if the received * packet compares byte-for-byte to the transmitted packet. */ - if((err = e100_rx_alloc_list(nic))) + if ((err = e100_rx_alloc_list(nic))) return err; - if((err = e100_alloc_cbs(nic))) + if ((err = e100_alloc_cbs(nic))) goto err_clean_rx; /* ICH PHY loopback is broken so do MAC loopback instead */ - if(nic->flags & ich && loopback_mode == lb_phy) + if (nic->flags & ich && loopback_mode == lb_phy) loopback_mode = lb_mac; nic->loopback = loopback_mode; - if((err = e100_hw_init(nic))) + if ((err = e100_hw_init(nic))) goto err_loopback_none; - if(loopback_mode == lb_phy) + if (loopback_mode == lb_phy) mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, BMCR_LOOPBACK); e100_start_receiver(nic, NULL); - if(!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) { + if (!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) { err = -ENOMEM; goto err_loopback_none; } @@ -2224,7 +2224,7 @@ static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode) pci_dma_sync_single_for_cpu(nic->pdev, nic->rx_to_clean->dma_addr, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); - if(memcmp(nic->rx_to_clean->skb->data + sizeof(struct rfd), + if (memcmp(nic->rx_to_clean->skb->data + sizeof(struct rfd), skb->data, ETH_DATA_LEN)) err = -EAGAIN; @@ -2301,7 +2301,7 @@ static void e100_get_regs(struct net_device *netdev, buff[0] = ioread8(&nic->csr->scb.cmd_hi) << 24 | ioread8(&nic->csr->scb.cmd_lo) << 16 | ioread16(&nic->csr->scb.status); - for(i = E100_PHY_REGS; i >= 0; i--) + for (i = E100_PHY_REGS; i >= 0; i--) buff[1 + E100_PHY_REGS - i] = mdio_read(netdev, nic->mii.phy_id, i); memset(nic->mem->dump_buf, 0, sizeof(nic->mem->dump_buf)); @@ -2326,7 +2326,7 @@ static int e100_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) !device_can_wakeup(&nic->pdev->dev)) return -EOPNOTSUPP; - if(wol->wolopts) + if (wol->wolopts) nic->flags |= wol_magic; else nic->flags &= ~wol_magic; @@ -2385,7 +2385,7 @@ static int e100_set_eeprom(struct net_device *netdev, { struct nic *nic = netdev_priv(netdev); - if(eeprom->magic != E100_EEPROM_MAGIC) + if (eeprom->magic != E100_EEPROM_MAGIC) return -EINVAL; memcpy(&((u8 *)nic->eeprom)[eeprom->offset], bytes, eeprom->len); @@ -2421,7 +2421,7 @@ static int e100_set_ringparam(struct net_device *netdev, if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) return -EINVAL; - if(netif_running(netdev)) + if (netif_running(netdev)) e100_down(nic); rfds->count = max(ring->rx_pending, rfds->min); rfds->count = min(rfds->count, rfds->max); @@ -2429,7 +2429,7 @@ static int e100_set_ringparam(struct net_device *netdev, cbs->count = min(cbs->count, cbs->max); DPRINTK(DRV, INFO, "Ring Param settings: rx: %d, tx %d\n", rfds->count, cbs->count); - if(netif_running(netdev)) + if (netif_running(netdev)) e100_up(nic); return 0; @@ -2454,12 +2454,12 @@ static void e100_diag_test(struct net_device *netdev, memset(data, 0, E100_TEST_LEN * sizeof(u64)); data[0] = !mii_link_ok(&nic->mii); data[1] = e100_eeprom_load(nic); - if(test->flags & ETH_TEST_FL_OFFLINE) { + if (test->flags & ETH_TEST_FL_OFFLINE) { /* save speed, duplex & autoneg settings */ err = mii_ethtool_gset(&nic->mii, &cmd); - if(netif_running(netdev)) + if (netif_running(netdev)) e100_down(nic); data[2] = e100_self_test(nic); data[3] = e100_loopback_test(nic, lb_mac); @@ -2468,10 +2468,10 @@ static void e100_diag_test(struct net_device *netdev, /* restore speed, duplex & autoneg settings */ err = mii_ethtool_sset(&nic->mii, &cmd); - if(netif_running(netdev)) + if (netif_running(netdev)) e100_up(nic); } - for(i = 0; i < E100_TEST_LEN; i++) + for (i = 0; i < E100_TEST_LEN; i++) test->flags |= data[i] ? ETH_TEST_FL_FAILED : 0; msleep_interruptible(4 * 1000); @@ -2481,7 +2481,7 @@ static int e100_phys_id(struct net_device *netdev, u32 data) { struct nic *nic = netdev_priv(netdev); - if(!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ)) + if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ)) data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ); mod_timer(&nic->blink_timer, jiffies); msleep_interruptible(data * 1000); @@ -2524,7 +2524,7 @@ static void e100_get_ethtool_stats(struct net_device *netdev, struct nic *nic = netdev_priv(netdev); int i; - for(i = 0; i < E100_NET_STATS_LEN; i++) + for (i = 0; i < E100_NET_STATS_LEN; i++) data[i] = ((unsigned long *)&netdev->stats)[i]; data[i++] = nic->tx_deferred; @@ -2539,7 +2539,7 @@ static void e100_get_ethtool_stats(struct net_device *netdev, static void e100_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { - switch(stringset) { + switch (stringset) { case ETH_SS_TEST: memcpy(data, *e100_gstrings_test, sizeof(e100_gstrings_test)); break; @@ -2589,7 +2589,7 @@ static int e100_alloc(struct nic *nic) static void e100_free(struct nic *nic) { - if(nic->mem) { + if (nic->mem) { pci_free_consistent(nic->pdev, sizeof(struct mem), nic->mem, nic->dma_addr); nic->mem = NULL; @@ -2602,7 +2602,7 @@ static int e100_open(struct net_device *netdev) int err = 0; netif_carrier_off(netdev); - if((err = e100_up(nic))) + if ((err = e100_up(nic))) DPRINTK(IFUP, ERR, "Cannot open interface, aborting.\n"); return err; } @@ -2635,8 +2635,8 @@ static int __devinit e100_probe(struct pci_dev *pdev, struct nic *nic; int err; - if(!(netdev = alloc_etherdev(sizeof(struct nic)))) { - if(((1 << debug) - 1) & NETIF_MSG_PROBE) + if (!(netdev = alloc_etherdev(sizeof(struct nic)))) { + if (((1 << debug) - 1) & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Etherdev alloc failed, abort.\n"); return -ENOMEM; } @@ -2653,24 +2653,24 @@ static int __devinit e100_probe(struct pci_dev *pdev, nic->msg_enable = (1 << debug) - 1; pci_set_drvdata(pdev, netdev); - if((err = pci_enable_device(pdev))) { + if ((err = pci_enable_device(pdev))) { DPRINTK(PROBE, ERR, "Cannot enable PCI device, aborting.\n"); goto err_out_free_dev; } - if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { DPRINTK(PROBE, ERR, "Cannot find proper PCI device " "base address, aborting.\n"); err = -ENODEV; goto err_out_disable_pdev; } - if((err = pci_request_regions(pdev, DRV_NAME))) { + if ((err = pci_request_regions(pdev, DRV_NAME))) { DPRINTK(PROBE, ERR, "Cannot obtain PCI resources, aborting.\n"); goto err_out_disable_pdev; } - if((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) { + if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) { DPRINTK(PROBE, ERR, "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } @@ -2681,13 +2681,13 @@ static int __devinit e100_probe(struct pci_dev *pdev, DPRINTK(PROBE, INFO, "using i/o access mode\n"); nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr)); - if(!nic->csr) { + if (!nic->csr) { DPRINTK(PROBE, ERR, "Cannot map device registers, aborting.\n"); err = -ENOMEM; goto err_out_free_res; } - if(ent->driver_data) + if (ent->driver_data) nic->flags |= ich; else nic->flags &= ~ich; @@ -2715,12 +2715,12 @@ static int __devinit e100_probe(struct pci_dev *pdev, INIT_WORK(&nic->tx_timeout_task, e100_tx_timeout_task); - if((err = e100_alloc(nic))) { + if ((err = e100_alloc(nic))) { DPRINTK(PROBE, ERR, "Cannot alloc driver memory, aborting.\n"); goto err_out_iounmap; } - if((err = e100_eeprom_load(nic))) + if ((err = e100_eeprom_load(nic))) goto err_out_free; e100_phy_init(nic); @@ -2740,7 +2740,7 @@ static int __devinit e100_probe(struct pci_dev *pdev, } /* Wol magic packet can be enabled from eeprom */ - if((nic->mac >= mac_82558_D101_A4) && + if ((nic->mac >= mac_82558_D101_A4) && (nic->eeprom[eeprom_id] & eeprom_id_wol)) { nic->flags |= wol_magic; device_set_wakeup_enable(&pdev->dev, true); @@ -2750,7 +2750,7 @@ static int __devinit e100_probe(struct pci_dev *pdev, pci_pme_active(pdev, false); strcpy(netdev->name, "eth%d"); - if((err = register_netdev(netdev))) { + if ((err = register_netdev(netdev))) { DPRINTK(PROBE, ERR, "Cannot register net device, aborting.\n"); goto err_out_free; } @@ -2779,7 +2779,7 @@ static void __devexit e100_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); - if(netdev) { + if (netdev) { struct nic *nic = netdev_priv(netdev); unregister_netdev(netdev); e100_free(nic); @@ -2932,7 +2932,7 @@ static struct pci_driver e100_driver = { static int __init e100_init_module(void) { - if(((1 << debug) - 1) & NETIF_MSG_DRV) { + if (((1 << debug) - 1) & NETIF_MSG_DRV) { printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT); } diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h index 9930d5f8b9e1..6271b9411ccf 100644 --- a/drivers/net/ehea/ehea.h +++ b/drivers/net/ehea/ehea.h @@ -478,7 +478,7 @@ struct ehea_port { int num_add_tx_qps; int num_mcs; int resets; - u64 flags; + unsigned long flags; u64 mac_addr; u32 logical_port_id; u32 port_speed; @@ -510,7 +510,6 @@ void ehea_set_ethtool_ops(struct net_device *netdev); int ehea_sense_port_attr(struct ehea_port *port); int ehea_set_portspeed(struct ehea_port *port, u32 port_speed); -extern u64 ehea_driver_flags; extern struct work_struct ehea_rereg_mr_task; #endif /* __EHEA_H__ */ diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index a2f1905a23df..e3131ea629cd 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -99,7 +99,7 @@ MODULE_PARM_DESC(use_lro, " Large Receive Offload, 1: enable, 0: disable, " static int port_name_cnt; static LIST_HEAD(adapter_list); -u64 ehea_driver_flags; +static unsigned long ehea_driver_flags; struct work_struct ehea_rereg_mr_task; static DEFINE_MUTEX(dlpar_mem_lock); struct ehea_fw_handle_array ehea_fw_handles; diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index b0ef46c51a9d..cefe1d98f93e 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -944,7 +944,7 @@ static void enc28j60_hw_rx(struct net_device *ndev) if (netif_msg_rx_status(priv)) enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat); - if (!RSV_GETBIT(rxstat, RSV_RXOK)) { + if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) { if (netif_msg_rx_err(priv)) dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat); ndev->stats.rx_errors++; @@ -952,6 +952,8 @@ static void enc28j60_hw_rx(struct net_device *ndev) ndev->stats.rx_crc_errors++; if (RSV_GETBIT(rxstat, RSV_LENCHECKERR)) ndev->stats.rx_frame_errors++; + if (len > MAX_FRAMELEN) + ndev->stats.rx_over_errors++; } else { skb = dev_alloc_skb(len + NET_IP_ALIGN); if (!skb) { diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 022794e579c7..b82b0fb2056c 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1457,8 +1457,8 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter) /* Number of supported queues. */ /* Having more queues than CPUs doesn't make sense. */ - adapter->num_rx_queues = min((u32)IGB_MAX_RX_QUEUES, (u32)num_online_cpus()); - adapter->num_tx_queues = min(IGB_MAX_TX_QUEUES, num_online_cpus()); + adapter->num_rx_queues = min_t(u32, IGB_MAX_RX_QUEUES, num_online_cpus()); + adapter->num_tx_queues = min_t(u32, IGB_MAX_TX_QUEUES, num_online_cpus()); /* This call may decrease the number of queues depending on * interrupt mode. */ diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 5e70180bf569..6bb71b687f7b 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -75,7 +75,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.4.4-1.395" +#define MYRI10GE_VERSION_STR "1.4.4-1.398" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -3929,6 +3929,10 @@ abort_with_firmware: myri10ge_dummy_rdma(mgp, 0); abort_with_ioremap: + if (mgp->mac_addr_string != NULL) + dev_err(&pdev->dev, + "myri10ge_probe() failed: MAC=%s, SN=%ld\n", + mgp->mac_addr_string, mgp->serial_number); iounmap(mgp->sram); abort_with_mtrr: diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index ba2e1c5b6bcf..459663a4023d 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h @@ -818,15 +818,6 @@ struct tx_doorbell_context { }; /* DATA STRUCTURES SHARED WITH HARDWARE. */ - -struct bq_element { - u32 addr_lo; -#define BQ_END 0x00000001 -#define BQ_CONT 0x00000002 -#define BQ_MASK 0x00000003 - u32 addr_hi; -} __attribute((packed)); - struct tx_buf_desc { __le64 addr; __le32 len; @@ -860,8 +851,8 @@ struct ob_mac_iocb_req { __le16 frame_len; #define OB_MAC_IOCB_LEN_MASK 0x3ffff __le16 reserved2; - __le32 tid; - __le32 txq_idx; + u32 tid; + u32 txq_idx; __le32 reserved3; __le16 vlan_tci; __le16 reserved4; @@ -880,8 +871,8 @@ struct ob_mac_iocb_rsp { u8 flags2; /* */ u8 flags3; /* */ #define OB_MAC_IOCB_RSP_B 0x80 /* */ - __le32 tid; - __le32 txq_idx; + u32 tid; + u32 txq_idx; __le32 reserved[13]; } __attribute((packed)); @@ -903,8 +894,8 @@ struct ob_mac_tso_iocb_req { #define OB_MAC_TSO_IOCB_V 0x04 __le32 reserved1[2]; __le32 frame_len; - __le32 tid; - __le32 txq_idx; + u32 tid; + u32 txq_idx; __le16 total_hdrs_len; __le16 net_trans_offset; #define OB_MAC_TRANSPORT_HDR_SHIFT 6 @@ -925,8 +916,8 @@ struct ob_mac_tso_iocb_rsp { u8 flags2; /* */ u8 flags3; /* */ #define OB_MAC_TSO_IOCB_RSP_B 0x8000 - __le32 tid; - __le32 txq_idx; + u32 tid; + u32 txq_idx; __le32 reserved2[13]; } __attribute((packed)); @@ -979,10 +970,11 @@ struct ib_mac_iocb_rsp { __le16 reserved1; __le32 reserved2[6]; - __le32 flags4; -#define IB_MAC_IOCB_RSP_HV 0x20000000 /* */ -#define IB_MAC_IOCB_RSP_HS 0x40000000 /* */ -#define IB_MAC_IOCB_RSP_HL 0x80000000 /* */ + u8 reserved3[3]; + u8 flags4; +#define IB_MAC_IOCB_RSP_HV 0x20 +#define IB_MAC_IOCB_RSP_HS 0x40 +#define IB_MAC_IOCB_RSP_HL 0x80 __le32 hdr_len; /* */ __le32 hdr_addr_lo; /* */ __le32 hdr_addr_hi; /* */ @@ -1126,7 +1118,7 @@ struct map_list { struct tx_ring_desc { struct sk_buff *skb; struct ob_mac_iocb_req *queue_entry; - int index; + u32 index; struct oal oal; struct map_list map[MAX_SKB_FRAGS + 1]; int map_cnt; @@ -1138,8 +1130,8 @@ struct bq_desc { struct page *lbq_page; struct sk_buff *skb; } p; - struct bq_element *bq; - int index; + __le64 *addr; + u32 index; DECLARE_PCI_UNMAP_ADDR(mapaddr); DECLARE_PCI_UNMAP_LEN(maplen); }; @@ -1189,7 +1181,7 @@ struct rx_ring { u32 cq_size; u32 cq_len; u16 cq_id; - u32 *prod_idx_sh_reg; /* Shadowed producer register. */ + volatile __le32 *prod_idx_sh_reg; /* Shadowed producer register. */ dma_addr_t prod_idx_sh_reg_dma; void __iomem *cnsmr_idx_db_reg; /* PCI doorbell mem area + 0 */ u32 cnsmr_idx; /* current sw idx */ @@ -1467,21 +1459,6 @@ static inline void ql_write_db_reg(u32 val, void __iomem *addr) mmiowb(); } -/* - * Shadow Registers: - * Outbound queues have a consumer index that is maintained by the chip. - * Inbound queues have a producer index that is maintained by the chip. - * For lower overhead, these registers are "shadowed" to host memory - * which allows the device driver to track the queue progress without - * PCI reads. When an entry is placed on an inbound queue, the chip will - * update the relevant index register and then copy the value to the - * shadow register in host memory. - */ -static inline unsigned int ql_read_sh_reg(const volatile void *addr) -{ - return *(volatile unsigned int __force *)addr; -} - extern char qlge_driver_name[]; extern const char qlge_driver_version[]; extern const struct ethtool_ops qlge_ethtool_ops; diff --git a/drivers/net/qlge/qlge_dbg.c b/drivers/net/qlge/qlge_dbg.c index 47df304a02c8..3f5e02d2e4a9 100644 --- a/drivers/net/qlge/qlge_dbg.c +++ b/drivers/net/qlge/qlge_dbg.c @@ -821,14 +821,11 @@ void ql_dump_ib_mac_rsp(struct ib_mac_iocb_rsp *ib_mac_rsp) le16_to_cpu(ib_mac_rsp->vlan_id)); printk(KERN_ERR PFX "flags4 = %s%s%s.\n", - le32_to_cpu(ib_mac_rsp-> - flags4) & IB_MAC_IOCB_RSP_HV ? "HV " : "", - le32_to_cpu(ib_mac_rsp-> - flags4) & IB_MAC_IOCB_RSP_HS ? "HS " : "", - le32_to_cpu(ib_mac_rsp-> - flags4) & IB_MAC_IOCB_RSP_HL ? "HL " : ""); - - if (le32_to_cpu(ib_mac_rsp->flags4) & IB_MAC_IOCB_RSP_HV) { + ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HV ? "HV " : "", + ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HS ? "HS " : "", + ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HL ? "HL " : ""); + + if (ib_mac_rsp->flags4 & IB_MAC_IOCB_RSP_HV) { printk(KERN_ERR PFX "hdr length = %d.\n", le32_to_cpu(ib_mac_rsp->hdr_len)); printk(KERN_ERR PFX "hdr addr_hi = 0x%x.\n", diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c index eefb81b13758..9d922e2ff226 100644 --- a/drivers/net/qlge/qlge_ethtool.c +++ b/drivers/net/qlge/qlge_ethtool.c @@ -56,9 +56,9 @@ static int ql_update_ring_coalescing(struct ql_adapter *qdev) for (i = 1; i < qdev->rss_ring_first_cq_id; i++, rx_ring++) { rx_ring = &qdev->rx_ring[i]; cqicb = (struct cqicb *)rx_ring; - cqicb->irq_delay = le16_to_cpu(qdev->tx_coalesce_usecs); + cqicb->irq_delay = cpu_to_le16(qdev->tx_coalesce_usecs); cqicb->pkt_delay = - le16_to_cpu(qdev->tx_max_coalesced_frames); + cpu_to_le16(qdev->tx_max_coalesced_frames); cqicb->flags = FLAGS_LI; status = ql_write_cfg(qdev, cqicb, sizeof(cqicb), CFG_LCQ, rx_ring->cq_id); @@ -79,9 +79,9 @@ static int ql_update_ring_coalescing(struct ql_adapter *qdev) i++) { rx_ring = &qdev->rx_ring[i]; cqicb = (struct cqicb *)rx_ring; - cqicb->irq_delay = le16_to_cpu(qdev->rx_coalesce_usecs); + cqicb->irq_delay = cpu_to_le16(qdev->rx_coalesce_usecs); cqicb->pkt_delay = - le16_to_cpu(qdev->rx_max_coalesced_frames); + cpu_to_le16(qdev->rx_max_coalesced_frames); cqicb->flags = FLAGS_LI; status = ql_write_cfg(qdev, cqicb, sizeof(cqicb), CFG_LCQ, rx_ring->cq_id); diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 718a7bd0cd1a..f4c016012f18 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -257,7 +257,7 @@ int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index, { status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, (offset++) | /* offset */ @@ -265,13 +265,13 @@ int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index, MAC_ADDR_ADR | MAC_ADDR_RS | type); /* type */ status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MR, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MR, 0); if (status) goto exit; *value++ = ql_read32(qdev, MAC_ADDR_DATA); status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, (offset++) | /* offset */ @@ -279,14 +279,14 @@ int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index, MAC_ADDR_ADR | MAC_ADDR_RS | type); /* type */ status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MR, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MR, 0); if (status) goto exit; *value++ = ql_read32(qdev, MAC_ADDR_DATA); if (type == MAC_ADDR_TYPE_CAM_MAC) { status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, (offset++) | /* offset */ @@ -294,7 +294,7 @@ int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index, MAC_ADDR_ADR | MAC_ADDR_RS | type); /* type */ status = ql_wait_reg_rdy(qdev, MAC_ADDR_IDX, - MAC_ADDR_MR, MAC_ADDR_E); + MAC_ADDR_MR, 0); if (status) goto exit; *value++ = ql_read32(qdev, MAC_ADDR_DATA); @@ -344,7 +344,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, (offset++) | /* offset */ @@ -353,7 +353,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, ql_write32(qdev, MAC_ADDR_DATA, lower); status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, (offset++) | /* offset */ @@ -362,7 +362,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, ql_write32(qdev, MAC_ADDR_DATA, upper); status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, (offset) | /* offset */ @@ -400,7 +400,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type, status = ql_wait_reg_rdy(qdev, - MAC_ADDR_IDX, MAC_ADDR_MW, MAC_ADDR_E); + MAC_ADDR_IDX, MAC_ADDR_MW, 0); if (status) goto exit; ql_write32(qdev, MAC_ADDR_IDX, offset | /* offset */ @@ -431,13 +431,13 @@ int ql_get_routing_reg(struct ql_adapter *qdev, u32 index, u32 *value) if (status) goto exit; - status = ql_wait_reg_rdy(qdev, RT_IDX, RT_IDX_MW, RT_IDX_E); + status = ql_wait_reg_rdy(qdev, RT_IDX, RT_IDX_MW, 0); if (status) goto exit; ql_write32(qdev, RT_IDX, RT_IDX_TYPE_NICQ | RT_IDX_RS | (index << RT_IDX_IDX_SHIFT)); - status = ql_wait_reg_rdy(qdev, RT_IDX, RT_IDX_MR, RT_IDX_E); + status = ql_wait_reg_rdy(qdev, RT_IDX, RT_IDX_MR, 0); if (status) goto exit; *value = ql_read32(qdev, RT_DATA); @@ -874,7 +874,6 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) { int clean_idx = rx_ring->lbq_clean_idx; struct bq_desc *lbq_desc; - struct bq_element *bq; u64 map; int i; @@ -884,7 +883,6 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) "lbq: try cleaning clean_idx = %d.\n", clean_idx); lbq_desc = &rx_ring->lbq[clean_idx]; - bq = lbq_desc->bq; if (lbq_desc->p.lbq_page == NULL) { QPRINTK(qdev, RX_STATUS, DEBUG, "lbq: getting new page for index %d.\n", @@ -906,10 +904,7 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) } pci_unmap_addr_set(lbq_desc, mapaddr, map); pci_unmap_len_set(lbq_desc, maplen, PAGE_SIZE); - bq->addr_lo = /*lbq_desc->addr_lo = */ - cpu_to_le32(map); - bq->addr_hi = /*lbq_desc->addr_hi = */ - cpu_to_le32(map >> 32); + *lbq_desc->addr = cpu_to_le64(map); } clean_idx++; if (clean_idx == rx_ring->lbq_len) @@ -934,7 +929,6 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) { int clean_idx = rx_ring->sbq_clean_idx; struct bq_desc *sbq_desc; - struct bq_element *bq; u64 map; int i; @@ -944,7 +938,6 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) QPRINTK(qdev, RX_STATUS, DEBUG, "sbq: try cleaning clean_idx = %d.\n", clean_idx); - bq = sbq_desc->bq; if (sbq_desc->p.skb == NULL) { QPRINTK(qdev, RX_STATUS, DEBUG, "sbq: getting new skb for index %d.\n", @@ -963,11 +956,15 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring) sbq_desc->p.skb->data, rx_ring->sbq_buf_size / 2, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(qdev->pdev, map)) { + QPRINTK(qdev, IFUP, ERR, "PCI mapping failed.\n"); + rx_ring->sbq_clean_idx = clean_idx; + return; + } pci_unmap_addr_set(sbq_desc, mapaddr, map); pci_unmap_len_set(sbq_desc, maplen, rx_ring->sbq_buf_size / 2); - bq->addr_lo = cpu_to_le32(map); - bq->addr_hi = cpu_to_le32(map >> 32); + *sbq_desc->addr = cpu_to_le64(map); } clean_idx++; @@ -1303,6 +1300,11 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, "No skb available, drop the packet.\n"); return NULL; } + pci_unmap_page(qdev->pdev, + pci_unmap_addr(lbq_desc, + mapaddr), + pci_unmap_len(lbq_desc, maplen), + PCI_DMA_FROMDEVICE); skb_reserve(skb, NET_IP_ALIGN); QPRINTK(qdev, RX_STATUS, DEBUG, "%d bytes of headers and data in large. Chain page to new skb and pull tail.\n", length); @@ -1330,7 +1332,7 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, * eventually be in trouble. */ int size, offset, i = 0; - struct bq_element *bq, bq_array[8]; + __le64 *bq, bq_array[8]; sbq_desc = ql_get_curr_sbuf(rx_ring); pci_unmap_single(qdev->pdev, pci_unmap_addr(sbq_desc, mapaddr), @@ -1356,16 +1358,10 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, } else { QPRINTK(qdev, RX_STATUS, DEBUG, "Headers in small, %d bytes of data in chain of large.\n", length); - bq = (struct bq_element *)sbq_desc->p.skb->data; + bq = (__le64 *)sbq_desc->p.skb->data; } while (length > 0) { lbq_desc = ql_get_curr_lbuf(rx_ring); - if ((bq->addr_lo & ~BQ_MASK) != lbq_desc->bq->addr_lo) { - QPRINTK(qdev, RX_STATUS, ERR, - "Panic!!! bad large buffer address, expected 0x%.08x, got 0x%.08x.\n", - lbq_desc->bq->addr_lo, bq->addr_lo); - return NULL; - } pci_unmap_page(qdev->pdev, pci_unmap_addr(lbq_desc, mapaddr), @@ -1549,7 +1545,7 @@ static void ql_process_chip_ae_intr(struct ql_adapter *qdev, static int ql_clean_outbound_rx_ring(struct rx_ring *rx_ring) { struct ql_adapter *qdev = rx_ring->qdev; - u32 prod = ql_read_sh_reg(rx_ring->prod_idx_sh_reg); + u32 prod = le32_to_cpu(*rx_ring->prod_idx_sh_reg); struct ob_mac_iocb_rsp *net_rsp = NULL; int count = 0; @@ -1575,7 +1571,7 @@ static int ql_clean_outbound_rx_ring(struct rx_ring *rx_ring) } count++; ql_update_cq(rx_ring); - prod = ql_read_sh_reg(rx_ring->prod_idx_sh_reg); + prod = le32_to_cpu(*rx_ring->prod_idx_sh_reg); } ql_write_cq_idx(rx_ring); if (netif_queue_stopped(qdev->ndev) && net_rsp != NULL) { @@ -1595,7 +1591,7 @@ static int ql_clean_outbound_rx_ring(struct rx_ring *rx_ring) static int ql_clean_inbound_rx_ring(struct rx_ring *rx_ring, int budget) { struct ql_adapter *qdev = rx_ring->qdev; - u32 prod = ql_read_sh_reg(rx_ring->prod_idx_sh_reg); + u32 prod = le32_to_cpu(*rx_ring->prod_idx_sh_reg); struct ql_net_rsp_iocb *net_rsp; int count = 0; @@ -1628,7 +1624,7 @@ static int ql_clean_inbound_rx_ring(struct rx_ring *rx_ring, int budget) } count++; ql_update_cq(rx_ring); - prod = ql_read_sh_reg(rx_ring->prod_idx_sh_reg); + prod = le32_to_cpu(*rx_ring->prod_idx_sh_reg); if (count == budget) break; } @@ -1791,7 +1787,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) * Check the default queue and wake handler if active. */ rx_ring = &qdev->rx_ring[0]; - if (ql_read_sh_reg(rx_ring->prod_idx_sh_reg) != rx_ring->cnsmr_idx) { + if (le32_to_cpu(*rx_ring->prod_idx_sh_reg) != rx_ring->cnsmr_idx) { QPRINTK(qdev, INTR, INFO, "Waking handler for rx_ring[0].\n"); ql_disable_completion_interrupt(qdev, intr_context->intr); queue_delayed_work_on(smp_processor_id(), qdev->q_workqueue, @@ -1805,7 +1801,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) */ for (i = 1; i < qdev->rx_ring_count; i++) { rx_ring = &qdev->rx_ring[i]; - if (ql_read_sh_reg(rx_ring->prod_idx_sh_reg) != + if (le32_to_cpu(*rx_ring->prod_idx_sh_reg) != rx_ring->cnsmr_idx) { QPRINTK(qdev, INTR, INFO, "Waking handler for rx_ring[%d].\n", i); @@ -1874,7 +1870,7 @@ static void ql_hw_csum_setup(struct sk_buff *skb, { int len; struct iphdr *iph = ip_hdr(skb); - u16 *check; + __sum16 *check; mac_iocb_ptr->opcode = OPCODE_OB_MAC_TSO_IOCB; mac_iocb_ptr->frame_len = cpu_to_le32((u32) skb->len); mac_iocb_ptr->net_trans_offset = @@ -2083,8 +2079,6 @@ static void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring put_page(lbq_desc->p.lbq_page); lbq_desc->p.lbq_page = NULL; } - lbq_desc->bq->addr_lo = 0; - lbq_desc->bq->addr_hi = 0; } } @@ -2097,12 +2091,12 @@ static int ql_alloc_lbq_buffers(struct ql_adapter *qdev, int i; struct bq_desc *lbq_desc; u64 map; - struct bq_element *bq = rx_ring->lbq_base; + __le64 *bq = rx_ring->lbq_base; for (i = 0; i < rx_ring->lbq_len; i++) { lbq_desc = &rx_ring->lbq[i]; memset(lbq_desc, 0, sizeof(lbq_desc)); - lbq_desc->bq = bq; + lbq_desc->addr = bq; lbq_desc->index = i; lbq_desc->p.lbq_page = alloc_page(GFP_ATOMIC); if (unlikely(!lbq_desc->p.lbq_page)) { @@ -2119,8 +2113,7 @@ static int ql_alloc_lbq_buffers(struct ql_adapter *qdev, } pci_unmap_addr_set(lbq_desc, mapaddr, map); pci_unmap_len_set(lbq_desc, maplen, PAGE_SIZE); - bq->addr_lo = cpu_to_le32(map); - bq->addr_hi = cpu_to_le32(map >> 32); + *lbq_desc->addr = cpu_to_le64(map); } bq++; } @@ -2149,13 +2142,6 @@ static void ql_free_sbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring dev_kfree_skb(sbq_desc->p.skb); sbq_desc->p.skb = NULL; } - if (sbq_desc->bq == NULL) { - QPRINTK(qdev, IFUP, ERR, "sbq_desc->bq %d is NULL.\n", - i); - return; - } - sbq_desc->bq->addr_lo = 0; - sbq_desc->bq->addr_hi = 0; } } @@ -2167,13 +2153,13 @@ static int ql_alloc_sbq_buffers(struct ql_adapter *qdev, struct bq_desc *sbq_desc; struct sk_buff *skb; u64 map; - struct bq_element *bq = rx_ring->sbq_base; + __le64 *bq = rx_ring->sbq_base; for (i = 0; i < rx_ring->sbq_len; i++) { sbq_desc = &rx_ring->sbq[i]; memset(sbq_desc, 0, sizeof(sbq_desc)); sbq_desc->index = i; - sbq_desc->bq = bq; + sbq_desc->addr = bq; skb = netdev_alloc_skb(qdev->ndev, rx_ring->sbq_buf_size); if (unlikely(!skb)) { /* Better luck next round */ @@ -2199,10 +2185,7 @@ static int ql_alloc_sbq_buffers(struct ql_adapter *qdev, } pci_unmap_addr_set(sbq_desc, mapaddr, map); pci_unmap_len_set(sbq_desc, maplen, rx_ring->sbq_buf_size / 2); - bq->addr_lo = /*sbq_desc->addr_lo = */ - cpu_to_le32(map); - bq->addr_hi = /*sbq_desc->addr_hi = */ - cpu_to_le32(map >> 32); + *sbq_desc->addr = cpu_to_le64(map); bq++; } return 0; @@ -2481,7 +2464,8 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) memset((void *)cqicb, 0, sizeof(struct cqicb)); cqicb->msix_vect = rx_ring->irq; - cqicb->len = cpu_to_le16(rx_ring->cq_len | LEN_V | LEN_CPP_CONT); + bq_len = (rx_ring->cq_len == 65536) ? 0 : (u16) rx_ring->cq_len; + cqicb->len = cpu_to_le16(bq_len | LEN_V | LEN_CPP_CONT); cqicb->addr_lo = cpu_to_le32(rx_ring->cq_base_dma); cqicb->addr_hi = cpu_to_le32((u64) rx_ring->cq_base_dma >> 32); @@ -2503,8 +2487,11 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) cpu_to_le32(rx_ring->lbq_base_indirect_dma); cqicb->lbq_addr_hi = cpu_to_le32((u64) rx_ring->lbq_base_indirect_dma >> 32); - cqicb->lbq_buf_size = cpu_to_le32(rx_ring->lbq_buf_size); - bq_len = (u16) rx_ring->lbq_len; + bq_len = (rx_ring->lbq_buf_size == 65536) ? 0 : + (u16) rx_ring->lbq_buf_size; + cqicb->lbq_buf_size = cpu_to_le16(bq_len); + bq_len = (rx_ring->lbq_len == 65536) ? 0 : + (u16) rx_ring->lbq_len; cqicb->lbq_len = cpu_to_le16(bq_len); rx_ring->lbq_prod_idx = rx_ring->lbq_len - 16; rx_ring->lbq_curr_idx = 0; @@ -2520,7 +2507,8 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) cpu_to_le32((u64) rx_ring->sbq_base_indirect_dma >> 32); cqicb->sbq_buf_size = cpu_to_le16(((rx_ring->sbq_buf_size / 2) + 8) & 0xfffffff8); - bq_len = (u16) rx_ring->sbq_len; + bq_len = (rx_ring->sbq_len == 65536) ? 0 : + (u16) rx_ring->sbq_len; cqicb->sbq_len = cpu_to_le16(bq_len); rx_ring->sbq_prod_idx = rx_ring->sbq_len - 16; rx_ring->sbq_curr_idx = 0; @@ -3341,11 +3329,11 @@ static int ql_configure_rings(struct ql_adapter *qdev) rx_ring->cq_len * sizeof(struct ql_net_rsp_iocb); rx_ring->lbq_len = NUM_LARGE_BUFFERS; rx_ring->lbq_size = - rx_ring->lbq_len * sizeof(struct bq_element); + rx_ring->lbq_len * sizeof(__le64); rx_ring->lbq_buf_size = LARGE_BUFFER_SIZE; rx_ring->sbq_len = NUM_SMALL_BUFFERS; rx_ring->sbq_size = - rx_ring->sbq_len * sizeof(struct bq_element); + rx_ring->sbq_len * sizeof(__le64); rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2; rx_ring->type = DEFAULT_Q; } else if (i < qdev->rss_ring_first_cq_id) { @@ -3372,11 +3360,11 @@ static int ql_configure_rings(struct ql_adapter *qdev) rx_ring->cq_len * sizeof(struct ql_net_rsp_iocb); rx_ring->lbq_len = NUM_LARGE_BUFFERS; rx_ring->lbq_size = - rx_ring->lbq_len * sizeof(struct bq_element); + rx_ring->lbq_len * sizeof(__le64); rx_ring->lbq_buf_size = LARGE_BUFFER_SIZE; rx_ring->sbq_len = NUM_SMALL_BUFFERS; rx_ring->sbq_size = - rx_ring->sbq_len * sizeof(struct bq_element); + rx_ring->sbq_len * sizeof(__le64); rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2; rx_ring->type = RX_Q; } diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index f54ac2389da2..57fb1f71c47b 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -42,11 +42,11 @@ #include <linux/mii.h> #include <linux/if_vlan.h> #include <linux/mm.h> +#include <linux/firmware.h> #include <asm/processor.h> /* Processor type for cache alignment. */ #include <asm/uaccess.h> #include <asm/io.h> -#include "starfire_firmware.h" /* * The current frame processor firmware fails to checksum a fragment * of length 1. If and when this is fixed, the #define below can be removed. @@ -173,6 +173,10 @@ static int full_duplex[MAX_UNITS] = {0, }; #define skb_first_frag_len(skb) skb_headlen(skb) #define skb_num_frags(skb) (skb_shinfo(skb)->nr_frags + 1) +/* Firmware names */ +#define FIRMWARE_RX "adaptec/starfire_rx.bin" +#define FIRMWARE_TX "adaptec/starfire_tx.bin" + /* These identify the driver base version and may not be removed. */ static char version[] = KERN_INFO "starfire.c:v1.03 7/26/2000 Written by Donald Becker <becker@scyld.com>\n" @@ -182,6 +186,8 @@ MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +MODULE_FIRMWARE(FIRMWARE_RX); +MODULE_FIRMWARE(FIRMWARE_TX); module_param(max_interrupt_work, int, 0); module_param(mtu, int, 0); @@ -902,9 +908,12 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int val static int netdev_open(struct net_device *dev) { + const struct firmware *fw_rx, *fw_tx; + const __be32 *fw_rx_data, *fw_tx_data; struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->base; int i, retval; + size_t tx_size, rx_size; size_t tx_done_q_size, rx_done_q_size, tx_ring_size, rx_ring_size; /* Do we ever need to reset the chip??? */ @@ -1040,11 +1049,40 @@ static int netdev_open(struct net_device *dev) writel(ETH_P_8021Q, ioaddr + VlanType); #endif /* VLAN_SUPPORT */ + retval = request_firmware(&fw_rx, FIRMWARE_RX, &np->pci_dev->dev); + if (retval) { + printk(KERN_ERR "starfire: Failed to load firmware \"%s\"\n", + FIRMWARE_RX); + return retval; + } + if (fw_rx->size % 4) { + printk(KERN_ERR "starfire: bogus length %zu in \"%s\"\n", + fw_rx->size, FIRMWARE_RX); + retval = -EINVAL; + goto out_rx; + } + retval = request_firmware(&fw_tx, FIRMWARE_TX, &np->pci_dev->dev); + if (retval) { + printk(KERN_ERR "starfire: Failed to load firmware \"%s\"\n", + FIRMWARE_TX); + goto out_rx; + } + if (fw_tx->size % 4) { + printk(KERN_ERR "starfire: bogus length %zu in \"%s\"\n", + fw_tx->size, FIRMWARE_TX); + retval = -EINVAL; + goto out_tx; + } + fw_rx_data = (const __be32 *)&fw_rx->data[0]; + fw_tx_data = (const __be32 *)&fw_tx->data[0]; + rx_size = fw_rx->size / 4; + tx_size = fw_tx->size / 4; + /* Load Rx/Tx firmware into the frame processors */ - for (i = 0; i < FIRMWARE_RX_SIZE * 2; i++) - writel(firmware_rx[i], ioaddr + RxGfpMem + i * 4); - for (i = 0; i < FIRMWARE_TX_SIZE * 2; i++) - writel(firmware_tx[i], ioaddr + TxGfpMem + i * 4); + for (i = 0; i < rx_size; i++) + writel(be32_to_cpup(&fw_rx_data[i]), ioaddr + RxGfpMem + i * 4); + for (i = 0; i < tx_size; i++) + writel(be32_to_cpup(&fw_tx_data[i]), ioaddr + TxGfpMem + i * 4); if (enable_hw_cksum) /* Enable the Rx and Tx units, and the Rx/Tx frame processors. */ writel(TxEnable|TxGFPEnable|RxEnable|RxGFPEnable, ioaddr + GenCtrl); @@ -1056,7 +1094,11 @@ static int netdev_open(struct net_device *dev) printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); - return 0; +out_tx: + release_firmware(fw_tx); +out_rx: + release_firmware(fw_rx); + return retval; } diff --git a/drivers/net/starfire_firmware.h b/drivers/net/starfire_firmware.h deleted file mode 100644 index 0a668528955d..000000000000 --- a/drivers/net/starfire_firmware.h +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright 2003 Adaptec, Inc. - * - * Please read the following license before using the Adaptec Software - * ("Program"). If you do not agree to the license terms, do not use the - * Program: - * - * You agree to be bound by version 2 of the General Public License ("GPL") - * dated June 1991, which can be found at http://www.fsf.org/licenses/gpl.html. - * If the link is broken, write to Free Software Foundation, 59 Temple Place, - * Boston, Massachusetts 02111-1307. - * - * BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE IT IS LICENSED "AS IS" AND - * THERE IS NO WARRANTY FOR THE PROGRAM, INCLUDING BUT NOT LIMITED TO THE - * IMPLIED WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR A PARTICULAR PURPOSE - * (TO THE EXTENT PERMITTED BY APPLICABLE LAW). USE OF THE PROGRAM IS AT YOUR - * OWN RISK. IN NO EVENT WILL ADAPTEC OR ITS LICENSORS BE LIABLE TO YOU FOR - * DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM. - * - */ - -static const u32 firmware_rx[] = { - 0x010003dc, 0x00000000, - 0x04000421, 0x00000086, - 0x80000015, 0x0000180e, - 0x81000015, 0x00006664, - 0x1a0040ab, 0x00000b06, - 0x14200011, 0x00000000, - 0x14204022, 0x0000aaaa, - 0x14204022, 0x00000300, - 0x14204022, 0x00000000, - 0x1a0040ab, 0x00000b14, - 0x14200011, 0x00000000, - 0x83000015, 0x00000002, - 0x04000021, 0x00000000, - 0x00000010, 0x00000000, - 0x04000421, 0x00000087, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00008015, 0x00000000, - 0x0000003e, 0x00000000, - 0x00000010, 0x00000000, - 0x82000015, 0x00004000, - 0x009e8050, 0x00000000, - 0x03008015, 0x00000000, - 0x86008015, 0x00000000, - 0x82000015, 0x00008000, - 0x0100001c, 0x00000000, - 0x000050a0, 0x0000010c, - 0x4e20d011, 0x00006008, - 0x1420d012, 0x00004008, - 0x0000f090, 0x00007000, - 0x0000c8b0, 0x00003000, - 0x00004040, 0x00000000, - 0x00108015, 0x00000000, - 0x00a2c150, 0x00004000, - 0x00a400b0, 0x00000014, - 0x00000020, 0x00000000, - 0x2500400d, 0x00002525, - 0x00047220, 0x00003100, - 0x00934070, 0x00000000, - 0x00000020, 0x00000000, - 0x00924460, 0x00000184, - 0x2b20c011, 0x00000000, - 0x0000c420, 0x00000540, - 0x36014018, 0x0000422d, - 0x14200011, 0x00000000, - 0x00924460, 0x00000183, - 0x3200001f, 0x00000034, - 0x02ac0015, 0x00000002, - 0x00a60110, 0x00000008, - 0x42200011, 0x00000000, - 0x00924060, 0x00000103, - 0x0000001e, 0x00000000, - 0x00000020, 0x00000100, - 0x0000001e, 0x00000000, - 0x00924460, 0x00000086, - 0x00004080, 0x00000000, - 0x0092c070, 0x00000000, - 0x00924060, 0x00000100, - 0x0000c890, 0x00005000, - 0x00a6c110, 0x00000000, - 0x00b0c090, 0x00000012, - 0x021c0015, 0x00000000, - 0x3200001f, 0x00000034, - 0x00924460, 0x00000510, - 0x44210011, 0x00000000, - 0x42000011, 0x00000000, - 0x83000015, 0x00000040, - 0x00924460, 0x00000508, - 0x45014018, 0x00004545, - 0x00808050, 0x00000000, - 0x62208012, 0x00000000, - 0x82000015, 0x00000800, - 0x15200011, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x80000015, 0x0000eea4, - 0x81000015, 0x0000005f, - 0x00000060, 0x00000000, - 0x00004120, 0x00000000, - 0x00004a00, 0x00004000, - 0x00924460, 0x00000190, - 0x5601401a, 0x00005956, - 0x14000011, 0x00000000, - 0x00934050, 0x00000018, - 0x00930050, 0x00000018, - 0x3601403a, 0x0000002d, - 0x000643a9, 0x00000000, - 0x0000c420, 0x00000140, - 0x5601401a, 0x00005956, - 0x14000011, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x000642a9, 0x00000000, - 0x00024420, 0x00000183, - 0x5601401a, 0x00005956, - 0x82000015, 0x00002000, - 0x15200011, 0x00000000, - 0x82000015, 0x00000010, - 0x15200011, 0x00000000, - 0x82000015, 0x00000010, - 0x15200011, 0x00000000, -}; /* 104 Rx instructions */ -#define FIRMWARE_RX_SIZE 104 - -static const u32 firmware_tx[] = { - 0x010003dc, 0x00000000, - 0x04000421, 0x00000086, - 0x80000015, 0x0000180e, - 0x81000015, 0x00006664, - 0x1a0040ab, 0x00000b06, - 0x14200011, 0x00000000, - 0x14204022, 0x0000aaaa, - 0x14204022, 0x00000300, - 0x14204022, 0x00000000, - 0x1a0040ab, 0x00000b14, - 0x14200011, 0x00000000, - 0x83000015, 0x00000002, - 0x04000021, 0x00000000, - 0x00000010, 0x00000000, - 0x04000421, 0x00000087, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00008015, 0x00000000, - 0x0000003e, 0x00000000, - 0x00000010, 0x00000000, - 0x82000015, 0x00004000, - 0x009e8050, 0x00000000, - 0x03008015, 0x00000000, - 0x86008015, 0x00000000, - 0x82000015, 0x00008000, - 0x0100001c, 0x00000000, - 0x000050a0, 0x0000010c, - 0x4e20d011, 0x00006008, - 0x1420d012, 0x00004008, - 0x0000f090, 0x00007000, - 0x0000c8b0, 0x00003000, - 0x00004040, 0x00000000, - 0x00108015, 0x00000000, - 0x00a2c150, 0x00004000, - 0x00a400b0, 0x00000014, - 0x00000020, 0x00000000, - 0x2500400d, 0x00002525, - 0x00047220, 0x00003100, - 0x00934070, 0x00000000, - 0x00000020, 0x00000000, - 0x00924460, 0x00000184, - 0x2b20c011, 0x00000000, - 0x0000c420, 0x00000540, - 0x36014018, 0x0000422d, - 0x14200011, 0x00000000, - 0x00924460, 0x00000183, - 0x3200001f, 0x00000034, - 0x02ac0015, 0x00000002, - 0x00a60110, 0x00000008, - 0x42200011, 0x00000000, - 0x00924060, 0x00000103, - 0x0000001e, 0x00000000, - 0x00000020, 0x00000100, - 0x0000001e, 0x00000000, - 0x00924460, 0x00000086, - 0x00004080, 0x00000000, - 0x0092c070, 0x00000000, - 0x00924060, 0x00000100, - 0x0000c890, 0x00005000, - 0x00a6c110, 0x00000000, - 0x00b0c090, 0x00000012, - 0x021c0015, 0x00000000, - 0x3200001f, 0x00000034, - 0x00924460, 0x00000510, - 0x44210011, 0x00000000, - 0x42000011, 0x00000000, - 0x83000015, 0x00000040, - 0x00924460, 0x00000508, - 0x45014018, 0x00004545, - 0x00808050, 0x00000000, - 0x62208012, 0x00000000, - 0x82000015, 0x00000800, - 0x15200011, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x80000015, 0x0000eea4, - 0x81000015, 0x0000005f, - 0x00000060, 0x00000000, - 0x00004120, 0x00000000, - 0x00004a00, 0x00004000, - 0x00924460, 0x00000190, - 0x5601401a, 0x00005956, - 0x14000011, 0x00000000, - 0x00934050, 0x00000018, - 0x00930050, 0x00000018, - 0x3601403a, 0x0000002d, - 0x000643a9, 0x00000000, - 0x0000c420, 0x00000140, - 0x5601401a, 0x00005956, - 0x14000011, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x000642a9, 0x00000000, - 0x00024420, 0x00000183, - 0x5601401a, 0x00005956, - 0x82000015, 0x00002000, - 0x15200011, 0x00000000, - 0x82000015, 0x00000010, - 0x15200011, 0x00000000, - 0x82000015, 0x00000010, - 0x15200011, 0x00000000, -}; /* 104 Tx instructions */ -#define FIRMWARE_TX_SIZE 104 -#if 0 -static const u32 firmware_wol[] = { - 0x010003dc, 0x00000000, - 0x19000421, 0x00000087, - 0x80000015, 0x00001a1a, - 0x81000015, 0x00001a1a, - 0x1a0040ab, 0x00000b06, - 0x15200011, 0x00000000, - 0x15204022, 0x0000aaaa, - 0x15204022, 0x00000300, - 0x15204022, 0x00000000, - 0x1a0040ab, 0x00000b15, - 0x15200011, 0x00000000, - 0x83000015, 0x00000002, - 0x04000021, 0x00000000, - 0x00000010, 0x00000000, - 0x04000421, 0x00000087, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00008015, 0x00000000, - 0x0000003e, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x82000015, 0x00004000, - 0x82000015, 0x00008000, - 0x0000000c, 0x00000000, - 0x00000010, 0x00000000, - 0x00004080, 0x00000100, - 0x1f20c011, 0x00001122, - 0x2720f011, 0x00003011, - 0x19200071, 0x00000000, - 0x1a200051, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x1d2040a4, 0x00003344, - 0x1d2040a2, 0x00005566, - 0x000040a0, 0x00000100, - 0x00108050, 0x00000001, - 0x1a208012, 0x00000006, - 0x82000015, 0x00008080, - 0x010003dc, 0x00000000, - 0x1d2040a4, 0x00002233, - 0x1d2040a4, 0x00004455, - 0x2d208011, 0x00000005, - 0x1d2040a4, 0x00006611, - 0x00108050, 0x00000001, - 0x27200011, 0x00000000, - 0x1d2050a4, 0x00006600, - 0x82000015, 0x00008080, - 0x010003dc, 0x00000000, - 0x00000050, 0x00000000, - 0x1b200031, 0x00000000, - 0x0000001e, 0x00000000, - 0x0000001e, 0x00000000, - 0x0000001e, 0x00000000, - 0x0000001e, 0x00000000, - 0x00924460, 0x00000086, - 0x00004080, 0x00000000, - 0x0092c070, 0x00000000, - 0x00924060, 0x00000100, - 0x0000c890, 0x00005000, - 0x00a6c110, 0x00000000, - 0x00b0c090, 0x00000012, - 0x021c0015, 0x00000000, - 0x3200001f, 0x00000034, - 0x00924460, 0x00000510, - 0x44210011, 0x00000000, - 0x42000011, 0x00000000, - 0x83000015, 0x00000040, - 0x00924460, 0x00000508, - 0x476a0012, 0x00000100, - 0x83000015, 0x00000008, - 0x16200011, 0x00000000, - 0x001e8050, 0x00000000, - 0x001e8050, 0x00000000, - 0x00808050, 0x00000000, - 0x03008015, 0x00000000, - 0x62208012, 0x00000000, - 0x82000015, 0x00000800, - 0x16200011, 0x00000000, - 0x80000015, 0x0000eea4, - 0x81000015, 0x0000005f, - 0x00000020, 0x00000000, - 0x00004120, 0x00000000, - 0x00004a00, 0x00004000, - 0x00924460, 0x00000190, - 0x5c01401a, 0x0000595c, - 0x15000011, 0x00000000, - 0x00934050, 0x00000018, - 0x00930050, 0x00000018, - 0x3601403a, 0x0000002d, - 0x00064029, 0x00000000, - 0x0000c420, 0x00000140, - 0x5c01401a, 0x0000595c, - 0x15000011, 0x00000000, - 0x00000010, 0x00000000, - 0x00000010, 0x00000000, - 0x00064029, 0x00000000, - 0x00024420, 0x00000183, - 0x5c01401a, 0x0000595c, - 0x82000015, 0x00002000, - 0x16200011, 0x00000000, - 0x82000015, 0x00000010, - 0x16200011, 0x00000000, - 0x82000015, 0x00000010, - 0x16200011, 0x00000000, -}; /* 104 WoL instructions */ -#define FIRMWARE_WOL_SIZE 104 -#endif diff --git a/drivers/net/starfire_firmware.pl b/drivers/net/starfire_firmware.pl deleted file mode 100644 index 0c82b80e1074..000000000000 --- a/drivers/net/starfire_firmware.pl +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/perl - -# This script can be used to generate a new starfire_firmware.h -# from GFP_RX.DAT and GFP_TX.DAT, files included with the DDK -# and also with the Novell drivers. - -open FW, "GFP_RX.DAT" || die; -open FWH, ">starfire_firmware.h" || die; - -printf(FWH "static u32 firmware_rx[] = {\n"); -$counter = 0; -while ($foo = <FW>) { - chomp; - printf(FWH " 0x%s, 0x0000%s,\n", substr($foo, 4, 8), substr($foo, 0, 4)); - $counter++; -} - -close FW; -open FW, "GFP_TX.DAT" || die; - -printf(FWH "};\t/* %d Rx instructions */\n#define FIRMWARE_RX_SIZE %d\n\nstatic u32 firmware_tx[] = {\n", $counter, $counter); -$counter = 0; -while ($foo = <FW>) { - chomp; - printf(FWH " 0x%s, 0x0000%s,\n", substr($foo, 4, 8), substr($foo, 0, 4)); - $counter++; -} - -close FW; -printf(FWH "};\t/* %d Tx instructions */\n#define FIRMWARE_TX_SIZE %d\n", $counter, $counter); -close(FWH); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 04ae1e86aeaa..5e2dbaee125b 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -40,6 +40,7 @@ #include <linux/workqueue.h> #include <linux/prefetch.h> #include <linux/dma-mapping.h> +#include <linux/firmware.h> #include <net/checksum.h> #include <net/ip.h> @@ -137,6 +138,10 @@ #define TG3_NUM_TEST 6 +#define FIRMWARE_TG3 "tigon/tg3.bin" +#define FIRMWARE_TG3TSO "tigon/tg3_tso.bin" +#define FIRMWARE_TG3TSO5 "tigon/tg3_tso5.bin" + static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -144,6 +149,10 @@ MODULE_AUTHOR("David S. Miller (davem@redhat.com) and Jeff Garzik (jgarzik@pobox MODULE_DESCRIPTION("Broadcom Tigon3 ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); +MODULE_FIRMWARE(FIRMWARE_TG3); +MODULE_FIRMWARE(FIRMWARE_TG3TSO); +MODULE_FIRMWARE(FIRMWARE_TG3TSO5); + static int tg3_debug = -1; /* -1 == use TG3_DEF_MSG_ENABLE as value */ module_param(tg3_debug, int, 0); @@ -6205,130 +6214,6 @@ static int tg3_halt(struct tg3 *tp, int kind, int silent) return 0; } -#define TG3_FW_RELEASE_MAJOR 0x0 -#define TG3_FW_RELASE_MINOR 0x0 -#define TG3_FW_RELEASE_FIX 0x0 -#define TG3_FW_START_ADDR 0x08000000 -#define TG3_FW_TEXT_ADDR 0x08000000 -#define TG3_FW_TEXT_LEN 0x9c0 -#define TG3_FW_RODATA_ADDR 0x080009c0 -#define TG3_FW_RODATA_LEN 0x60 -#define TG3_FW_DATA_ADDR 0x08000a40 -#define TG3_FW_DATA_LEN 0x20 -#define TG3_FW_SBSS_ADDR 0x08000a60 -#define TG3_FW_SBSS_LEN 0xc -#define TG3_FW_BSS_ADDR 0x08000a70 -#define TG3_FW_BSS_LEN 0x10 - -static const u32 tg3FwText[(TG3_FW_TEXT_LEN / sizeof(u32)) + 1] = { - 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c1d0800, - 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100000, 0x0e000018, 0x00000000, - 0x0000000d, 0x3c1d0800, 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100034, - 0x0e00021c, 0x00000000, 0x0000000d, 0x00000000, 0x00000000, 0x00000000, - 0x27bdffe0, 0x3c1cc000, 0xafbf0018, 0xaf80680c, 0x0e00004c, 0x241b2105, - 0x97850000, 0x97870002, 0x9782002c, 0x9783002e, 0x3c040800, 0x248409c0, - 0xafa00014, 0x00021400, 0x00621825, 0x00052c00, 0xafa30010, 0x8f860010, - 0x00e52825, 0x0e000060, 0x24070102, 0x3c02ac00, 0x34420100, 0x3c03ac01, - 0x34630100, 0xaf820490, 0x3c02ffff, 0xaf820494, 0xaf830498, 0xaf82049c, - 0x24020001, 0xaf825ce0, 0x0e00003f, 0xaf825d00, 0x0e000140, 0x00000000, - 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x2402ffff, 0xaf825404, 0x8f835400, - 0x34630400, 0xaf835400, 0xaf825404, 0x3c020800, 0x24420034, 0xaf82541c, - 0x03e00008, 0xaf805400, 0x00000000, 0x00000000, 0x3c020800, 0x34423000, - 0x3c030800, 0x34633000, 0x3c040800, 0x348437ff, 0x3c010800, 0xac220a64, - 0x24020040, 0x3c010800, 0xac220a68, 0x3c010800, 0xac200a60, 0xac600000, - 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, - 0x00804821, 0x8faa0010, 0x3c020800, 0x8c420a60, 0x3c040800, 0x8c840a68, - 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010800, 0xac230a60, 0x14400003, - 0x00004021, 0x3c010800, 0xac200a60, 0x3c020800, 0x8c420a60, 0x3c030800, - 0x8c630a64, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001, - 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020800, 0x8c420a60, - 0x3c030800, 0x8c630a64, 0x8f84680c, 0x00021140, 0x00431021, 0xac440008, - 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0x02000008, 0x00000000, 0x0a0001e3, 0x3c0a0001, 0x0a0001e3, 0x3c0a0002, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x3c0a0007, 0x0a0001e3, 0x3c0a0008, 0x0a0001e3, 0x3c0a0009, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000b, - 0x0a0001e3, 0x3c0a000c, 0x0a0001e3, 0x3c0a000d, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a000e, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x00000000, - 0x0a0001e3, 0x00000000, 0x0a0001e3, 0x3c0a0013, 0x0a0001e3, 0x3c0a0014, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0x27bdffe0, 0x00001821, 0x00001021, 0xafbf0018, 0xafb10014, 0xafb00010, - 0x3c010800, 0x00220821, 0xac200a70, 0x3c010800, 0x00220821, 0xac200a74, - 0x3c010800, 0x00220821, 0xac200a78, 0x24630001, 0x1860fff5, 0x2442000c, - 0x24110001, 0x8f906810, 0x32020004, 0x14400005, 0x24040001, 0x3c020800, - 0x8c420a78, 0x18400003, 0x00002021, 0x0e000182, 0x00000000, 0x32020001, - 0x10400003, 0x00000000, 0x0e000169, 0x00000000, 0x0a000153, 0xaf915028, - 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c050800, - 0x8ca50a70, 0x3c060800, 0x8cc60a80, 0x3c070800, 0x8ce70a78, 0x27bdffe0, - 0x3c040800, 0x248409d0, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, - 0x0e00017b, 0x00002021, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x24020001, - 0x8f836810, 0x00821004, 0x00021027, 0x00621824, 0x03e00008, 0xaf836810, - 0x27bdffd8, 0xafbf0024, 0x1080002e, 0xafb00020, 0x8f825cec, 0xafa20018, - 0x8f825cec, 0x3c100800, 0x26100a78, 0xafa2001c, 0x34028000, 0xaf825cec, - 0x8e020000, 0x18400016, 0x00000000, 0x3c020800, 0x94420a74, 0x8fa3001c, - 0x000221c0, 0xac830004, 0x8fa2001c, 0x3c010800, 0x0e000201, 0xac220a74, - 0x10400005, 0x00000000, 0x8e020000, 0x24420001, 0x0a0001df, 0xae020000, - 0x3c020800, 0x8c420a70, 0x00021c02, 0x000321c0, 0x0a0001c5, 0xafa2001c, - 0x0e000201, 0x00000000, 0x1040001f, 0x00000000, 0x8e020000, 0x8fa3001c, - 0x24420001, 0x3c010800, 0xac230a70, 0x3c010800, 0xac230a74, 0x0a0001df, - 0xae020000, 0x3c100800, 0x26100a78, 0x8e020000, 0x18400028, 0x00000000, - 0x0e000201, 0x00000000, 0x14400024, 0x00000000, 0x8e020000, 0x3c030800, - 0x8c630a70, 0x2442ffff, 0xafa3001c, 0x18400006, 0xae020000, 0x00031402, - 0x000221c0, 0x8c820004, 0x3c010800, 0xac220a70, 0x97a2001e, 0x2442ff00, - 0x2c420300, 0x1440000b, 0x24024000, 0x3c040800, 0x248409dc, 0xafa00010, - 0xafa00014, 0x8fa6001c, 0x24050008, 0x0e000060, 0x00003821, 0x0a0001df, - 0x00000000, 0xaf825cf8, 0x3c020800, 0x8c420a40, 0x8fa3001c, 0x24420001, - 0xaf835cf8, 0x3c010800, 0xac220a40, 0x8fbf0024, 0x8fb00020, 0x03e00008, - 0x27bd0028, 0x27bdffe0, 0x3c040800, 0x248409e8, 0x00002821, 0x00003021, - 0x00003821, 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x8fbf0018, - 0x03e00008, 0x27bd0020, 0x8f82680c, 0x8f85680c, 0x00021827, 0x0003182b, - 0x00031823, 0x00431024, 0x00441021, 0x00a2282b, 0x10a00006, 0x00000000, - 0x00401821, 0x8f82680c, 0x0043102b, 0x1440fffd, 0x00000000, 0x03e00008, - 0x00000000, 0x3c040800, 0x8c840000, 0x3c030800, 0x8c630a40, 0x0064102b, - 0x54400002, 0x00831023, 0x00641023, 0x2c420008, 0x03e00008, 0x38420001, - 0x27bdffe0, 0x00802821, 0x3c040800, 0x24840a00, 0x00003021, 0x00003821, - 0xafbf0018, 0xafa00010, 0x0e000060, 0xafa00014, 0x0a000216, 0x00000000, - 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, 0x27bdffe0, 0x3c1cc000, - 0xafbf0018, 0x0e00004c, 0xaf80680c, 0x3c040800, 0x24840a10, 0x03802821, - 0x00003021, 0x00003821, 0xafa00010, 0x0e000060, 0xafa00014, 0x2402ffff, - 0xaf825404, 0x3c0200aa, 0x0e000234, 0xaf825434, 0x8fbf0018, 0x03e00008, - 0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe8, 0xafb00010, - 0x24100001, 0xafbf0014, 0x3c01c003, 0xac200000, 0x8f826810, 0x30422000, - 0x10400003, 0x00000000, 0x0e000246, 0x00000000, 0x0a00023a, 0xaf905428, - 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdfff8, 0x8f845d0c, - 0x3c0200ff, 0x3c030800, 0x8c630a50, 0x3442fff8, 0x00821024, 0x1043001e, - 0x3c0500ff, 0x34a5fff8, 0x3c06c003, 0x3c074000, 0x00851824, 0x8c620010, - 0x3c010800, 0xac230a50, 0x30420008, 0x10400005, 0x00871025, 0x8cc20000, - 0x24420001, 0xacc20000, 0x00871025, 0xaf825d0c, 0x8fa20000, 0x24420001, - 0xafa20000, 0x8fa20000, 0x8fa20000, 0x24420001, 0xafa20000, 0x8fa20000, - 0x8f845d0c, 0x3c030800, 0x8c630a50, 0x00851024, 0x1443ffe8, 0x00851824, - 0x27bd0008, 0x03e00008, 0x00000000, 0x00000000, 0x00000000 -}; - -static const u32 tg3FwRodata[(TG3_FW_RODATA_LEN / sizeof(u32)) + 1] = { - 0x35373031, 0x726c7341, 0x00000000, 0x00000000, 0x53774576, 0x656e7430, - 0x00000000, 0x726c7045, 0x76656e74, 0x31000000, 0x556e6b6e, 0x45766e74, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x66617461, 0x6c457272, - 0x00000000, 0x00000000, 0x4d61696e, 0x43707542, 0x00000000, 0x00000000, - 0x00000000 -}; - -#if 0 /* All zeros, don't eat up space with it. */ -u32 tg3FwData[(TG3_FW_DATA_LEN / sizeof(u32)) + 1] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; -#endif - #define RX_CPU_SCRATCH_BASE 0x30000 #define RX_CPU_SCRATCH_SIZE 0x04000 #define TX_CPU_SCRATCH_BASE 0x34000 @@ -6383,15 +6268,9 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset) } struct fw_info { - unsigned int text_base; - unsigned int text_len; - const u32 *text_data; - unsigned int rodata_base; - unsigned int rodata_len; - const u32 *rodata_data; - unsigned int data_base; - unsigned int data_len; - const u32 *data_data; + unsigned int fw_base; + unsigned int fw_len; + const __be32 *fw_data; }; /* tp->lock is held. */ @@ -6428,24 +6307,11 @@ static int tg3_load_firmware_cpu(struct tg3 *tp, u32 cpu_base, u32 cpu_scratch_b write_op(tp, cpu_scratch_base + i, 0); tw32(cpu_base + CPU_STATE, 0xffffffff); tw32(cpu_base + CPU_MODE, tr32(cpu_base+CPU_MODE)|CPU_MODE_HALT); - for (i = 0; i < (info->text_len / sizeof(u32)); i++) - write_op(tp, (cpu_scratch_base + - (info->text_base & 0xffff) + - (i * sizeof(u32))), - (info->text_data ? - info->text_data[i] : 0)); - for (i = 0; i < (info->rodata_len / sizeof(u32)); i++) - write_op(tp, (cpu_scratch_base + - (info->rodata_base & 0xffff) + - (i * sizeof(u32))), - (info->rodata_data ? - info->rodata_data[i] : 0)); - for (i = 0; i < (info->data_len / sizeof(u32)); i++) + for (i = 0; i < (info->fw_len / sizeof(u32)); i++) write_op(tp, (cpu_scratch_base + - (info->data_base & 0xffff) + + (info->fw_base & 0xffff) + (i * sizeof(u32))), - (info->data_data ? - info->data_data[i] : 0)); + be32_to_cpu(info->fw_data[i])); err = 0; @@ -6457,17 +6323,20 @@ out: static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) { struct fw_info info; + const __be32 *fw_data; int err, i; - info.text_base = TG3_FW_TEXT_ADDR; - info.text_len = TG3_FW_TEXT_LEN; - info.text_data = &tg3FwText[0]; - info.rodata_base = TG3_FW_RODATA_ADDR; - info.rodata_len = TG3_FW_RODATA_LEN; - info.rodata_data = &tg3FwRodata[0]; - info.data_base = TG3_FW_DATA_ADDR; - info.data_len = TG3_FW_DATA_LEN; - info.data_data = NULL; + fw_data = (void *)tp->fw->data; + + /* Firmware blob starts with version numbers, followed by + start address and length. We are setting complete length. + length = end_address_of_bss - start_address_of_text. + Remainder is the blob to be loaded contiguously + from start address. */ + + info.fw_base = be32_to_cpu(fw_data[1]); + info.fw_len = tp->fw->size - 12; + info.fw_data = &fw_data[3]; err = tg3_load_firmware_cpu(tp, RX_CPU_BASE, RX_CPU_SCRATCH_BASE, RX_CPU_SCRATCH_SIZE, @@ -6483,21 +6352,21 @@ static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) /* Now startup only the RX cpu. */ tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); - tw32_f(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); + tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base); for (i = 0; i < 5; i++) { - if (tr32(RX_CPU_BASE + CPU_PC) == TG3_FW_TEXT_ADDR) + if (tr32(RX_CPU_BASE + CPU_PC) == info.fw_base) break; tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); tw32(RX_CPU_BASE + CPU_MODE, CPU_MODE_HALT); - tw32_f(RX_CPU_BASE + CPU_PC, TG3_FW_TEXT_ADDR); + tw32_f(RX_CPU_BASE + CPU_PC, info.fw_base); udelay(1000); } if (i >= 5) { printk(KERN_ERR PFX "tg3_load_firmware fails for %s " "to set RX CPU PC, is %08x should be %08x\n", tp->dev->name, tr32(RX_CPU_BASE + CPU_PC), - TG3_FW_TEXT_ADDR); + info.fw_base); return -ENODEV; } tw32(RX_CPU_BASE + CPU_STATE, 0xffffffff); @@ -6506,547 +6375,36 @@ static int tg3_load_5701_a0_firmware_fix(struct tg3 *tp) return 0; } - -#define TG3_TSO_FW_RELEASE_MAJOR 0x1 -#define TG3_TSO_FW_RELASE_MINOR 0x6 -#define TG3_TSO_FW_RELEASE_FIX 0x0 -#define TG3_TSO_FW_START_ADDR 0x08000000 -#define TG3_TSO_FW_TEXT_ADDR 0x08000000 -#define TG3_TSO_FW_TEXT_LEN 0x1aa0 -#define TG3_TSO_FW_RODATA_ADDR 0x08001aa0 -#define TG3_TSO_FW_RODATA_LEN 0x60 -#define TG3_TSO_FW_DATA_ADDR 0x08001b20 -#define TG3_TSO_FW_DATA_LEN 0x30 -#define TG3_TSO_FW_SBSS_ADDR 0x08001b50 -#define TG3_TSO_FW_SBSS_LEN 0x2c -#define TG3_TSO_FW_BSS_ADDR 0x08001b80 -#define TG3_TSO_FW_BSS_LEN 0x894 - -static const u32 tg3TsoFwText[(TG3_TSO_FW_TEXT_LEN / 4) + 1] = { - 0x0e000003, 0x00000000, 0x08001b24, 0x00000000, 0x10000003, 0x00000000, - 0x0000000d, 0x0000000d, 0x3c1d0800, 0x37bd4000, 0x03a0f021, 0x3c100800, - 0x26100000, 0x0e000010, 0x00000000, 0x0000000d, 0x27bdffe0, 0x3c04fefe, - 0xafbf0018, 0x0e0005d8, 0x34840002, 0x0e000668, 0x00000000, 0x3c030800, - 0x90631b68, 0x24020002, 0x3c040800, 0x24841aac, 0x14620003, 0x24050001, - 0x3c040800, 0x24841aa0, 0x24060006, 0x00003821, 0xafa00010, 0x0e00067c, - 0xafa00014, 0x8f625c50, 0x34420001, 0xaf625c50, 0x8f625c90, 0x34420001, - 0xaf625c90, 0x2402ffff, 0x0e000034, 0xaf625404, 0x8fbf0018, 0x03e00008, - 0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe0, 0xafbf001c, - 0xafb20018, 0xafb10014, 0x0e00005b, 0xafb00010, 0x24120002, 0x24110001, - 0x8f706820, 0x32020100, 0x10400003, 0x00000000, 0x0e0000bb, 0x00000000, - 0x8f706820, 0x32022000, 0x10400004, 0x32020001, 0x0e0001f0, 0x24040001, - 0x32020001, 0x10400003, 0x00000000, 0x0e0000a3, 0x00000000, 0x3c020800, - 0x90421b98, 0x14520003, 0x00000000, 0x0e0004c0, 0x00000000, 0x0a00003c, - 0xaf715028, 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, - 0x27bd0020, 0x27bdffe0, 0x3c040800, 0x24841ac0, 0x00002821, 0x00003021, - 0x00003821, 0xafbf0018, 0xafa00010, 0x0e00067c, 0xafa00014, 0x3c040800, - 0x248423d8, 0xa4800000, 0x3c010800, 0xa0201b98, 0x3c010800, 0xac201b9c, - 0x3c010800, 0xac201ba0, 0x3c010800, 0xac201ba4, 0x3c010800, 0xac201bac, - 0x3c010800, 0xac201bb8, 0x3c010800, 0xac201bbc, 0x8f624434, 0x3c010800, - 0xac221b88, 0x8f624438, 0x3c010800, 0xac221b8c, 0x8f624410, 0xac80f7a8, - 0x3c010800, 0xac201b84, 0x3c010800, 0xac2023e0, 0x3c010800, 0xac2023c8, - 0x3c010800, 0xac2023cc, 0x3c010800, 0xac202400, 0x3c010800, 0xac221b90, - 0x8f620068, 0x24030007, 0x00021702, 0x10430005, 0x00000000, 0x8f620068, - 0x00021702, 0x14400004, 0x24020001, 0x3c010800, 0x0a000097, 0xac20240c, - 0xac820034, 0x3c040800, 0x24841acc, 0x3c050800, 0x8ca5240c, 0x00003021, - 0x00003821, 0xafa00010, 0x0e00067c, 0xafa00014, 0x8fbf0018, 0x03e00008, - 0x27bd0020, 0x27bdffe0, 0x3c040800, 0x24841ad8, 0x00002821, 0x00003021, - 0x00003821, 0xafbf0018, 0xafa00010, 0x0e00067c, 0xafa00014, 0x0e00005b, - 0x00000000, 0x0e0000b4, 0x00002021, 0x8fbf0018, 0x03e00008, 0x27bd0020, - 0x24020001, 0x8f636820, 0x00821004, 0x00021027, 0x00621824, 0x03e00008, - 0xaf636820, 0x27bdffd0, 0xafbf002c, 0xafb60028, 0xafb50024, 0xafb40020, - 0xafb3001c, 0xafb20018, 0xafb10014, 0xafb00010, 0x8f675c5c, 0x3c030800, - 0x24631bbc, 0x8c620000, 0x14470005, 0x3c0200ff, 0x3c020800, 0x90421b98, - 0x14400119, 0x3c0200ff, 0x3442fff8, 0x00e28824, 0xac670000, 0x00111902, - 0x306300ff, 0x30e20003, 0x000211c0, 0x00622825, 0x00a04021, 0x00071602, - 0x3c030800, 0x90631b98, 0x3044000f, 0x14600036, 0x00804821, 0x24020001, - 0x3c010800, 0xa0221b98, 0x00051100, 0x00821025, 0x3c010800, 0xac201b9c, - 0x3c010800, 0xac201ba0, 0x3c010800, 0xac201ba4, 0x3c010800, 0xac201bac, - 0x3c010800, 0xac201bb8, 0x3c010800, 0xac201bb0, 0x3c010800, 0xac201bb4, - 0x3c010800, 0xa42223d8, 0x9622000c, 0x30437fff, 0x3c010800, 0xa4222410, - 0x30428000, 0x3c010800, 0xa4231bc6, 0x10400005, 0x24020001, 0x3c010800, - 0xac2223f4, 0x0a000102, 0x2406003e, 0x24060036, 0x3c010800, 0xac2023f4, - 0x9622000a, 0x3c030800, 0x94631bc6, 0x3c010800, 0xac2023f0, 0x3c010800, - 0xac2023f8, 0x00021302, 0x00021080, 0x00c21021, 0x00621821, 0x3c010800, - 0xa42223d0, 0x3c010800, 0x0a000115, 0xa4231b96, 0x9622000c, 0x3c010800, - 0xa42223ec, 0x3c040800, 0x24841b9c, 0x8c820000, 0x00021100, 0x3c010800, - 0x00220821, 0xac311bc8, 0x8c820000, 0x00021100, 0x3c010800, 0x00220821, - 0xac271bcc, 0x8c820000, 0x25030001, 0x306601ff, 0x00021100, 0x3c010800, - 0x00220821, 0xac261bd0, 0x8c820000, 0x00021100, 0x3c010800, 0x00220821, - 0xac291bd4, 0x96230008, 0x3c020800, 0x8c421bac, 0x00432821, 0x3c010800, - 0xac251bac, 0x9622000a, 0x30420004, 0x14400018, 0x00061100, 0x8f630c14, - 0x3063000f, 0x2c620002, 0x1440000b, 0x3c02c000, 0x8f630c14, 0x3c020800, - 0x8c421b40, 0x3063000f, 0x24420001, 0x3c010800, 0xac221b40, 0x2c620002, - 0x1040fff7, 0x3c02c000, 0x00e21825, 0xaf635c5c, 0x8f625c50, 0x30420002, - 0x10400014, 0x00000000, 0x0a000147, 0x00000000, 0x3c030800, 0x8c631b80, - 0x3c040800, 0x94841b94, 0x01221025, 0x3c010800, 0xa42223da, 0x24020001, - 0x3c010800, 0xac221bb8, 0x24630001, 0x0085202a, 0x3c010800, 0x10800003, - 0xac231b80, 0x3c010800, 0xa4251b94, 0x3c060800, 0x24c61b9c, 0x8cc20000, - 0x24420001, 0xacc20000, 0x28420080, 0x14400005, 0x00000000, 0x0e000656, - 0x24040002, 0x0a0001e6, 0x00000000, 0x3c020800, 0x8c421bb8, 0x10400078, - 0x24020001, 0x3c050800, 0x90a51b98, 0x14a20072, 0x00000000, 0x3c150800, - 0x96b51b96, 0x3c040800, 0x8c841bac, 0x32a3ffff, 0x0083102a, 0x1440006c, - 0x00000000, 0x14830003, 0x00000000, 0x3c010800, 0xac2523f0, 0x1060005c, - 0x00009021, 0x24d60004, 0x0060a021, 0x24d30014, 0x8ec20000, 0x00028100, - 0x3c110800, 0x02308821, 0x0e000625, 0x8e311bc8, 0x00402821, 0x10a00054, - 0x00000000, 0x9628000a, 0x31020040, 0x10400005, 0x2407180c, 0x8e22000c, - 0x2407188c, 0x00021400, 0xaca20018, 0x3c030800, 0x00701821, 0x8c631bd0, - 0x3c020800, 0x00501021, 0x8c421bd4, 0x00031d00, 0x00021400, 0x00621825, - 0xaca30014, 0x8ec30004, 0x96220008, 0x00432023, 0x3242ffff, 0x3083ffff, - 0x00431021, 0x0282102a, 0x14400002, 0x02b23023, 0x00803021, 0x8e620000, - 0x30c4ffff, 0x00441021, 0xae620000, 0x8e220000, 0xaca20000, 0x8e220004, - 0x8e63fff4, 0x00431021, 0xaca20004, 0xa4a6000e, 0x8e62fff4, 0x00441021, - 0xae62fff4, 0x96230008, 0x0043102a, 0x14400005, 0x02469021, 0x8e62fff0, - 0xae60fff4, 0x24420001, 0xae62fff0, 0xaca00008, 0x3242ffff, 0x14540008, - 0x24020305, 0x31020080, 0x54400001, 0x34e70010, 0x24020905, 0xa4a2000c, - 0x0a0001cb, 0x34e70020, 0xa4a2000c, 0x3c020800, 0x8c4223f0, 0x10400003, - 0x3c024b65, 0x0a0001d3, 0x34427654, 0x3c02b49a, 0x344289ab, 0xaca2001c, - 0x30e2ffff, 0xaca20010, 0x0e0005a2, 0x00a02021, 0x3242ffff, 0x0054102b, - 0x1440ffa9, 0x00000000, 0x24020002, 0x3c010800, 0x0a0001e6, 0xa0221b98, - 0x8ec2083c, 0x24420001, 0x0a0001e6, 0xaec2083c, 0x0e0004c0, 0x00000000, - 0x8fbf002c, 0x8fb60028, 0x8fb50024, 0x8fb40020, 0x8fb3001c, 0x8fb20018, - 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0030, 0x27bdffd0, 0xafbf0028, - 0xafb30024, 0xafb20020, 0xafb1001c, 0xafb00018, 0x8f725c9c, 0x3c0200ff, - 0x3442fff8, 0x3c070800, 0x24e71bb4, 0x02428824, 0x9623000e, 0x8ce20000, - 0x00431021, 0xace20000, 0x8e220010, 0x30420020, 0x14400011, 0x00809821, - 0x0e00063b, 0x02202021, 0x3c02c000, 0x02421825, 0xaf635c9c, 0x8f625c90, - 0x30420002, 0x1040011e, 0x00000000, 0xaf635c9c, 0x8f625c90, 0x30420002, - 0x10400119, 0x00000000, 0x0a00020d, 0x00000000, 0x8e240008, 0x8e230014, - 0x00041402, 0x000231c0, 0x00031502, 0x304201ff, 0x2442ffff, 0x3042007f, - 0x00031942, 0x30637800, 0x00021100, 0x24424000, 0x00624821, 0x9522000a, - 0x3084ffff, 0x30420008, 0x104000b0, 0x000429c0, 0x3c020800, 0x8c422400, - 0x14400024, 0x24c50008, 0x94c20014, 0x3c010800, 0xa42223d0, 0x8cc40010, - 0x00041402, 0x3c010800, 0xa42223d2, 0x3c010800, 0xa42423d4, 0x94c2000e, - 0x3083ffff, 0x00431023, 0x3c010800, 0xac222408, 0x94c2001a, 0x3c010800, - 0xac262400, 0x3c010800, 0xac322404, 0x3c010800, 0xac2223fc, 0x3c02c000, - 0x02421825, 0xaf635c9c, 0x8f625c90, 0x30420002, 0x104000e5, 0x00000000, - 0xaf635c9c, 0x8f625c90, 0x30420002, 0x104000e0, 0x00000000, 0x0a000246, - 0x00000000, 0x94c2000e, 0x3c030800, 0x946323d4, 0x00434023, 0x3103ffff, - 0x2c620008, 0x1040001c, 0x00000000, 0x94c20014, 0x24420028, 0x00a22821, - 0x00031042, 0x1840000b, 0x00002021, 0x24e60848, 0x00403821, 0x94a30000, - 0x8cc20000, 0x24840001, 0x00431021, 0xacc20000, 0x0087102a, 0x1440fff9, - 0x24a50002, 0x31020001, 0x1040001f, 0x3c024000, 0x3c040800, 0x248423fc, - 0xa0a00001, 0x94a30000, 0x8c820000, 0x00431021, 0x0a000285, 0xac820000, - 0x8f626800, 0x3c030010, 0x00431024, 0x10400009, 0x00000000, 0x94c2001a, - 0x3c030800, 0x8c6323fc, 0x00431021, 0x3c010800, 0xac2223fc, 0x0a000286, - 0x3c024000, 0x94c2001a, 0x94c4001c, 0x3c030800, 0x8c6323fc, 0x00441023, - 0x00621821, 0x3c010800, 0xac2323fc, 0x3c024000, 0x02421825, 0xaf635c9c, - 0x8f625c90, 0x30420002, 0x1440fffc, 0x00000000, 0x9522000a, 0x30420010, - 0x1040009b, 0x00000000, 0x3c030800, 0x946323d4, 0x3c070800, 0x24e72400, - 0x8ce40000, 0x8f626800, 0x24630030, 0x00832821, 0x3c030010, 0x00431024, - 0x1440000a, 0x00000000, 0x94a20004, 0x3c040800, 0x8c842408, 0x3c030800, - 0x8c6323fc, 0x00441023, 0x00621821, 0x3c010800, 0xac2323fc, 0x3c040800, - 0x8c8423fc, 0x00041c02, 0x3082ffff, 0x00622021, 0x00041402, 0x00822021, - 0x00041027, 0xa4a20006, 0x3c030800, 0x8c632404, 0x3c0200ff, 0x3442fff8, - 0x00628824, 0x96220008, 0x24050001, 0x24034000, 0x000231c0, 0x00801021, - 0xa4c2001a, 0xa4c0001c, 0xace00000, 0x3c010800, 0xac251b60, 0xaf635cb8, - 0x8f625cb0, 0x30420002, 0x10400003, 0x00000000, 0x3c010800, 0xac201b60, - 0x8e220008, 0xaf625cb8, 0x8f625cb0, 0x30420002, 0x10400003, 0x00000000, - 0x3c010800, 0xac201b60, 0x3c020800, 0x8c421b60, 0x1040ffec, 0x00000000, - 0x3c040800, 0x0e00063b, 0x8c842404, 0x0a00032a, 0x00000000, 0x3c030800, - 0x90631b98, 0x24020002, 0x14620003, 0x3c034b65, 0x0a0002e1, 0x00008021, - 0x8e22001c, 0x34637654, 0x10430002, 0x24100002, 0x24100001, 0x00c02021, - 0x0e000350, 0x02003021, 0x24020003, 0x3c010800, 0xa0221b98, 0x24020002, - 0x1202000a, 0x24020001, 0x3c030800, 0x8c6323f0, 0x10620006, 0x00000000, - 0x3c020800, 0x944223d8, 0x00021400, 0x0a00031f, 0xae220014, 0x3c040800, - 0x248423da, 0x94820000, 0x00021400, 0xae220014, 0x3c020800, 0x8c421bbc, - 0x3c03c000, 0x3c010800, 0xa0201b98, 0x00431025, 0xaf625c5c, 0x8f625c50, - 0x30420002, 0x10400009, 0x00000000, 0x2484f7e2, 0x8c820000, 0x00431025, - 0xaf625c5c, 0x8f625c50, 0x30420002, 0x1440fffa, 0x00000000, 0x3c020800, - 0x24421b84, 0x8c430000, 0x24630001, 0xac430000, 0x8f630c14, 0x3063000f, - 0x2c620002, 0x1440000c, 0x3c024000, 0x8f630c14, 0x3c020800, 0x8c421b40, - 0x3063000f, 0x24420001, 0x3c010800, 0xac221b40, 0x2c620002, 0x1040fff7, - 0x00000000, 0x3c024000, 0x02421825, 0xaf635c9c, 0x8f625c90, 0x30420002, - 0x1440fffc, 0x00000000, 0x12600003, 0x00000000, 0x0e0004c0, 0x00000000, - 0x8fbf0028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x03e00008, - 0x27bd0030, 0x8f634450, 0x3c040800, 0x24841b88, 0x8c820000, 0x00031c02, - 0x0043102b, 0x14400007, 0x3c038000, 0x8c840004, 0x8f624450, 0x00021c02, - 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, 0x00431024, - 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, 0x3c024000, - 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, 0x00000000, - 0x03e00008, 0x00000000, 0x27bdffe0, 0x00805821, 0x14c00011, 0x256e0008, - 0x3c020800, 0x8c4223f4, 0x10400007, 0x24020016, 0x3c010800, 0xa42223d2, - 0x2402002a, 0x3c010800, 0x0a000364, 0xa42223d4, 0x8d670010, 0x00071402, - 0x3c010800, 0xa42223d2, 0x3c010800, 0xa42723d4, 0x3c040800, 0x948423d4, - 0x3c030800, 0x946323d2, 0x95cf0006, 0x3c020800, 0x944223d0, 0x00832023, - 0x01e2c023, 0x3065ffff, 0x24a20028, 0x01c24821, 0x3082ffff, 0x14c0001a, - 0x01226021, 0x9582000c, 0x3042003f, 0x3c010800, 0xa42223d6, 0x95820004, - 0x95830006, 0x3c010800, 0xac2023e4, 0x3c010800, 0xac2023e8, 0x00021400, - 0x00431025, 0x3c010800, 0xac221bc0, 0x95220004, 0x3c010800, 0xa4221bc4, - 0x95230002, 0x01e51023, 0x0043102a, 0x10400010, 0x24020001, 0x3c010800, - 0x0a000398, 0xac2223f8, 0x3c030800, 0x8c6323e8, 0x3c020800, 0x94421bc4, - 0x00431021, 0xa5220004, 0x3c020800, 0x94421bc0, 0xa5820004, 0x3c020800, - 0x8c421bc0, 0xa5820006, 0x3c020800, 0x8c4223f0, 0x3c0d0800, 0x8dad23e4, - 0x3c0a0800, 0x144000e5, 0x8d4a23e8, 0x3c020800, 0x94421bc4, 0x004a1821, - 0x3063ffff, 0x0062182b, 0x24020002, 0x10c2000d, 0x01435023, 0x3c020800, - 0x944223d6, 0x30420009, 0x10400008, 0x00000000, 0x9582000c, 0x3042fff6, - 0xa582000c, 0x3c020800, 0x944223d6, 0x30420009, 0x01a26823, 0x3c020800, - 0x8c4223f8, 0x1040004a, 0x01203821, 0x3c020800, 0x944223d2, 0x00004021, - 0xa520000a, 0x01e21023, 0xa5220002, 0x3082ffff, 0x00021042, 0x18400008, - 0x00003021, 0x00401821, 0x94e20000, 0x25080001, 0x00c23021, 0x0103102a, - 0x1440fffb, 0x24e70002, 0x00061c02, 0x30c2ffff, 0x00623021, 0x00061402, - 0x00c23021, 0x00c02821, 0x00061027, 0xa522000a, 0x00003021, 0x2527000c, - 0x00004021, 0x94e20000, 0x25080001, 0x00c23021, 0x2d020004, 0x1440fffb, - 0x24e70002, 0x95220002, 0x00004021, 0x91230009, 0x00442023, 0x01803821, - 0x3082ffff, 0xa4e00010, 0x00621821, 0x00021042, 0x18400010, 0x00c33021, - 0x00404821, 0x94e20000, 0x24e70002, 0x00c23021, 0x30e2007f, 0x14400006, - 0x25080001, 0x8d630000, 0x3c02007f, 0x3442ff80, 0x00625824, 0x25670008, - 0x0109102a, 0x1440fff3, 0x00000000, 0x30820001, 0x10400005, 0x00061c02, - 0xa0e00001, 0x94e20000, 0x00c23021, 0x00061c02, 0x30c2ffff, 0x00623021, - 0x00061402, 0x00c23021, 0x0a00047d, 0x30c6ffff, 0x24020002, 0x14c20081, - 0x00000000, 0x3c020800, 0x8c42240c, 0x14400007, 0x00000000, 0x3c020800, - 0x944223d2, 0x95230002, 0x01e21023, 0x10620077, 0x00000000, 0x3c020800, - 0x944223d2, 0x01e21023, 0xa5220002, 0x3c020800, 0x8c42240c, 0x1040001a, - 0x31e3ffff, 0x8dc70010, 0x3c020800, 0x94421b96, 0x00e04021, 0x00072c02, - 0x00aa2021, 0x00431023, 0x00823823, 0x00072402, 0x30e2ffff, 0x00823821, - 0x00071027, 0xa522000a, 0x3102ffff, 0x3c040800, 0x948423d4, 0x00453023, - 0x00e02821, 0x00641823, 0x006d1821, 0x00c33021, 0x00061c02, 0x30c2ffff, - 0x0a00047d, 0x00623021, 0x01203821, 0x00004021, 0x3082ffff, 0x00021042, - 0x18400008, 0x00003021, 0x00401821, 0x94e20000, 0x25080001, 0x00c23021, - 0x0103102a, 0x1440fffb, 0x24e70002, 0x00061c02, 0x30c2ffff, 0x00623021, - 0x00061402, 0x00c23021, 0x00c02821, 0x00061027, 0xa522000a, 0x00003021, - 0x2527000c, 0x00004021, 0x94e20000, 0x25080001, 0x00c23021, 0x2d020004, - 0x1440fffb, 0x24e70002, 0x95220002, 0x00004021, 0x91230009, 0x00442023, - 0x01803821, 0x3082ffff, 0xa4e00010, 0x3c040800, 0x948423d4, 0x00621821, - 0x00c33021, 0x00061c02, 0x30c2ffff, 0x00623021, 0x00061c02, 0x3c020800, - 0x944223d0, 0x00c34821, 0x00441023, 0x00021fc2, 0x00431021, 0x00021043, - 0x18400010, 0x00003021, 0x00402021, 0x94e20000, 0x24e70002, 0x00c23021, - 0x30e2007f, 0x14400006, 0x25080001, 0x8d630000, 0x3c02007f, 0x3442ff80, - 0x00625824, 0x25670008, 0x0104102a, 0x1440fff3, 0x00000000, 0x3c020800, - 0x944223ec, 0x00c23021, 0x3122ffff, 0x00c23021, 0x00061c02, 0x30c2ffff, - 0x00623021, 0x00061402, 0x00c23021, 0x00c04021, 0x00061027, 0xa5820010, - 0xadc00014, 0x0a00049d, 0xadc00000, 0x8dc70010, 0x00e04021, 0x11400007, - 0x00072c02, 0x00aa3021, 0x00061402, 0x30c3ffff, 0x00433021, 0x00061402, - 0x00c22821, 0x00051027, 0xa522000a, 0x3c030800, 0x946323d4, 0x3102ffff, - 0x01e21021, 0x00433023, 0x00cd3021, 0x00061c02, 0x30c2ffff, 0x00623021, - 0x00061402, 0x00c23021, 0x00c04021, 0x00061027, 0xa5820010, 0x3102ffff, - 0x00051c00, 0x00431025, 0xadc20010, 0x3c020800, 0x8c4223f4, 0x10400005, - 0x2de205eb, 0x14400002, 0x25e2fff2, 0x34028870, 0xa5c20034, 0x3c030800, - 0x246323e8, 0x8c620000, 0x24420001, 0xac620000, 0x3c040800, 0x8c8423e4, - 0x3c020800, 0x8c421bc0, 0x3303ffff, 0x00832021, 0x00431821, 0x0062102b, - 0x3c010800, 0xac2423e4, 0x10400003, 0x2482ffff, 0x3c010800, 0xac2223e4, - 0x3c010800, 0xac231bc0, 0x03e00008, 0x27bd0020, 0x27bdffb8, 0x3c050800, - 0x24a51b96, 0xafbf0044, 0xafbe0040, 0xafb7003c, 0xafb60038, 0xafb50034, - 0xafb40030, 0xafb3002c, 0xafb20028, 0xafb10024, 0xafb00020, 0x94a90000, - 0x3c020800, 0x944223d0, 0x3c030800, 0x8c631bb0, 0x3c040800, 0x8c841bac, - 0x01221023, 0x0064182a, 0xa7a9001e, 0x106000be, 0xa7a20016, 0x24be0022, - 0x97b6001e, 0x24b3001a, 0x24b70016, 0x8fc20000, 0x14400008, 0x00000000, - 0x8fc2fff8, 0x97a30016, 0x8fc4fff4, 0x00431021, 0x0082202a, 0x148000b0, - 0x00000000, 0x97d50818, 0x32a2ffff, 0x104000a3, 0x00009021, 0x0040a021, - 0x00008821, 0x0e000625, 0x00000000, 0x00403021, 0x14c00007, 0x00000000, - 0x3c020800, 0x8c4223dc, 0x24420001, 0x3c010800, 0x0a000596, 0xac2223dc, - 0x3c100800, 0x02118021, 0x8e101bc8, 0x9608000a, 0x31020040, 0x10400005, - 0x2407180c, 0x8e02000c, 0x2407188c, 0x00021400, 0xacc20018, 0x31020080, - 0x54400001, 0x34e70010, 0x3c020800, 0x00511021, 0x8c421bd0, 0x3c030800, - 0x00711821, 0x8c631bd4, 0x00021500, 0x00031c00, 0x00431025, 0xacc20014, - 0x96040008, 0x3242ffff, 0x00821021, 0x0282102a, 0x14400002, 0x02b22823, - 0x00802821, 0x8e020000, 0x02459021, 0xacc20000, 0x8e020004, 0x00c02021, - 0x26310010, 0xac820004, 0x30e2ffff, 0xac800008, 0xa485000e, 0xac820010, - 0x24020305, 0x0e0005a2, 0xa482000c, 0x3242ffff, 0x0054102b, 0x1440ffc5, - 0x3242ffff, 0x0a00058e, 0x00000000, 0x8e620000, 0x8e63fffc, 0x0043102a, - 0x10400067, 0x00000000, 0x8e62fff0, 0x00028900, 0x3c100800, 0x02118021, - 0x0e000625, 0x8e101bc8, 0x00403021, 0x14c00005, 0x00000000, 0x8e62082c, - 0x24420001, 0x0a000596, 0xae62082c, 0x9608000a, 0x31020040, 0x10400005, - 0x2407180c, 0x8e02000c, 0x2407188c, 0x00021400, 0xacc20018, 0x3c020800, - 0x00511021, 0x8c421bd0, 0x3c030800, 0x00711821, 0x8c631bd4, 0x00021500, - 0x00031c00, 0x00431025, 0xacc20014, 0x8e63fff4, 0x96020008, 0x00432023, - 0x3242ffff, 0x3083ffff, 0x00431021, 0x02c2102a, 0x10400003, 0x00802821, - 0x97a9001e, 0x01322823, 0x8e620000, 0x30a4ffff, 0x00441021, 0xae620000, - 0xa4c5000e, 0x8e020000, 0xacc20000, 0x8e020004, 0x8e63fff4, 0x00431021, - 0xacc20004, 0x8e63fff4, 0x96020008, 0x00641821, 0x0062102a, 0x14400006, - 0x02459021, 0x8e62fff0, 0xae60fff4, 0x24420001, 0x0a000571, 0xae62fff0, - 0xae63fff4, 0xacc00008, 0x3242ffff, 0x10560003, 0x31020004, 0x10400006, - 0x24020305, 0x31020080, 0x54400001, 0x34e70010, 0x34e70020, 0x24020905, - 0xa4c2000c, 0x8ee30000, 0x8ee20004, 0x14620007, 0x3c02b49a, 0x8ee20860, - 0x54400001, 0x34e70400, 0x3c024b65, 0x0a000588, 0x34427654, 0x344289ab, - 0xacc2001c, 0x30e2ffff, 0xacc20010, 0x0e0005a2, 0x00c02021, 0x3242ffff, - 0x0056102b, 0x1440ff9b, 0x00000000, 0x8e620000, 0x8e63fffc, 0x0043102a, - 0x1440ff48, 0x00000000, 0x8fbf0044, 0x8fbe0040, 0x8fb7003c, 0x8fb60038, - 0x8fb50034, 0x8fb40030, 0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, - 0x03e00008, 0x27bd0048, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f624450, - 0x8f634410, 0x0a0005b1, 0x00808021, 0x8f626820, 0x30422000, 0x10400003, - 0x00000000, 0x0e0001f0, 0x00002021, 0x8f624450, 0x8f634410, 0x3042ffff, - 0x0043102b, 0x1440fff5, 0x00000000, 0x8f630c14, 0x3063000f, 0x2c620002, - 0x1440000b, 0x00000000, 0x8f630c14, 0x3c020800, 0x8c421b40, 0x3063000f, - 0x24420001, 0x3c010800, 0xac221b40, 0x2c620002, 0x1040fff7, 0x00000000, - 0xaf705c18, 0x8f625c10, 0x30420002, 0x10400009, 0x00000000, 0x8f626820, - 0x30422000, 0x1040fff8, 0x00000000, 0x0e0001f0, 0x00002021, 0x0a0005c4, - 0x00000000, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x00000000, - 0x00000000, 0x00000000, 0x27bdffe8, 0x3c1bc000, 0xafbf0014, 0xafb00010, - 0xaf60680c, 0x8f626804, 0x34420082, 0xaf626804, 0x8f634000, 0x24020b50, - 0x3c010800, 0xac221b54, 0x24020b78, 0x3c010800, 0xac221b64, 0x34630002, - 0xaf634000, 0x0e000605, 0x00808021, 0x3c010800, 0xa0221b68, 0x304200ff, - 0x24030002, 0x14430005, 0x00000000, 0x3c020800, 0x8c421b54, 0x0a0005f8, - 0xac5000c0, 0x3c020800, 0x8c421b54, 0xac5000bc, 0x8f624434, 0x8f634438, - 0x8f644410, 0x3c010800, 0xac221b5c, 0x3c010800, 0xac231b6c, 0x3c010800, - 0xac241b58, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c040800, - 0x8c870000, 0x3c03aa55, 0x3463aa55, 0x3c06c003, 0xac830000, 0x8cc20000, - 0x14430007, 0x24050002, 0x3c0355aa, 0x346355aa, 0xac830000, 0x8cc20000, - 0x50430001, 0x24050001, 0x3c020800, 0xac470000, 0x03e00008, 0x00a01021, - 0x27bdfff8, 0x18800009, 0x00002821, 0x8f63680c, 0x8f62680c, 0x1043fffe, - 0x00000000, 0x24a50001, 0x00a4102a, 0x1440fff9, 0x00000000, 0x03e00008, - 0x27bd0008, 0x8f634450, 0x3c020800, 0x8c421b5c, 0x00031c02, 0x0043102b, - 0x14400008, 0x3c038000, 0x3c040800, 0x8c841b6c, 0x8f624450, 0x00021c02, - 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, 0x00431024, - 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, 0x3082ffff, - 0x2442e000, 0x2c422001, 0x14400003, 0x3c024000, 0x0a000648, 0x2402ffff, - 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, 0x00001021, - 0x03e00008, 0x00000000, 0x8f624450, 0x3c030800, 0x8c631b58, 0x0a000651, - 0x3042ffff, 0x8f624450, 0x3042ffff, 0x0043102b, 0x1440fffc, 0x00000000, - 0x03e00008, 0x00000000, 0x27bdffe0, 0x00802821, 0x3c040800, 0x24841af0, - 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, 0x0e00067c, 0xafa00014, - 0x0a000660, 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, - 0x00000000, 0x00000000, 0x3c020800, 0x34423000, 0x3c030800, 0x34633000, - 0x3c040800, 0x348437ff, 0x3c010800, 0xac221b74, 0x24020040, 0x3c010800, - 0xac221b78, 0x3c010800, 0xac201b70, 0xac600000, 0x24630004, 0x0083102b, - 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, 0x00804821, 0x8faa0010, - 0x3c020800, 0x8c421b70, 0x3c040800, 0x8c841b78, 0x8fab0014, 0x24430001, - 0x0044102b, 0x3c010800, 0xac231b70, 0x14400003, 0x00004021, 0x3c010800, - 0xac201b70, 0x3c020800, 0x8c421b70, 0x3c030800, 0x8c631b74, 0x91240000, - 0x00021140, 0x00431021, 0x00481021, 0x25080001, 0xa0440000, 0x29020008, - 0x1440fff4, 0x25290001, 0x3c020800, 0x8c421b70, 0x3c030800, 0x8c631b74, - 0x8f64680c, 0x00021140, 0x00431021, 0xac440008, 0xac45000c, 0xac460010, - 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, 0x00000000, 0x00000000, -}; - -static const u32 tg3TsoFwRodata[] = { - 0x4d61696e, 0x43707542, 0x00000000, 0x4d61696e, 0x43707541, 0x00000000, - 0x00000000, 0x00000000, 0x73746b6f, 0x66666c64, 0x496e0000, 0x73746b6f, - 0x66662a2a, 0x00000000, 0x53774576, 0x656e7430, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x66617461, 0x6c457272, 0x00000000, 0x00000000, - 0x00000000, -}; - -static const u32 tg3TsoFwData[] = { - 0x00000000, 0x73746b6f, 0x66666c64, 0x5f76312e, 0x362e3000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, -}; - /* 5705 needs a special version of the TSO firmware. */ -#define TG3_TSO5_FW_RELEASE_MAJOR 0x1 -#define TG3_TSO5_FW_RELASE_MINOR 0x2 -#define TG3_TSO5_FW_RELEASE_FIX 0x0 -#define TG3_TSO5_FW_START_ADDR 0x00010000 -#define TG3_TSO5_FW_TEXT_ADDR 0x00010000 -#define TG3_TSO5_FW_TEXT_LEN 0xe90 -#define TG3_TSO5_FW_RODATA_ADDR 0x00010e90 -#define TG3_TSO5_FW_RODATA_LEN 0x50 -#define TG3_TSO5_FW_DATA_ADDR 0x00010f00 -#define TG3_TSO5_FW_DATA_LEN 0x20 -#define TG3_TSO5_FW_SBSS_ADDR 0x00010f20 -#define TG3_TSO5_FW_SBSS_LEN 0x28 -#define TG3_TSO5_FW_BSS_ADDR 0x00010f50 -#define TG3_TSO5_FW_BSS_LEN 0x88 - -static const u32 tg3Tso5FwText[(TG3_TSO5_FW_TEXT_LEN / 4) + 1] = { - 0x0c004003, 0x00000000, 0x00010f04, 0x00000000, 0x10000003, 0x00000000, - 0x0000000d, 0x0000000d, 0x3c1d0001, 0x37bde000, 0x03a0f021, 0x3c100001, - 0x26100000, 0x0c004010, 0x00000000, 0x0000000d, 0x27bdffe0, 0x3c04fefe, - 0xafbf0018, 0x0c0042e8, 0x34840002, 0x0c004364, 0x00000000, 0x3c030001, - 0x90630f34, 0x24020002, 0x3c040001, 0x24840e9c, 0x14620003, 0x24050001, - 0x3c040001, 0x24840e90, 0x24060002, 0x00003821, 0xafa00010, 0x0c004378, - 0xafa00014, 0x0c00402c, 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, - 0x00000000, 0x00000000, 0x27bdffe0, 0xafbf001c, 0xafb20018, 0xafb10014, - 0x0c0042d4, 0xafb00010, 0x3c128000, 0x24110001, 0x8f706810, 0x32020400, - 0x10400007, 0x00000000, 0x8f641008, 0x00921024, 0x14400003, 0x00000000, - 0x0c004064, 0x00000000, 0x3c020001, 0x90420f56, 0x10510003, 0x32020200, - 0x1040fff1, 0x00000000, 0x0c0041b4, 0x00000000, 0x08004034, 0x00000000, - 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, - 0x27bdffe0, 0x3c040001, 0x24840eb0, 0x00002821, 0x00003021, 0x00003821, - 0xafbf0018, 0xafa00010, 0x0c004378, 0xafa00014, 0x0000d021, 0x24020130, - 0xaf625000, 0x3c010001, 0xa4200f50, 0x3c010001, 0xa0200f57, 0x8fbf0018, - 0x03e00008, 0x27bd0020, 0x00000000, 0x00000000, 0x3c030001, 0x24630f60, - 0x90620000, 0x27bdfff0, 0x14400003, 0x0080c021, 0x08004073, 0x00004821, - 0x3c022000, 0x03021024, 0x10400003, 0x24090002, 0x08004073, 0xa0600000, - 0x24090001, 0x00181040, 0x30431f80, 0x346f8008, 0x1520004b, 0x25eb0028, - 0x3c040001, 0x00832021, 0x8c848010, 0x3c050001, 0x24a50f7a, 0x00041402, - 0xa0a20000, 0x3c010001, 0xa0240f7b, 0x3c020001, 0x00431021, 0x94428014, - 0x3c010001, 0xa0220f7c, 0x3c0c0001, 0x01836021, 0x8d8c8018, 0x304200ff, - 0x24420008, 0x000220c3, 0x24020001, 0x3c010001, 0xa0220f60, 0x0124102b, - 0x1040000c, 0x00003821, 0x24a6000e, 0x01602821, 0x8ca20000, 0x8ca30004, - 0x24a50008, 0x24e70001, 0xacc20000, 0xacc30004, 0x00e4102b, 0x1440fff8, - 0x24c60008, 0x00003821, 0x3c080001, 0x25080f7b, 0x91060000, 0x3c020001, - 0x90420f7c, 0x2503000d, 0x00c32821, 0x00461023, 0x00021fc2, 0x00431021, - 0x00021043, 0x1840000c, 0x00002021, 0x91020001, 0x00461023, 0x00021fc2, - 0x00431021, 0x00021843, 0x94a20000, 0x24e70001, 0x00822021, 0x00e3102a, - 0x1440fffb, 0x24a50002, 0x00041c02, 0x3082ffff, 0x00622021, 0x00041402, - 0x00822021, 0x3c02ffff, 0x01821024, 0x3083ffff, 0x00431025, 0x3c010001, - 0x080040fa, 0xac220f80, 0x3c050001, 0x24a50f7c, 0x90a20000, 0x3c0c0001, - 0x01836021, 0x8d8c8018, 0x000220c2, 0x1080000e, 0x00003821, 0x01603021, - 0x24a5000c, 0x8ca20000, 0x8ca30004, 0x24a50008, 0x24e70001, 0xacc20000, - 0xacc30004, 0x00e4102b, 0x1440fff8, 0x24c60008, 0x3c050001, 0x24a50f7c, - 0x90a20000, 0x30430007, 0x24020004, 0x10620011, 0x28620005, 0x10400005, - 0x24020002, 0x10620008, 0x000710c0, 0x080040fa, 0x00000000, 0x24020006, - 0x1062000e, 0x000710c0, 0x080040fa, 0x00000000, 0x00a21821, 0x9463000c, - 0x004b1021, 0x080040fa, 0xa4430000, 0x000710c0, 0x00a21821, 0x8c63000c, - 0x004b1021, 0x080040fa, 0xac430000, 0x00a21821, 0x8c63000c, 0x004b2021, - 0x00a21021, 0xac830000, 0x94420010, 0xa4820004, 0x95e70006, 0x3c020001, - 0x90420f7c, 0x3c030001, 0x90630f7a, 0x00e2c823, 0x3c020001, 0x90420f7b, - 0x24630028, 0x01e34021, 0x24420028, 0x15200012, 0x01e23021, 0x94c2000c, - 0x3c010001, 0xa4220f78, 0x94c20004, 0x94c30006, 0x3c010001, 0xa4200f76, - 0x3c010001, 0xa4200f72, 0x00021400, 0x00431025, 0x3c010001, 0xac220f6c, - 0x95020004, 0x3c010001, 0x08004124, 0xa4220f70, 0x3c020001, 0x94420f70, - 0x3c030001, 0x94630f72, 0x00431021, 0xa5020004, 0x3c020001, 0x94420f6c, - 0xa4c20004, 0x3c020001, 0x8c420f6c, 0xa4c20006, 0x3c040001, 0x94840f72, - 0x3c020001, 0x94420f70, 0x3c0a0001, 0x954a0f76, 0x00441821, 0x3063ffff, - 0x0062182a, 0x24020002, 0x1122000b, 0x00832023, 0x3c030001, 0x94630f78, - 0x30620009, 0x10400006, 0x3062fff6, 0xa4c2000c, 0x3c020001, 0x94420f78, - 0x30420009, 0x01425023, 0x24020001, 0x1122001b, 0x29220002, 0x50400005, - 0x24020002, 0x11200007, 0x31a2ffff, 0x08004197, 0x00000000, 0x1122001d, - 0x24020016, 0x08004197, 0x31a2ffff, 0x3c0e0001, 0x95ce0f80, 0x10800005, - 0x01806821, 0x01c42021, 0x00041c02, 0x3082ffff, 0x00627021, 0x000e1027, - 0xa502000a, 0x3c030001, 0x90630f7b, 0x31a2ffff, 0x00e21021, 0x0800418d, - 0x00432023, 0x3c020001, 0x94420f80, 0x00442021, 0x00041c02, 0x3082ffff, - 0x00622021, 0x00807021, 0x00041027, 0x08004185, 0xa502000a, 0x3c050001, - 0x24a50f7a, 0x90a30000, 0x14620002, 0x24e2fff2, 0xa5e20034, 0x90a20000, - 0x00e21023, 0xa5020002, 0x3c030001, 0x94630f80, 0x3c020001, 0x94420f5a, - 0x30e5ffff, 0x00641821, 0x00451023, 0x00622023, 0x00041c02, 0x3082ffff, - 0x00622021, 0x00041027, 0xa502000a, 0x3c030001, 0x90630f7c, 0x24620001, - 0x14a20005, 0x00807021, 0x01631021, 0x90420000, 0x08004185, 0x00026200, - 0x24620002, 0x14a20003, 0x306200fe, 0x004b1021, 0x944c0000, 0x3c020001, - 0x94420f82, 0x3183ffff, 0x3c040001, 0x90840f7b, 0x00431021, 0x00e21021, - 0x00442023, 0x008a2021, 0x00041c02, 0x3082ffff, 0x00622021, 0x00041402, - 0x00822021, 0x00806821, 0x00041027, 0xa4c20010, 0x31a2ffff, 0x000e1c00, - 0x00431025, 0x3c040001, 0x24840f72, 0xade20010, 0x94820000, 0x3c050001, - 0x94a50f76, 0x3c030001, 0x8c630f6c, 0x24420001, 0x00b92821, 0xa4820000, - 0x3322ffff, 0x00622021, 0x0083182b, 0x3c010001, 0xa4250f76, 0x10600003, - 0x24a2ffff, 0x3c010001, 0xa4220f76, 0x3c024000, 0x03021025, 0x3c010001, - 0xac240f6c, 0xaf621008, 0x03e00008, 0x27bd0010, 0x3c030001, 0x90630f56, - 0x27bdffe8, 0x24020001, 0xafbf0014, 0x10620026, 0xafb00010, 0x8f620cf4, - 0x2442ffff, 0x3042007f, 0x00021100, 0x8c434000, 0x3c010001, 0xac230f64, - 0x8c434008, 0x24444000, 0x8c5c4004, 0x30620040, 0x14400002, 0x24020088, - 0x24020008, 0x3c010001, 0xa4220f68, 0x30620004, 0x10400005, 0x24020001, - 0x3c010001, 0xa0220f57, 0x080041d5, 0x00031402, 0x3c010001, 0xa0200f57, - 0x00031402, 0x3c010001, 0xa4220f54, 0x9483000c, 0x24020001, 0x3c010001, - 0xa4200f50, 0x3c010001, 0xa0220f56, 0x3c010001, 0xa4230f62, 0x24020001, - 0x1342001e, 0x00000000, 0x13400005, 0x24020003, 0x13420067, 0x00000000, - 0x080042cf, 0x00000000, 0x3c020001, 0x94420f62, 0x241a0001, 0x3c010001, - 0xa4200f5e, 0x3c010001, 0xa4200f52, 0x304407ff, 0x00021bc2, 0x00031823, - 0x3063003e, 0x34630036, 0x00021242, 0x3042003c, 0x00621821, 0x3c010001, - 0xa4240f58, 0x00832021, 0x24630030, 0x3c010001, 0xa4240f5a, 0x3c010001, - 0xa4230f5c, 0x3c060001, 0x24c60f52, 0x94c50000, 0x94c30002, 0x3c040001, - 0x94840f5a, 0x00651021, 0x0044102a, 0x10400013, 0x3c108000, 0x00a31021, - 0xa4c20000, 0x3c02a000, 0xaf620cf4, 0x3c010001, 0xa0200f56, 0x8f641008, - 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, - 0x00501024, 0x104000b7, 0x00000000, 0x0800420f, 0x00000000, 0x3c030001, - 0x94630f50, 0x00851023, 0xa4c40000, 0x00621821, 0x3042ffff, 0x3c010001, - 0xa4230f50, 0xaf620ce8, 0x3c020001, 0x94420f68, 0x34420024, 0xaf620cec, - 0x94c30002, 0x3c020001, 0x94420f50, 0x14620012, 0x3c028000, 0x3c108000, - 0x3c02a000, 0xaf620cf4, 0x3c010001, 0xa0200f56, 0x8f641008, 0x00901024, - 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, - 0x1440fff7, 0x00000000, 0x080042cf, 0x241a0003, 0xaf620cf4, 0x3c108000, - 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, - 0x8f620cf4, 0x00501024, 0x1440fff7, 0x00000000, 0x080042cf, 0x241a0003, - 0x3c070001, 0x24e70f50, 0x94e20000, 0x03821021, 0xaf620ce0, 0x3c020001, - 0x8c420f64, 0xaf620ce4, 0x3c050001, 0x94a50f54, 0x94e30000, 0x3c040001, - 0x94840f58, 0x3c020001, 0x94420f5e, 0x00a32823, 0x00822023, 0x30a6ffff, - 0x3083ffff, 0x00c3102b, 0x14400043, 0x00000000, 0x3c020001, 0x94420f5c, - 0x00021400, 0x00621025, 0xaf620ce8, 0x94e20000, 0x3c030001, 0x94630f54, - 0x00441021, 0xa4e20000, 0x3042ffff, 0x14430021, 0x3c020008, 0x3c020001, - 0x90420f57, 0x10400006, 0x3c03000c, 0x3c020001, 0x94420f68, 0x34630624, - 0x0800427c, 0x0000d021, 0x3c020001, 0x94420f68, 0x3c030008, 0x34630624, - 0x00431025, 0xaf620cec, 0x3c108000, 0x3c02a000, 0xaf620cf4, 0x3c010001, - 0xa0200f56, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, - 0x00000000, 0x8f620cf4, 0x00501024, 0x10400015, 0x00000000, 0x08004283, - 0x00000000, 0x3c030001, 0x94630f68, 0x34420624, 0x3c108000, 0x00621825, - 0x3c028000, 0xaf630cec, 0xaf620cf4, 0x8f641008, 0x00901024, 0x14400003, - 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x1440fff7, - 0x00000000, 0x3c010001, 0x080042cf, 0xa4200f5e, 0x3c020001, 0x94420f5c, - 0x00021400, 0x00c21025, 0xaf620ce8, 0x3c020001, 0x90420f57, 0x10400009, - 0x3c03000c, 0x3c020001, 0x94420f68, 0x34630624, 0x0000d021, 0x00431025, - 0xaf620cec, 0x080042c1, 0x3c108000, 0x3c020001, 0x94420f68, 0x3c030008, - 0x34630604, 0x00431025, 0xaf620cec, 0x3c020001, 0x94420f5e, 0x00451021, - 0x3c010001, 0xa4220f5e, 0x3c108000, 0x3c02a000, 0xaf620cf4, 0x3c010001, - 0xa0200f56, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, - 0x00000000, 0x8f620cf4, 0x00501024, 0x1440fff7, 0x00000000, 0x8fbf0014, - 0x8fb00010, 0x03e00008, 0x27bd0018, 0x00000000, 0x27bdffe0, 0x3c040001, - 0x24840ec0, 0x00002821, 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, - 0x0c004378, 0xafa00014, 0x0000d021, 0x24020130, 0xaf625000, 0x3c010001, - 0xa4200f50, 0x3c010001, 0xa0200f57, 0x8fbf0018, 0x03e00008, 0x27bd0020, - 0x27bdffe8, 0x3c1bc000, 0xafbf0014, 0xafb00010, 0xaf60680c, 0x8f626804, - 0x34420082, 0xaf626804, 0x8f634000, 0x24020b50, 0x3c010001, 0xac220f20, - 0x24020b78, 0x3c010001, 0xac220f30, 0x34630002, 0xaf634000, 0x0c004315, - 0x00808021, 0x3c010001, 0xa0220f34, 0x304200ff, 0x24030002, 0x14430005, - 0x00000000, 0x3c020001, 0x8c420f20, 0x08004308, 0xac5000c0, 0x3c020001, - 0x8c420f20, 0xac5000bc, 0x8f624434, 0x8f634438, 0x8f644410, 0x3c010001, - 0xac220f28, 0x3c010001, 0xac230f38, 0x3c010001, 0xac240f24, 0x8fbf0014, - 0x8fb00010, 0x03e00008, 0x27bd0018, 0x03e00008, 0x24020001, 0x27bdfff8, - 0x18800009, 0x00002821, 0x8f63680c, 0x8f62680c, 0x1043fffe, 0x00000000, - 0x24a50001, 0x00a4102a, 0x1440fff9, 0x00000000, 0x03e00008, 0x27bd0008, - 0x8f634450, 0x3c020001, 0x8c420f28, 0x00031c02, 0x0043102b, 0x14400008, - 0x3c038000, 0x3c040001, 0x8c840f38, 0x8f624450, 0x00021c02, 0x0083102b, - 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, 0x00431024, 0x1440fffd, - 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, 0x3082ffff, 0x2442e000, - 0x2c422001, 0x14400003, 0x3c024000, 0x08004347, 0x2402ffff, 0x00822025, - 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, 0x00001021, 0x03e00008, - 0x00000000, 0x8f624450, 0x3c030001, 0x8c630f24, 0x08004350, 0x3042ffff, - 0x8f624450, 0x3042ffff, 0x0043102b, 0x1440fffc, 0x00000000, 0x03e00008, - 0x00000000, 0x27bdffe0, 0x00802821, 0x3c040001, 0x24840ed0, 0x00003021, - 0x00003821, 0xafbf0018, 0xafa00010, 0x0c004378, 0xafa00014, 0x0800435f, - 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x3c020001, 0x3442d600, - 0x3c030001, 0x3463d600, 0x3c040001, 0x3484ddff, 0x3c010001, 0xac220f40, - 0x24020040, 0x3c010001, 0xac220f44, 0x3c010001, 0xac200f3c, 0xac600000, - 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, - 0x00804821, 0x8faa0010, 0x3c020001, 0x8c420f3c, 0x3c040001, 0x8c840f44, - 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010001, 0xac230f3c, 0x14400003, - 0x00004021, 0x3c010001, 0xac200f3c, 0x3c020001, 0x8c420f3c, 0x3c030001, - 0x8c630f40, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001, - 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020001, 0x8c420f3c, - 0x3c030001, 0x8c630f40, 0x8f64680c, 0x00021140, 0x00431021, 0xac440008, - 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, - 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 tg3Tso5FwRodata[(TG3_TSO5_FW_RODATA_LEN / 4) + 1] = { - 0x4d61696e, 0x43707542, 0x00000000, 0x4d61696e, 0x43707541, 0x00000000, - 0x00000000, 0x00000000, 0x73746b6f, 0x66666c64, 0x00000000, 0x00000000, - 0x73746b6f, 0x66666c64, 0x00000000, 0x00000000, 0x66617461, 0x6c457272, - 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 tg3Tso5FwData[(TG3_TSO5_FW_DATA_LEN / 4) + 1] = { - 0x00000000, 0x73746b6f, 0x66666c64, 0x5f76312e, 0x322e3000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, -}; /* tp->lock is held. */ static int tg3_load_tso_firmware(struct tg3 *tp) { struct fw_info info; + const __be32 *fw_data; unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size; int err, i; if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) return 0; + fw_data = (void *)tp->fw->data; + + /* Firmware blob starts with version numbers, followed by + start address and length. We are setting complete length. + length = end_address_of_bss - start_address_of_text. + Remainder is the blob to be loaded contiguously + from start address. */ + + info.fw_base = be32_to_cpu(fw_data[1]); + cpu_scratch_size = tp->fw_len; + info.fw_len = tp->fw->size - 12; + info.fw_data = &fw_data[3]; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { - info.text_base = TG3_TSO5_FW_TEXT_ADDR; - info.text_len = TG3_TSO5_FW_TEXT_LEN; - info.text_data = &tg3Tso5FwText[0]; - info.rodata_base = TG3_TSO5_FW_RODATA_ADDR; - info.rodata_len = TG3_TSO5_FW_RODATA_LEN; - info.rodata_data = &tg3Tso5FwRodata[0]; - info.data_base = TG3_TSO5_FW_DATA_ADDR; - info.data_len = TG3_TSO5_FW_DATA_LEN; - info.data_data = &tg3Tso5FwData[0]; cpu_base = RX_CPU_BASE; cpu_scratch_base = NIC_SRAM_MBUF_POOL_BASE5705; - cpu_scratch_size = (info.text_len + - info.rodata_len + - info.data_len + - TG3_TSO5_FW_SBSS_LEN + - TG3_TSO5_FW_BSS_LEN); } else { - info.text_base = TG3_TSO_FW_TEXT_ADDR; - info.text_len = TG3_TSO_FW_TEXT_LEN; - info.text_data = &tg3TsoFwText[0]; - info.rodata_base = TG3_TSO_FW_RODATA_ADDR; - info.rodata_len = TG3_TSO_FW_RODATA_LEN; - info.rodata_data = &tg3TsoFwRodata[0]; - info.data_base = TG3_TSO_FW_DATA_ADDR; - info.data_len = TG3_TSO_FW_DATA_LEN; - info.data_data = &tg3TsoFwData[0]; cpu_base = TX_CPU_BASE; cpu_scratch_base = TX_CPU_SCRATCH_BASE; cpu_scratch_size = TX_CPU_SCRATCH_SIZE; @@ -7060,21 +6418,21 @@ static int tg3_load_tso_firmware(struct tg3 *tp) /* Now startup the cpu. */ tw32(cpu_base + CPU_STATE, 0xffffffff); - tw32_f(cpu_base + CPU_PC, info.text_base); + tw32_f(cpu_base + CPU_PC, info.fw_base); for (i = 0; i < 5; i++) { - if (tr32(cpu_base + CPU_PC) == info.text_base) + if (tr32(cpu_base + CPU_PC) == info.fw_base) break; tw32(cpu_base + CPU_STATE, 0xffffffff); tw32(cpu_base + CPU_MODE, CPU_MODE_HALT); - tw32_f(cpu_base + CPU_PC, info.text_base); + tw32_f(cpu_base + CPU_PC, info.fw_base); udelay(1000); } if (i >= 5) { printk(KERN_ERR PFX "tg3_load_tso_firmware fails for %s " "to set CPU PC, is %08x should be %08x\n", tp->dev->name, tr32(cpu_base + CPU_PC), - info.text_base); + info.fw_base); return -ENODEV; } tw32(cpu_base + CPU_STATE, 0xffffffff); @@ -7299,11 +6657,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) else if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { int fw_len; - fw_len = (TG3_TSO5_FW_TEXT_LEN + - TG3_TSO5_FW_RODATA_LEN + - TG3_TSO5_FW_DATA_LEN + - TG3_TSO5_FW_SBSS_LEN + - TG3_TSO5_FW_BSS_LEN); + fw_len = tp->fw_len; fw_len = (fw_len + (0x80 - 1)) & ~(0x80 - 1); tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE5705 + fw_len); @@ -13580,6 +12934,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, struct net_device *dev; struct tg3 *tp; int err, pm_cap; + const char *fw_name = NULL; char str[40]; u64 dma_mask, persist_dma_mask; @@ -13735,6 +13090,9 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tg3_init_bufmgr_config(tp); + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) + fw_name = FIRMWARE_TG3; + if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; } @@ -13747,6 +13105,37 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, } else { tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG; } + if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + fw_name = FIRMWARE_TG3TSO5; + else + fw_name = FIRMWARE_TG3TSO; + } + + if (fw_name) { + const __be32 *fw_data; + + err = request_firmware(&tp->fw, fw_name, &tp->pdev->dev); + if (err) { + printk(KERN_ERR "tg3: Failed to load firmware \"%s\"\n", + fw_name); + goto err_out_iounmap; + } + + fw_data = (void *)tp->fw->data; + + /* Firmware blob starts with version numbers, followed by + start address and _full_ length including BSS sections + (which must be longer than the actual data, of course */ + + tp->fw_len = be32_to_cpu(fw_data[2]); /* includes bss */ + if (tp->fw_len < (tp->fw->size - 12)) { + printk(KERN_ERR "tg3: bogus length %d in \"%s\"\n", + tp->fw_len, fw_name); + err = -EINVAL; + goto err_out_fw; + } + } /* TSO is on by default on chips that support hardware TSO. * Firmware TSO on older chips gives lower performance, so it @@ -13778,7 +13167,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, if (err) { printk(KERN_ERR PFX "Could not obtain valid ethernet address, " "aborting.\n"); - goto err_out_iounmap; + goto err_out_fw; } if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) { @@ -13787,7 +13176,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, printk(KERN_ERR PFX "Cannot map APE registers, " "aborting.\n"); err = -ENOMEM; - goto err_out_iounmap; + goto err_out_fw; } tg3_ape_lock_init(tp); @@ -13867,6 +13256,10 @@ err_out_apeunmap: tp->aperegs = NULL; } +err_out_fw: + if (tp->fw) + release_firmware(tp->fw); + err_out_iounmap: if (tp->regs) { iounmap(tp->regs); @@ -13892,6 +13285,9 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev) if (dev) { struct tg3 *tp = netdev_priv(dev); + if (tp->fw) + release_firmware(tp->fw); + flush_scheduled_work(); if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) { diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 8936edfb0438..ae5da603c6af 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2762,6 +2762,10 @@ struct tg3 { #define SST_25VF0X0_PAGE_SIZE 4098 struct ethtool_coalesce coal; + + /* firmware info */ + const struct firmware *fw; + u32 fw_len; /* includes BSS */ }; #endif /* !(_T3_H) */ diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 69f9a0ec764d..d7b81e4fdd56 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -213,7 +213,7 @@ static int check_filter(struct tap_filter *filter, const struct sk_buff *skb) /* Network device part of the driver */ -static unsigned int tun_net_id; +static int tun_net_id; struct tun_net { struct list_head dev_list; }; diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c index ddc4c59f02dc..b7e4cee24269 100644 --- a/drivers/oprofile/oprofilefs.c +++ b/drivers/oprofile/oprofilefs.c @@ -29,9 +29,6 @@ static struct inode *oprofilefs_get_inode(struct super_block *sb, int mode) if (inode) { inode->i_mode = mode; - inode->i_uid = 0; - inode->i_gid = 0; - inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; } return inode; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 8e0c2b47803c..668472405a57 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -29,6 +29,13 @@ config APM_POWER Say Y here to enable support APM status emulation using battery class devices. +config WM8350_POWER + tristate "WM8350 PMU support" + depends on MFD_WM8350 + help + Say Y here to enable support for the power management unit + provided by the Wolfson Microelectronics WM8350 PMIC. + config BATTERY_DS2760 tristate "DS2760 battery driver (HP iPAQ & others)" select W1 @@ -68,4 +75,11 @@ config BATTERY_BQ27x00 help Say Y here to enable support for batteries with BQ27200(I2C) chip. +config BATTERY_DA9030 + tristate "DA9030 battery driver" + depends on PMIC_DA903X + help + Say Y here to enable support for batteries charger integrated into + DA9030 PMIC. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index e8f1ecec5d8f..eebb15505a40 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_APM_POWER) += apm_power.o +obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o @@ -23,3 +24,4 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o +obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c new file mode 100644 index 000000000000..1662bb0f23a5 --- /dev/null +++ b/drivers/power/da9030_battery.c @@ -0,0 +1,600 @@ +/* + * Battery charger driver for Dialog Semiconductor DA9030 + * + * Copyright (C) 2008 Compulab, Ltd. + * Mike Rapoport <mike@compulab.co.il> + * + * 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/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/mfd/da903x.h> + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#define DA9030_STATUS_CHDET (1 << 3) + +#define DA9030_FAULT_LOG 0x0a +#define DA9030_FAULT_LOG_OVER_TEMP (1 << 7) +#define DA9030_FAULT_LOG_VBAT_OVER (1 << 4) + +#define DA9030_CHARGE_CONTROL 0x28 +#define DA9030_CHRG_CHARGER_ENABLE (1 << 7) + +#define DA9030_ADC_MAN_CONTROL 0x30 +#define DA9030_ADC_TBATREF_ENABLE (1 << 5) +#define DA9030_ADC_LDO_INT_ENABLE (1 << 4) + +#define DA9030_ADC_AUTO_CONTROL 0x31 +#define DA9030_ADC_TBAT_ENABLE (1 << 5) +#define DA9030_ADC_VBAT_IN_TXON (1 << 4) +#define DA9030_ADC_VCH_ENABLE (1 << 3) +#define DA9030_ADC_ICH_ENABLE (1 << 2) +#define DA9030_ADC_VBAT_ENABLE (1 << 1) +#define DA9030_ADC_AUTO_SLEEP_ENABLE (1 << 0) + +#define DA9030_VBATMON 0x32 +#define DA9030_VBATMONTXON 0x33 +#define DA9030_TBATHIGHP 0x34 +#define DA9030_TBATHIGHN 0x35 +#define DA9030_TBATLOW 0x36 + +#define DA9030_VBAT_RES 0x41 +#define DA9030_VBATMIN_RES 0x42 +#define DA9030_VBATMINTXON_RES 0x43 +#define DA9030_ICHMAX_RES 0x44 +#define DA9030_ICHMIN_RES 0x45 +#define DA9030_ICHAVERAGE_RES 0x46 +#define DA9030_VCHMAX_RES 0x47 +#define DA9030_VCHMIN_RES 0x48 +#define DA9030_TBAT_RES 0x49 + +struct da9030_adc_res { + uint8_t vbat_res; + uint8_t vbatmin_res; + uint8_t vbatmintxon; + uint8_t ichmax_res; + uint8_t ichmin_res; + uint8_t ichaverage_res; + uint8_t vchmax_res; + uint8_t vchmin_res; + uint8_t tbat_res; + uint8_t adc_in4_res; + uint8_t adc_in5_res; +}; + +struct da9030_battery_thresholds { + int tbat_low; + int tbat_high; + int tbat_restart; + + int vbat_low; + int vbat_crit; + int vbat_charge_start; + int vbat_charge_stop; + int vbat_charge_restart; + + int vcharge_min; + int vcharge_max; +}; + +struct da9030_charger { + struct power_supply psy; + + struct device *master; + + struct da9030_adc_res adc; + struct delayed_work work; + unsigned int interval; + + struct power_supply_info *battery_info; + + struct da9030_battery_thresholds thresholds; + + unsigned int charge_milliamp; + unsigned int charge_millivolt; + + /* charger status */ + bool chdet; + uint8_t fault; + int mA; + int mV; + bool is_on; + + struct notifier_block nb; + + /* platform callbacks for battery low and critical events */ + void (*battery_low)(void); + void (*battery_critical)(void); + + struct dentry *debug_file; +}; + +static inline int da9030_reg_to_mV(int reg) +{ + return ((reg * 2650) >> 8) + 2650; +} + +static inline int da9030_millivolt_to_reg(int mV) +{ + return ((mV - 2650) << 8) / 2650; +} + +static inline int da9030_reg_to_mA(int reg) +{ + return ((reg * 24000) >> 8) / 15; +} + +#ifdef CONFIG_DEBUG_FS +static int bat_debug_show(struct seq_file *s, void *data) +{ + struct da9030_charger *charger = s->private; + + seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off"); + if (charger->chdet) { + seq_printf(s, "iset = %dmA, vset = %dmV\n", + charger->mA, charger->mV); + } + + seq_printf(s, "vbat_res = %d (%dmV)\n", + charger->adc.vbat_res, + da9030_reg_to_mV(charger->adc.vbat_res)); + seq_printf(s, "vbatmin_res = %d (%dmV)\n", + charger->adc.vbatmin_res, + da9030_reg_to_mV(charger->adc.vbatmin_res)); + seq_printf(s, "vbatmintxon = %d (%dmV)\n", + charger->adc.vbatmintxon, + da9030_reg_to_mV(charger->adc.vbatmintxon)); + seq_printf(s, "ichmax_res = %d (%dmA)\n", + charger->adc.ichmax_res, + da9030_reg_to_mV(charger->adc.ichmax_res)); + seq_printf(s, "ichmin_res = %d (%dmA)\n", + charger->adc.ichmin_res, + da9030_reg_to_mA(charger->adc.ichmin_res)); + seq_printf(s, "ichaverage_res = %d (%dmA)\n", + charger->adc.ichaverage_res, + da9030_reg_to_mA(charger->adc.ichaverage_res)); + seq_printf(s, "vchmax_res = %d (%dmV)\n", + charger->adc.vchmax_res, + da9030_reg_to_mA(charger->adc.vchmax_res)); + seq_printf(s, "vchmin_res = %d (%dmV)\n", + charger->adc.vchmin_res, + da9030_reg_to_mV(charger->adc.vchmin_res)); + + return 0; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, bat_debug_show, inode->i_private); +} + +static const struct file_operations bat_debug_fops = { + .open = debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) +{ + charger->debug_file = debugfs_create_file("charger", 0666, 0, charger, + &bat_debug_fops); + return charger->debug_file; +} + +static void da9030_bat_remove_debugfs(struct da9030_charger *charger) +{ + debugfs_remove(charger->debug_file); +} +#else +static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) +{ + return NULL; +} +static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger) +{ +} +#endif + +static inline void da9030_read_adc(struct da9030_charger *charger, + struct da9030_adc_res *adc) +{ + da903x_reads(charger->master, DA9030_VBAT_RES, + sizeof(*adc), (uint8_t *)adc); +} + +static void da9030_charger_update_state(struct da9030_charger *charger) +{ + uint8_t val; + + da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val); + charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0; + charger->mA = ((val >> 3) & 0xf) * 100; + charger->mV = (val & 0x7) * 50 + 4000; + + da9030_read_adc(charger, &charger->adc); + da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault); + charger->chdet = da903x_query_status(charger->master, + DA9030_STATUS_CHDET); +} + +static void da9030_set_charge(struct da9030_charger *charger, int on) +{ + uint8_t val; + + if (on) { + val = DA9030_CHRG_CHARGER_ENABLE; + val |= (charger->charge_milliamp / 100) << 3; + val |= (charger->charge_millivolt - 4000) / 50; + charger->is_on = 1; + } else { + val = 0; + charger->is_on = 0; + } + + da903x_write(charger->master, DA9030_CHARGE_CONTROL, val); +} + +static void da9030_charger_check_state(struct da9030_charger *charger) +{ + da9030_charger_update_state(charger); + + /* we wake or boot with external power on */ + if (!charger->is_on) { + if ((charger->chdet) && + (charger->adc.vbat_res < + charger->thresholds.vbat_charge_start)) { + da9030_set_charge(charger, 1); + } + } else { + if (charger->adc.vbat_res >= + charger->thresholds.vbat_charge_stop) { + da9030_set_charge(charger, 0); + da903x_write(charger->master, DA9030_VBATMON, + charger->thresholds.vbat_charge_restart); + } else if (charger->adc.vbat_res > + charger->thresholds.vbat_low) { + /* we are charging and passed LOW_THRESH, + so upate DA9030 VBAT threshold + */ + da903x_write(charger->master, DA9030_VBATMON, + charger->thresholds.vbat_low); + } + if (charger->adc.vchmax_res > charger->thresholds.vcharge_max || + charger->adc.vchmin_res < charger->thresholds.vcharge_min || + /* Tempreture readings are negative */ + charger->adc.tbat_res < charger->thresholds.tbat_high || + charger->adc.tbat_res > charger->thresholds.tbat_low) { + /* disable charger */ + da9030_set_charge(charger, 0); + } + } +} + +static void da9030_charging_monitor(struct work_struct *work) +{ + struct da9030_charger *charger; + + charger = container_of(work, struct da9030_charger, work.work); + + da9030_charger_check_state(charger); + + /* reschedule for the next time */ + schedule_delayed_work(&charger->work, charger->interval); +} + +static enum power_supply_property da9030_battery_props[] = { + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, +}; + +static void da9030_battery_check_status(struct da9030_charger *charger, + union power_supply_propval *val) +{ + if (charger->chdet) { + if (charger->is_on) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } +} + +static void da9030_battery_check_health(struct da9030_charger *charger, + union power_supply_propval *val) +{ + if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; +} + +static int da9030_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct da9030_charger *charger; + charger = container_of(psy, struct da9030_charger, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + da9030_battery_check_status(charger, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + da9030_battery_check_health(charger, val); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = charger->battery_info->technology; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = charger->battery_info->voltage_max_design; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = charger->battery_info->voltage_min_design; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = + da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = charger->battery_info->name; + break; + default: + break; + } + + return 0; +} + +static void da9030_battery_vbat_event(struct da9030_charger *charger) +{ + da9030_read_adc(charger, &charger->adc); + + if (charger->is_on) + return; + + if (charger->adc.vbat_res < charger->thresholds.vbat_low) { + /* set VBAT threshold for critical */ + da903x_write(charger->master, DA9030_VBATMON, + charger->thresholds.vbat_crit); + if (charger->battery_low) + charger->battery_low(); + } else if (charger->adc.vbat_res < + charger->thresholds.vbat_crit) { + /* notify the system of battery critical */ + if (charger->battery_critical) + charger->battery_critical(); + } +} + +static int da9030_battery_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct da9030_charger *charger = + container_of(nb, struct da9030_charger, nb); + int status; + + switch (event) { + case DA9030_EVENT_CHDET: + status = da903x_query_status(charger->master, + DA9030_STATUS_CHDET); + da9030_set_charge(charger, status); + break; + case DA9030_EVENT_VBATMON: + da9030_battery_vbat_event(charger); + break; + case DA9030_EVENT_CHIOVER: + case DA9030_EVENT_TBAT: + da9030_set_charge(charger, 0); + break; + } + + return 0; +} + +static void da9030_battery_convert_thresholds(struct da9030_charger *charger, + struct da9030_battery_info *pdata) +{ + charger->thresholds.tbat_low = pdata->tbat_low; + charger->thresholds.tbat_high = pdata->tbat_high; + charger->thresholds.tbat_restart = pdata->tbat_restart; + + charger->thresholds.vbat_low = + da9030_millivolt_to_reg(pdata->vbat_low); + charger->thresholds.vbat_crit = + da9030_millivolt_to_reg(pdata->vbat_crit); + charger->thresholds.vbat_charge_start = + da9030_millivolt_to_reg(pdata->vbat_charge_start); + charger->thresholds.vbat_charge_stop = + da9030_millivolt_to_reg(pdata->vbat_charge_stop); + charger->thresholds.vbat_charge_restart = + da9030_millivolt_to_reg(pdata->vbat_charge_restart); + + charger->thresholds.vcharge_min = + da9030_millivolt_to_reg(pdata->vcharge_min); + charger->thresholds.vcharge_max = + da9030_millivolt_to_reg(pdata->vcharge_max); +} + +static void da9030_battery_setup_psy(struct da9030_charger *charger) +{ + struct power_supply *psy = &charger->psy; + struct power_supply_info *info = charger->battery_info; + + psy->name = info->name; + psy->use_for_apm = info->use_for_apm; + psy->type = POWER_SUPPLY_TYPE_BATTERY; + psy->get_property = da9030_battery_get_property; + + psy->properties = da9030_battery_props; + psy->num_properties = ARRAY_SIZE(da9030_battery_props); +}; + +static int da9030_battery_charger_init(struct da9030_charger *charger) +{ + char v[5]; + int ret; + + v[0] = v[1] = charger->thresholds.vbat_low; + v[2] = charger->thresholds.tbat_high; + v[3] = charger->thresholds.tbat_restart; + v[4] = charger->thresholds.tbat_low; + + ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v); + if (ret) + return ret; + + /* + * Enable reference voltage supply for ADC from the LDO_INTERNAL + * regulator. Must be set before ADC measurements can be made. + */ + ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL, + DA9030_ADC_LDO_INT_ENABLE | + DA9030_ADC_TBATREF_ENABLE); + if (ret) + return ret; + + /* enable auto ADC measuremnts */ + return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL, + DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON | + DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE | + DA9030_ADC_VBAT_ENABLE | + DA9030_ADC_AUTO_SLEEP_ENABLE); +} + +static int da9030_battery_probe(struct platform_device *pdev) +{ + struct da9030_charger *charger; + struct da9030_battery_info *pdata = pdev->dev.platform_data; + int ret; + + if (pdata == NULL) + return -EINVAL; + + if (pdata->charge_milliamp >= 1500 || + pdata->charge_millivolt < 4000 || + pdata->charge_millivolt > 4350) + return -EINVAL; + + charger = kzalloc(sizeof(*charger), GFP_KERNEL); + if (charger == NULL) + return -ENOMEM; + + charger->master = pdev->dev.parent; + + /* 10 seconds between monotor runs unless platfrom defines other + interval */ + charger->interval = msecs_to_jiffies( + (pdata->batmon_interval ? : 10) * 1000); + + charger->charge_milliamp = pdata->charge_milliamp; + charger->charge_millivolt = pdata->charge_millivolt; + charger->battery_info = pdata->battery_info; + charger->battery_low = pdata->battery_low; + charger->battery_critical = pdata->battery_critical; + + da9030_battery_convert_thresholds(charger, pdata); + + ret = da9030_battery_charger_init(charger); + if (ret) + goto err_charger_init; + + INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor); + schedule_delayed_work(&charger->work, charger->interval); + + charger->nb.notifier_call = da9030_battery_event; + ret = da903x_register_notifier(charger->master, &charger->nb, + DA9030_EVENT_CHDET | + DA9030_EVENT_VBATMON | + DA9030_EVENT_CHIOVER | + DA9030_EVENT_TBAT); + if (ret) + goto err_notifier; + + da9030_battery_setup_psy(charger); + ret = power_supply_register(&pdev->dev, &charger->psy); + if (ret) + goto err_ps_register; + + charger->debug_file = da9030_bat_create_debugfs(charger); + platform_set_drvdata(pdev, charger); + return 0; + +err_ps_register: + da903x_unregister_notifier(charger->master, &charger->nb, + DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | + DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); +err_notifier: + cancel_delayed_work(&charger->work); + +err_charger_init: + kfree(charger); + + return ret; +} + +static int da9030_battery_remove(struct platform_device *dev) +{ + struct da9030_charger *charger = platform_get_drvdata(dev); + + da9030_bat_remove_debugfs(charger); + + da903x_unregister_notifier(charger->master, &charger->nb, + DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON | + DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT); + cancel_delayed_work(&charger->work); + power_supply_unregister(&charger->psy); + + kfree(charger); + + return 0; +} + +static struct platform_driver da903x_battery_driver = { + .driver = { + .name = "da903x-battery", + .owner = THIS_MODULE, + }, + .probe = da9030_battery_probe, + .remove = da9030_battery_remove, +}; + +static int da903x_battery_init(void) +{ + return platform_driver_register(&da903x_battery_driver); +} + +static void da903x_battery_exit(void) +{ + platform_driver_unregister(&da903x_battery_driver); +} + +module_init(da903x_battery_init); +module_exit(da903x_battery_exit); + +MODULE_DESCRIPTION("DA9030 battery charger driver"); +MODULE_AUTHOR("Mike Rapoport, CompuLab"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 23ae8460f5c1..ac01e06817fb 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -45,7 +45,7 @@ static ssize_t power_supply_show_property(struct device *dev, }; static char *health_text[] = { "Unknown", "Good", "Overheat", "Dead", "Over voltage", - "Unspecified failure" + "Unspecified failure", "Cold", }; static char *technology_text[] = { "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c new file mode 100644 index 000000000000..1b16bf343f2f --- /dev/null +++ b/drivers/power/wm8350_power.c @@ -0,0 +1,532 @@ +/* + * Battery driver for wm8350 PMIC + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Based on OLPC Battery Driver + * + * Copyright 2006 David Woodhouse <dwmw2@infradead.org> + * + * 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/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/mfd/wm8350/supply.h> +#include <linux/mfd/wm8350/core.h> +#include <linux/mfd/wm8350/comparator.h> + +static int wm8350_read_battery_uvolts(struct wm8350 *wm8350) +{ + return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0) + * WM8350_AUX_COEFF; +} + +static int wm8350_read_line_uvolts(struct wm8350 *wm8350) +{ + return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0) + * WM8350_AUX_COEFF; +} + +static int wm8350_read_usb_uvolts(struct wm8350 *wm8350) +{ + return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0) + * WM8350_AUX_COEFF; +} + +#define WM8350_BATT_SUPPLY 1 +#define WM8350_USB_SUPPLY 2 +#define WM8350_LINE_SUPPLY 4 + +static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min) +{ + if (!wm8350->power.rev_g_coeff) + return (((min - 30) / 15) & 0xf) << 8; + else + return (((min - 30) / 30) & 0xf) << 8; +} + +static int wm8350_get_supplies(struct wm8350 *wm8350) +{ + u16 sm, ov, co, chrg; + int supplies = 0; + + sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS); + ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES); + co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES); + chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2); + + /* USB_SM */ + sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT; + + /* CHG_ISEL */ + chrg &= WM8350_CHG_ISEL_MASK; + + /* If the USB state machine is active then we're using that with or + * without battery, otherwise check for wall supply */ + if (((sm == WM8350_USB_SM_100_SLV) || + (sm == WM8350_USB_SM_500_SLV) || + (sm == WM8350_USB_SM_STDBY_SLV)) + && !(ov & WM8350_USB_LIMIT_OVRDE)) + supplies = WM8350_USB_SUPPLY; + else if (((sm == WM8350_USB_SM_100_SLV) || + (sm == WM8350_USB_SM_500_SLV) || + (sm == WM8350_USB_SM_STDBY_SLV)) + && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0)) + supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY; + else if (co & WM8350_WALL_FB_OVRDE) + supplies = WM8350_LINE_SUPPLY; + else + supplies = WM8350_BATT_SUPPLY; + + return supplies; +} + +static int wm8350_charger_config(struct wm8350 *wm8350, + struct wm8350_charger_policy *policy) +{ + u16 reg, eoc_mA, fast_limit_mA; + + if (!policy) { + dev_warn(wm8350->dev, + "No charger policy, charger not configured.\n"); + return -EINVAL; + } + + /* make sure USB fast charge current is not > 500mA */ + if (policy->fast_limit_USB_mA > 500) { + dev_err(wm8350->dev, "USB fast charge > 500mA\n"); + return -EINVAL; + } + + eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA); + + wm8350_reg_unlock(wm8350); + + reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1) + & WM8350_CHG_ENA_R168; + wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, + reg | eoc_mA | policy->trickle_start_mV | + WM8350_CHG_TRICKLE_TEMP_CHOKE | + WM8350_CHG_TRICKLE_USB_CHOKE | + WM8350_CHG_FAST_USB_THROTTLE); + + if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) { + fast_limit_mA = + WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA); + wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2, + policy->charge_mV | policy->trickle_charge_USB_mA | + fast_limit_mA | wm8350_charge_time_min(wm8350, + policy->charge_timeout)); + + } else { + fast_limit_mA = + WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA); + wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2, + policy->charge_mV | policy->trickle_charge_mA | + fast_limit_mA | wm8350_charge_time_min(wm8350, + policy->charge_timeout)); + } + + wm8350_reg_lock(wm8350); + return 0; +} + +static int wm8350_batt_status(struct wm8350 *wm8350) +{ + u16 state; + + state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2); + state &= WM8350_CHG_STS_MASK; + + switch (state) { + case WM8350_CHG_STS_OFF: + return POWER_SUPPLY_STATUS_DISCHARGING; + + case WM8350_CHG_STS_TRICKLE: + case WM8350_CHG_STS_FAST: + return POWER_SUPPLY_STATUS_CHARGING; + + default: + return POWER_SUPPLY_STATUS_UNKNOWN; + } +} + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + char *charge; + int state; + + state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) & + WM8350_CHG_STS_MASK; + switch (state) { + case WM8350_CHG_STS_OFF: + charge = "Charger Off"; + break; + case WM8350_CHG_STS_TRICKLE: + charge = "Trickle Charging"; + break; + case WM8350_CHG_STS_FAST: + charge = "Fast Charging"; + break; + default: + return 0; + } + + return sprintf(buf, "%s\n", charge); +} + +static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL); + +static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data) +{ + struct wm8350_power *power = &wm8350->power; + struct wm8350_charger_policy *policy = power->policy; + + switch (irq) { + case WM8350_IRQ_CHG_BAT_FAIL: + dev_err(wm8350->dev, "battery failed\n"); + break; + case WM8350_IRQ_CHG_TO: + dev_err(wm8350->dev, "charger timeout\n"); + power_supply_changed(&power->battery); + break; + + case WM8350_IRQ_CHG_BAT_HOT: + case WM8350_IRQ_CHG_BAT_COLD: + case WM8350_IRQ_CHG_START: + case WM8350_IRQ_CHG_END: + power_supply_changed(&power->battery); + break; + + case WM8350_IRQ_CHG_FAST_RDY: + dev_dbg(wm8350->dev, "fast charger ready\n"); + wm8350_charger_config(wm8350, policy); + wm8350_reg_unlock(wm8350); + wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1, + WM8350_CHG_FAST); + wm8350_reg_lock(wm8350); + break; + + case WM8350_IRQ_CHG_VBATT_LT_3P9: + dev_warn(wm8350->dev, "battery < 3.9V\n"); + break; + case WM8350_IRQ_CHG_VBATT_LT_3P1: + dev_warn(wm8350->dev, "battery < 3.1V\n"); + break; + case WM8350_IRQ_CHG_VBATT_LT_2P85: + dev_warn(wm8350->dev, "battery < 2.85V\n"); + break; + + /* Supply change. We will overnotify but it should do + * no harm. */ + case WM8350_IRQ_EXT_USB_FB: + case WM8350_IRQ_EXT_WALL_FB: + wm8350_charger_config(wm8350, policy); + case WM8350_IRQ_EXT_BAT_FB: /* Fall through */ + power_supply_changed(&power->battery); + power_supply_changed(&power->usb); + power_supply_changed(&power->ac); + break; + + default: + dev_err(wm8350->dev, "Unknown interrupt %d\n", irq); + } +} + +/********************************************************************* + * AC Power + *********************************************************************/ +static int wm8350_ac_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(wm8350_get_supplies(wm8350) & + WM8350_LINE_SUPPLY); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = wm8350_read_line_uvolts(wm8350); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static enum power_supply_property wm8350_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +/********************************************************************* + * USB Power + *********************************************************************/ +static int wm8350_usb_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(wm8350_get_supplies(wm8350) & + WM8350_USB_SUPPLY); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = wm8350_read_usb_uvolts(wm8350); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static enum power_supply_property wm8350_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +/********************************************************************* + * Battery properties + *********************************************************************/ + +static int wm8350_bat_check_health(struct wm8350 *wm8350) +{ + u16 reg; + + if (wm8350_read_battery_uvolts(wm8350) < 2850000) + return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + + reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES); + if (reg & WM8350_CHG_BATT_HOT_OVRDE) + return POWER_SUPPLY_HEALTH_OVERHEAT; + + if (reg & WM8350_CHG_BATT_COLD_OVRDE) + return POWER_SUPPLY_HEALTH_COLD; + + return POWER_SUPPLY_HEALTH_GOOD; +} + +static int wm8350_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wm8350 *wm8350 = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = wm8350_batt_status(wm8350); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(wm8350_get_supplies(wm8350) & + WM8350_BATT_SUPPLY); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = wm8350_read_battery_uvolts(wm8350); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = wm8350_bat_check_health(wm8350); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property wm8350_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_HEALTH, +}; + +/********************************************************************* + * Initialisation + *********************************************************************/ + +static void wm8350_init_charger(struct wm8350 *wm8350) +{ + /* register our interest in charger events */ + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); + wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); + + /* and supply change events */ + wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_USB_FB); + wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); + wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, + wm8350_charger_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); +} + +static void free_charger_irq(struct wm8350 *wm8350) +{ + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); + wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); + wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_USB_FB); + wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB); + wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); + wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); + wm8350_mask_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); + wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); +} + +static __devinit int wm8350_power_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_power *power = &wm8350->power; + struct wm8350_charger_policy *policy = power->policy; + struct power_supply *usb = &power->usb; + struct power_supply *battery = &power->battery; + struct power_supply *ac = &power->ac; + int ret; + + ac->name = "wm8350-ac"; + ac->type = POWER_SUPPLY_TYPE_MAINS; + ac->properties = wm8350_ac_props; + ac->num_properties = ARRAY_SIZE(wm8350_ac_props); + ac->get_property = wm8350_ac_get_prop; + ret = power_supply_register(&pdev->dev, ac); + if (ret) + return ret; + + battery->name = "wm8350-battery"; + battery->properties = wm8350_bat_props; + battery->num_properties = ARRAY_SIZE(wm8350_bat_props); + battery->get_property = wm8350_bat_get_property; + battery->use_for_apm = 1; + ret = power_supply_register(&pdev->dev, battery); + if (ret) + goto battery_failed; + + usb->name = "wm8350-usb", + usb->type = POWER_SUPPLY_TYPE_USB; + usb->properties = wm8350_usb_props; + usb->num_properties = ARRAY_SIZE(wm8350_usb_props); + usb->get_property = wm8350_usb_get_prop; + ret = power_supply_register(&pdev->dev, usb); + if (ret) + goto usb_failed; + + ret = device_create_file(&pdev->dev, &dev_attr_charger_state); + if (ret < 0) + dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret); + ret = 0; + + wm8350_init_charger(wm8350); + if (wm8350_charger_config(wm8350, policy) == 0) { + wm8350_reg_unlock(wm8350); + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA); + wm8350_reg_lock(wm8350); + } + + return ret; + +usb_failed: + power_supply_unregister(battery); +battery_failed: + power_supply_unregister(ac); + + return ret; +} + +static __devexit int wm8350_power_remove(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_power *power = &wm8350->power; + + free_charger_irq(wm8350); + device_remove_file(&pdev->dev, &dev_attr_charger_state); + power_supply_unregister(&power->battery); + power_supply_unregister(&power->ac); + power_supply_unregister(&power->usb); + return 0; +} + +static struct platform_driver wm8350_power_driver = { + .probe = wm8350_power_probe, + .remove = __devexit_p(wm8350_power_remove), + .driver = { + .name = "wm8350-power", + }, +}; + +static int __init wm8350_power_init(void) +{ + return platform_driver_register(&wm8350_power_driver); +} +module_init(wm8350_power_init); + +static void __exit wm8350_power_exit(void) +{ + platform_driver_unregister(&wm8350_power_driver); +} +module_exit(wm8350_power_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Power supply driver for WM8350"); +MODULE_ALIAS("platform:wm8350-power"); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 1f44b17e23b1..c68c496b2c49 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1380,6 +1380,13 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg, if (wm8350->pmic.pdev[reg]) return -EBUSY; + if (reg >= WM8350_DCDC_1 && reg <= WM8350_DCDC_6 && + reg > wm8350->pmic.max_dcdc) + return -ENODEV; + if (reg >= WM8350_ISINK_A && reg <= WM8350_ISINK_B && + reg > wm8350->pmic.max_isink) + return -ENODEV; + pdev = platform_device_alloc("wm8350-regulator", reg); if (!pdev) return -ENOMEM; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 123092d8a984..165a81843357 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -102,9 +102,13 @@ config RTC_INTF_DEV_UIE_EMUL depends on RTC_INTF_DEV help Provides an emulation for RTC_UIE if the underlying rtc chip - driver does not expose RTC_UIE ioctls. Those requests generate + driver does not expose RTC_UIE ioctls. Those requests generate once-per-second update interrupts, used for synchronization. + The emulation code will read the time from the hardware + clock several times per second, please enable this option + only if you know that you really need it. + config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index a04c1b6b1575..fd2c652504ff 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -307,6 +307,60 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } EXPORT_SYMBOL_GPL(rtc_set_alarm); +int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) +{ + int err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->alarm_irq_enable) + err = -EINVAL; + else + err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); + + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); + +int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) +{ + int err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + if (enabled == 0 && rtc->uie_irq_active) { + mutex_unlock(&rtc->ops_lock); + return rtc_dev_update_irq_enable_emul(rtc, enabled); + } +#endif + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->update_irq_enable) + err = -EINVAL; + else + err = rtc->ops->update_irq_enable(rtc->dev.parent, enabled); + + mutex_unlock(&rtc->ops_lock); + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + /* + * Enable emulation if the driver did not provide + * the update_irq_enable function pointer or if returned + * -EINVAL to signal that it has been configured without + * interrupts or that are not available at the moment. + */ + if (err == -EINVAL) + err = rtc_dev_update_irq_enable_emul(rtc, enabled); +#endif + return err; +} +EXPORT_SYMBOL_GPL(rtc_update_irq_enable); + /** * rtc_update_irq - report RTC periodic, alarm, and/or update irqs * @rtc: the rtc device diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index ecdea44ae4e5..45152f4952d6 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -92,10 +92,10 @@ static void rtc_uie_timer(unsigned long data) spin_unlock_irqrestore(&rtc->irq_lock, flags); } -static void clear_uie(struct rtc_device *rtc) +static int clear_uie(struct rtc_device *rtc) { spin_lock_irq(&rtc->irq_lock); - if (rtc->irq_active) { + if (rtc->uie_irq_active) { rtc->stop_uie_polling = 1; if (rtc->uie_timer_active) { spin_unlock_irq(&rtc->irq_lock); @@ -108,9 +108,10 @@ static void clear_uie(struct rtc_device *rtc) flush_scheduled_work(); spin_lock_irq(&rtc->irq_lock); } - rtc->irq_active = 0; + rtc->uie_irq_active = 0; } spin_unlock_irq(&rtc->irq_lock); + return 0; } static int set_uie(struct rtc_device *rtc) @@ -122,8 +123,8 @@ static int set_uie(struct rtc_device *rtc) if (err) return err; spin_lock_irq(&rtc->irq_lock); - if (!rtc->irq_active) { - rtc->irq_active = 1; + if (!rtc->uie_irq_active) { + rtc->uie_irq_active = 1; rtc->stop_uie_polling = 0; rtc->oldsecs = tm.tm_sec; rtc->uie_task_active = 1; @@ -134,6 +135,16 @@ static int set_uie(struct rtc_device *rtc) spin_unlock_irq(&rtc->irq_lock); return 0; } + +int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) +{ + if (enabled) + return set_uie(rtc); + else + return clear_uie(rtc); +} +EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); + #endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ static ssize_t @@ -357,6 +368,22 @@ static long rtc_dev_ioctl(struct file *file, err = rtc_irq_set_state(rtc, NULL, 0); break; + case RTC_AIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 1); + + case RTC_AIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 0); + + case RTC_UIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 1); + + case RTC_UIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 0); + case RTC_IRQP_SET: err = rtc_irq_set_freq(rtc, NULL, arg); break; @@ -401,17 +428,6 @@ static long rtc_dev_ioctl(struct file *file, err = -EFAULT; return err; -#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL - case RTC_UIE_OFF: - mutex_unlock(&rtc->ops_lock); - clear_uie(rtc); - return 0; - - case RTC_UIE_ON: - mutex_unlock(&rtc->ops_lock); - err = set_uie(rtc); - return err; -#endif default: err = -ENOTTY; break; @@ -440,7 +456,10 @@ static int rtc_dev_release(struct inode *inode, struct file *file) * Leave the alarm alone; it may be set to trigger a system wakeup * later, or be used by kernel code, and is a one-shot event anyway. */ + + /* Keep ioctl until all drivers are converted */ rtc_dev_ioctl(file, RTC_UIE_OFF, 0); + rtc_update_irq_enable(rtc, 0); rtc_irq_set_state(rtc, NULL, 0); if (rtc->ops->release) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d5ccce1643e4..e0c45574b0c8 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -643,7 +643,6 @@ struct qeth_card_options { int macaddr_mode; int fake_broadcast; int add_hhlen; - int fake_ll; int layer2; enum qeth_large_send_types large_send; int performance_stats; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index e783644a2105..6811dd529f48 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -287,8 +287,15 @@ int qeth_set_large_send(struct qeth_card *card, card->options.large_send = type; switch (card->options.large_send) { case QETH_LARGE_SEND_EDDP: - card->dev->features |= NETIF_F_TSO | NETIF_F_SG | + if (card->info.type != QETH_CARD_TYPE_IQD) { + card->dev->features |= NETIF_F_TSO | NETIF_F_SG | NETIF_F_HW_CSUM; + } else { + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); + card->options.large_send = QETH_LARGE_SEND_NO; + rc = -EOPNOTSUPP; + } break; case QETH_LARGE_SEND_TSO: if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) { @@ -572,6 +579,10 @@ static void qeth_send_control_data_cb(struct qeth_channel *channel, card = CARD_FROM_CDEV(channel->ccwdev); if (qeth_check_idx_response(iob->data)) { qeth_clear_ipacmd_list(card); + if (((iob->data[2] & 0xc0) == 0xc0) && iob->data[4] == 0xf6) + dev_err(&card->gdev->dev, + "The qeth device is not configured " + "for the OSI layer required by z/VM\n"); qeth_schedule_recovery(card); goto out; } @@ -1072,7 +1083,6 @@ static void qeth_set_intial_options(struct qeth_card *card) card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL; card->options.fake_broadcast = 0; card->options.add_hhlen = DEFAULT_ADD_HHLEN; - card->options.fake_ll = 0; card->options.performance_stats = 0; card->options.rx_sg_cb = QETH_RX_SG_CB; } @@ -1682,6 +1692,7 @@ int qeth_send_control_data(struct qeth_card *card, int len, unsigned long flags; struct qeth_reply *reply = NULL; unsigned long timeout; + struct qeth_ipa_cmd *cmd; QETH_DBF_TEXT(TRACE, 2, "sendctl"); @@ -1728,17 +1739,34 @@ int qeth_send_control_data(struct qeth_card *card, int len, wake_up(&card->wait_q); return rc; } - while (!atomic_read(&reply->received)) { - if (time_after(jiffies, timeout)) { - spin_lock_irqsave(&reply->card->lock, flags); - list_del_init(&reply->list); - spin_unlock_irqrestore(&reply->card->lock, flags); - reply->rc = -ETIME; - atomic_inc(&reply->received); - wake_up(&reply->wait_q); - } - cpu_relax(); - }; + + /* we have only one long running ipassist, since we can ensure + process context of this command we can sleep */ + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + if ((cmd->hdr.command == IPA_CMD_SETIP) && + (cmd->hdr.prot_version == QETH_PROT_IPV4)) { + if (!wait_event_timeout(reply->wait_q, + atomic_read(&reply->received), timeout)) + goto time_err; + } else { + while (!atomic_read(&reply->received)) { + if (time_after(jiffies, timeout)) + goto time_err; + cpu_relax(); + }; + } + + rc = reply->rc; + qeth_put_reply(reply); + return rc; + +time_err: + spin_lock_irqsave(&reply->card->lock, flags); + list_del_init(&reply->list); + spin_unlock_irqrestore(&reply->card->lock, flags); + reply->rc = -ETIME; + atomic_inc(&reply->received); + wake_up(&reply->wait_q); rc = reply->rc; qeth_put_reply(reply); return rc; @@ -2250,7 +2278,8 @@ void qeth_print_status_message(struct qeth_card *card) } /* fallthrough */ case QETH_CARD_TYPE_IQD: - if (card->info.guestlan) { + if ((card->info.guestlan) || + (card->info.mcl_level[0] & 0x80)) { card->info.mcl_level[0] = (char) _ebcasc[(__u8) card->info.mcl_level[0]]; card->info.mcl_level[1] = (char) _ebcasc[(__u8) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 2c48591ced44..21627ba3093b 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1126,9 +1126,11 @@ static int qeth_l2_recover(void *ptr) dev_info(&card->gdev->dev, "Device successfully recovered!\n"); else { - rtnl_lock(); - dev_close(card->dev); - rtnl_unlock(); + if (card->dev) { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } dev_warn(&card->gdev->dev, "The qeth device driver " "failed to recover an error on the device\n"); } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c0b30b25a5f1..cfda1ecffdf2 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1047,7 +1047,7 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card) rc = qeth_setadpparms_change_macaddr(card); if (rc) dev_warn(&card->gdev->dev, "Reading the adapter MAC" - " address failed\n", rc); + " address failed\n"); } if ((card->info.link_type == QETH_LINK_TYPE_HSTR) || @@ -1207,12 +1207,9 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) QETH_DBF_TEXT(TRACE, 3, "stsrcmac"); - if (!card->options.fake_ll) - return -EOPNOTSUPP; - if (!qeth_is_supported(card, IPA_SOURCE_MAC)) { dev_info(&card->gdev->dev, - "Inbound source address not supported on %s\n", + "Inbound source MAC-address not supported on %s\n", QETH_CARD_IFNAME(card)); return -EOPNOTSUPP; } @@ -1221,7 +1218,7 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) IPA_CMD_ASS_START, 0); if (rc) dev_warn(&card->gdev->dev, - "Starting proxy ARP support for %s failed\n", + "Starting source MAC-address support for %s failed\n", QETH_CARD_IFNAME(card)); return rc; } @@ -1921,8 +1918,13 @@ static inline __u16 qeth_l3_rebuild_skb(struct qeth_card *card, memcpy(tg_addr, card->dev->dev_addr, card->dev->addr_len); } - card->dev->header_ops->create(skb, card->dev, prot, tg_addr, - "FAKELL", card->dev->addr_len); + if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR) + card->dev->header_ops->create(skb, card->dev, prot, + tg_addr, &hdr->hdr.l3.dest_addr[2], + card->dev->addr_len); + else + card->dev->header_ops->create(skb, card->dev, prot, + tg_addr, "FAKELL", card->dev->addr_len); } #ifdef CONFIG_TR @@ -2080,9 +2082,11 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) if (recovery_mode) qeth_l3_stop(card->dev); else { - rtnl_lock(); - dev_close(card->dev); - rtnl_unlock(); + if (card->dev) { + rtnl_lock(); + dev_close(card->dev); + rtnl_unlock(); + } } if (!card->use_hard_stop) { rc = qeth_send_stoplan(card); diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 185be760833e..2a129cb7bb56 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -279,7 +279,6 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de inode->i_mode = mode; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); - inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c4e62a6297d7..2e71368f45b4 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -1863,26 +1863,10 @@ static int do_write(struct fsg_dev *fsg) static int fsync_sub(struct lun *curlun) { struct file *filp = curlun->filp; - struct inode *inode; - int rc, err; if (curlun->ro || !filp) return 0; - if (!filp->f_op->fsync) - return -EINVAL; - - inode = filp->f_path.dentry->d_inode; - mutex_lock(&inode->i_mutex); - rc = filemap_fdatawrite(inode->i_mapping); - err = filp->f_op->fsync(filp, filp->f_path.dentry, 1); - if (!rc) - rc = err; - err = filemap_fdatawait(inode->i_mapping); - if (!rc) - rc = err; - mutex_unlock(&inode->i_mutex); - VLDBG(curlun, "fdatasync -> %d\n", rc); - return rc; + return vfs_fsync(filp, filp->f_path.dentry, 1); } static void fsync_all(struct fsg_dev *fsg) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index eeb26c0f88e5..317b48fdbf01 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -2001,7 +2001,6 @@ gadgetfs_make_inode (struct super_block *sb, inode->i_mode = mode; inode->i_uid = default_uid; inode->i_gid = default_gid; - inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_private = data; diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 73ac754ad801..e21fe5b6f9ff 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -546,23 +546,25 @@ static int viafb_blank(int blank_mode, struct fb_info *info) static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) { - struct viafb_ioctl_mode viamode; - struct viafb_ioctl_samm viasamm; - struct viafb_driver_version driver_version; - struct fb_var_screeninfo sec_var; - struct _panel_size_pos_info panel_pos_size_para; + union { + struct viafb_ioctl_mode viamode; + struct viafb_ioctl_samm viasamm; + struct viafb_driver_version driver_version; + struct fb_var_screeninfo sec_var; + struct _panel_size_pos_info panel_pos_size_para; + struct viafb_ioctl_setting viafb_setting; + struct device_t active_dev; + } u; u32 state_info = 0; - u32 viainfo_size = sizeof(struct viafb_ioctl_info); u32 *viafb_gamma_table; char driver_name[] = "viafb"; u32 __user *argp = (u32 __user *) arg; u32 gpu32; u32 video_dev_info = 0; - struct viafb_ioctl_setting viafb_setting = {}; - struct device_t active_dev = {}; DEBUG_MSG(KERN_INFO "viafb_ioctl: 0x%X !!\n", cmd); + memset(&u, 0, sizeof(u)); switch (cmd) { case VIAFB_GET_CHIP_INFO: @@ -571,7 +573,7 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) return -EFAULT; break; case VIAFB_GET_INFO_SIZE: - return put_user(viainfo_size, argp); + return put_user((u32)sizeof(struct viafb_ioctl_info), argp); case VIAFB_GET_INFO: return viafb_ioctl_get_viafb_info(arg); case VIAFB_HOTPLUG: @@ -584,60 +586,60 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) viafb_hotplug = (gpu32) ? 1 : 0; break; case VIAFB_GET_RESOLUTION: - viamode.xres = (u32) viafb_hotplug_Xres; - viamode.yres = (u32) viafb_hotplug_Yres; - viamode.refresh = (u32) viafb_hotplug_refresh; - viamode.bpp = (u32) viafb_hotplug_bpp; + u.viamode.xres = (u32) viafb_hotplug_Xres; + u.viamode.yres = (u32) viafb_hotplug_Yres; + u.viamode.refresh = (u32) viafb_hotplug_refresh; + u.viamode.bpp = (u32) viafb_hotplug_bpp; if (viafb_SAMM_ON == 1) { - viamode.xres_sec = viafb_second_xres; - viamode.yres_sec = viafb_second_yres; - viamode.virtual_xres_sec = viafb_second_virtual_xres; - viamode.virtual_yres_sec = viafb_second_virtual_yres; - viamode.refresh_sec = viafb_refresh1; - viamode.bpp_sec = viafb_bpp1; + u.viamode.xres_sec = viafb_second_xres; + u.viamode.yres_sec = viafb_second_yres; + u.viamode.virtual_xres_sec = viafb_second_virtual_xres; + u.viamode.virtual_yres_sec = viafb_second_virtual_yres; + u.viamode.refresh_sec = viafb_refresh1; + u.viamode.bpp_sec = viafb_bpp1; } else { - viamode.xres_sec = 0; - viamode.yres_sec = 0; - viamode.virtual_xres_sec = 0; - viamode.virtual_yres_sec = 0; - viamode.refresh_sec = 0; - viamode.bpp_sec = 0; + u.viamode.xres_sec = 0; + u.viamode.yres_sec = 0; + u.viamode.virtual_xres_sec = 0; + u.viamode.virtual_yres_sec = 0; + u.viamode.refresh_sec = 0; + u.viamode.bpp_sec = 0; } - if (copy_to_user(argp, &viamode, sizeof(viamode))) + if (copy_to_user(argp, &u.viamode, sizeof(u.viamode))) return -EFAULT; break; case VIAFB_GET_SAMM_INFO: - viasamm.samm_status = viafb_SAMM_ON; + u.viasamm.samm_status = viafb_SAMM_ON; if (viafb_SAMM_ON == 1) { if (viafb_dual_fb) { - viasamm.size_prim = viaparinfo->fbmem_free; - viasamm.size_sec = viaparinfo1->fbmem_free; + u.viasamm.size_prim = viaparinfo->fbmem_free; + u.viasamm.size_sec = viaparinfo1->fbmem_free; } else { if (viafb_second_size) { - viasamm.size_prim = + u.viasamm.size_prim = viaparinfo->fbmem_free - viafb_second_size * 1024 * 1024; - viasamm.size_sec = + u.viasamm.size_sec = viafb_second_size * 1024 * 1024; } else { - viasamm.size_prim = + u.viasamm.size_prim = viaparinfo->fbmem_free >> 1; - viasamm.size_sec = + u.viasamm.size_sec = (viaparinfo->fbmem_free >> 1); } } - viasamm.mem_base = viaparinfo->fbmem; - viasamm.offset_sec = viafb_second_offset; + u.viasamm.mem_base = viaparinfo->fbmem; + u.viasamm.offset_sec = viafb_second_offset; } else { - viasamm.size_prim = + u.viasamm.size_prim = viaparinfo->memsize - viaparinfo->fbmem_used; - viasamm.size_sec = 0; - viasamm.mem_base = viaparinfo->fbmem; - viasamm.offset_sec = 0; + u.viasamm.size_sec = 0; + u.viasamm.mem_base = viaparinfo->fbmem; + u.viasamm.offset_sec = 0; } - if (copy_to_user(argp, &viasamm, sizeof(viasamm))) + if (copy_to_user(argp, &u.viasamm, sizeof(u.viasamm))) return -EFAULT; break; @@ -662,74 +664,75 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) viafb_lcd_disable(); break; case VIAFB_SET_DEVICE: - if (copy_from_user(&active_dev, (void *)argp, - sizeof(active_dev))) + if (copy_from_user(&u.active_dev, (void *)argp, + sizeof(u.active_dev))) return -EFAULT; - viafb_set_device(active_dev); + viafb_set_device(u.active_dev); viafb_set_par(info); break; case VIAFB_GET_DEVICE: - active_dev.crt = viafb_CRT_ON; - active_dev.dvi = viafb_DVI_ON; - active_dev.lcd = viafb_LCD_ON; - active_dev.samm = viafb_SAMM_ON; - active_dev.primary_dev = viafb_primary_dev; + u.active_dev.crt = viafb_CRT_ON; + u.active_dev.dvi = viafb_DVI_ON; + u.active_dev.lcd = viafb_LCD_ON; + u.active_dev.samm = viafb_SAMM_ON; + u.active_dev.primary_dev = viafb_primary_dev; - active_dev.lcd_dsp_cent = viafb_lcd_dsp_method; - active_dev.lcd_panel_id = viafb_lcd_panel_id; - active_dev.lcd_mode = viafb_lcd_mode; + u.active_dev.lcd_dsp_cent = viafb_lcd_dsp_method; + u.active_dev.lcd_panel_id = viafb_lcd_panel_id; + u.active_dev.lcd_mode = viafb_lcd_mode; - active_dev.xres = viafb_hotplug_Xres; - active_dev.yres = viafb_hotplug_Yres; + u.active_dev.xres = viafb_hotplug_Xres; + u.active_dev.yres = viafb_hotplug_Yres; - active_dev.xres1 = viafb_second_xres; - active_dev.yres1 = viafb_second_yres; + u.active_dev.xres1 = viafb_second_xres; + u.active_dev.yres1 = viafb_second_yres; - active_dev.bpp = viafb_bpp; - active_dev.bpp1 = viafb_bpp1; - active_dev.refresh = viafb_refresh; - active_dev.refresh1 = viafb_refresh1; + u.active_dev.bpp = viafb_bpp; + u.active_dev.bpp1 = viafb_bpp1; + u.active_dev.refresh = viafb_refresh; + u.active_dev.refresh1 = viafb_refresh1; - active_dev.epia_dvi = viafb_platform_epia_dvi; - active_dev.lcd_dual_edge = viafb_device_lcd_dualedge; - active_dev.bus_width = viafb_bus_width; + u.active_dev.epia_dvi = viafb_platform_epia_dvi; + u.active_dev.lcd_dual_edge = viafb_device_lcd_dualedge; + u.active_dev.bus_width = viafb_bus_width; - if (copy_to_user(argp, &active_dev, sizeof(active_dev))) + if (copy_to_user(argp, &u.active_dev, sizeof(u.active_dev))) return -EFAULT; break; case VIAFB_GET_DRIVER_VERSION: - driver_version.iMajorNum = VERSION_MAJOR; - driver_version.iKernelNum = VERSION_KERNEL; - driver_version.iOSNum = VERSION_OS; - driver_version.iMinorNum = VERSION_MINOR; + u.driver_version.iMajorNum = VERSION_MAJOR; + u.driver_version.iKernelNum = VERSION_KERNEL; + u.driver_version.iOSNum = VERSION_OS; + u.driver_version.iMinorNum = VERSION_MINOR; - if (copy_to_user(argp, &driver_version, - sizeof(driver_version))) + if (copy_to_user(argp, &u.driver_version, + sizeof(u.driver_version))) return -EFAULT; break; case VIAFB_SET_DEVICE_INFO: - if (copy_from_user(&viafb_setting, - argp, sizeof(viafb_setting))) + if (copy_from_user(&u.viafb_setting, + argp, sizeof(u.viafb_setting))) return -EFAULT; - if (apply_device_setting(viafb_setting, info) < 0) + if (apply_device_setting(u.viafb_setting, info) < 0) return -EINVAL; break; case VIAFB_SET_SECOND_MODE: - if (copy_from_user(&sec_var, argp, sizeof(sec_var))) + if (copy_from_user(&u.sec_var, argp, sizeof(u.sec_var))) return -EFAULT; - apply_second_mode_setting(&sec_var); + apply_second_mode_setting(&u.sec_var); break; case VIAFB_GET_DEVICE_INFO: - retrieve_device_setting(&viafb_setting); + retrieve_device_setting(&u.viafb_setting); - if (copy_to_user(argp, &viafb_setting, sizeof(viafb_setting))) + if (copy_to_user(argp, &u.viafb_setting, + sizeof(u.viafb_setting))) return -EFAULT; break; @@ -806,51 +809,51 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg) break; case VIAFB_GET_PANEL_MAX_SIZE: - if (copy_from_user - (&panel_pos_size_para, argp, sizeof(panel_pos_size_para))) + if (copy_from_user(&u.panel_pos_size_para, argp, + sizeof(u.panel_pos_size_para))) return -EFAULT; - panel_pos_size_para.x = panel_pos_size_para.y = 0; - if (copy_to_user(argp, &panel_pos_size_para, - sizeof(panel_pos_size_para))) + u.panel_pos_size_para.x = u.panel_pos_size_para.y = 0; + if (copy_to_user(argp, &u.panel_pos_size_para, + sizeof(u.panel_pos_size_para))) return -EFAULT; break; case VIAFB_GET_PANEL_MAX_POSITION: - if (copy_from_user - (&panel_pos_size_para, argp, sizeof(panel_pos_size_para))) + if (copy_from_user(&u.panel_pos_size_para, argp, + sizeof(u.panel_pos_size_para))) return -EFAULT; - panel_pos_size_para.x = panel_pos_size_para.y = 0; - if (copy_to_user(argp, &panel_pos_size_para, - sizeof(panel_pos_size_para))) + u.panel_pos_size_para.x = u.panel_pos_size_para.y = 0; + if (copy_to_user(argp, &u.panel_pos_size_para, + sizeof(u.panel_pos_size_para))) return -EFAULT; break; case VIAFB_GET_PANEL_POSITION: - if (copy_from_user - (&panel_pos_size_para, argp, sizeof(panel_pos_size_para))) + if (copy_from_user(&u.panel_pos_size_para, argp, + sizeof(u.panel_pos_size_para))) return -EFAULT; - panel_pos_size_para.x = panel_pos_size_para.y = 0; - if (copy_to_user(argp, &panel_pos_size_para, - sizeof(panel_pos_size_para))) + u.panel_pos_size_para.x = u.panel_pos_size_para.y = 0; + if (copy_to_user(argp, &u.panel_pos_size_para, + sizeof(u.panel_pos_size_para))) return -EFAULT; break; case VIAFB_GET_PANEL_SIZE: - if (copy_from_user - (&panel_pos_size_para, argp, sizeof(panel_pos_size_para))) + if (copy_from_user(&u.panel_pos_size_para, argp, + sizeof(u.panel_pos_size_para))) return -EFAULT; - panel_pos_size_para.x = panel_pos_size_para.y = 0; - if (copy_to_user(argp, &panel_pos_size_para, - sizeof(panel_pos_size_para))) + u.panel_pos_size_para.x = u.panel_pos_size_para.y = 0; + if (copy_to_user(argp, &u.panel_pos_size_para, + sizeof(u.panel_pos_size_para))) return -EFAULT; break; case VIAFB_SET_PANEL_POSITION: - if (copy_from_user - (&panel_pos_size_para, argp, sizeof(panel_pos_size_para))) + if (copy_from_user(&u.panel_pos_size_para, argp, + sizeof(u.panel_pos_size_para))) return -EFAULT; break; case VIAFB_SET_PANEL_SIZE: - if (copy_from_user - (&panel_pos_size_para, argp, sizeof(panel_pos_size_para))) + if (copy_from_user(&u.panel_pos_size_para, argp, + sizeof(u.panel_pos_size_para))) return -EFAULT; break; @@ -1052,10 +1055,8 @@ static void viafb_imageblit(struct fb_info *info, static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) { - u8 data[CURSOR_SIZE / 8]; - u32 data_bak[CURSOR_SIZE / 32]; u32 temp, xx, yy, bg_col = 0, fg_col = 0; - int size, i, j = 0; + int i, j = 0; static int hw_cursor; struct viafb_par *p_viafb_par; @@ -1178,22 +1179,29 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) } if (cursor->set & FB_CUR_SETSHAPE) { - size = + struct { + u8 data[CURSOR_SIZE / 8]; + u32 bak[CURSOR_SIZE / 32]; + } *cr_data = kzalloc(sizeof(*cr_data), GFP_ATOMIC); + int size = ((viacursor.image.width + 7) >> 3) * viacursor.image.height; + if (cr_data == NULL) + goto out; + if (MAX_CURS == 32) { for (i = 0; i < (CURSOR_SIZE / 32); i++) { - data_bak[i] = 0x0; - data_bak[i + 1] = 0xFFFFFFFF; + cr_data->bak[i] = 0x0; + cr_data->bak[i + 1] = 0xFFFFFFFF; i += 1; } } else if (MAX_CURS == 64) { for (i = 0; i < (CURSOR_SIZE / 32); i++) { - data_bak[i] = 0x0; - data_bak[i + 1] = 0x0; - data_bak[i + 2] = 0xFFFFFFFF; - data_bak[i + 3] = 0xFFFFFFFF; + cr_data->bak[i] = 0x0; + cr_data->bak[i + 1] = 0x0; + cr_data->bak[i + 2] = 0xFFFFFFFF; + cr_data->bak[i + 3] = 0xFFFFFFFF; i += 3; } } @@ -1201,12 +1209,12 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) switch (viacursor.rop) { case ROP_XOR: for (i = 0; i < size; i++) - data[i] = viacursor.mask[i]; + cr_data->data[i] = viacursor.mask[i]; break; case ROP_COPY: for (i = 0; i < size; i++) - data[i] = viacursor.mask[i]; + cr_data->data[i] = viacursor.mask[i]; break; default: break; @@ -1214,23 +1222,25 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor) if (MAX_CURS == 32) { for (i = 0; i < size; i++) { - data_bak[j] = (u32) data[i]; - data_bak[j + 1] = ~data_bak[j]; + cr_data->bak[j] = (u32) cr_data->data[i]; + cr_data->bak[j + 1] = ~cr_data->bak[j]; j += 2; } } else if (MAX_CURS == 64) { for (i = 0; i < size; i++) { - data_bak[j] = (u32) data[i]; - data_bak[j + 1] = 0x0; - data_bak[j + 2] = ~data_bak[j]; - data_bak[j + 3] = ~data_bak[j + 1]; + cr_data->bak[j] = (u32) cr_data->data[i]; + cr_data->bak[j + 1] = 0x0; + cr_data->bak[j + 2] = ~cr_data->bak[j]; + cr_data->bak[j + 3] = ~cr_data->bak[j + 1]; j += 4; } } memcpy(((struct viafb_par *)(info->par))->fbmem_virt + ((struct viafb_par *)(info->par))->cursor_start, - data_bak, CURSOR_SIZE); + cr_data->bak, CURSOR_SIZE); +out: + kfree(cr_data); } if (viacursor.enable) |