mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Driver core merge for 3.6-rc1
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> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAlARgIUACgkQMUfUDdst+ynDHgCfRNwIB9L+zZvjcKE5e1BhDbUl wVUAn398DFgbJ1+PjGkd1EMR2uVTh7Ou =MIFu -----END PGP SIGNATURE----- Merge tag 'driver-core-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core 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 ...
This commit is contained in:
		
						commit
						fa93669a19
					
				
					 47 changed files with 2159 additions and 227 deletions
				
			
		
							
								
								
									
										15
									
								
								Documentation/ABI/stable/sysfs-driver-w1_ds28e04
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Documentation/ABI/stable/sysfs-driver-w1_ds28e04
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										36
									
								
								Documentation/w1/slaves/w1_ds28e04
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Documentation/w1/slaves/w1_ds28e04
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -743,7 +743,6 @@ int bus_add_driver(struct device_driver *drv)
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kobject_uevent(&priv->kobj, KOBJ_ADD);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_unregister:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 @@ static int really_probe(struct device *dev, struct device_driver *drv)
 | 
			
		|||
	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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										490
									
								
								drivers/extcon/extcon-arizona.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										490
									
								
								drivers/extcon/extcon-arizona.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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");
 | 
			
		||||
							
								
								
									
										779
									
								
								drivers/extcon/extcon-max77693.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										779
									
								
								drivers/extcon/extcon-max77693.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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");
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,8 +124,6 @@
 | 
			
		|||
 | 
			
		||||
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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -129,8 +129,6 @@
 | 
			
		|||
 | 
			
		||||
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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										469
									
								
								drivers/w1/slaves/w1_ds28e04.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										469
									
								
								drivers/w1/slaves/w1_ds28e04.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										159
									
								
								kernel/printk.c
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								kernel/printk.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -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 @@ void console_unlock(void)
 | 
			
		|||
			 * will properly dump everything later.
 | 
			
		||||
			 */
 | 
			
		||||
			msg->flags &= ~LOG_NOCONS;
 | 
			
		||||
			console_prev = msg->flags;
 | 
			
		||||
			goto skip;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue