mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 15:14:18 +08:00
Driver core merge for 3.6-rc1
Here's the big driver core pull request for 3.6-rc1. Unlike 3.5, this kernel should be a lot tamer, with the printk changes now settled down. All we have here is some extcon driver updates, w1 driver updates, a few printk cleanups that weren't needed for 3.5, but are good to have now, and some other minor fixes/changes in the driver core. All of these have been in the linux-next releases for a while now. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAlARgIUACgkQMUfUDdst+ynDHgCfRNwIB9L+zZvjcKE5e1BhDbUl wVUAn398DFgbJ1+PjGkd1EMR2uVTh7Ou =MIFu -----END PGP SIGNATURE----- Merge tag 'driver-core-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core changes from Greg Kroah-Hartman: "Here's the big driver core pull request for 3.6-rc1. Unlike 3.5, this kernel should be a lot tamer, with the printk changes now settled down. All we have here is some extcon driver updates, w1 driver updates, a few printk cleanups that weren't needed for 3.5, but are good to have now, and some other minor fixes/changes in the driver core. All of these have been in the linux-next releases for a while now. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" * tag 'driver-core-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (38 commits) printk: Export struct log size and member offsets through vmcoreinfo Drivers: hv: Change the hex constant to a decimal constant driver core: don't trigger uevent after failure extcon: MAX77693: Add extcon-max77693 driver to support Maxim MAX77693 MUIC device sysfs: fail dentry revalidation after namespace change fix sysfs: fail dentry revalidation after namespace change extcon: spelling of detach in function doc extcon: arizona: Stop microphone detection if we give up on it extcon: arizona: Update cable reporting calls and split headset PM / Runtime: Do not increment device usage counts before probing kmsg - do not flush partial lines when the console is busy kmsg - export "continuation record" flag to /dev/kmsg kmsg - avoid warning for CONFIG_PRINTK=n compilations kmsg - properly print over-long continuation lines driver-core: Use kobj_to_dev instead of re-implementing it driver-core: Move kobj_to_dev from genhd.h to device.h driver core: Move deferred devices to the end of dpm_list before probing driver core: move uevent call to driver_register driver core: fix shutdown races with probe/remove(v3) Extcon: Arizona: Add driver for Wolfson Arizona class devices ...
This commit is contained in:
commit
fa93669a19
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,8 +1753,7 @@ 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;
|
||||
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... */
|
||||
@ -1774,6 +1772,7 @@ int device_move(struct device *dev, struct device *new_parent,
|
||||
put_device(new_parent);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
switch (dpm_order) {
|
||||
case DPM_ORDER_NONE:
|
||||
break;
|
||||
@ -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,9 +63,6 @@ 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);
|
||||
}
|
||||
|
||||
@ -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,10 +61,6 @@ 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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
end:
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
#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)
|
||||
|
155
kernel/printk.c
155
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);
|
||||
|
||||
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,6 +1467,7 @@ static size_t cont_print_text(char *text, size_t size)
|
||||
}
|
||||
|
||||
if (cont.flushed) {
|
||||
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…
Reference in New Issue
Block a user