Commit fa93669a authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents b13bc8dd 6791457a
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
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:
......
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 @@ static int really_probe(struct device *dev, struct device_driver *drv)
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */
......@@ -356,10 +369,9 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);
pm_runtime_idle(dev);
return ret;
}
......@@ -406,9 +418,8 @@ int device_attach(struct device *dev)
ret = 0;
}
} else {
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
pm_runtime_idle(dev);
}
out_unlock:
device_unlock(dev);
......@@ -487,6 +498,7 @@ static void __device_release_driver(struct device *dev)
drv->remove(dev);
devres_release_all(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
klist_remove(&dev->p->knode_driver);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
......
......@@ -493,6 +493,7 @@ EXPORT_SYMBOL_GPL(dma_buf_vmap);
/**
* dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap.
* @dmabuf: [in] buffer to vunmap
* @vaddr: [in] vmap to vunmap
*/
void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
{
......
......@@ -186,6 +186,7 @@ EXPORT_SYMBOL(dma_release_from_coherent);
* @vma: vm_area for the userspace memory
* @vaddr: cpu address returned by dma_alloc_from_coherent
* @size: size of the memory buffer allocated by dma_alloc_from_coherent
* @ret: result from remap_pfn_range()
*
* This checks whether the memory was allocated from the per-device
* coherent memory pool and if so, maps that memory to the provided vma.
......
......@@ -185,8 +185,12 @@ int driver_register(struct device_driver *drv)
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
......
......@@ -22,8 +22,6 @@
#include <linux/slab.h>
#include <linux/sched.h>
#define to_dev(obj) container_of(obj, struct device, kobj)
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");
......@@ -290,7 +288,7 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = to_dev(kobj);
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw;
ssize_t ret_count;
......@@ -384,7 +382,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = to_dev(kobj);
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
struct firmware *fw;
ssize_t retval;
......
......@@ -21,6 +21,16 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
config EXTCON_MAX77693
tristate "MAX77693 EXTCON Support"
depends on MFD_MAX77693
select IRQ_DOMAIN
select REGMAP_I2C
help
If you say yes here you get support for the MUIC device of
Maxim MAX77693 PMIC. The MAX77693 MUIC is a USB port accessory
detector and switch.
config EXTCON_MAX8997
tristate "MAX8997 EXTCON Support"
depends on MFD_MAX8997
......@@ -29,4 +39,12 @@ config EXTCON_MAX8997
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
detector and switch.
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
depends on MFD_ARIZONA
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
advanced audio accessory detection support.
endif # MULTISTATE_SWITCH
......@@ -4,4 +4,6 @@
obj-$(CONFIG_EXTCON) += extcon_class.o
obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
/*
* 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");
/*
* 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 */
/*
* 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)
......
......@@ -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 @@ void console_unlock(void)
* will properly dump everything later.
*/
msg->flags &= ~LOG_NOCONS;
console_prev = msg->flags;
goto skip;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment