Merge tag 'driver-core-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 Jul 2012 18:25:33 +0000 (11:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 Jul 2012 18:25:33 +0000 (11:25 -0700)
Pull driver core changes from Greg Kroah-Hartman:
 "Here's the big driver core pull request for 3.6-rc1.

  Unlike 3.5, this kernel should be a lot tamer, with the printk changes
  now settled down.  All we have here is some extcon driver updates, w1
  driver updates, a few printk cleanups that weren't needed for 3.5, but
  are good to have now, and some other minor fixes/changes in the driver
  core.

  All of these have been in the linux-next releases for a while now.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
* tag 'driver-core-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (38 commits)
  printk: Export struct log size and member offsets through vmcoreinfo
  Drivers: hv: Change the hex constant to a decimal constant
  driver core: don't trigger uevent after failure
  extcon: MAX77693: Add extcon-max77693 driver to support Maxim MAX77693 MUIC device
  sysfs: fail dentry revalidation after namespace change fix
  sysfs: fail dentry revalidation after namespace change
  extcon: spelling of detach in function doc
  extcon: arizona: Stop microphone detection if we give up on it
  extcon: arizona: Update cable reporting calls and split headset
  PM / Runtime: Do not increment device usage counts before probing
  kmsg - do not flush partial lines when the console is busy
  kmsg - export "continuation record" flag to /dev/kmsg
  kmsg - avoid warning for CONFIG_PRINTK=n compilations
  kmsg - properly print over-long continuation lines
  driver-core: Use kobj_to_dev instead of re-implementing it
  driver-core: Move kobj_to_dev from genhd.h to device.h
  driver core: Move deferred devices to the end of dpm_list before probing
  driver core: move uevent call to driver_register
  driver core: fix shutdown races with probe/remove(v3)
  Extcon: Arizona: Add driver for Wolfson Arizona class devices
  ...

47 files changed:
Documentation/ABI/stable/sysfs-driver-w1_ds28e04 [new file with mode: 0644]
Documentation/ABI/testing/dev-kmsg
Documentation/stable_kernel_rules.txt
Documentation/w1/slaves/w1_ds28e04 [new file with mode: 0644]
MAINTAINERS
drivers/base/bus.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/dma-buf.c
drivers/base/dma-coherent.c
drivers/base/driver.c
drivers/base/firmware_class.c
drivers/extcon/Kconfig
drivers/extcon/Makefile
drivers/extcon/extcon-arizona.c [new file with mode: 0644]
drivers/extcon/extcon-max77693.c [new file with mode: 0644]
drivers/extcon/extcon_class.c
drivers/extcon/extcon_gpio.c
drivers/hv/hyperv_vmbus.h
drivers/power/ds2780_battery.c
drivers/power/ds2781_battery.c
drivers/w1/masters/ds1wm.c
drivers/w1/masters/omap_hdq.c
drivers/w1/slaves/Kconfig
drivers/w1/slaves/Makefile
drivers/w1/slaves/w1_bq27000.c
drivers/w1/slaves/w1_ds2408.c
drivers/w1/slaves/w1_ds2423.c
drivers/w1/slaves/w1_ds2431.c
drivers/w1/slaves/w1_ds2433.c
drivers/w1/slaves/w1_ds2760.c
drivers/w1/slaves/w1_ds2780.c
drivers/w1/slaves/w1_ds2780.h
drivers/w1/slaves/w1_ds2781.c
drivers/w1/slaves/w1_ds2781.h
drivers/w1/slaves/w1_ds28e04.c [new file with mode: 0644]
drivers/w1/slaves/w1_therm.c
drivers/w1/w1.c
drivers/w1/w1.h
drivers/w1/w1_family.h
drivers/w1/w1_int.c
fs/debugfs/inode.c
fs/sysfs/dir.c
include/linux/device.h
include/linux/extcon/extcon_gpio.h
include/linux/genhd.h
kernel/printk.c

diff --git a/Documentation/ABI/stable/sysfs-driver-w1_ds28e04 b/Documentation/ABI/stable/sysfs-driver-w1_ds28e04
new file mode 100644 (file)
index 0000000..26579ee
--- /dev/null
@@ -0,0 +1,15 @@
+What:          /sys/bus/w1/devices/.../pio
+Date:          May 2012
+Contact:       Markus Franke <franm@hrz.tu-chemnitz.de>
+Description:   read/write the contents of the two PIO's of the DS28E04-100
+               see Documentation/w1/slaves/w1_ds28e04 for detailed information
+Users:         any user space application which wants to communicate with DS28E04-100
+
+
+
+What:          /sys/bus/w1/devices/.../eeprom
+Date:          May 2012
+Contact:       Markus Franke <franm@hrz.tu-chemnitz.de>
+Description:   read/write the contents of the EEPROM memory of the DS28E04-100
+               see Documentation/w1/slaves/w1_ds28e04 for detailed information
+Users:         any user space application which wants to communicate with DS28E04-100
index 281ecc5f97092d6634c5a3b540677ec5d9b577db..7e7e07a82e0ec15dbe3255683acd67dba8d2f429 100644 (file)
@@ -58,16 +58,18 @@ Description:        The /dev/kmsg character device node provides userspace access
 
                The output format consists of a prefix carrying the syslog
                prefix including priority and facility, the 64 bit message
-               sequence number and the monotonic timestamp in microseconds.
-               The values are separated by a ','. Future extensions might
-               add more comma separated values before the terminating ';'.
-               Unknown values should be gracefully ignored.
+               sequence number and the monotonic timestamp in microseconds,
+               and a flag field. All fields are separated by a ','.
+
+               Future extensions might add more comma separated values before
+               the terminating ';'. Unknown fields and values should be
+               gracefully ignored.
 
                The human readable text string starts directly after the ';'
                and is terminated by a '\n'. Untrusted values derived from
                hardware or other facilities are printed, therefore
-               all non-printable characters in the log message are escaped
-               by "\x00" C-style hex encoding.
+               all non-printable characters and '\' itself in the log message
+               are escaped by "\x00" C-style hex encoding.
 
                A line starting with ' ', is a continuation line, adding
                key/value pairs to the log message, which provide the machine
@@ -75,11 +77,11 @@ Description:        The /dev/kmsg character device node provides userspace access
                userspace.
 
                Example:
-               7,160,424069;pci_root PNP0A03:00: host bridge window [io  0x0000-0x0cf7] (ignored)
+               7,160,424069,-;pci_root PNP0A03:00: host bridge window [io  0x0000-0x0cf7] (ignored)
                 SUBSYSTEM=acpi
                 DEVICE=+acpi:PNP0A03:00
-               6,339,5140900;NET: Registered protocol family 10
-               30,340,5690716;udevd[80]: starting version 181
+               6,339,5140900,-;NET: Registered protocol family 10
+               30,340,5690716,-;udevd[80]: starting version 181
 
                The DEVICE= key uniquely identifies devices the following way:
                  b12:8        - block dev_t
@@ -87,4 +89,13 @@ Description: The /dev/kmsg character device node provides userspace access
                  n8           - netdev ifindex
                  +sound:card0 - subsystem:devname
 
+               The flags field carries '-' by default. A 'c' indicates a
+               fragment of a line. All following fragments are flagged with
+               '+'. Note, that these hints about continuation lines are not
+               neccessarily correct, and the stream could be interleaved with
+               unrelated messages, but merging the lines in the output
+               usually produces better human readable results. A similar
+               logic is used internally when messages are printed to the
+               console, /proc/kmsg or the syslog() syscall.
+
 Users:         dmesg(1), userspace kernel log consumers
index 4a7b54bd37e8c18b2eebfcd21608a2488589641b..b0714d8f678ac51d0c280a4f5f2980196052421f 100644 (file)
@@ -1,4 +1,4 @@
-Everything you ever wanted to know about Linux 2.6 -stable releases.
+Everything you ever wanted to know about Linux -stable releases.
 
 Rules on what kind of patches are accepted, and which ones are not, into the
 "-stable" tree:
@@ -42,10 +42,10 @@ Procedure for submitting patches to the -stable tree:
    cherry-picked than this can be specified in the following format in
    the sign-off area:
 
-     Cc: <stable@vger.kernel.org> # .32.x: a1f84a3: sched: Check for idle
-     Cc: <stable@vger.kernel.org> # .32.x: 1b9508f: sched: Rate-limit newidle
-     Cc: <stable@vger.kernel.org> # .32.x: fd21073: sched: Fix affinity logic
-     Cc: <stable@vger.kernel.org> # .32.x
+     Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle
+     Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle
+     Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic
+     Cc: <stable@vger.kernel.org> # 3.3.x
     Signed-off-by: Ingo Molnar <mingo@elte.hu>
 
    The tag sequence has the meaning of:
@@ -79,6 +79,15 @@ Review cycle:
    security kernel team, and not go through the normal review cycle.
    Contact the kernel security team for more details on this procedure.
 
+Trees:
+
+ - The queues of patches, for both completed versions and in progress
+   versions can be found at:
+       http://git.kernel.org/?p=linux/kernel/git/stable/stable-queue.git
+ - The finalized and tagged releases of all stable kernels can be found
+   in separate branches per version at:
+       http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git
+
 
 Review committee:
 
diff --git a/Documentation/w1/slaves/w1_ds28e04 b/Documentation/w1/slaves/w1_ds28e04
new file mode 100644 (file)
index 0000000..85bc9a7
--- /dev/null
@@ -0,0 +1,36 @@
+Kernel driver w1_ds28e04
+========================
+
+Supported chips:
+  * Maxim DS28E04-100 4096-Bit Addressable 1-Wire EEPROM with PIO
+
+supported family codes:
+       W1_FAMILY_DS28E04       0x1C
+
+Author: Markus Franke, <franke.m@sebakmt.com> <franm@hrz.tu-chemnitz.de>
+
+Description
+-----------
+
+Support is provided through the sysfs files "eeprom" and "pio". CRC checking
+during memory accesses can optionally be enabled/disabled via the device
+attribute "crccheck". The strong pull-up can optionally be enabled/disabled
+via the module parameter "w1_strong_pullup".
+
+Memory Access
+
+       A read operation on the "eeprom" file reads the given amount of bytes
+       from the EEPROM of the DS28E04.
+
+       A write operation on the "eeprom" file writes the given byte sequence
+       to the EEPROM of the DS28E04. If CRC checking mode is enabled only
+       fully alligned blocks of 32 bytes with valid CRC16 values (in bytes 30
+       and 31) are allowed to be written.
+
+PIO Access
+
+       The 2 PIOs of the DS28E04-100 are accessible via the "pio" sysfs file.
+
+       The current status of the PIO's is returned as an 8 bit value. Bit 0/1
+       represent the state of PIO_0/PIO_1. Bits 2..7 do not care. The PIO's are
+       driven low-active, i.e. the driver delivers/expects low-active values.
index 9b8a5daaf403e52345be2096f0b697f6c3dfc97f..429e72cc13fc5d378f56ef9c2cc7a16386dccf51 100644 (file)
@@ -2728,6 +2728,14 @@ M:       Mimi Zohar <zohar@us.ibm.com>
 S:     Supported
 F:     security/integrity/evm/
 
+EXTERNAL CONNECTOR SUBSYSTEM (EXTCON)
+M:     MyungJoo Ham <myungjoo.ham@samsung.com>
+M:     Chanwoo Choi <cw00.choi@samsung.com>
+L:     linux-kernel@vger.kernel.org
+S:     Maintained
+F:     drivers/extcon/
+F:     Documentation/extcon/
+
 EXYNOS DP DRIVER
 M:     Jingoo Han <jg1.han@samsung.com>
 L:     linux-fbdev@vger.kernel.org
index 2bcef657a60c79943171085cd8638b3f5efa6903..181ed2660b33b7f4e96e18250b493edd90539def 100644 (file)
@@ -743,7 +743,6 @@ int bus_add_driver(struct device_driver *drv)
                }
        }
 
-       kobject_uevent(&priv->kobj, KOBJ_ADD);
        return 0;
 
 out_unregister:
index 346be8b78b247e78fbe987fdfae435f8919cb2c6..f338037a4f3d9109e4d69da7a22a4e41070615b4 100644 (file)
@@ -85,14 +85,13 @@ const char *dev_driver_string(const struct device *dev)
 }
 EXPORT_SYMBOL(dev_driver_string);
 
-#define to_dev(obj) container_of(obj, struct device, kobj)
 #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
 
 static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                             char *buf)
 {
        struct device_attribute *dev_attr = to_dev_attr(attr);
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        ssize_t ret = -EIO;
 
        if (dev_attr->show)
@@ -108,7 +107,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
                              const char *buf, size_t count)
 {
        struct device_attribute *dev_attr = to_dev_attr(attr);
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        ssize_t ret = -EIO;
 
        if (dev_attr->store)
@@ -182,7 +181,7 @@ EXPORT_SYMBOL_GPL(device_show_int);
  */
 static void device_release(struct kobject *kobj)
 {
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct device_private *p = dev->p;
 
        if (dev->release)
@@ -200,7 +199,7 @@ static void device_release(struct kobject *kobj)
 
 static const void *device_namespace(struct kobject *kobj)
 {
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        const void *ns = NULL;
 
        if (dev->class && dev->class->ns_type)
@@ -221,7 +220,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
        struct kobj_type *ktype = get_ktype(kobj);
 
        if (ktype == &device_ktype) {
-               struct device *dev = to_dev(kobj);
+               struct device *dev = kobj_to_dev(kobj);
                if (dev->bus)
                        return 1;
                if (dev->class)
@@ -232,7 +231,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
 
 static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
 {
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
 
        if (dev->bus)
                return dev->bus->name;
@@ -244,7 +243,7 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
 static int dev_uevent(struct kset *kset, struct kobject *kobj,
                      struct kobj_uevent_env *env)
 {
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        int retval = 0;
 
        /* add device node properties if present */
@@ -1132,7 +1131,7 @@ int device_register(struct device *dev)
  */
 struct device *get_device(struct device *dev)
 {
-       return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
+       return dev ? kobj_to_dev(kobject_get(&dev->kobj)) : NULL;
 }
 
 /**
@@ -1754,25 +1753,25 @@ int device_move(struct device *dev, struct device *new_parent,
                set_dev_node(dev, dev_to_node(new_parent));
        }
 
-       if (!dev->class)
-               goto out_put;
-       error = device_move_class_links(dev, old_parent, new_parent);
-       if (error) {
-               /* We ignore errors on cleanup since we're hosed anyway... */
-               device_move_class_links(dev, new_parent, old_parent);
-               if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
-                       if (new_parent)
-                               klist_remove(&dev->p->knode_parent);
-                       dev->parent = old_parent;
-                       if (old_parent) {
-                               klist_add_tail(&dev->p->knode_parent,
-                                              &old_parent->p->klist_children);
-                               set_dev_node(dev, dev_to_node(old_parent));
+       if (dev->class) {
+               error = device_move_class_links(dev, old_parent, new_parent);
+               if (error) {
+                       /* We ignore errors on cleanup since we're hosed anyway... */
+                       device_move_class_links(dev, new_parent, old_parent);
+                       if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
+                               if (new_parent)
+                                       klist_remove(&dev->p->knode_parent);
+                               dev->parent = old_parent;
+                               if (old_parent) {
+                                       klist_add_tail(&dev->p->knode_parent,
+                                                      &old_parent->p->klist_children);
+                                       set_dev_node(dev, dev_to_node(old_parent));
+                               }
                        }
+                       cleanup_glue_dir(dev, new_parent_kobj);
+                       put_device(new_parent);
+                       goto out;
                }
-               cleanup_glue_dir(dev, new_parent_kobj);
-               put_device(new_parent);
-               goto out;
        }
        switch (dpm_order) {
        case DPM_ORDER_NONE:
@@ -1787,7 +1786,7 @@ int device_move(struct device *dev, struct device *new_parent,
                device_pm_move_last(dev);
                break;
        }
-out_put:
+
        put_device(old_parent);
 out:
        device_pm_unlock();
@@ -1812,6 +1811,13 @@ void device_shutdown(void)
        while (!list_empty(&devices_kset->list)) {
                dev = list_entry(devices_kset->list.prev, struct device,
                                kobj.entry);
+
+               /*
+                * hold reference count of device's parent to
+                * prevent it from being freed because parent's
+                * lock is to be held
+                */
+               get_device(dev->parent);
                get_device(dev);
                /*
                 * Make sure the device is off the kset list, in the
@@ -1820,6 +1826,11 @@ void device_shutdown(void)
                list_del_init(&dev->kobj.entry);
                spin_unlock(&devices_kset->list_lock);
 
+               /* hold lock to avoid race with probe/release */
+               if (dev->parent)
+                       device_lock(dev->parent);
+               device_lock(dev);
+
                /* Don't allow any more runtime suspends */
                pm_runtime_get_noresume(dev);
                pm_runtime_barrier(dev);
@@ -1831,7 +1842,13 @@ void device_shutdown(void)
                        dev_dbg(dev, "shutdown\n");
                        dev->driver->shutdown(dev);
                }
+
+               device_unlock(dev);
+               if (dev->parent)
+                       device_unlock(dev->parent);
+
                put_device(dev);
+               put_device(dev->parent);
 
                spin_lock(&devices_kset->list_lock);
        }
index dcb8a6e4869249e4b26b405a9348f02113809ba6..e3bbed8a617c257373a1fa2d75318d9ff930601a 100644 (file)
@@ -85,8 +85,20 @@ static void deferred_probe_work_func(struct work_struct *work)
                 * manipulate the deferred list
                 */
                mutex_unlock(&deferred_probe_mutex);
+
+               /*
+                * Force the device to the end of the dpm_list since
+                * the PM code assumes that the order we add things to
+                * the list is a good order for suspend but deferred
+                * probe makes that very unsafe.
+                */
+               device_pm_lock();
+               device_pm_move_last(dev);
+               device_pm_unlock();
+
                dev_dbg(dev, "Retrying from deferred list\n");
                bus_probe_device(dev);
+
                mutex_lock(&deferred_probe_mutex);
 
                put_device(dev);
@@ -283,6 +295,7 @@ probe_failed:
        devres_release_all(dev);
        driver_sysfs_remove(dev);
        dev->driver = NULL;
+       dev_set_drvdata(dev, NULL);
 
        if (ret == -EPROBE_DEFER) {
                /* Driver requested deferred probing */
@@ -356,10 +369,9 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
        pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                 drv->bus->name, __func__, dev_name(dev), drv->name);
 
-       pm_runtime_get_noresume(dev);
        pm_runtime_barrier(dev);
        ret = really_probe(dev, drv);
-       pm_runtime_put_sync(dev);
+       pm_runtime_idle(dev);
 
        return ret;
 }
@@ -406,9 +418,8 @@ int device_attach(struct device *dev)
                        ret = 0;
                }
        } else {
-               pm_runtime_get_noresume(dev);
                ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-               pm_runtime_put_sync(dev);
+               pm_runtime_idle(dev);
        }
 out_unlock:
        device_unlock(dev);
@@ -487,6 +498,7 @@ static void __device_release_driver(struct device *dev)
                        drv->remove(dev);
                devres_release_all(dev);
                dev->driver = NULL;
+               dev_set_drvdata(dev, NULL);
                klist_remove(&dev->p->knode_driver);
                if (dev->bus)
                        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
index 24e88fe29ec19801f627e55b7b454fbbe8268181..c30f3e1d0efcc7bcb443051cab59930b06d72b74 100644 (file)
@@ -493,6 +493,7 @@ EXPORT_SYMBOL_GPL(dma_buf_vmap);
 /**
  * dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap.
  * @dmabuf:    [in]    buffer to vunmap
+ * @vaddr:     [in]    vmap to vunmap
  */
 void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
 {
index 1b85949e3d2f3f562ea18c61e890309fe18601af..560a7173f810015a9532d25395a8ad1a57366e6c 100644 (file)
@@ -186,6 +186,7 @@ EXPORT_SYMBOL(dma_release_from_coherent);
  * @vma:       vm_area for the userspace memory
  * @vaddr:     cpu address returned by dma_alloc_from_coherent
  * @size:      size of the memory buffer allocated by dma_alloc_from_coherent
+ * @ret:       result from remap_pfn_range()
  *
  * This checks whether the memory was allocated from the per-device
  * coherent memory pool and if so, maps that memory to the provided vma.
index 207c27ddf828392ef2ffb74ace6d0b4c8269282a..974e301a1ef07ae02064be5e003bc4ac034df5c7 100644 (file)
@@ -185,8 +185,12 @@ int driver_register(struct device_driver *drv)
        if (ret)
                return ret;
        ret = driver_add_groups(drv, drv->groups);
-       if (ret)
+       if (ret) {
                bus_remove_driver(drv);
+               return ret;
+       }
+       kobject_uevent(&drv->p->kobj, KOBJ_ADD);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(driver_register);
index 5401814c874df066833c8b5c3ac2dc28e2842490..803cfc1597a9c89c5faf0edd0b76e418eb843d64 100644 (file)
@@ -22,8 +22,6 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 
-#define to_dev(obj) container_of(obj, struct device, kobj)
-
 MODULE_AUTHOR("Manuel Estrada Sainz");
 MODULE_DESCRIPTION("Multi purpose firmware loading support");
 MODULE_LICENSE("GPL");
@@ -290,7 +288,7 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
                                  struct bin_attribute *bin_attr,
                                  char *buffer, loff_t offset, size_t count)
 {
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
        struct firmware *fw;
        ssize_t ret_count;
@@ -384,7 +382,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
                                   struct bin_attribute *bin_attr,
                                   char *buffer, loff_t offset, size_t count)
 {
-       struct device *dev = to_dev(kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
        struct firmware *fw;
        ssize_t retval;
index 29c5cf852efc3d4476bdeab03077f8e69837cab4..16716356d1fee89dc4344c08bd0d11b57b87bf85 100644 (file)
@@ -21,6 +21,16 @@ config EXTCON_GPIO
          Say Y here to enable GPIO based extcon support. Note that GPIO
          extcon supports single state per extcon instance.
 
+config EXTCON_MAX77693
+       tristate "MAX77693 EXTCON Support"
+       depends on MFD_MAX77693
+       select IRQ_DOMAIN
+       select REGMAP_I2C
+       help
+         If you say yes here you get support for the MUIC device of
+         Maxim MAX77693 PMIC. The MAX77693 MUIC is a USB port accessory
+         detector and switch.
+
 config EXTCON_MAX8997
        tristate "MAX8997 EXTCON Support"
        depends on MFD_MAX8997
@@ -29,4 +39,12 @@ config EXTCON_MAX8997
          Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
          detector and switch.
 
+config EXTCON_ARIZONA
+       tristate "Wolfson Arizona EXTCON support"
+       depends on MFD_ARIZONA
+       help
+         Say Y here to enable support for external accessory detection
+         with Wolfson Arizona devices. These are audio CODECs with
+         advanced audio accessory detection support.
+
 endif # MULTISTATE_SWITCH
index 86020bdb6da08ae65eecc28f3b409d1873abfc2f..88961b33234818bbd29356140d17558f37c347ec 100644 (file)
@@ -4,4 +4,6 @@
 
 obj-$(CONFIG_EXTCON)           += extcon_class.o
 obj-$(CONFIG_EXTCON_GPIO)      += extcon_gpio.o
+obj-$(CONFIG_EXTCON_MAX77693)  += extcon-max77693.o
 obj-$(CONFIG_EXTCON_MAX8997)   += extcon-max8997.o
+obj-$(CONFIG_EXTCON_ARIZONA)   += extcon-arizona.o
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
new file mode 100644 (file)
index 0000000..427a289
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * extcon-arizona.c - Extcon driver Wolfson Arizona devices
+ *
+ *  Copyright (C) 2012 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/extcon.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_extcon_info {
+       struct device *dev;
+       struct arizona *arizona;
+       struct mutex lock;
+       struct regulator *micvdd;
+
+       int micd_mode;
+       const struct arizona_micd_config *micd_modes;
+       int micd_num_modes;
+
+       bool micd_reva;
+
+       bool mic;
+       bool detecting;
+       int jack_flips;
+
+       struct extcon_dev edev;
+};
+
+static const struct arizona_micd_config micd_default_modes[] = {
+       { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
+       { 0,                  2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
+};
+
+#define ARIZONA_CABLE_MECHANICAL 0
+#define ARIZONA_CABLE_MICROPHONE 1
+#define ARIZONA_CABLE_HEADPHONE  2
+
+static const char *arizona_cable[] = {
+       "Mechanical",
+       "Microphone",
+       "Headphone",
+       NULL,
+};
+
+static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
+{
+       struct arizona *arizona = info->arizona;
+
+       gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
+                               info->micd_modes[mode].gpio);
+       regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                          ARIZONA_MICD_BIAS_SRC_MASK,
+                          info->micd_modes[mode].bias);
+       regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
+                          ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
+
+       info->micd_mode = mode;
+
+       dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
+}
+
+static void arizona_start_mic(struct arizona_extcon_info *info)
+{
+       struct arizona *arizona = info->arizona;
+       bool change;
+       int ret;
+
+       info->detecting = true;
+       info->mic = false;
+       info->jack_flips = 0;
+
+       /* Microphone detection can't use idle mode */
+       pm_runtime_get(info->dev);
+
+       ret = regulator_enable(info->micvdd);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to enable MICVDD: %d\n",
+                       ret);
+       }
+
+       if (info->micd_reva) {
+               regmap_write(arizona->regmap, 0x80, 0x3);
+               regmap_write(arizona->regmap, 0x294, 0);
+               regmap_write(arizona->regmap, 0x80, 0x0);
+       }
+
+       regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                                ARIZONA_MICD_ENA, ARIZONA_MICD_ENA,
+                                &change);
+       if (!change) {
+               regulator_disable(info->micvdd);
+               pm_runtime_put_autosuspend(info->dev);
+       }
+}
+
+static void arizona_stop_mic(struct arizona_extcon_info *info)
+{
+       struct arizona *arizona = info->arizona;
+       bool change;
+
+       regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                                ARIZONA_MICD_ENA, 0,
+                                &change);
+
+       if (info->micd_reva) {
+               regmap_write(arizona->regmap, 0x80, 0x3);
+               regmap_write(arizona->regmap, 0x294, 2);
+               regmap_write(arizona->regmap, 0x80, 0x0);
+       }
+
+       if (change) {
+               regulator_disable(info->micvdd);
+               pm_runtime_put_autosuspend(info->dev);
+       }
+}
+
+static irqreturn_t arizona_micdet(int irq, void *data)
+{
+       struct arizona_extcon_info *info = data;
+       struct arizona *arizona = info->arizona;
+       unsigned int val;
+       int ret;
+
+       mutex_lock(&info->lock);
+
+       ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
+               return IRQ_NONE;
+       }
+
+       dev_dbg(arizona->dev, "MICDET: %x\n", val);
+
+       if (!(val & ARIZONA_MICD_VALID)) {
+               dev_warn(arizona->dev, "Microphone detection state invalid\n");
+               mutex_unlock(&info->lock);
+               return IRQ_NONE;
+       }
+
+       /* Due to jack detect this should never happen */
+       if (!(val & ARIZONA_MICD_STS)) {
+               dev_warn(arizona->dev, "Detected open circuit\n");
+               info->detecting = false;
+               goto handled;
+       }
+
+       /* If we got a high impedence we should have a headset, report it. */
+       if (info->detecting && (val & 0x400)) {
+               ret = extcon_update_state(&info->edev,
+                                         1 << ARIZONA_CABLE_MICROPHONE |
+                                         1 << ARIZONA_CABLE_HEADPHONE,
+                                         1 << ARIZONA_CABLE_MICROPHONE |
+                                         1 << ARIZONA_CABLE_HEADPHONE);
+
+               if (ret != 0)
+                       dev_err(arizona->dev, "Headset report failed: %d\n",
+                               ret);
+
+               info->mic = true;
+               info->detecting = false;
+               goto handled;
+       }
+
+       /* If we detected a lower impedence during initial startup
+        * then we probably have the wrong polarity, flip it.  Don't
+        * do this for the lowest impedences to speed up detection of
+        * plain headphones.  If both polarities report a low
+        * impedence then give up and report headphones.
+        */
+       if (info->detecting && (val & 0x3f8)) {
+               info->jack_flips++;
+
+               if (info->jack_flips >= info->micd_num_modes) {
+                       dev_dbg(arizona->dev, "Detected headphone\n");
+                       info->detecting = false;
+                       arizona_stop_mic(info);
+
+                       ret = extcon_set_cable_state_(&info->edev,
+                                                     ARIZONA_CABLE_HEADPHONE,
+                                                     true);
+                       if (ret != 0)
+                               dev_err(arizona->dev,
+                                       "Headphone report failed: %d\n",
+                               ret);
+               } else {
+                       info->micd_mode++;
+                       if (info->micd_mode == info->micd_num_modes)
+                               info->micd_mode = 0;
+                       arizona_extcon_set_mode(info, info->micd_mode);
+
+                       info->jack_flips++;
+               }
+
+               goto handled;
+       }
+
+       /*
+        * If we're still detecting and we detect a short then we've
+        * got a headphone.  Otherwise it's a button press, the
+        * button reporting is stubbed out for now.
+        */
+       if (val & 0x3fc) {
+               if (info->mic) {
+                       dev_dbg(arizona->dev, "Mic button detected\n");
+
+               } else if (info->detecting) {
+                       dev_dbg(arizona->dev, "Headphone detected\n");
+                       info->detecting = false;
+                       arizona_stop_mic(info);
+
+                       ret = extcon_set_cable_state_(&info->edev,
+                                                     ARIZONA_CABLE_HEADPHONE,
+                                                     true);
+                       if (ret != 0)
+                               dev_err(arizona->dev,
+                                       "Headphone report failed: %d\n",
+                               ret);
+               } else {
+                       dev_warn(arizona->dev, "Button with no mic: %x\n",
+                                val);
+               }
+       } else {
+               dev_dbg(arizona->dev, "Mic button released\n");
+       }
+
+handled:
+       pm_runtime_mark_last_busy(info->dev);
+       mutex_unlock(&info->lock);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_jackdet(int irq, void *data)
+{
+       struct arizona_extcon_info *info = data;
+       struct arizona *arizona = info->arizona;
+       unsigned int val;
+       int ret;
+
+       pm_runtime_get_sync(info->dev);
+
+       mutex_lock(&info->lock);
+
+       ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to read jackdet status: %d\n",
+                       ret);
+               mutex_unlock(&info->lock);
+               pm_runtime_put_autosuspend(info->dev);
+               return IRQ_NONE;
+       }
+
+       if (val & ARIZONA_JD1_STS) {
+               dev_dbg(arizona->dev, "Detected jack\n");
+               ret = extcon_set_cable_state_(&info->edev,
+                                             ARIZONA_CABLE_MECHANICAL, true);
+
+               if (ret != 0)
+                       dev_err(arizona->dev, "Mechanical report failed: %d\n",
+                               ret);
+
+               arizona_start_mic(info);
+       } else {
+               dev_dbg(arizona->dev, "Detected jack removal\n");
+
+               arizona_stop_mic(info);
+
+               ret = extcon_update_state(&info->edev, 0xffffffff, 0);
+               if (ret != 0)
+                       dev_err(arizona->dev, "Removal report failed: %d\n",
+                               ret);
+       }
+
+       mutex_unlock(&info->lock);
+
+       pm_runtime_mark_last_busy(info->dev);
+       pm_runtime_put_autosuspend(info->dev);
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit arizona_extcon_probe(struct platform_device *pdev)
+{
+       struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+       struct arizona_pdata *pdata;
+       struct arizona_extcon_info *info;
+       int ret, mode;
+
+       pdata = dev_get_platdata(arizona->dev);
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
+       if (IS_ERR(info->micvdd)) {
+               ret = PTR_ERR(info->micvdd);
+               dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
+               goto err;
+       }
+
+       mutex_init(&info->lock);
+       info->arizona = arizona;
+       info->dev = &pdev->dev;
+       info->detecting = true;
+       platform_set_drvdata(pdev, info);
+
+       switch (arizona->type) {
+       case WM5102:
+               switch (arizona->rev) {
+               case 0:
+                       info->micd_reva = true;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       info->edev.name = "Headset Jack";
+       info->edev.supported_cable = arizona_cable;
+
+       ret = extcon_dev_register(&info->edev, arizona->dev);
+       if (ret < 0) {
+               dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
+                       ret);
+               goto err;
+       }
+
+       if (pdata->num_micd_configs) {
+               info->micd_modes = pdata->micd_configs;
+               info->micd_num_modes = pdata->num_micd_configs;
+       } else {
+               info->micd_modes = micd_default_modes;
+               info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
+       }
+
+       if (arizona->pdata.micd_pol_gpio > 0) {
+               if (info->micd_modes[0].gpio)
+                       mode = GPIOF_OUT_INIT_HIGH;
+               else
+                       mode = GPIOF_OUT_INIT_LOW;
+
+               ret = devm_gpio_request_one(&pdev->dev,
+                                           arizona->pdata.micd_pol_gpio,
+                                           mode,
+                                           "MICD polarity");
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+                               arizona->pdata.micd_pol_gpio, ret);
+                       goto err_register;
+               }
+       }
+
+       arizona_extcon_set_mode(info, 0);
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_idle(&pdev->dev);
+       pm_runtime_get_sync(&pdev->dev);
+
+       ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE,
+                                 "JACKDET rise", arizona_jackdet, info);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
+                       ret);
+               goto err_register;
+       }
+
+       ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n",
+                       ret);
+               goto err_rise;
+       }
+
+       ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_FALL,
+                                 "JACKDET fall", arizona_jackdet, info);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret);
+               goto err_rise_wake;
+       }
+
+       ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 1);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n",
+                       ret);
+               goto err_fall;
+       }
+
+       ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
+                                 "MICDET", arizona_micdet, info);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to get MICDET IRQ: %d\n", ret);
+               goto err_fall_wake;
+       }
+
+       regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+                          ARIZONA_MICD_BIAS_STARTTIME_MASK |
+                          ARIZONA_MICD_RATE_MASK,
+                          7 << ARIZONA_MICD_BIAS_STARTTIME_SHIFT |
+                          8 << ARIZONA_MICD_RATE_SHIFT);
+
+       arizona_clk32k_enable(arizona);
+       regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
+                          ARIZONA_JD1_DB, ARIZONA_JD1_DB);
+       regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+                          ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
+
+       pm_runtime_put(&pdev->dev);
+
+       return 0;
+
+err_fall_wake:
+       arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
+err_fall:
+       arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
+err_rise_wake:
+       arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
+err_rise:
+       arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
+err_register:
+       pm_runtime_disable(&pdev->dev);
+       extcon_dev_unregister(&info->edev);
+err:
+       return ret;
+}
+
+static int __devexit arizona_extcon_remove(struct platform_device *pdev)
+{
+       struct arizona_extcon_info *info = platform_get_drvdata(pdev);
+       struct arizona *arizona = info->arizona;
+
+       pm_runtime_disable(&pdev->dev);
+
+       arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
+       arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
+       arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+       arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
+       arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
+       regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+                          ARIZONA_JD1_ENA, 0);
+       arizona_clk32k_disable(arizona);
+       extcon_dev_unregister(&info->edev);
+
+       return 0;
+}
+
+static struct platform_driver arizona_extcon_driver = {
+       .driver         = {
+               .name   = "arizona-extcon",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = arizona_extcon_probe,
+       .remove         = __devexit_p(arizona_extcon_remove),
+};
+
+module_platform_driver(arizona_extcon_driver);
+
+MODULE_DESCRIPTION("Arizona Extcon driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:extcon-arizona");
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
new file mode 100644 (file)
index 0000000..920a609
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * extcon-max77693.c - MAX77693 extcon driver to support MAX77693 MUIC
+ *
+ * Copyright (C) 2012 Samsung Electrnoics
+ * Chanwoo Choi <cw00.choi@samsung.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
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/extcon.h>
+#include <linux/regmap.h>
+#include <linux/irqdomain.h>
+
+#define        DEV_NAME                        "max77693-muic"
+
+/* MAX77693 MUIC - STATUS1~3 Register */
+#define STATUS1_ADC_SHIFT              (0)
+#define STATUS1_ADCLOW_SHIFT           (5)
+#define STATUS1_ADCERR_SHIFT           (6)
+#define STATUS1_ADC1K_SHIFT            (7)
+#define STATUS1_ADC_MASK               (0x1f << STATUS1_ADC_SHIFT)
+#define STATUS1_ADCLOW_MASK            (0x1 << STATUS1_ADCLOW_SHIFT)
+#define STATUS1_ADCERR_MASK            (0x1 << STATUS1_ADCERR_SHIFT)
+#define STATUS1_ADC1K_MASK             (0x1 << STATUS1_ADC1K_SHIFT)
+
+#define STATUS2_CHGTYP_SHIFT           (0)
+#define STATUS2_CHGDETRUN_SHIFT                (3)
+#define STATUS2_DCDTMR_SHIFT           (4)
+#define STATUS2_DXOVP_SHIFT            (5)
+#define STATUS2_VBVOLT_SHIFT           (6)
+#define STATUS2_VIDRM_SHIFT            (7)
+#define STATUS2_CHGTYP_MASK            (0x7 << STATUS2_CHGTYP_SHIFT)
+#define STATUS2_CHGDETRUN_MASK         (0x1 << STATUS2_CHGDETRUN_SHIFT)
+#define STATUS2_DCDTMR_MASK            (0x1 << STATUS2_DCDTMR_SHIFT)
+#define STATUS2_DXOVP_MASK             (0x1 << STATUS2_DXOVP_SHIFT)
+#define STATUS2_VBVOLT_MASK            (0x1 << STATUS2_VBVOLT_SHIFT)
+#define STATUS2_VIDRM_MASK             (0x1 << STATUS2_VIDRM_SHIFT)
+
+#define STATUS3_OVP_SHIFT              (2)
+#define STATUS3_OVP_MASK               (0x1 << STATUS3_OVP_SHIFT)
+
+/* MAX77693 CDETCTRL1~2 register */
+#define CDETCTRL1_CHGDETEN_SHIFT       (0)
+#define CDETCTRL1_CHGTYPMAN_SHIFT      (1)
+#define CDETCTRL1_DCDEN_SHIFT          (2)
+#define CDETCTRL1_DCD2SCT_SHIFT                (3)
+#define CDETCTRL1_CDDELAY_SHIFT                (4)
+#define CDETCTRL1_DCDCPL_SHIFT         (5)
+#define CDETCTRL1_CDPDET_SHIFT         (7)
+#define CDETCTRL1_CHGDETEN_MASK                (0x1 << CDETCTRL1_CHGDETEN_SHIFT)
+#define CDETCTRL1_CHGTYPMAN_MASK       (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT)
+#define CDETCTRL1_DCDEN_MASK           (0x1 << CDETCTRL1_DCDEN_SHIFT)
+#define CDETCTRL1_DCD2SCT_MASK         (0x1 << CDETCTRL1_DCD2SCT_SHIFT)
+#define CDETCTRL1_CDDELAY_MASK         (0x1 << CDETCTRL1_CDDELAY_SHIFT)
+#define CDETCTRL1_DCDCPL_MASK          (0x1 << CDETCTRL1_DCDCPL_SHIFT)
+#define CDETCTRL1_CDPDET_MASK          (0x1 << CDETCTRL1_CDPDET_SHIFT)
+
+#define CDETCTRL2_VIDRMEN_SHIFT                (1)
+#define CDETCTRL2_DXOVPEN_SHIFT                (3)
+#define CDETCTRL2_VIDRMEN_MASK         (0x1 << CDETCTRL2_VIDRMEN_SHIFT)
+#define CDETCTRL2_DXOVPEN_MASK         (0x1 << CDETCTRL2_DXOVPEN_SHIFT)
+
+/* MAX77693 MUIC - CONTROL1~3 register */
+#define COMN1SW_SHIFT                  (0)
+#define COMP2SW_SHIFT                  (3)
+#define COMN1SW_MASK                   (0x7 << COMN1SW_SHIFT)
+#define COMP2SW_MASK                   (0x7 << COMP2SW_SHIFT)
+#define COMP_SW_MASK                   (COMP2SW_MASK | COMN1SW_MASK)
+#define CONTROL1_SW_USB                        ((1 << COMP2SW_SHIFT) \
+                                               | (1 << COMN1SW_SHIFT))
+#define CONTROL1_SW_AUDIO              ((2 << COMP2SW_SHIFT) \
+                                               | (2 << COMN1SW_SHIFT))
+#define CONTROL1_SW_UART               ((3 << COMP2SW_SHIFT) \
+                                               | (3 << COMN1SW_SHIFT))
+#define CONTROL1_SW_OPEN               ((0 << COMP2SW_SHIFT) \
+                                               | (0 << COMN1SW_SHIFT))
+
+#define CONTROL2_LOWPWR_SHIFT          (0)
+#define CONTROL2_ADCEN_SHIFT           (1)
+#define CONTROL2_CPEN_SHIFT            (2)
+#define CONTROL2_SFOUTASRT_SHIFT       (3)
+#define CONTROL2_SFOUTORD_SHIFT                (4)
+#define CONTROL2_ACCDET_SHIFT          (5)
+#define CONTROL2_USBCPINT_SHIFT                (6)
+#define CONTROL2_RCPS_SHIFT            (7)
+#define CONTROL2_LOWPWR_MASK           (0x1 << CONTROL2_LOWPWR_SHIFT)
+#define CONTROL2_ADCEN_MASK            (0x1 << CONTROL2_ADCEN_SHIFT)
+#define CONTROL2_CPEN_MASK             (0x1 << CONTROL2_CPEN_SHIFT)
+#define CONTROL2_SFOUTASRT_MASK                (0x1 << CONTROL2_SFOUTASRT_SHIFT)
+#define CONTROL2_SFOUTORD_MASK         (0x1 << CONTROL2_SFOUTORD_SHIFT)
+#define CONTROL2_ACCDET_MASK           (0x1 << CONTROL2_ACCDET_SHIFT)
+#define CONTROL2_USBCPINT_MASK         (0x1 << CONTROL2_USBCPINT_SHIFT)
+#define CONTROL2_RCPS_MASK             (0x1 << CONTROL2_RCPS_SHIFT)
+
+#define CONTROL3_JIGSET_SHIFT          (0)
+#define CONTROL3_BTLDSET_SHIFT         (2)
+#define CONTROL3_ADCDBSET_SHIFT                (4)
+#define CONTROL3_JIGSET_MASK           (0x3 << CONTROL3_JIGSET_SHIFT)
+#define CONTROL3_BTLDSET_MASK          (0x3 << CONTROL3_BTLDSET_SHIFT)
+#define CONTROL3_ADCDBSET_MASK         (0x3 << CONTROL3_ADCDBSET_SHIFT)
+
+enum max77693_muic_adc_debounce_time {
+       ADC_DEBOUNCE_TIME_5MS = 0,
+       ADC_DEBOUNCE_TIME_10MS,
+       ADC_DEBOUNCE_TIME_25MS,
+       ADC_DEBOUNCE_TIME_38_62MS,
+};
+
+struct max77693_muic_info {
+       struct device *dev;
+       struct max77693_dev *max77693;
+       struct extcon_dev *edev;
+       int prev_adc;
+       int prev_adc_gnd;
+       int prev_chg_type;
+       u8 status[2];
+
+       int irq;
+       struct work_struct irq_work;
+       struct mutex mutex;
+};
+
+enum max77693_muic_charger_type {
+       MAX77693_CHARGER_TYPE_NONE = 0,
+       MAX77693_CHARGER_TYPE_USB,
+       MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT,
+       MAX77693_CHARGER_TYPE_DEDICATED_CHG,
+       MAX77693_CHARGER_TYPE_APPLE_500MA,
+       MAX77693_CHARGER_TYPE_APPLE_1A_2A,
+       MAX77693_CHARGER_TYPE_DEAD_BATTERY = 7,
+};
+
+/**
+ * struct max77693_muic_irq
+ * @irq: the index of irq list of MUIC device.
+ * @name: the name of irq.
+ * @virq: the virtual irq to use irq domain
+ */
+struct max77693_muic_irq {
+       unsigned int irq;
+       const char *name;
+       unsigned int virq;
+};
+
+static struct max77693_muic_irq muic_irqs[] = {
+       { MAX77693_MUIC_IRQ_INT1_ADC,           "muic-ADC" },
+       { MAX77693_MUIC_IRQ_INT1_ADC_LOW,       "muic-ADCLOW" },
+       { MAX77693_MUIC_IRQ_INT1_ADC_ERR,       "muic-ADCError" },
+       { MAX77693_MUIC_IRQ_INT1_ADC1K,         "muic-ADC1K" },
+       { MAX77693_MUIC_IRQ_INT2_CHGTYP,        "muic-CHGTYP" },
+       { MAX77693_MUIC_IRQ_INT2_CHGDETREUN,    "muic-CHGDETREUN" },
+       { MAX77693_MUIC_IRQ_INT2_DCDTMR,        "muic-DCDTMR" },
+       { MAX77693_MUIC_IRQ_INT2_DXOVP,         "muic-DXOVP" },
+       { MAX77693_MUIC_IRQ_INT2_VBVOLT,        "muic-VBVOLT" },
+       { MAX77693_MUIC_IRQ_INT2_VIDRM,         "muic-VIDRM" },
+       { MAX77693_MUIC_IRQ_INT3_EOC,           "muic-EOC" },
+       { MAX77693_MUIC_IRQ_INT3_CGMBC,         "muic-CGMBC" },
+       { MAX77693_MUIC_IRQ_INT3_OVP,           "muic-OVP" },
+       { MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR,    "muic-MBCCHG_ERR" },
+       { MAX77693_MUIC_IRQ_INT3_CHG_ENABLED,   "muic-CHG_ENABLED" },
+       { MAX77693_MUIC_IRQ_INT3_BAT_DET,       "muic-BAT_DET" },
+};
+
+/* Define supported accessory type */
+enum max77693_muic_acc_type {
+       MAX77693_MUIC_ADC_GROUND = 0x0,
+       MAX77693_MUIC_ADC_SEND_END_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S1_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S2_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S3_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S4_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S5_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S6_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S7_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S8_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S9_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S10_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S11_BUTTON,
+       MAX77693_MUIC_ADC_REMOTE_S12_BUTTON,
+       MAX77693_MUIC_ADC_RESERVED_ACC_1,
+       MAX77693_MUIC_ADC_RESERVED_ACC_2,
+       MAX77693_MUIC_ADC_RESERVED_ACC_3,
+       MAX77693_MUIC_ADC_RESERVED_ACC_4,
+       MAX77693_MUIC_ADC_RESERVED_ACC_5,
+       MAX77693_MUIC_ADC_CEA936_AUDIO,
+       MAX77693_MUIC_ADC_PHONE_POWERED_DEV,
+       MAX77693_MUIC_ADC_TTY_CONVERTER,
+       MAX77693_MUIC_ADC_UART_CABLE,
+       MAX77693_MUIC_ADC_CEA936A_TYPE1_CHG,
+       MAX77693_MUIC_ADC_FACTORY_MODE_USB_OFF,
+       MAX77693_MUIC_ADC_FACTORY_MODE_USB_ON,
+       MAX77693_MUIC_ADC_AV_CABLE_NOLOAD,
+       MAX77693_MUIC_ADC_CEA936A_TYPE2_CHG,
+       MAX77693_MUIC_ADC_FACTORY_MODE_UART_OFF,
+       MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON,
+       MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE,
+       MAX77693_MUIC_ADC_OPEN,
+
+       /* The below accessories have same ADC value so ADCLow and
+          ADC1K bit is used to separate specific accessory */
+       MAX77693_MUIC_GND_USB_OTG = 0x100,      /* ADC:0x0, ADCLow:0, ADC1K:0 */
+       MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* ADC:0x0, ADCLow:1, ADC1K:0 */
+       MAX77693_MUIC_GND_MHL_CABLE = 0x103,    /* ADC:0x0, ADCLow:1, ADC1K:1 */
+};
+
+/* MAX77693 MUIC device support below list of accessories(external connector) */
+const char *max77693_extcon_cable[] = {
+       [0] = "USB",
+       [1] = "USB-Host",
+       [2] = "TA",
+       [3] = "Fast-charger",
+       [4] = "Slow-charger",
+       [5] = "Charge-downstream",
+       [6] = "MHL",
+       [7] = "Audio-video-load",
+       [8] = "Audio-video-noload",
+       [9] = "JIG",
+
+       NULL,
+};
+
+static int max77693_muic_set_debounce_time(struct max77693_muic_info *info,
+               enum max77693_muic_adc_debounce_time time)
+{
+       int ret = 0;
+       u8 ctrl3;
+
+       switch (time) {
+       case ADC_DEBOUNCE_TIME_5MS:
+       case ADC_DEBOUNCE_TIME_10MS:
+       case ADC_DEBOUNCE_TIME_25MS:
+       case ADC_DEBOUNCE_TIME_38_62MS:
+               ret = max77693_read_reg(info->max77693->regmap_muic,
+                               MAX77693_MUIC_REG_CTRL3, &ctrl3);
+               ctrl3 &= ~CONTROL3_ADCDBSET_MASK;
+               ctrl3 |= (time << CONTROL3_ADCDBSET_SHIFT);
+
+               ret = max77693_write_reg(info->max77693->regmap_muic,
+                               MAX77693_MUIC_REG_CTRL3, ctrl3);
+               if (ret) {
+                       dev_err(info->dev, "failed to set ADC debounce time\n");
+                       ret = -EINVAL;
+               }
+               break;
+       default:
+               dev_err(info->dev, "invalid ADC debounce time\n");
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+};
+
+static int max77693_muic_set_path(struct max77693_muic_info *info,
+               u8 val, bool attached)
+{
+       int ret = 0;
+       u8 ctrl1, ctrl2 = 0;
+
+       if (attached)
+               ctrl1 = val;
+       else
+               ctrl1 = CONTROL1_SW_OPEN;
+
+       ret = max77693_update_reg(info->max77693->regmap_muic,
+                       MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK);
+       if (ret < 0) {
+               dev_err(info->dev, "failed to update MUIC register\n");
+               goto out;
+       }
+
+       if (attached)
+               ctrl2 |= CONTROL2_CPEN_MASK;    /* LowPwr=0, CPEn=1 */
+       else
+               ctrl2 |= CONTROL2_LOWPWR_MASK;  /* LowPwr=1, CPEn=0 */
+
+       ret = max77693_update_reg(info->max77693->regmap_muic,
+                       MAX77693_MUIC_REG_CTRL2, ctrl2,
+                       CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
+       if (ret < 0) {
+               dev_err(info->dev, "failed to update MUIC register\n");
+               goto out;
+       }
+
+       dev_info(info->dev,
+               "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n",
+               ctrl1, ctrl2, attached ? "attached" : "detached");
+out:
+       return ret;
+}
+
+static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info,
+               bool attached)
+{
+       int ret = 0;
+       int type;
+       int adc, adc1k, adclow;
+
+       if (attached) {
+               adc = info->status[0] & STATUS1_ADC_MASK;
+               adclow = info->status[0] & STATUS1_ADCLOW_MASK;
+               adclow >>= STATUS1_ADCLOW_SHIFT;
+               adc1k = info->status[0] & STATUS1_ADC1K_MASK;
+               adc1k >>= STATUS1_ADC1K_SHIFT;
+
+               /**
+                * [0x1][ADCLow][ADC1K]
+                * [0x1    0       0  ] : USB_OTG
+                * [0x1    1       0  ] : Audio Video Cable with load
+                * [0x1    1       1  ] : MHL
+                */
+               type = ((0x1 << 8) | (adclow << 1) | adc1k);
+
+               /* Store previous ADC value to handle accessory
+                  when accessory will be detached */
+               info->prev_adc = adc;
+               info->prev_adc_gnd = type;
+       } else
+               type = info->prev_adc_gnd;
+
+       switch (type) {
+       case MAX77693_MUIC_GND_USB_OTG:
+               /* USB_OTG */
+               ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       goto out;
+               extcon_set_cable_state(info->edev, "USB-Host", attached);
+               break;
+       case MAX77693_MUIC_GND_AV_CABLE_LOAD:
+               /* Audio Video Cable with load */
+               ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
+               if (ret < 0)
+                       goto out;
+               extcon_set_cable_state(info->edev,
+                               "Audio-video-load", attached);
+               break;
+       case MAX77693_MUIC_GND_MHL_CABLE:
+               /* MHL */
+               extcon_set_cable_state(info->edev, "MHL", attached);
+               break;
+       default:
+               dev_err(info->dev, "faild to detect %s accessory\n",
+                       attached ? "attached" : "detached");
+               dev_err(info->dev, "- adc:0x%x, adclow:0x%x, adc1k:0x%x\n",
+                       adc, adclow, adc1k);
+               ret = -EINVAL;
+               break;
+       }
+
+out:
+       return ret;
+}
+
+static int max77693_muic_adc_handler(struct max77693_muic_info *info,
+               int curr_adc, bool attached)
+{
+       int ret = 0;
+       int adc;
+
+       if (attached) {
+               /* Store ADC value to handle accessory
+                  when accessory will be detached */
+               info->prev_adc = curr_adc;
+               adc = curr_adc;
+       } else
+               adc = info->prev_adc;
+
+       dev_info(info->dev,
+               "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n",
+               attached ? "attached" : "detached", curr_adc, info->prev_adc);
+
+       switch (adc) {
+       case MAX77693_MUIC_ADC_GROUND:
+               /* USB_OTG/MHL/Audio */
+               max77693_muic_adc_ground_handler(info, attached);
+               break;
+       case MAX77693_MUIC_ADC_FACTORY_MODE_USB_OFF:
+       case MAX77693_MUIC_ADC_FACTORY_MODE_USB_ON:
+               /* USB */
+               ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       goto out;
+               extcon_set_cable_state(info->edev, "USB", attached);
+               break;
+       case MAX77693_MUIC_ADC_FACTORY_MODE_UART_OFF:
+       case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON:
+               /* JIG */
+               ret = max77693_muic_set_path(info, CONTROL1_SW_UART, attached);
+               if (ret < 0)
+                       goto out;
+               extcon_set_cable_state(info->edev, "JIG", attached);
+               break;
+       case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE:
+               /* Audio Video cable with no-load */
+               ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
+               if (ret < 0)
+                       goto out;
+               extcon_set_cable_state(info->edev,
+                               "Audio-video-noload", attached);
+               break;
+       case MAX77693_MUIC_ADC_SEND_END_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S1_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S2_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S4_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S5_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S6_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S8_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S11_BUTTON:
+       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:
+       case MAX77693_MUIC_ADC_RESERVED_ACC_1:
+       case MAX77693_MUIC_ADC_RESERVED_ACC_2:
+       case MAX77693_MUIC_ADC_RESERVED_ACC_3:
+       case MAX77693_MUIC_ADC_RESERVED_ACC_4:
+       case MAX77693_MUIC_ADC_RESERVED_ACC_5:
+       case MAX77693_MUIC_ADC_CEA936_AUDIO:
+       case MAX77693_MUIC_ADC_PHONE_POWERED_DEV:
+       case MAX77693_MUIC_ADC_TTY_CONVERTER:
+       case MAX77693_MUIC_ADC_UART_CABLE:
+       case MAX77693_MUIC_ADC_CEA936A_TYPE1_CHG:
+       case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:
+       case MAX77693_MUIC_ADC_CEA936A_TYPE2_CHG:
+               /* This accessory isn't used in general case if it is specially
+                  needed to detect additional accessory, should implement
+                  proper operation when this accessory is attached/detached. */
+               dev_info(info->dev,
+                       "accessory is %s but it isn't used (adc:0x%x)\n",
+                       attached ? "attached" : "detached", adc);
+               goto out;
+       default:
+               dev_err(info->dev,
+                       "failed to detect %s accessory (adc:0x%x)\n",
+                       attached ? "attached" : "detached", adc);
+               ret = -EINVAL;
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+static int max77693_muic_chg_handler(struct max77693_muic_info *info,
+               int curr_chg_type, bool attached)
+{
+       int ret = 0;
+       int chg_type;
+
+       if (attached) {
+               /* Store previous charger type to control
+                  when charger accessory will be detached */
+               info->prev_chg_type = curr_chg_type;
+               chg_type = curr_chg_type;
+       } else
+               chg_type = info->prev_chg_type;
+
+       dev_info(info->dev,
+               "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n",
+                       attached ? "attached" : "detached",
+                       curr_chg_type, info->prev_chg_type);
+
+       switch (chg_type) {
+       case MAX77693_CHARGER_TYPE_USB:
+               ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       goto out;
+               extcon_set_cable_state(info->edev, "USB", attached);
+               break;
+       case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
+               extcon_set_cable_state(info->edev,
+                               "Charge-downstream", attached);
+               break;
+       case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
+               extcon_set_cable_state(info->edev, "TA", attached);
+               break;
+       case MAX77693_CHARGER_TYPE_APPLE_500MA:
+               extcon_set_cable_state(info->edev, "Slow-charger", attached);
+               break;
+       case MAX77693_CHARGER_TYPE_APPLE_1A_2A:
+               extcon_set_cable_state(info->edev, "Fast-charger", attached);
+               break;
+       case MAX77693_CHARGER_TYPE_DEAD_BATTERY:
+               break;
+       default:
+               dev_err(info->dev,
+                       "failed to detect %s accessory (chg_type:0x%x)\n",
+                       attached ? "attached" : "detached", chg_type);
+               ret = -EINVAL;
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+static void max77693_muic_irq_work(struct work_struct *work)
+{
+       struct max77693_muic_info *info = container_of(work,
+                       struct max77693_muic_info, irq_work);
+       int curr_adc, curr_chg_type;
+       int irq_type = -1;
+       int i, ret = 0;
+       bool attached = true;
+
+       if (!info->edev)
+               return;
+
+       mutex_lock(&info->mutex);
+
+       for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+               if (info->irq == muic_irqs[i].virq)
+                       irq_type = muic_irqs[i].irq;
+
+       ret = max77693_bulk_read(info->max77693->regmap_muic,
+                       MAX77693_MUIC_REG_STATUS1, 2, info->status);
+       if (ret) {
+               dev_err(info->dev, "failed to read MUIC register\n");
+               mutex_unlock(&info->mutex);
+               return;
+       }
+
+       switch (irq_type) {
+       case MAX77693_MUIC_IRQ_INT1_ADC:
+       case MAX77693_MUIC_IRQ_INT1_ADC_LOW:
+       case MAX77693_MUIC_IRQ_INT1_ADC_ERR:
+       case MAX77693_MUIC_IRQ_INT1_ADC1K:
+               /* Handle all of accessory except for
+                  type of charger accessory */
+               curr_adc = info->status[0] & STATUS1_ADC_MASK;
+               curr_adc >>= STATUS1_ADC_SHIFT;
+
+               /* Check accossory state which is either detached or attached */
+               if (curr_adc == MAX77693_MUIC_ADC_OPEN)
+                       attached = false;
+
+               ret = max77693_muic_adc_handler(info, curr_adc, attached);
+               break;
+       case MAX77693_MUIC_IRQ_INT2_CHGTYP:
+       case MAX77693_MUIC_IRQ_INT2_CHGDETREUN:
+       case MAX77693_MUIC_IRQ_INT2_DCDTMR:
+       case MAX77693_MUIC_IRQ_INT2_DXOVP:
+       case MAX77693_MUIC_IRQ_INT2_VBVOLT:
+       case MAX77693_MUIC_IRQ_INT2_VIDRM:
+               /* Handle charger accessory */
+               curr_chg_type = info->status[1] & STATUS2_CHGTYP_MASK;
+               curr_chg_type >>= STATUS2_CHGTYP_SHIFT;
+
+               /* Check charger accossory state which
+                  is either detached or attached */
+               if (curr_chg_type == MAX77693_CHARGER_TYPE_NONE)
+                       attached = false;
+
+               ret = max77693_muic_chg_handler(info, curr_chg_type, attached);
+               break;
+       case MAX77693_MUIC_IRQ_INT3_EOC:
+       case MAX77693_MUIC_IRQ_INT3_CGMBC:
+       case MAX77693_MUIC_IRQ_INT3_OVP:
+       case MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR:
+       case MAX77693_MUIC_IRQ_INT3_CHG_ENABLED:
+       case MAX77693_MUIC_IRQ_INT3_BAT_DET:
+               break;
+       default:
+               dev_err(info->dev, "muic interrupt: irq %d occurred\n",
+                               irq_type);
+               break;
+       }
+
+       if (ret < 0)
+               dev_err(info->dev, "failed to handle MUIC interrupt\n");
+
+       mutex_unlock(&info->mutex);
+
+       return;
+}
+
+static irqreturn_t max77693_muic_irq_handler(int irq, void *data)
+{
+       struct max77693_muic_info *info = data;
+
+       info->irq = irq;
+       schedule_work(&info->irq_work);
+
+       return IRQ_HANDLED;
+}
+
+static struct regmap_config max77693_muic_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static int max77693_muic_detect_accessory(struct max77693_muic_info *info)
+{
+       int ret = 0;
+       int adc, chg_type;
+
+       mutex_lock(&info->mutex);
+
+       /* Read STATUSx register to detect accessory */
+       ret = max77693_bulk_read(info->max77693->regmap_muic,
+                       MAX77693_MUIC_REG_STATUS1, 2, info->status);
+       if (ret) {
+               dev_err(info->dev, "failed to read MUIC register\n");
+               mutex_unlock(&info->mutex);
+               return -EINVAL;
+       }
+
+       adc = info->status[0] & STATUS1_ADC_MASK;
+       adc >>= STATUS1_ADC_SHIFT;
+
+       if (adc != MAX77693_MUIC_ADC_OPEN) {
+               dev_info(info->dev,
+                       "external connector is attached (adc:0x%02x)\n", adc);
+
+               ret = max77693_muic_adc_handler(info, adc, true);
+               if (ret < 0)
+                       dev_err(info->dev, "failed to detect accessory\n");
+               goto out;
+       }
+
+       chg_type = info->status[1] & STATUS2_CHGTYP_MASK;
+       chg_type >>= STATUS2_CHGTYP_SHIFT;
+
+       if (chg_type != MAX77693_CHARGER_TYPE_NONE) {
+               dev_info(info->dev,
+                       "external connector is attached (chg_type:0x%x)\n",
+                       chg_type);
+
+               max77693_muic_chg_handler(info, chg_type, true);
+               if (ret < 0)
+                       dev_err(info->dev, "failed to detect charger accessory\n");
+       }
+
+out:
+       mutex_unlock(&info->mutex);
+       return ret;
+}
+
+static int __devinit max77693_muic_probe(struct platform_device *pdev)
+{
+       struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
+       struct max77693_muic_info *info;
+       int ret, i;
+       u8 id;
+
+       info = kzalloc(sizeof(struct max77693_muic_info), GFP_KERNEL);
+       if (!info) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               ret = -ENOMEM;
+               goto err_kfree;
+       }
+       info->dev = &pdev->dev;
+       info->max77693 = max77693;
+       info->max77693->regmap_muic = regmap_init_i2c(info->max77693->muic,
+                                        &max77693_muic_regmap_config);
+       if (IS_ERR(info->max77693->regmap_muic)) {
+               ret = PTR_ERR(info->max77693->regmap_muic);
+               dev_err(max77693->dev,
+                       "failed to allocate register map: %d\n", ret);
+               goto err_regmap;
+       }
+       platform_set_drvdata(pdev, info);
+       mutex_init(&info->mutex);
+
+       INIT_WORK(&info->irq_work, max77693_muic_irq_work);
+
+       /* Support irq domain for MAX77693 MUIC device */
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
+               struct max77693_muic_irq *muic_irq = &muic_irqs[i];
+               int virq = 0;
+
+               virq = irq_create_mapping(max77693->irq_domain, muic_irq->irq);
+               if (!virq)
+                       goto err_irq;
+               muic_irq->virq = virq;
+
+               ret = request_threaded_irq(virq, NULL,
+                               max77693_muic_irq_handler,
+                               0, muic_irq->name, info);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "failed: irq request (IRQ: %d,"
+                               " error :%d)\n",
+                               muic_irq->irq, ret);
+
+                       for (i = i - 1; i >= 0; i--)
+                               free_irq(muic_irq->virq, info);
+                       goto err_irq;
+               }
+       }
+
+       /* Initialize extcon device */
+       info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL);
+       if (!info->edev) {
+               dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
+               ret = -ENOMEM;
+               goto err_irq;
+       }
+       info->edev->name = DEV_NAME;
+       info->edev->supported_cable = max77693_extcon_cable;
+       ret = extcon_dev_register(info->edev, NULL);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register extcon device\n");
+               goto err_extcon;
+       }
+
+       /* Check revision number of MUIC device*/
+       ret = max77693_read_reg(info->max77693->regmap_muic,
+                       MAX77693_MUIC_REG_ID, &id);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to read revision number\n");
+               goto err_extcon;
+       }
+       dev_info(info->dev, "device ID : 0x%x\n", id);
+
+       /* Set ADC debounce time */
+       max77693_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS);
+
+       /* Detect accessory on boot */
+       max77693_muic_detect_accessory(info);
+
+       return ret;
+
+err_extcon:
+       kfree(info->edev);
+err_irq:
+err_regmap:
+       kfree(info);
+err_kfree:
+       return ret;
+}
+
+static int __devexit max77693_muic_remove(struct platform_device *pdev)
+{
+       struct max77693_muic_info *info = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
+               free_irq(muic_irqs[i].virq, info);
+       cancel_work_sync(&info->irq_work);
+       extcon_dev_unregister(info->edev);
+       kfree(info);
+
+       return 0;
+}
+
+static struct platform_driver max77693_muic_driver = {
+       .driver         = {
+               .name   = DEV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = max77693_muic_probe,
+       .remove         = __devexit_p(max77693_muic_remove),
+};
+
+module_platform_driver(max77693_muic_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77693 Extcon driver");
+MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:extcon-max77693");
index 159aeb07b3baf332a9df0660f082ccfc283fca64..f6419f9db76c103c29a981129abdfa08be8110b8 100644 (file)
@@ -65,7 +65,7 @@ const char *extcon_cable_name[] = {
        NULL,
 };
 
-struct class *extcon_class;
+static struct class *extcon_class;
 #if defined(CONFIG_ANDROID)
 static struct class_compat *switch_class;
 #endif /* CONFIG_ANDROID */
index 8a0dcc11c7c73e9c1abb64b7537047d58115064b..fe3db45fa83c13604db19590cb29546dc38efae1 100644 (file)
@@ -105,25 +105,25 @@ static int __devinit gpio_extcon_probe(struct platform_device *pdev)
 
        ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
        if (ret < 0)
-               goto err_extcon_dev_register;
+               return ret;
 
        ret = gpio_request_one(extcon_data->gpio, GPIOF_DIR_IN, pdev->name);
        if (ret < 0)
-               goto err_request_gpio;
+               goto err;
 
        INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
 
        extcon_data->irq = gpio_to_irq(extcon_data->gpio);
        if (extcon_data->irq < 0) {
                ret = extcon_data->irq;
-               goto err_detect_irq_num_failed;
+               goto err;
        }
 
        ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
                                      pdata->irq_flags, pdev->name,
                                      extcon_data);
        if (ret < 0)
-               goto err_request_irq;
+               goto err;
 
        platform_set_drvdata(pdev, extcon_data);
        /* Perform initial detection */
@@ -131,13 +131,8 @@ static int __devinit gpio_extcon_probe(struct platform_device *pdev)
 
        return 0;
 
-err_request_irq:
-err_detect_irq_num_failed:
-       gpio_free(extcon_data->gpio);
-err_request_gpio:
+err:
        extcon_dev_unregister(&extcon_data->edev);
-err_extcon_dev_register:
-       devm_kfree(&pdev->dev, extcon_data);
 
        return ret;
 }
@@ -148,9 +143,7 @@ static int __devexit gpio_extcon_remove(struct platform_device *pdev)
 
        cancel_delayed_work_sync(&extcon_data->work);
        free_irq(extcon_data->irq, extcon_data);
-       gpio_free(extcon_data->gpio);
        extcon_dev_unregister(&extcon_data->edev);
-       devm_kfree(&pdev->dev, extcon_data);
 
        return 0;
 }
index b9426a6592ee558f9b4945346818de30a4c3816e..0614ff3a7d7e2ece52e0a4e487b60f96c9bc521d 100644 (file)
@@ -411,7 +411,7 @@ enum {
 #define HV_PRESENT_BIT                 0x80000000
 
 #define HV_LINUX_GUEST_ID_LO           0x00000000
-#define HV_LINUX_GUEST_ID_HI           0xB16B00B5
+#define HV_LINUX_GUEST_ID_HI           2976579765
 #define HV_LINUX_GUEST_ID              (((u64)HV_LINUX_GUEST_ID_HI << 32) | \
                                           HV_LINUX_GUEST_ID_LO)
 
index de31cae1ba53cc4d2dbc6eb661b9d741be4bca19..74fad941c56c226c2d20ae667c37bd36191d65a7 100644 (file)
@@ -39,7 +39,6 @@ struct ds2780_device_info {
        struct device *dev;
        struct power_supply bat;
        struct device *w1_dev;
-       struct task_struct *mutex_holder;
 };
 
 enum current_types {
@@ -64,10 +63,7 @@ static inline struct power_supply *to_power_supply(struct device *dev)
 static inline int ds2780_battery_io(struct ds2780_device_info *dev_info,
        char *buf, int addr, size_t count, int io)
 {
-       if (dev_info->mutex_holder == current)
-               return w1_ds2780_io_nolock(dev_info->w1_dev, buf, addr, count, io);
-       else
-               return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io);
+       return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io);
 }
 
 static inline int ds2780_read8(struct ds2780_device_info *dev_info, u8 *val,
@@ -779,7 +775,6 @@ static int __devinit ds2780_battery_probe(struct platform_device *pdev)
        dev_info->bat.properties        = ds2780_battery_props;
        dev_info->bat.num_properties    = ARRAY_SIZE(ds2780_battery_props);
        dev_info->bat.get_property      = ds2780_battery_get_property;
-       dev_info->mutex_holder          = current;
 
        ret = power_supply_register(&pdev->dev, &dev_info->bat);
        if (ret) {
@@ -809,8 +804,6 @@ static int __devinit ds2780_battery_probe(struct platform_device *pdev)
                goto fail_remove_bin_file;
        }
 
-       dev_info->mutex_holder = NULL;
-
        return 0;
 
 fail_remove_bin_file:
@@ -830,8 +823,6 @@ static int __devexit ds2780_battery_remove(struct platform_device *pdev)
 {
        struct ds2780_device_info *dev_info = platform_get_drvdata(pdev);
 
-       dev_info->mutex_holder = current;
-
        /* remove attributes */
        sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
 
index 975684a40f1519ad33e5f630ecfa82020d71d960..5f92a4bb33f95aaafe772c3aeea1b2bc54a00186 100644 (file)
@@ -37,7 +37,6 @@ struct ds2781_device_info {
        struct device *dev;
        struct power_supply bat;
        struct device *w1_dev;
-       struct task_struct *mutex_holder;
 };
 
 enum current_types {
@@ -62,11 +61,7 @@ static inline struct power_supply *to_power_supply(struct device *dev)
 static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
        char *buf, int addr, size_t count, int io)
 {
-       if (dev_info->mutex_holder == current)
-               return w1_ds2781_io_nolock(dev_info->w1_dev, buf, addr,
-                               count, io);
-       else
-               return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
+       return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
 }
 
 int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
@@ -775,7 +770,6 @@ static int __devinit ds2781_battery_probe(struct platform_device *pdev)
        dev_info->bat.properties        = ds2781_battery_props;
        dev_info->bat.num_properties    = ARRAY_SIZE(ds2781_battery_props);
        dev_info->bat.get_property      = ds2781_battery_get_property;
-       dev_info->mutex_holder          = current;
 
        ret = power_supply_register(&pdev->dev, &dev_info->bat);
        if (ret) {
@@ -805,8 +799,6 @@ static int __devinit ds2781_battery_probe(struct platform_device *pdev)
                goto fail_remove_bin_file;
        }
 
-       dev_info->mutex_holder = NULL;
-
        return 0;
 
 fail_remove_bin_file:
@@ -826,8 +818,6 @@ static int __devexit ds2781_battery_remove(struct platform_device *pdev)
 {
        struct ds2781_device_info *dev_info = platform_get_drvdata(pdev);
 
-       dev_info->mutex_holder = current;
-
        /* remove attributes */
        sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group);
 
index a0c8965c1a793cb00f157eaa506355dedbd8ca00..530a2d30906308fbbc864ecf421ecd8ae47f60f3 100644 (file)
@@ -334,7 +334,9 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
                        return;
                }
 
+               mutex_lock(&master_dev->bus_mutex);
                if (ds1wm_reset(ds1wm_data)) {
+                       mutex_unlock(&master_dev->bus_mutex);
                        dev_dbg(&ds1wm_data->pdev->dev,
                                "pass: %d reset error (or no slaves)\n", pass);
                        break;
@@ -387,6 +389,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
 
                }
                if (ds1wm_data->read_error) {
+                       mutex_unlock(&master_dev->bus_mutex);
                        dev_err(&ds1wm_data->pdev->dev,
                                "pass: %d read error, retrying\n", pass);
                        break;
@@ -400,6 +403,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
                dev_dbg(&ds1wm_data->pdev->dev,
                        "pass: %d resetting bus\n", pass);
                ds1wm_reset(ds1wm_data);
+               mutex_unlock(&master_dev->bus_mutex);
                if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) {
                        dev_err(&ds1wm_data->pdev->dev,
                                "pass: %d bus error, retrying\n", pass);
index 291897c881be5c67462ec86d6dafb0b073097a5d..4b0fcf3c2d035f8aba0d0e0ba938f34df6621ef2 100644 (file)
@@ -178,6 +178,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
                hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT);
        if (ret == 0) {
                dev_dbg(hdq_data->dev, "TX wait elapsed\n");
+               ret = -ETIMEDOUT;
                goto out;
        }
 
@@ -185,7 +186,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
        /* check irqstatus */
        if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) {
                dev_dbg(hdq_data->dev, "timeout waiting for"
-                       "TXCOMPLETE/RXCOMPLETE, %x", *status);
+                       " TXCOMPLETE/RXCOMPLETE, %x", *status);
                ret = -ETIMEDOUT;
                goto out;
        }
@@ -196,7 +197,7 @@ static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status)
                        OMAP_HDQ_FLAG_CLEAR, &tmp_status);
        if (ret) {
                dev_dbg(hdq_data->dev, "timeout waiting GO bit"
-                       "return to zero, %x", tmp_status);
+                       " return to zero, %x", tmp_status);
        }
 
 out:
@@ -339,7 +340,7 @@ static int omap_hdq_break(struct hdq_data *hdq_data)
                        &tmp_status);
        if (ret)
                dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits"
-                       "return to zero, %x", tmp_status);
+                       " return to zero, %x", tmp_status);
 
 out:
        mutex_unlock(&hdq_data->hdq_mutex);
@@ -351,7 +352,6 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
 {
        int ret = 0;
        u8 status;
-       unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;
 
        ret = mutex_lock_interruptible(&hdq_data->hdq_mutex);
        if (ret < 0) {
@@ -369,22 +369,20 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
                        OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO,
                        OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
                /*
-                * The RX comes immediately after TX. It
-                * triggers another interrupt before we
-                * sleep. So we have to wait for RXCOMPLETE bit.
+                * The RX comes immediately after TX.
                 */
-               while (!(hdq_data->hdq_irqstatus
-                       & OMAP_HDQ_INT_STATUS_RXCOMPLETE)
-                       && time_before(jiffies, timeout)) {
-                       schedule_timeout_uninterruptible(1);
-               }
+               wait_event_timeout(hdq_wait_queue,
+                                  (hdq_data->hdq_irqstatus
+                                   & OMAP_HDQ_INT_STATUS_RXCOMPLETE),
+                                  OMAP_HDQ_TIMEOUT);
+
                hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0,
                        OMAP_HDQ_CTRL_STATUS_DIR);
                status = hdq_data->hdq_irqstatus;
                /* check irqstatus */
                if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
                        dev_dbg(hdq_data->dev, "timeout waiting for"
-                               "RXCOMPLETE, %x", status);
+                               " RXCOMPLETE, %x", status);
                        ret = -ETIMEDOUT;
                        goto out;
                }
@@ -394,7 +392,7 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val)
 out:
        mutex_unlock(&hdq_data->hdq_mutex);
 rtn:
-       return 0;
+       return ret;
 
 }
 
@@ -456,7 +454,7 @@ static int omap_hdq_put(struct hdq_data *hdq_data)
 
        if (0 == hdq_data->hdq_usecount) {
                dev_dbg(hdq_data->dev, "attempt to decrement use count"
-                       "when it is zero");
+                       " when it is zero");
                ret = -EINVAL;
        } else {
                hdq_data->hdq_usecount--;
@@ -524,7 +522,7 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)
        mutex_unlock(&hdq_data->hdq_mutex);
 
        ret = hdq_write_byte(hdq_data, byte, &status);
-       if (ret == 0) {
+       if (ret < 0) {
                dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status);
                return;
        }
index eb9e376d624478bb143238970ffb54c2791c0917..67526690acbcb85ad211459a39a7f36ac65e5d4e 100644 (file)
@@ -94,6 +94,19 @@ config W1_SLAVE_DS2781
 
          If you are unsure, say N.
 
+config W1_SLAVE_DS28E04
+       tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)"
+       depends on W1
+       select CRC16
+       help
+         If you enable this you will have the DS28E04-100
+         chip support.
+
+         Say Y here if you want to use a 1-wire
+         4kb EEPROM with PIO family device (DS28E04).
+
+         If you are unsure, say N.
+
 config W1_SLAVE_BQ27000
        tristate "BQ27000 slave support"
        depends on W1
index c4f1859fb520ad5218b0ab40f32b102ac2db35d9..05188f6aab5ab3dc6bcd32565a7ca17de42fffad 100644 (file)
@@ -12,3 +12,4 @@ obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
 obj-$(CONFIG_W1_SLAVE_DS2780)  += w1_ds2780.o
 obj-$(CONFIG_W1_SLAVE_DS2781)  += w1_ds2781.o
 obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
+obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o
index 52ad812fa1e7e73c8bc07545b82e967c73ff6a50..773dca5beafe7e2f7e77f3046870148c514d75ce 100644 (file)
@@ -31,10 +31,10 @@ static int w1_bq27000_read(struct device *dev, unsigned int reg)
        u8 val;
        struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev);
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
        w1_write_8(sl->master, HDQ_CMD_READ | reg);
        val = w1_read_8(sl->master);
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return val;
 }
index 8e813eed0f0ae8167304dae1c62faf974498dbcc..441ad3a3b586b05b6a993a58fff0eb22bdadb0db 100644 (file)
@@ -52,11 +52,11 @@ static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf)
        if (!buf)
                return -EINVAL;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
        dev_dbg(&sl->dev, "mutex locked");
 
        if (w1_reset_select_slave(sl)) {
-               mutex_unlock(&sl->master->mutex);
+               mutex_unlock(&sl->master->bus_mutex);
                return -EIO;
        }
 
@@ -66,7 +66,7 @@ static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf)
        w1_write_block(sl->master, wrbuf, 3);
        *buf = w1_read_8(sl->master);
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
        dev_dbg(&sl->dev, "mutex unlocked");
        return 1;
 }
@@ -165,7 +165,7 @@ static ssize_t w1_f29_write_output(
                return -EFAULT;
 
        dev_dbg(&sl->dev, "locking mutex for write_output");
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
        dev_dbg(&sl->dev, "mutex locked");
 
        if (w1_reset_select_slave(sl))
@@ -200,14 +200,14 @@ static ssize_t w1_f29_write_output(
                /* read the result of the READ_PIO_REGS command */
                if (w1_read_8(sl->master) == *buf) {
                        /* success! */
-                       mutex_unlock(&sl->master->mutex);
+                       mutex_unlock(&sl->master->bus_mutex);
                        dev_dbg(&sl->dev,
                                "mutex unlocked, retries:%d", retries);
                        return 1;
                }
        }
 error:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
        dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries);
 
        return -EIO;
@@ -228,7 +228,7 @@ static ssize_t w1_f29_write_activity(
        if (count != 1 || off != 0)
                return -EFAULT;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        if (w1_reset_select_slave(sl))
                goto error;
@@ -236,7 +236,7 @@ static ssize_t w1_f29_write_activity(
        while (retries--) {
                w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES);
                if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) {
-                       mutex_unlock(&sl->master->mutex);
+                       mutex_unlock(&sl->master->bus_mutex);
                        return 1;
                }
                if (w1_reset_resume_command(sl->master))
@@ -244,7 +244,7 @@ static ssize_t w1_f29_write_activity(
        }
 
 error:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
        return -EIO;
 }
 
@@ -263,7 +263,7 @@ static ssize_t w1_f29_write_status_control(
        if (count != 1 || off != 0)
                return -EFAULT;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        if (w1_reset_select_slave(sl))
                goto error;
@@ -285,12 +285,12 @@ static ssize_t w1_f29_write_status_control(
                w1_write_block(sl->master, w1_buf, 3);
                if (w1_read_8(sl->master) == *buf) {
                        /* success! */
-                       mutex_unlock(&sl->master->mutex);
+                       mutex_unlock(&sl->master->bus_mutex);
                        return 1;
                }
        }
 error:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return -EIO;
 }
index 7a7dbe5026f1f0495efa8451a004fb41e4374eb6..40a10b5ed120c134d14e77481d1fdf70101ed2ef 100644 (file)
@@ -66,7 +66,7 @@ static ssize_t w1_counter_read(struct device *device,
        wrbuf[0]        = 0xA5;
        wrbuf[1]        = rom_addr & 0xFF;
        wrbuf[2]        = rom_addr >> 8;
-       mutex_lock(&dev->mutex);
+       mutex_lock(&dev->bus_mutex);
        if (!w1_reset_select_slave(sl)) {
                w1_write_block(dev, wrbuf, 3);
                read_byte_count = 0;
@@ -124,7 +124,7 @@ static ssize_t w1_counter_read(struct device *device,
        } else {
                c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error");
        }
-       mutex_unlock(&dev->mutex);
+       mutex_unlock(&dev->bus_mutex);
        return PAGE_SIZE - c;
 }
 
index 84e2410aec1dd5b187d9e60487d1e0a993989afe..984b30331a45be06e3f307753f5ee26fe3f82a98 100644 (file)
@@ -107,7 +107,7 @@ static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj,
        if (count == 0)
                return 0;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        /* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */
        while (todo > 0) {
@@ -126,7 +126,7 @@ static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj,
                off += W1_F2D_READ_MAXLEN;
        }
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return count;
 }
@@ -214,7 +214,7 @@ static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj,
        if (count == 0)
                return 0;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        /* Can only write data in blocks of the size of the scratchpad */
        addr = off;
@@ -259,7 +259,7 @@ static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj,
        }
 
 out_up:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return count;
 }
index 0f7b8f9c509a87577a76daad431447fe0f97375c..85f2cdb27fa2dd258b8ee3c72d32dcdc73ba1010 100644 (file)
@@ -107,7 +107,7 @@ static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj,
        if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
                return 0;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
 #ifdef CONFIG_W1_SLAVE_DS2433_CRC
 
@@ -138,7 +138,7 @@ static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj,
 #endif /* CONFIG_W1_SLAVE_DS2433_CRC */
 
 out_up:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return count;
 }
@@ -233,7 +233,7 @@ static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj,
        }
 #endif /* CONFIG_W1_SLAVE_DS2433_CRC */
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        /* Can only write data to one page at a time */
        idx = 0;
@@ -251,7 +251,7 @@ static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj,
        }
 
 out_up:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return count;
 }
index 5754c9a4f58b49be5237ac50b70234779f0b07bd..aa7bd5fa2fa8e5bb79aefe265636651eae7da8e4 100644 (file)
@@ -31,7 +31,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,
        if (!dev)
                return 0;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        if (addr > DS2760_DATA_SIZE || addr < 0) {
                count = 0;
@@ -54,7 +54,7 @@ static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,
        }
 
 out:
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return count;
 }
@@ -76,14 +76,14 @@ static int w1_ds2760_eeprom_cmd(struct device *dev, int addr, int cmd)
        if (!dev)
                return -EINVAL;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        if (w1_reset_select_slave(sl) == 0) {
                w1_write_8(sl->master, cmd);
                w1_write_8(sl->master, addr);
        }
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
        return 0;
 }
 
index 39f78c0b143cb495a15eff9d67f5e4b7eb6ce4be..7b09307de0ef418f7f670038f7a3e5a923ff4f3c 100644 (file)
@@ -60,30 +60,16 @@ int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
        if (!dev)
                return -ENODEV;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        ret = w1_ds2780_do_io(dev, buf, addr, count, io);
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return ret;
 }
 EXPORT_SYMBOL(w1_ds2780_io);
 
-int w1_ds2780_io_nolock(struct device *dev, char *buf, int addr, size_t count,
-                       int io)
-{
-       int ret;
-
-       if (!dev)
-               return -ENODEV;
-
-       ret = w1_ds2780_do_io(dev, buf, addr, count, io);
-
-       return ret;
-}
-EXPORT_SYMBOL(w1_ds2780_io_nolock);
-
 int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
 {
        struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
@@ -91,14 +77,14 @@ int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
        if (!dev)
                return -EINVAL;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        if (w1_reset_select_slave(sl) == 0) {
                w1_write_8(sl->master, cmd);
                w1_write_8(sl->master, addr);
        }
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
        return 0;
 }
 EXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
index 7373793650216cca9eed315cf9875c40863aa726..a1fba79eb1b54ea990dc9cf6923452e482a6e3e2 100644 (file)
 
 extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
                        int io);
-extern int w1_ds2780_io_nolock(struct device *dev, char *buf, int addr,
-                       size_t count, int io);
 extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd);
 
 #endif /* !_W1_DS2780_H */
index 0d0c7985293fb02ca0a6f76292b8e32738e2acde..877daf74159c98b7c8a77284d6ce31105b6fb9d3 100644 (file)
@@ -58,30 +58,16 @@ int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
        if (!dev)
                return -ENODEV;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        ret = w1_ds2781_do_io(dev, buf, addr, count, io);
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
 
        return ret;
 }
 EXPORT_SYMBOL(w1_ds2781_io);
 
-int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr, size_t count,
-                       int io)
-{
-       int ret;
-
-       if (!dev)
-               return -ENODEV;
-
-       ret = w1_ds2781_do_io(dev, buf, addr, count, io);
-
-       return ret;
-}
-EXPORT_SYMBOL(w1_ds2781_io_nolock);
-
 int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd)
 {
        struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
@@ -89,14 +75,14 @@ int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd)
        if (!dev)
                return -EINVAL;
 
-       mutex_lock(&sl->master->mutex);
+       mutex_lock(&sl->master->bus_mutex);
 
        if (w1_reset_select_slave(sl) == 0) {
                w1_write_8(sl->master, cmd);
                w1_write_8(sl->master, addr);
        }
 
-       mutex_unlock(&sl->master->mutex);
+       mutex_unlock(&sl->master->bus_mutex);
        return 0;
 }
 EXPORT_SYMBOL(w1_ds2781_eeprom_cmd);
index 82bc66497b43a8c0c7116163eb7d1c8808718bca..557dfb0b4f6479d117314d0d6ce1372f37ad6236 100644 (file)
 
 extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
                        int io);
-extern int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr,
-                       size_t count, int io);
 extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd);
 
 #endif /* !_W1_DS2781_H */
diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c
new file mode 100644 (file)
index 0000000..98117db
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ *     w1_ds28e04.c - w1 family 1C (DS28E04) driver
+ *
+ * Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/crc16.h>
+#include <linux/uaccess.h>
+
+#define CRC16_INIT             0
+#define CRC16_VALID            0xb001
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>");
+MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO");
+
+/* Allow the strong pullup to be disabled, but default to enabled.
+ * If it was disabled a parasite powered device might not get the required
+ * current to copy the data from the scratchpad to EEPROM.  If it is enabled
+ * parasite powered devices have a better chance of getting the current
+ * required.
+ */
+static int w1_strong_pullup = 1;
+module_param_named(strong_pullup, w1_strong_pullup, int, 0);
+
+/* enable/disable CRC checking on DS28E04-100 memory accesses */
+static char w1_enable_crccheck = 1;
+
+#define W1_EEPROM_SIZE         512
+#define W1_PAGE_COUNT          16
+#define W1_PAGE_SIZE           32
+#define W1_PAGE_BITS           5
+#define W1_PAGE_MASK           0x1F
+
+#define W1_F1C_READ_EEPROM     0xF0
+#define W1_F1C_WRITE_SCRATCH   0x0F
+#define W1_F1C_READ_SCRATCH    0xAA
+#define W1_F1C_COPY_SCRATCH    0x55
+#define W1_F1C_ACCESS_WRITE    0x5A
+
+#define W1_1C_REG_LOGIC_STATE  0x220
+
+struct w1_f1C_data {
+       u8      memory[W1_EEPROM_SIZE];
+       u32     validcrc;
+};
+
+/**
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f1C_fix_count(loff_t off, size_t count, size_t size)
+{
+       if (off > size)
+               return 0;
+
+       if ((off + count) > size)
+               return size - off;
+
+       return count;
+}
+
+static int w1_f1C_refresh_block(struct w1_slave *sl, struct w1_f1C_data *data,
+                               int block)
+{
+       u8      wrbuf[3];
+       int     off = block * W1_PAGE_SIZE;
+
+       if (data->validcrc & (1 << block))
+               return 0;
+
+       if (w1_reset_select_slave(sl)) {
+               data->validcrc = 0;
+               return -EIO;
+       }
+
+       wrbuf[0] = W1_F1C_READ_EEPROM;
+       wrbuf[1] = off & 0xff;
+       wrbuf[2] = off >> 8;
+       w1_write_block(sl->master, wrbuf, 3);
+       w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+       /* cache the block if the CRC is valid */
+       if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+               data->validcrc |= (1 << block);
+
+       return 0;
+}
+
+static int w1_f1C_read(struct w1_slave *sl, int addr, int len, char *data)
+{
+       u8 wrbuf[3];
+
+       /* read directly from the EEPROM */
+       if (w1_reset_select_slave(sl))
+               return -EIO;
+
+       wrbuf[0] = W1_F1C_READ_EEPROM;
+       wrbuf[1] = addr & 0xff;
+       wrbuf[2] = addr >> 8;
+
+       w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
+       return w1_read_block(sl->master, data, len);
+}
+
+static ssize_t w1_f1C_read_bin(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr,
+                              char *buf, loff_t off, size_t count)
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       struct w1_f1C_data *data = sl->family_data;
+       int i, min_page, max_page;
+
+       count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);
+       if (count == 0)
+               return 0;
+
+       mutex_lock(&sl->master->mutex);
+
+       if (w1_enable_crccheck) {
+               min_page = (off >> W1_PAGE_BITS);
+               max_page = (off + count - 1) >> W1_PAGE_BITS;
+               for (i = min_page; i <= max_page; i++) {
+                       if (w1_f1C_refresh_block(sl, data, i)) {
+                               count = -EIO;
+                               goto out_up;
+                       }
+               }
+               memcpy(buf, &data->memory[off], count);
+       } else {
+               count = w1_f1C_read(sl, off, count, buf);
+       }
+
+out_up:
+       mutex_unlock(&sl->master->mutex);
+
+       return count;
+}
+
+/**
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be on one page.
+ * The master must be locked.
+ *
+ * @param sl   The slave structure
+ * @param addr Address for the write
+ * @param len   length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
+ * @param data The data to write
+ * @return     0=Success -1=failure
+ */
+static int w1_f1C_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+       u8 wrbuf[4];
+       u8 rdbuf[W1_PAGE_SIZE + 3];
+       u8 es = (addr + len - 1) & 0x1f;
+       unsigned int tm = 10;
+       int i;
+       struct w1_f1C_data *f1C = sl->family_data;
+
+       /* Write the data to the scratchpad */
+       if (w1_reset_select_slave(sl))
+               return -1;
+
+       wrbuf[0] = W1_F1C_WRITE_SCRATCH;
+       wrbuf[1] = addr & 0xff;
+       wrbuf[2] = addr >> 8;
+
+       w1_write_block(sl->master, wrbuf, 3);
+       w1_write_block(sl->master, data, len);
+
+       /* Read the scratchpad and verify */
+       if (w1_reset_select_slave(sl))
+               return -1;
+
+       w1_write_8(sl->master, W1_F1C_READ_SCRATCH);
+       w1_read_block(sl->master, rdbuf, len + 3);
+
+       /* Compare what was read against the data written */
+       if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+           (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
+               return -1;
+
+       /* Copy the scratchpad to EEPROM */
+       if (w1_reset_select_slave(sl))
+               return -1;
+
+       wrbuf[0] = W1_F1C_COPY_SCRATCH;
+       wrbuf[3] = es;
+
+       for (i = 0; i < sizeof(wrbuf); ++i) {
+               /* issue 10ms strong pullup (or delay) on the last byte
+                  for writing the data from the scratchpad to EEPROM */
+               if (w1_strong_pullup && i == sizeof(wrbuf)-1)
+                       w1_next_pullup(sl->master, tm);
+
+               w1_write_8(sl->master, wrbuf[i]);
+       }
+
+       if (!w1_strong_pullup)
+               msleep(tm);
+
+       if (w1_enable_crccheck) {
+               /* invalidate cached data */
+               f1C->validcrc &= ~(1 << (addr >> W1_PAGE_BITS));
+       }
+
+       /* Reset the bus to wake up the EEPROM (this may not be needed) */
+       w1_reset_bus(sl->master);
+
+       return 0;
+}
+
+static ssize_t w1_f1C_write_bin(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr,
+                              char *buf, loff_t off, size_t count)
+
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       int addr, len, idx;
+
+       count = w1_f1C_fix_count(off, count, W1_EEPROM_SIZE);
+       if (count == 0)
+               return 0;
+
+       if (w1_enable_crccheck) {
+               /* can only write full blocks in cached mode */
+               if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
+                       dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
+                               (int)off, count);
+                       return -EINVAL;
+               }
+
+               /* make sure the block CRCs are valid */
+               for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
+                       if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE)
+                               != CRC16_VALID) {
+                               dev_err(&sl->dev, "bad CRC at offset %d\n",
+                                       (int)off);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       mutex_lock(&sl->master->mutex);
+
+       /* Can only write data to one page at a time */
+       idx = 0;
+       while (idx < count) {
+               addr = off + idx;
+               len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
+               if (len > (count - idx))
+                       len = count - idx;
+
+               if (w1_f1C_write(sl, addr, len, &buf[idx]) < 0) {
+                       count = -EIO;
+                       goto out_up;
+               }
+               idx += len;
+       }
+
+out_up:
+       mutex_unlock(&sl->master->mutex);
+
+       return count;
+}
+
+static ssize_t w1_f1C_read_pio(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr,
+                              char *buf, loff_t off, size_t count)
+
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       int ret;
+
+       /* check arguments */
+       if (off != 0 || count != 1 || buf == NULL)
+               return -EINVAL;
+
+       mutex_lock(&sl->master->mutex);
+       ret = w1_f1C_read(sl, W1_1C_REG_LOGIC_STATE, count, buf);
+       mutex_unlock(&sl->master->mutex);
+
+       return ret;
+}
+
+static ssize_t w1_f1C_write_pio(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+
+{
+       struct w1_slave *sl = kobj_to_w1_slave(kobj);
+       u8 wrbuf[3];
+       u8 ack;
+
+       /* check arguments */
+       if (off != 0 || count != 1 || buf == NULL)
+               return -EINVAL;
+
+       mutex_lock(&sl->master->mutex);
+
+       /* Write the PIO data */
+       if (w1_reset_select_slave(sl)) {
+               mutex_unlock(&sl->master->mutex);
+               return -1;
+       }
+
+       /* set bit 7..2 to value '1' */
+       *buf = *buf | 0xFC;
+
+       wrbuf[0] = W1_F1C_ACCESS_WRITE;
+       wrbuf[1] = *buf;
+       wrbuf[2] = ~(*buf);
+       w1_write_block(sl->master, wrbuf, 3);
+
+       w1_read_block(sl->master, &ack, sizeof(ack));
+
+       mutex_unlock(&sl->master->mutex);
+
+       /* check for acknowledgement */
+       if (ack != 0xAA)
+               return -EIO;
+
+       return count;
+}
+
+static ssize_t w1_f1C_show_crccheck(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       if (put_user(w1_enable_crccheck + 0x30, buf))
+               return -EFAULT;
+
+       return sizeof(w1_enable_crccheck);
+}
+
+static ssize_t w1_f1C_store_crccheck(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       char val;
+
+       if (count != 1 || !buf)
+               return -EINVAL;
+
+       if (get_user(val, buf))
+               return -EFAULT;
+
+       /* convert to decimal */
+       val = val - 0x30;
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       /* set the new value */
+       w1_enable_crccheck = val;
+
+       return sizeof(w1_enable_crccheck);
+}
+
+#define NB_SYSFS_BIN_FILES 2
+static struct bin_attribute w1_f1C_bin_attr[NB_SYSFS_BIN_FILES] = {
+       {
+               .attr = {
+                       .name = "eeprom",
+                       .mode = S_IRUGO | S_IWUSR,
+               },
+               .size = W1_EEPROM_SIZE,
+               .read = w1_f1C_read_bin,
+               .write = w1_f1C_write_bin,
+       },
+       {
+               .attr = {
+                       .name = "pio",
+                       .mode = S_IRUGO | S_IWUSR,
+               },
+               .size = 1,
+               .read = w1_f1C_read_pio,
+               .write = w1_f1C_write_pio,
+       }
+};
+
+static DEVICE_ATTR(crccheck, S_IWUSR | S_IRUGO,
+                  w1_f1C_show_crccheck, w1_f1C_store_crccheck);
+
+static int w1_f1C_add_slave(struct w1_slave *sl)
+{
+       int err = 0;
+       int i;
+       struct w1_f1C_data *data = NULL;
+
+       if (w1_enable_crccheck) {
+               data = kzalloc(sizeof(struct w1_f1C_data), GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+               sl->family_data = data;
+       }
+
+       /* create binary sysfs attributes */
+       for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
+               err = sysfs_create_bin_file(
+                       &sl->dev.kobj, &(w1_f1C_bin_attr[i]));
+
+       if (!err) {
+               /* create device attributes */
+               err = device_create_file(&sl->dev, &dev_attr_crccheck);
+       }
+
+       if (err) {
+               /* remove binary sysfs attributes */
+               for (i = 0; i < NB_SYSFS_BIN_FILES; ++i)
+                       sysfs_remove_bin_file(
+                               &sl->dev.kobj, &(w1_f1C_bin_attr[i]));
+
+               kfree(data);
+       }
+
+       return err;
+}
+
+static void w1_f1C_remove_slave(struct w1_slave *sl)
+{
+       int i;
+
+       kfree(sl->family_data);
+       sl->family_data = NULL;
+
+       /* remove device attributes */
+       device_remove_file(&sl->dev, &dev_attr_crccheck);
+
+       /* remove binary sysfs attributes */
+       for (i = 0; i < NB_SYSFS_BIN_FILES; ++i)
+               sysfs_remove_bin_file(&sl->dev.kobj, &(w1_f1C_bin_attr[i]));
+}
+
+static struct w1_family_ops w1_f1C_fops = {
+       .add_slave      = w1_f1C_add_slave,
+       .remove_slave   = w1_f1C_remove_slave,
+};
+
+static struct w1_family w1_family_1C = {
+       .fid = W1_FAMILY_DS28E04,
+       .fops = &w1_f1C_fops,
+};
+
+static int __init w1_f1C_init(void)
+{
+       return w1_register_family(&w1_family_1C);
+}
+
+static void __exit w1_f1C_fini(void)
+{
+       w1_unregister_family(&w1_family_1C);
+}
+
+module_init(w1_f1C_init);
+module_exit(w1_f1C_fini);
index ff29ae747ee82e41455d5db1b49bbd4b0fbebbf9..d90062b211f8a107a142f439129a6f8d699aa1de 100644 (file)
@@ -179,7 +179,7 @@ static ssize_t w1_therm_read(struct device *device,
        int i, max_trying = 10;
        ssize_t c = PAGE_SIZE;
 
-       i = mutex_lock_interruptible(&dev->mutex);
+       i = mutex_lock_interruptible(&dev->bus_mutex);
        if (i != 0)
                return i;
 
@@ -207,19 +207,19 @@ static ssize_t w1_therm_read(struct device *device,
                        w1_write_8(dev, W1_CONVERT_TEMP);
 
                        if (external_power) {
-                               mutex_unlock(&dev->mutex);
+                               mutex_unlock(&dev->bus_mutex);
 
                                sleep_rem = msleep_interruptible(tm);
                                if (sleep_rem != 0)
                                        return -EINTR;
 
-                               i = mutex_lock_interruptible(&dev->mutex);
+                               i = mutex_lock_interruptible(&dev->bus_mutex);
                                if (i != 0)
                                        return i;
                        } else if (!w1_strong_pullup) {
                                sleep_rem = msleep_interruptible(tm);
                                if (sleep_rem != 0) {
-                                       mutex_unlock(&dev->mutex);
+                                       mutex_unlock(&dev->bus_mutex);
                                        return -EINTR;
                                }
                        }
@@ -258,7 +258,7 @@ static ssize_t w1_therm_read(struct device *device,
 
        c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
                w1_convert_temp(rom, sl->family->fid));
-       mutex_unlock(&dev->mutex);
+       mutex_unlock(&dev->bus_mutex);
 
        return PAGE_SIZE - c;
 }
index 2f2e894ea0c84e23392db10c0fb4e46b25b00ab3..1a574370d2cd22ec4686604520c9f139052afba9 100644 (file)
@@ -557,7 +557,7 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
        struct w1_master *md = NULL;
        struct w1_slave *sl = NULL;
        char *event_owner, *name;
-       int err;
+       int err = 0;
 
        if (dev->driver == &w1_master_driver) {
                md = container_of(dev, struct w1_master, dev);
@@ -576,19 +576,17 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
                        event_owner, name, dev_name(dev));
 
        if (dev->driver != &w1_slave_driver || !sl)
-               return 0;
+               goto end;
 
        err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family);
        if (err)
-               return err;
+               goto end;
 
        err = add_uevent_var(env, "W1_SLAVE_ID=%024LX",
                             (unsigned long long)sl->reg_num.id);
-       if (err)
-               return err;
-
-       return 0;
-};
+end:
+       return err;
+}
 #else
 static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
@@ -887,16 +885,21 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
                 *
                 * Return 0 - device(s) present, 1 - no devices present.
                 */
+               mutex_lock(&dev->bus_mutex);
                if (w1_reset_bus(dev)) {
+                       mutex_unlock(&dev->bus_mutex);
                        dev_dbg(&dev->dev, "No devices present on the wire.\n");
                        break;
                }
 
                /* Do fast search on single slave bus */
                if (dev->max_slave_count == 1) {
+                       int rv;
                        w1_write_8(dev, W1_READ_ROM);
+                       rv = w1_read_block(dev, (u8 *)&rn, 8);
+                       mutex_unlock(&dev->bus_mutex);
 
-                       if (w1_read_block(dev, (u8 *)&rn, 8) == 8 && rn)
+                       if (rv == 8 && rn)
                                cb(dev, rn);
 
                        break;
@@ -929,10 +932,12 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb
                        rn |= (tmp64 << i);
 
                        if (kthread_should_stop()) {
+                               mutex_unlock(&dev->bus_mutex);
                                dev_dbg(&dev->dev, "Abort w1_search\n");
                                return;
                        }
                }
+               mutex_unlock(&dev->bus_mutex);
 
                if ( (triplet_ret & 0x03) != 0x03 ) {
                        if ( (desc_bit == last_zero) || (last_zero < 0))
index 4d012ca3f32c42a27c2ccd54b9b506907abd597e..45908e56c2f834180ca0b166e44bd9fca50a5e2d 100644 (file)
@@ -180,6 +180,7 @@ struct w1_master
 
        struct task_struct      *thread;
        struct mutex            mutex;
+       struct mutex            bus_mutex;
 
        struct device_driver    *driver;
        struct device           dev;
index 874aeb05011b04d2b83145627436a56755425623..b00ada44a89be8989a46912fb85e976624d5e586 100644 (file)
@@ -30,6 +30,7 @@
 #define W1_FAMILY_SMEM_01      0x01
 #define W1_FAMILY_SMEM_81      0x81
 #define W1_THERM_DS18S20       0x10
+#define W1_FAMILY_DS28E04      0x1C
 #define W1_COUNTER_DS2423      0x1D
 #define W1_THERM_DS1822        0x22
 #define W1_EEPROM_DS2433       0x23
index 68288355727a13377d97652727c97d448431b0b2..5a98649f6abc27d563bf8ac957ddc0523a21f8e0 100644 (file)
@@ -76,6 +76,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
 
        INIT_LIST_HEAD(&dev->slist);
        mutex_init(&dev->mutex);
+       mutex_init(&dev->bus_mutex);
 
        memcpy(&dev->dev, device, sizeof(struct device));
        dev_set_name(&dev->dev, "w1_bus_master%u", dev->id);
@@ -117,7 +118,7 @@ int w1_add_master_device(struct w1_bus_master *master)
                return(-EINVAL);
         }
        /* While it would be electrically possible to make a device that
-        * generated a strong pullup in bit bang mode, only hardare that
+        * generated a strong pullup in bit bang mode, only hardware that
         * controls 1-wire time frames are even expected to support a strong
         * pullup.  w1_io.c would need to support calling set_pullup before
         * the last write_bit operation of a w1_write_8 which it currently
index d17c20fd74e6958f171e41e6c74bcf27ab620b56..4733eab34a237faa58f57ea14bce96c9659cb2d7 100644 (file)
@@ -495,7 +495,7 @@ void debugfs_remove(struct dentry *dentry)
        struct dentry *parent;
        int ret;
 
-       if (!dentry)
+       if (IS_ERR_OR_NULL(dentry))
                return;
 
        parent = dentry->d_parent;
@@ -527,7 +527,7 @@ void debugfs_remove_recursive(struct dentry *dentry)
        struct dentry *child;
        struct dentry *parent;
 
-       if (!dentry)
+       if (IS_ERR_OR_NULL(dentry))
                return;
 
        parent = dentry->d_parent;
index a5cf784f9cc2abec93c2a5272345838e20fb62ad..6b0bb00d4d2b6061add5a1f266371df257e5c4a9 100644 (file)
@@ -307,6 +307,7 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
 {
        struct sysfs_dirent *sd;
        int is_dir;
+       int type;
 
        if (flags & LOOKUP_RCU)
                return -ECHILD;
@@ -326,6 +327,15 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
        if (strcmp(dentry->d_name.name, sd->s_name) != 0)
                goto out_bad;
 
+       /* The sysfs dirent has been moved to a different namespace */
+       type = KOBJ_NS_TYPE_NONE;
+       if (sd->s_parent) {
+               type = sysfs_ns_type(sd->s_parent);
+               if (type != KOBJ_NS_TYPE_NONE &&
+                               sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns)
+                       goto out_bad;
+       }
+
        mutex_unlock(&sysfs_mutex);
 out_valid:
        return 1;
index 5083bccae9676779ba4368d84d2672d37e6222ef..52a5f15a2223ecb916391138ca9cb44f5d5d108a 100644 (file)
@@ -691,6 +691,11 @@ struct device {
        struct iommu_group      *iommu_group;
 };
 
+static inline struct device *kobj_to_dev(struct kobject *kobj)
+{
+       return container_of(kobj, struct device, kobj);
+}
+
 /* Get the wakeup routines, which depend on struct device */
 #include <linux/pm_wakeup.h>
 
index a2129b73dcb1daa75769ee877295edff7075e504..2d8307f7d67decd0f0729b1f121cf0475e4b04b7 100644 (file)
@@ -31,7 +31,7 @@
  * @irq_flags  IRQ Flags (e.g., IRQF_TRIGGER_LOW).
  * @state_on   print_state is overriden with state_on if attached. If Null,
  *             default method of extcon class is used.
- * @state_off  print_state is overriden with state_on if dettached. If Null,
+ * @state_off  print_state is overriden with state_on if detached. If Null,
  *             default method of extcon class is used.
  *
  * Note that in order for state_on or state_off to be valid, both state_on
index 017a7fb5a1fcb7ea3a2e9e5e3d2116be6bf26922..ae0aaa9d42faacf9d250ae509a93947fc1d06424 100644 (file)
@@ -16,7 +16,6 @@
 
 #ifdef CONFIG_BLOCK
 
-#define kobj_to_dev(k)         container_of((k), struct device, kobj)
 #define dev_to_disk(device)    container_of((device), struct gendisk, part0.__dev)
 #define dev_to_part(device)    container_of((device), struct hd_struct, __dev)
 #define disk_to_dev(disk)      (&(disk)->part0.__dev)
index ac4bc9e79465ed6cf416d9694f8ddc463e2e77ab..50c96b5651b696766c915b57dec92b5629a78866 100644 (file)
@@ -216,6 +216,7 @@ struct log {
  */
 static DEFINE_RAW_SPINLOCK(logbuf_lock);
 
+#ifdef CONFIG_PRINTK
 /* the next printk record to read by syslog(READ) or /proc/kmsg */
 static u64 syslog_seq;
 static u32 syslog_idx;
@@ -228,14 +229,19 @@ static u32 log_first_idx;
 
 /* index and sequence number of the next record to store in the buffer */
 static u64 log_next_seq;
-#ifdef CONFIG_PRINTK
 static u32 log_next_idx;
 
+/* the next printk record to write to the console */
+static u64 console_seq;
+static u32 console_idx;
+static enum log_flags console_prev;
+
 /* the next printk record to read after the last 'clear' command */
 static u64 clear_seq;
 static u32 clear_idx;
 
-#define LOG_LINE_MAX 1024
+#define PREFIX_MAX             32
+#define LOG_LINE_MAX           1024 - PREFIX_MAX
 
 /* record buffer */
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
@@ -360,6 +366,7 @@ static void log_store(int facility, int level,
 struct devkmsg_user {
        u64 seq;
        u32 idx;
+       enum log_flags prev;
        struct mutex lock;
        char buf[8192];
 };
@@ -425,6 +432,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
        struct log *msg;
        u64 ts_usec;
        size_t i;
+       char cont = '-';
        size_t len;
        ssize_t ret;
 
@@ -462,8 +470,25 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
        msg = log_from_idx(user->idx);
        ts_usec = msg->ts_nsec;
        do_div(ts_usec, 1000);
-       len = sprintf(user->buf, "%u,%llu,%llu;",
-                     (msg->facility << 3) | msg->level, user->seq, ts_usec);
+
+       /*
+        * If we couldn't merge continuation line fragments during the print,
+        * export the stored flags to allow an optional external merge of the
+        * records. Merging the records isn't always neccessarily correct, like
+        * when we hit a race during printing. In most cases though, it produces
+        * better readable output. 'c' in the record flags mark the first
+        * fragment of a line, '+' the following.
+        */
+       if (msg->flags & LOG_CONT && !(user->prev & LOG_CONT))
+               cont = 'c';
+       else if ((msg->flags & LOG_CONT) ||
+                ((user->prev & LOG_CONT) && !(msg->flags & LOG_PREFIX)))
+               cont = '+';
+
+       len = sprintf(user->buf, "%u,%llu,%llu,%c;",
+                     (msg->facility << 3) | msg->level,
+                     user->seq, ts_usec, cont);
+       user->prev = msg->flags;
 
        /* escape non-printable characters */
        for (i = 0; i < msg->text_len; i++) {
@@ -646,6 +671,15 @@ void log_buf_kexec_setup(void)
        VMCOREINFO_SYMBOL(log_buf_len);
        VMCOREINFO_SYMBOL(log_first_idx);
        VMCOREINFO_SYMBOL(log_next_idx);
+       /*
+        * Export struct log size and field offsets. User space tools can
+        * parse it and detect any changes to structure down the line.
+        */
+       VMCOREINFO_STRUCT_SIZE(log);
+       VMCOREINFO_OFFSET(log, ts_nsec);
+       VMCOREINFO_OFFSET(log, len);
+       VMCOREINFO_OFFSET(log, text_len);
+       VMCOREINFO_OFFSET(log, dict_len);
 }
 #endif
 
@@ -876,7 +910,7 @@ static size_t msg_print_text(const struct log *msg, enum log_flags prev,
 
                if (buf) {
                        if (print_prefix(msg, syslog, NULL) +
-                           text_len + 1>= size - len)
+                           text_len + 1 >= size - len)
                                break;
 
                        if (prefix)
@@ -907,7 +941,7 @@ static int syslog_print(char __user *buf, int size)
        struct log *msg;
        int len = 0;
 
-       text = kmalloc(LOG_LINE_MAX, GFP_KERNEL);
+       text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
        if (!text)
                return -ENOMEM;
 
@@ -930,7 +964,8 @@ static int syslog_print(char __user *buf, int size)
 
                skip = syslog_partial;
                msg = log_from_idx(syslog_idx);
-               n = msg_print_text(msg, syslog_prev, true, text, LOG_LINE_MAX);
+               n = msg_print_text(msg, syslog_prev, true, text,
+                                  LOG_LINE_MAX + PREFIX_MAX);
                if (n - syslog_partial <= size) {
                        /* message fits into buffer, move forward */
                        syslog_idx = log_next(syslog_idx);
@@ -969,7 +1004,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
        char *text;
        int len = 0;
 
-       text = kmalloc(LOG_LINE_MAX, GFP_KERNEL);
+       text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
        if (!text)
                return -ENOMEM;
 
@@ -1022,7 +1057,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
                        struct log *msg = log_from_idx(idx);
                        int textlen;
 
-                       textlen = msg_print_text(msg, prev, true, text, LOG_LINE_MAX);
+                       textlen = msg_print_text(msg, prev, true, text,
+                                                LOG_LINE_MAX + PREFIX_MAX);
                        if (textlen < 0) {
                                len = textlen;
                                break;
@@ -1349,20 +1385,36 @@ static struct cont {
        u64 ts_nsec;                    /* time of first print */
        u8 level;                       /* log level of first message */
        u8 facility;                    /* log level of first message */
+       enum log_flags flags;           /* prefix, newline flags */
        bool flushed:1;                 /* buffer sealed and committed */
 } cont;
 
-static void cont_flush(void)
+static void cont_flush(enum log_flags flags)
 {
        if (cont.flushed)
                return;
        if (cont.len == 0)
                return;
 
-       log_store(cont.facility, cont.level, LOG_NOCONS, cont.ts_nsec,
-                 NULL, 0, cont.buf, cont.len);
-
-       cont.flushed = true;
+       if (cont.cons) {
+               /*
+                * If a fragment of this line was directly flushed to the
+                * console; wait for the console to pick up the rest of the
+                * line. LOG_NOCONS suppresses a duplicated output.
+                */
+               log_store(cont.facility, cont.level, flags | LOG_NOCONS,
+                         cont.ts_nsec, NULL, 0, cont.buf, cont.len);
+               cont.flags = flags;
+               cont.flushed = true;
+       } else {
+               /*
+                * If no fragment of this line ever reached the console,
+                * just submit it to the store and free the buffer.
+                */
+               log_store(cont.facility, cont.level, flags, 0,
+                         NULL, 0, cont.buf, cont.len);
+               cont.len = 0;
+       }
 }
 
 static bool cont_add(int facility, int level, const char *text, size_t len)
@@ -1371,7 +1423,8 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
                return false;
 
        if (cont.len + len > sizeof(cont.buf)) {
-               cont_flush();
+               /* the line gets too long, split it up in separate records */
+               cont_flush(LOG_CONT);
                return false;
        }
 
@@ -1380,12 +1433,17 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
                cont.level = level;
                cont.owner = current;
                cont.ts_nsec = local_clock();
+               cont.flags = 0;
                cont.cons = 0;
                cont.flushed = false;
        }
 
        memcpy(cont.buf + cont.len, text, len);
        cont.len += len;
+
+       if (cont.len > (sizeof(cont.buf) * 80) / 100)
+               cont_flush(LOG_CONT);
+
        return true;
 }
 
@@ -1394,7 +1452,7 @@ static size_t cont_print_text(char *text, size_t size)
        size_t textlen = 0;
        size_t len;
 
-       if (cont.cons == 0) {
+       if (cont.cons == 0 && (console_prev & LOG_NEWLINE)) {
                textlen += print_time(cont.ts_nsec, text);
                size -= textlen;
        }
@@ -1409,7 +1467,8 @@ static size_t cont_print_text(char *text, size_t size)
        }
 
        if (cont.flushed) {
-               text[textlen++] = '\n';
+               if (cont.flags & LOG_NEWLINE)
+                       text[textlen++] = '\n';
                /* got everything, release buffer */
                cont.len = 0;
        }
@@ -1507,7 +1566,7 @@ asmlinkage int vprintk_emit(int facility, int level,
                 * or another task also prints continuation lines.
                 */
                if (cont.len && (lflags & LOG_PREFIX || cont.owner != current))
-                       cont_flush();
+                       cont_flush(LOG_NEWLINE);
 
                /* buffer line if possible, otherwise store it right away */
                if (!cont_add(facility, level, text, text_len))
@@ -1525,7 +1584,7 @@ asmlinkage int vprintk_emit(int facility, int level,
                if (cont.len && cont.owner == current) {
                        if (!(lflags & LOG_PREFIX))
                                stored = cont_add(facility, level, text, text_len);
-                       cont_flush();
+                       cont_flush(LOG_NEWLINE);
                }
 
                if (!stored)
@@ -1616,9 +1675,20 @@ asmlinkage int printk(const char *fmt, ...)
 }
 EXPORT_SYMBOL(printk);
 
-#else
+#else /* CONFIG_PRINTK */
 
+#define LOG_LINE_MAX           0
+#define PREFIX_MAX             0
 #define LOG_LINE_MAX 0
+static u64 syslog_seq;
+static u32 syslog_idx;
+static u64 console_seq;
+static u32 console_idx;
+static enum log_flags syslog_prev;
+static u64 log_first_seq;
+static u32 log_first_idx;
+static u64 log_next_seq;
+static enum log_flags console_prev;
 static struct cont {
        size_t len;
        size_t cons;
@@ -1902,10 +1972,34 @@ void wake_up_klogd(void)
                this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP);
 }
 
-/* the next printk record to write to the console */
-static u64 console_seq;
-static u32 console_idx;
-static enum log_flags console_prev;
+static void console_cont_flush(char *text, size_t size)
+{
+       unsigned long flags;
+       size_t len;
+
+       raw_spin_lock_irqsave(&logbuf_lock, flags);
+
+       if (!cont.len)
+               goto out;
+
+       /*
+        * We still queue earlier records, likely because the console was
+        * busy. The earlier ones need to be printed before this one, we
+        * did not flush any fragment so far, so just let it queue up.
+        */
+       if (console_seq < log_next_seq && !cont.cons)
+               goto out;
+
+       len = cont_print_text(text, size);
+       raw_spin_unlock(&logbuf_lock);
+       stop_critical_timings();
+       call_console_drivers(cont.level, text, len);
+       start_critical_timings();
+       local_irq_restore(flags);
+       return;
+out:
+       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+}
 
 /**
  * console_unlock - unlock the console system
@@ -1923,7 +2017,7 @@ static enum log_flags console_prev;
  */
 void console_unlock(void)
 {
-       static char text[LOG_LINE_MAX];
+       static char text[LOG_LINE_MAX + PREFIX_MAX];
        static u64 seen_seq;
        unsigned long flags;
        bool wake_klogd = false;
@@ -1937,19 +2031,7 @@ void console_unlock(void)
        console_may_schedule = 0;
 
        /* flush buffered message fragment immediately to console */
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
-       if (cont.len && (cont.cons < cont.len || cont.flushed)) {
-               size_t len;
-
-               len = cont_print_text(text, sizeof(text));
-               raw_spin_unlock(&logbuf_lock);
-               stop_critical_timings();
-               call_console_drivers(cont.level, text, len);
-               start_critical_timings();
-               local_irq_restore(flags);
-       } else
-               raw_spin_unlock_irqrestore(&logbuf_lock, flags);
-
+       console_cont_flush(text, sizeof(text));
 again:
        for (;;) {
                struct log *msg;
@@ -1986,6 +2068,7 @@ skip:
                         * will properly dump everything later.
                         */
                        msg->flags &= ~LOG_NOCONS;
+                       console_prev = msg->flags;
                        goto skip;
                }