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 @@ probe_failed: | |||
| 	devres_release_all(dev); | ||||
| 	driver_sysfs_remove(dev); | ||||
| 	dev->driver = NULL; | ||||
| 	dev_set_drvdata(dev, NULL); | ||||
| 
 | ||||
| 	if (ret == -EPROBE_DEFER) { | ||||
| 		/* Driver requested deferred probing */ | ||||
|  | @ -356,10 +369,9 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) | |||
| 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n", | ||||
| 		 drv->bus->name, __func__, dev_name(dev), drv->name); | ||||
| 
 | ||||
| 	pm_runtime_get_noresume(dev); | ||||
| 	pm_runtime_barrier(dev); | ||||
| 	ret = really_probe(dev, drv); | ||||
| 	pm_runtime_put_sync(dev); | ||||
| 	pm_runtime_idle(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -406,9 +418,8 @@ int device_attach(struct device *dev) | |||
| 			ret = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		pm_runtime_get_noresume(dev); | ||||
| 		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); | ||||
| 		pm_runtime_put_sync(dev); | ||||
| 		pm_runtime_idle(dev); | ||||
| 	} | ||||
| out_unlock: | ||||
| 	device_unlock(dev); | ||||
|  | @ -487,6 +498,7 @@ static void __device_release_driver(struct device *dev) | |||
| 			drv->remove(dev); | ||||
| 		devres_release_all(dev); | ||||
| 		dev->driver = NULL; | ||||
| 		dev_set_drvdata(dev, NULL); | ||||
| 		klist_remove(&dev->p->knode_driver); | ||||
| 		if (dev->bus) | ||||
| 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier, | ||||
|  |  | |||
|  | @ -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 @@ skip: | |||
| 			 * will properly dump everything later. | ||||
| 			 */ | ||||
| 			msg->flags &= ~LOG_NOCONS; | ||||
| 			console_prev = msg->flags; | ||||
| 			goto skip; | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus Torvalds
				Linus Torvalds