From bdd4034df8b37841eeaf7b05f86e732ab8e0b08a Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Mon, 23 Apr 2012 09:16:36 +0200 Subject: driver core: always handle dpm_order If !dev->class, device_move() does not respect the dpm_order. Fix it to do so. Acked-by: Rafael J. Wysocki Signed-off-by: Rabin Vincent Reviewed-by: Srinidhi Kasagar [Fixed a small dangling label compile warning] Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 346be8b78b2..846d12b5d57 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1754,25 +1754,25 @@ int device_move(struct device *dev, struct device *new_parent, set_dev_node(dev, dev_to_node(new_parent)); } - if (!dev->class) - goto out_put; - error = device_move_class_links(dev, old_parent, new_parent); - if (error) { - /* We ignore errors on cleanup since we're hosed anyway... */ - device_move_class_links(dev, new_parent, old_parent); - if (!kobject_move(&dev->kobj, &old_parent->kobj)) { - if (new_parent) - klist_remove(&dev->p->knode_parent); - dev->parent = old_parent; - if (old_parent) { - klist_add_tail(&dev->p->knode_parent, - &old_parent->p->klist_children); - set_dev_node(dev, dev_to_node(old_parent)); + if (dev->class) { + error = device_move_class_links(dev, old_parent, new_parent); + if (error) { + /* We ignore errors on cleanup since we're hosed anyway... */ + device_move_class_links(dev, new_parent, old_parent); + if (!kobject_move(&dev->kobj, &old_parent->kobj)) { + if (new_parent) + klist_remove(&dev->p->knode_parent); + dev->parent = old_parent; + if (old_parent) { + klist_add_tail(&dev->p->knode_parent, + &old_parent->p->klist_children); + set_dev_node(dev, dev_to_node(old_parent)); + } } + cleanup_glue_dir(dev, new_parent_kobj); + put_device(new_parent); + goto out; } - cleanup_glue_dir(dev, new_parent_kobj); - put_device(new_parent); - goto out; } switch (dpm_order) { case DPM_ORDER_NONE: @@ -1787,7 +1787,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(); -- cgit v1.2.3 From 59d4467be405316916a4087d5b02d99196eeef04 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 1 May 2012 11:49:24 -0700 Subject: w1: introduce a slave mutex for serializing IO w1 devices need a mutex to serial IO. Most use master->mutex. However that is used for other purposes and they can conflict. In particular master->mutex is held while w1_attach_slave_device is called. For bq27000, this registers a 'powersupply' device which tries to read the current status. The attempt to read will cause a deadlock on master->mutex. So create a new per-slave mutex and use that for serializing IO for bq27000. Signed-off-by: NeilBrown Cc: Evgeniy Polyakov Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_bq27000.c | 4 ++-- drivers/w1/w1.c | 1 + drivers/w1/w1.h | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 52ad812fa1e..87554788fa3 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -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->mutex); w1_write_8(sl->master, HDQ_CMD_READ | reg); val = w1_read_8(sl->master); - mutex_unlock(&sl->master->mutex); + mutex_unlock(&sl->mutex); return val; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 2f2e894ea0c..69075c3dfc7 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -688,6 +688,7 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); atomic_set(&sl->refcnt, 0); init_completion(&sl->released); + mutex_init(&sl->mutex); spin_lock(&w1_flock); f = w1_family_registered(rn->family); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 4d012ca3f32..8c8be380a98 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -75,6 +75,7 @@ struct w1_slave void *family_data; struct device dev; struct completion released; + struct mutex mutex; }; typedef void (*w1_slave_found_callback)(struct w1_master *, u64); -- cgit v1.2.3 From 526be41625a8b2282d5720dcb19f927edbf193b4 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Sat, 19 May 2012 07:31:54 +0400 Subject: w1: cleanup w1_uevent There were some return statements around in the w1_uevent, used goto to cleanup those return statements with the help of err variable, and also removed a semi colon at the end of the w1_uevent's closing brace. Signed-off-by: Devendra Naga Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 69075c3dfc7..79e488db510 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -557,7 +557,7 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) struct w1_master *md = NULL; struct w1_slave *sl = NULL; char *event_owner, *name; - int err; + int err = 0; if (dev->driver == &w1_master_driver) { md = container_of(dev, struct w1_master, dev); @@ -576,19 +576,17 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) event_owner, name, dev_name(dev)); if (dev->driver != &w1_slave_driver || !sl) - return 0; + goto end; err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family); if (err) - return err; + goto end; err = add_uevent_var(env, "W1_SLAVE_ID=%024LX", (unsigned long long)sl->reg_num.id); - if (err) - return err; - - return 0; -}; +end: + return err; +} #else static int w1_uevent(struct device *dev, struct kobj_uevent_env *env) { -- cgit v1.2.3 From dd0aa67cd7cf3f4a3ca52ecc1d6b82f3fad1352b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 Jun 2012 16:33:54 -0700 Subject: Revert "w1: introduce a slave mutex for serializing IO" This reverts commit 59d4467be405316916a4087d5b02d99196eeef04. Turns out it was the wrong version, will apply the correct version after this. Reported-by: NeilBrown Cc: Evgeniy Polyakov Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_bq27000.c | 4 ++-- drivers/w1/w1.c | 1 - drivers/w1/w1.h | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 87554788fa3..52ad812fa1e 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -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->mutex); + mutex_lock(&sl->master->mutex); w1_write_8(sl->master, HDQ_CMD_READ | reg); val = w1_read_8(sl->master); - mutex_unlock(&sl->mutex); + mutex_unlock(&sl->master->mutex); return val; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 79e488db510..bfb89864102 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -686,7 +686,6 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); atomic_set(&sl->refcnt, 0); init_completion(&sl->released); - mutex_init(&sl->mutex); spin_lock(&w1_flock); f = w1_family_registered(rn->family); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 8c8be380a98..4d012ca3f32 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -75,7 +75,6 @@ struct w1_slave void *family_data; struct device dev; struct completion released; - struct mutex mutex; }; typedef void (*w1_slave_found_callback)(struct w1_master *, u64); -- cgit v1.2.3 From 7b5362a603a1ecc9a25b97dafd702b8098090f41 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 22 May 2012 09:43:02 +1000 Subject: w1: omap_hdq: Fix some error/debug handling. - some debug messages missed spaces - sometimes no error was returned when it should have been - sometimes a message is printed when there is no error, rather than when there is one. Acked-by: Evgeniy Polyakov Signed-off-by: NeilBrown Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 5ef385bfed1..3036b6113ff 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -180,6 +180,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; } @@ -187,7 +188,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; } @@ -198,7 +199,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: @@ -341,7 +342,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); @@ -386,7 +387,7 @@ static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) /* 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; } @@ -396,7 +397,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; } @@ -470,7 +471,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--; @@ -540,7 +541,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; } -- cgit v1.2.3 From b7e938d06d0de43bdbee8844a8736c81480c1031 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 22 May 2012 09:43:02 +1000 Subject: w1: omap_hdq: use wait_event_timeout to wait for read to complete. There is no gain in having a loop - there is no risk of missing the interrupt with wait_event_timeout. Signed-off-by: NeilBrown Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 3036b6113ff..848399bfb9b 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -354,7 +354,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) { @@ -372,15 +371,13 @@ 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; -- cgit v1.2.3 From b02f8bede217a4b145ecc16d3940c78d83941147 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 18 May 2012 15:59:52 +1000 Subject: W1: split master mutex to avoid deadlocks. The 'mutex' in struct w1_master is use for two very different purposes. Firstly it protects various data structures such as the list of all slaves. Secondly it protects the w1 buss against concurrent accesses. This can lead to deadlocks when the ->probe code called while adding a slave needs to talk on the bus, as is the case for power_supply devices. ds2780 and ds2781 drivers contain a work around to track which process hold the lock simply to avoid this deadlock. bq27000 doesn't have that work around and so deadlocks. There are other possible deadlocks involving sysfs. When removing a device the sysfs s_active lock is held, so the lock that protects the slave list must take precedence over s_active. However when access power_supply attributes via sysfs, the s_active lock must take precedence over the lock that protects accesses to the bus. So to avoid deadlocks between w1 slaves and sysfs, these must be two separate locks. Making them separate means that the work around in ds2780 and ds2781 can be removed. So this patch: - adds a new mutex: "bus_mutex" which serialises access to the bus. - takes in mutex in w1_search and ds1wm_search while they access the bus for searching. The mutex is dropped before calling the callback which adds the slave. - changes all slaves to use bus_mutex instead of mutex to protect access to the bus - removes w1_ds2790_io_nolock and w1_ds2781_io_nolock, and the related code from drivers/power/ds278[01]_battery.c which calls them. Signed-off-by: NeilBrown Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/power/ds2780_battery.c | 11 +---------- drivers/power/ds2781_battery.c | 12 +----------- drivers/w1/masters/ds1wm.c | 4 ++++ drivers/w1/slaves/w1_bq27000.c | 4 ++-- drivers/w1/slaves/w1_ds2408.c | 24 ++++++++++++------------ drivers/w1/slaves/w1_ds2423.c | 4 ++-- drivers/w1/slaves/w1_ds2431.c | 8 ++++---- drivers/w1/slaves/w1_ds2433.c | 8 ++++---- drivers/w1/slaves/w1_ds2760.c | 8 ++++---- drivers/w1/slaves/w1_ds2780.c | 22 ++++------------------ drivers/w1/slaves/w1_ds2780.h | 2 -- drivers/w1/slaves/w1_ds2781.c | 22 ++++------------------ drivers/w1/slaves/w1_ds2781.h | 2 -- drivers/w1/slaves/w1_therm.c | 10 +++++----- drivers/w1/w1.c | 9 ++++++++- drivers/w1/w1.h | 1 + drivers/w1/w1_int.c | 1 + 17 files changed, 57 insertions(+), 95 deletions(-) (limited to 'drivers') diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c index de31cae1ba5..74fad941c56 100644 --- a/drivers/power/ds2780_battery.c +++ b/drivers/power/ds2780_battery.c @@ -39,7 +39,6 @@ struct ds2780_device_info { struct device *dev; struct power_supply bat; struct device *w1_dev; - struct task_struct *mutex_holder; }; enum current_types { @@ -64,10 +63,7 @@ static inline struct power_supply *to_power_supply(struct device *dev) static inline int ds2780_battery_io(struct ds2780_device_info *dev_info, char *buf, int addr, size_t count, int io) { - if (dev_info->mutex_holder == current) - return w1_ds2780_io_nolock(dev_info->w1_dev, buf, addr, count, io); - else - return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io); + return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io); } static inline int ds2780_read8(struct ds2780_device_info *dev_info, u8 *val, @@ -779,7 +775,6 @@ static int __devinit ds2780_battery_probe(struct platform_device *pdev) dev_info->bat.properties = ds2780_battery_props; dev_info->bat.num_properties = ARRAY_SIZE(ds2780_battery_props); dev_info->bat.get_property = ds2780_battery_get_property; - dev_info->mutex_holder = current; ret = power_supply_register(&pdev->dev, &dev_info->bat); if (ret) { @@ -809,8 +804,6 @@ static int __devinit ds2780_battery_probe(struct platform_device *pdev) goto fail_remove_bin_file; } - dev_info->mutex_holder = NULL; - return 0; fail_remove_bin_file: @@ -830,8 +823,6 @@ static int __devexit ds2780_battery_remove(struct platform_device *pdev) { struct ds2780_device_info *dev_info = platform_get_drvdata(pdev); - dev_info->mutex_holder = current; - /* remove attributes */ sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index 975684a40f1..5f92a4bb33f 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c @@ -37,7 +37,6 @@ struct ds2781_device_info { struct device *dev; struct power_supply bat; struct device *w1_dev; - struct task_struct *mutex_holder; }; enum current_types { @@ -62,11 +61,7 @@ static inline struct power_supply *to_power_supply(struct device *dev) static inline int ds2781_battery_io(struct ds2781_device_info *dev_info, char *buf, int addr, size_t count, int io) { - if (dev_info->mutex_holder == current) - return w1_ds2781_io_nolock(dev_info->w1_dev, buf, addr, - count, io); - else - return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io); + return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io); } int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, @@ -775,7 +770,6 @@ static int __devinit ds2781_battery_probe(struct platform_device *pdev) dev_info->bat.properties = ds2781_battery_props; dev_info->bat.num_properties = ARRAY_SIZE(ds2781_battery_props); dev_info->bat.get_property = ds2781_battery_get_property; - dev_info->mutex_holder = current; ret = power_supply_register(&pdev->dev, &dev_info->bat); if (ret) { @@ -805,8 +799,6 @@ static int __devinit ds2781_battery_probe(struct platform_device *pdev) goto fail_remove_bin_file; } - dev_info->mutex_holder = NULL; - return 0; fail_remove_bin_file: @@ -826,8 +818,6 @@ static int __devexit ds2781_battery_remove(struct platform_device *pdev) { struct ds2781_device_info *dev_info = platform_get_drvdata(pdev); - dev_info->mutex_holder = current; - /* remove attributes */ sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index a0c8965c1a7..530a2d30906 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -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); diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 52ad812fa1e..773dca5beaf 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -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; } diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 8e813eed0f0..441ad3a3b58 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -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; } diff --git a/drivers/w1/slaves/w1_ds2423.c b/drivers/w1/slaves/w1_ds2423.c index 7a7dbe5026f..40a10b5ed12 100644 --- a/drivers/w1/slaves/w1_ds2423.c +++ b/drivers/w1/slaves/w1_ds2423.c @@ -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; } diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 84e2410aec1..984b30331a4 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.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; } diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 0f7b8f9c509..85f2cdb27fa 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -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; } diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index 5754c9a4f58..aa7bd5fa2fa 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -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; } diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index 39f78c0b143..7b09307de0e 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -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); diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h index 73737936502..a1fba79eb1b 100644 --- a/drivers/w1/slaves/w1_ds2780.h +++ b/drivers/w1/slaves/w1_ds2780.h @@ -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 */ diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c index 0d0c7985293..877daf74159 100644 --- a/drivers/w1/slaves/w1_ds2781.c +++ b/drivers/w1/slaves/w1_ds2781.c @@ -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); diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h index 82bc66497b4..557dfb0b4f6 100644 --- a/drivers/w1/slaves/w1_ds2781.h +++ b/drivers/w1/slaves/w1_ds2781.h @@ -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 */ diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index ff29ae747ee..d90062b211f 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -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; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index bfb89864102..1a574370d2c 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -885,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; @@ -927,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)) diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 4d012ca3f32..45908e56c2f 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -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; diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 68288355727..531434180af 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -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); -- cgit v1.2.3 From 0998d0631001288a5974afc0b2a5f568bcdecb4d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 May 2012 00:09:34 +0200 Subject: device-core: Ensure drvdata = NULL when no driver is bound 1) drvdata is for a driver to store a pointer to driver specific data 2) If no driver is bound, there is no driver specific data associated with the device 3) Thus logically drvdata should be NULL if no driver is bound. But many drivers don't clear drvdata on device_release, or set drvdata early on in probe and leave it set on probe error. Both of which results in a dangling pointer in drvdata. This patch enforce for drvdata to be NULL after device_release or on probe failure. Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 1b1cbb571d3..9a1e9704d78 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -283,6 +283,7 @@ probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; + dev_set_drvdata(dev, NULL); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ @@ -487,6 +488,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, -- cgit v1.2.3 From fbf7f7b4e2ae40f790828c86d31beff2d49e9ac8 Mon Sep 17 00:00:00 2001 From: Markus Franke Date: Sat, 26 May 2012 00:45:12 +0200 Subject: w1: Add 1-wire slave device driver for DS28E04-100 Signed-off-by: Markus Franke Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/Kconfig | 13 ++ drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds28e04.c | 469 +++++++++++++++++++++++++++++++++++++++++ drivers/w1/w1_family.h | 1 + 4 files changed, 484 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds28e04.c (limited to 'drivers') diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index eb9e376d624..67526690acb 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -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 diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index c4f1859fb52..05188f6aab5 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -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 diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c new file mode 100644 index 00000000000..98117db595b --- /dev/null +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -0,0 +1,469 @@ +/* + * w1_ds28e04.c - w1 family 1C (DS28E04) driver + * + * Copyright (c) 2012 Markus Franke + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 , "); +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); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 874aeb05011..b00ada44a89 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -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 -- cgit v1.2.3 From be3a07f71ca3ae300d652a653279321e85b9f3d0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Jun 2012 16:14:38 +0100 Subject: Extcon: Staticise extcon_class It's not referenced outside the core file. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon_class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c index f598a700ec1..99f7227834e 100644 --- a/drivers/extcon/extcon_class.c +++ b/drivers/extcon/extcon_class.c @@ -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 */ -- cgit v1.2.3 From 6e7b4a59b3d7bb2dcd11c019354bf0c91037dadd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 9 Jun 2012 15:02:59 -0700 Subject: driver core: fix some kernel-doc warnings in dma*.c Fix kernel-doc warnings in drivers/base/dma*.c: Warning(drivers/base/dma-buf.c:498): No description found for parameter 'vaddr' Warning(drivers/base/dma-coherent.c:199): No description found for parameter 'ret' Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- drivers/base/dma-buf.c | 1 + drivers/base/dma-coherent.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 24e88fe29ec..c30f3e1d0ef 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -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) { diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 1b85949e3d2..560a7173f81 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -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. -- cgit v1.2.3 From b945f3fa829a7315488f4d39a866dfbb29e8a49a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 16 Jun 2012 11:55:18 +0800 Subject: extcon: Set platform drvdata in gpio_extcon_probe() and fix irq leak Add missing platform_set_drvdata() in gpio_extcon_probe(), otherwise calling platform_get_drvdata in gpio_extcon_remove() returns NULL. Also add missing free_irq call in gpio_extcon_remove(). Signed-off-by: Axel Lin Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon_gpio.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c index fe7a07b4733..8a0dcc11c7c 100644 --- a/drivers/extcon/extcon_gpio.c +++ b/drivers/extcon/extcon_gpio.c @@ -125,6 +125,7 @@ static int __devinit gpio_extcon_probe(struct platform_device *pdev) if (ret < 0) goto err_request_irq; + platform_set_drvdata(pdev, extcon_data); /* Perform initial detection */ gpio_extcon_work(&extcon_data->work.work); @@ -146,6 +147,7 @@ static int __devexit gpio_extcon_remove(struct platform_device *pdev) struct gpio_extcon_data *extcon_data = platform_get_drvdata(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); -- cgit v1.2.3 From 01eaf2458773b276e219a48df69351d230c63d0b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 16 Jun 2012 11:56:24 +0800 Subject: extcon: Convert extcon_gpio to devm_gpio_request_one Also remove unneeded devm_kfree calls. Signed-off-by: Axel Lin Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon_gpio.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c index 8a0dcc11c7c..fe3db45fa83 100644 --- a/drivers/extcon/extcon_gpio.c +++ b/drivers/extcon/extcon_gpio.c @@ -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; } -- cgit v1.2.3 From 679012655acecfa807038ccf9237bba246f9fad8 Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Sun, 17 Jun 2012 12:17:31 -0300 Subject: w1: Fix a typo in 'hardware' word Signed-off-by: Otavio Salvador Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1_int.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 531434180af..5a98649f6ab 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -118,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 -- cgit v1.2.3 From f2c32a882d2c1cde6fc552a5a3d34b4c1330edb8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 24 Jun 2012 12:09:45 +0100 Subject: Extcon: Arizona: Add driver for Wolfson Arizona class devices Most Wolfson Arizona class audio hub CODECs include a flexible ultra low power accessory detection subsystem. This driver exposes initial support for this subsystem via the Extcon framework, implementing support for ultra low power detection of headphone and headset with the ability to detect the polarity of a headset. The functionality of the devices is much richer and more flexible than the current driver, future patches will extend the features of the driver to more fully exploit this. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 8 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-arizona.c | 491 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 500 insertions(+) create mode 100644 drivers/extcon/extcon-arizona.c (limited to 'drivers') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 29c5cf852ef..bb385ac629a 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -29,4 +29,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 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 86020bdb6da..e932caaa311 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o +obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c new file mode 100644 index 00000000000..b068bc9defe --- /dev/null +++ b/drivers/extcon/extcon-arizona.c @@ -0,0 +1,491 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 "Mechanical" +#define ARIZONA_CABLE_HEADPHONE "Headphone" +#define ARIZONA_CABLE_HEADSET "Headset" + +static const char *arizona_cable[] = { + ARIZONA_CABLE_MECHANICAL, + ARIZONA_CABLE_HEADSET, + ARIZONA_CABLE_HEADPHONE, + NULL, +}; + +static const u32 arizona_exclusions[] = { + 0x6, /* Headphone and headset */ + 0, +}; + +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_set_cable_state(&info->edev, + ARIZONA_CABLE_HEADSET, true); + + 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; + 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; + info->edev.mutually_exclusive = arizona_exclusions; + + 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 "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:extcon-arizona"); -- cgit v1.2.3 From d1c6c030fcec6f860d9bb6c632a3ebe62e28440b Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 22 Jun 2012 18:01:40 +0800 Subject: driver core: fix shutdown races with probe/remove(v3) Firstly, .shutdown callback may touch a uninitialized hardware if dev->driver is set and .probe is not completed. Secondly, device_shutdown() may dereference a null pointer to cause oops when dev->driver is cleared after it has been checked in device_shutdown(). So just hold device lock and its parent lock(if it has) to fix the races. Cc: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 846d12b5d57..65849a9aeec 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1812,6 +1812,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 +1827,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 +1843,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); } -- cgit v1.2.3 From 5a7689fd5b4f2094e7a32beae67f290f8619b042 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 2 Jul 2012 19:08:15 +0200 Subject: driver core: move uevent call to driver_register Device driver attribute groups are created after userspace is notified via an add event. Fix this by moving the kobject_uevent call to driver_register after the attribute groups are added. Signed-off-by: Sebastian Ott Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 1 - drivers/base/driver.c | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 2bcef657a60..181ed2660b3 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -743,7 +743,6 @@ int bus_add_driver(struct device_driver *drv) } } - kobject_uevent(&priv->kobj, KOBJ_ADD); return 0; out_unregister: diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 207c27ddf82..1b500d6fcc2 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -187,6 +187,9 @@ int driver_register(struct device_driver *drv) ret = driver_add_groups(drv, drv->groups); if (ret) bus_remove_driver(drv); + + kobject_uevent(&drv->p->kobj, KOBJ_ADD); + return ret; } EXPORT_SYMBOL_GPL(driver_register); -- cgit v1.2.3 From 8153584e3fdf78753bf653d5f583b6ecb86e5e70 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 14:04:44 +0100 Subject: driver core: Move deferred devices to the end of dpm_list before probing When deferred probe was originally added the idea was that devices which defer their probes would move themselves to the end of dpm_list in order to try to keep the assumptions that we're making about the list being in roughly the order things should be suspended correct. However this hasn't been what's been happening and doing it requires a lot of duplicated code to do the moves. Instead take a simple, brute force solution and have the deferred probe code push devices to the end of dpm_list before it retries the probe. This does mean we lock the dpm_list a bit more often but it's very simple and the code shouldn't be a fast path. We do the move with the deferred mutex dropped since doing things with fewer locks held simultaneously seems like a good idea. This approach was most recently suggested by Grant Likely. Signed-off-by: Mark Brown Acked-by: Rafael J. Wysocki Cc: Grant Likely , Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6cd2c6ca9b0..9b0aca47958 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -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); -- cgit v1.2.3 From b0d1f807f340e91b27aa721f6474956af11571da Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 3 Jul 2012 18:49:36 +0200 Subject: driver-core: Use kobj_to_dev instead of re-implementing it Signed-off-by: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 17 ++++++++--------- drivers/base/firmware_class.c | 6 ++---- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 65849a9aeec..f338037a4f3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -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; } /** diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5401814c874..803cfc1597a 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -22,8 +22,6 @@ #include #include -#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; -- cgit v1.2.3 From eed5d2150752bd08b22333d739f3120151773d28 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Jul 2012 11:51:48 +0200 Subject: PM / Runtime: Do not increment device usage counts before probing The pm_runtime_get_noresume() calls before really_probe() and before executing __device_attach() for each driver on the device's bus cause problems to happen if probing fails and if the driver has enabled runtime PM for the device in its .probe() callback. Namely, in that case, if the device has been resumed by the driver after enabling its runtime PM and if it turns out that .probe() should return an error, the driver is supposed to suspend the device and disable its runtime PM before exiting .probe(). However, because the device's runtime PM usage counter was incremented by the core before calling .probe(), the driver's attempt to suspend the device will not succeed and the device will remain in the full-power state after the failing .probe() has returned. To fix this issue, remove the pm_runtime_get_noresume() calls from driver_probe_device() and from device_attach() and replace the corresponding pm_runtime_put_sync() calls with pm_runtime_idle() to preserve the existing behavior (which is to check if the device is idle and to suspend it eventually in that case after probing). Reported-and-tested-by: Kevin Hilman Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 9b0aca47958..e3bbed8a617 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -369,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; } @@ -419,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); -- cgit v1.2.3 From 325c642380fb9a8c628696ed8d64544d73b653ed Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 28 Jun 2012 13:08:30 +0100 Subject: extcon: arizona: Update cable reporting calls and split headset Use extcon_set_state_ for performance and split the headset into separate headphone and microphone reports as this is more idiomatic. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-arizona.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index b068bc9defe..0626754d6e0 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -54,22 +54,17 @@ static const struct arizona_micd_config micd_default_modes[] = { { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, }; -#define ARIZONA_CABLE_MECHANICAL "Mechanical" -#define ARIZONA_CABLE_HEADPHONE "Headphone" -#define ARIZONA_CABLE_HEADSET "Headset" +#define ARIZONA_CABLE_MECHANICAL 0 +#define ARIZONA_CABLE_MICROPHONE 1 +#define ARIZONA_CABLE_HEADPHONE 2 static const char *arizona_cable[] = { - ARIZONA_CABLE_MECHANICAL, - ARIZONA_CABLE_HEADSET, - ARIZONA_CABLE_HEADPHONE, + "Mechanical", + "Microphone", + "Headphone", NULL, }; -static const u32 arizona_exclusions[] = { - 0x6, /* Headphone and headset */ - 0, -}; - static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) { struct arizona *arizona = info->arizona; @@ -174,8 +169,11 @@ static irqreturn_t arizona_micdet(int irq, void *data) /* If we got a high impedence we should have a headset, report it. */ if (info->detecting && (val & 0x400)) { - ret = extcon_set_cable_state(&info->edev, - ARIZONA_CABLE_HEADSET, true); + 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", @@ -198,9 +196,9 @@ static irqreturn_t arizona_micdet(int irq, void *data) if (info->jack_flips >= info->micd_num_modes) { dev_dbg(arizona->dev, "Detected headphone\n"); info->detecting = false; - ret = extcon_set_cable_state(&info->edev, - ARIZONA_CABLE_HEADPHONE, - true); + ret = extcon_set_cable_state_(&info->edev, + ARIZONA_CABLE_HEADPHONE, + true); if (ret != 0) dev_err(arizona->dev, "Headphone report failed: %d\n", @@ -231,9 +229,9 @@ static irqreturn_t arizona_micdet(int irq, void *data) info->detecting = false; arizona_stop_mic(info); - ret = extcon_set_cable_state(&info->edev, - ARIZONA_CABLE_HEADPHONE, - true); + ret = extcon_set_cable_state_(&info->edev, + ARIZONA_CABLE_HEADPHONE, + true); if (ret != 0) dev_err(arizona->dev, "Headphone report failed: %d\n", @@ -275,8 +273,8 @@ static irqreturn_t arizona_jackdet(int irq, void *data) if (val & ARIZONA_JD1_STS) { dev_dbg(arizona->dev, "Detected jack\n"); - ret = extcon_set_cable_state(&info->edev, - ARIZONA_CABLE_MECHANICAL, true); + ret = extcon_set_cable_state_(&info->edev, + ARIZONA_CABLE_MECHANICAL, true); if (ret != 0) dev_err(arizona->dev, "Mechanical report failed: %d\n", @@ -347,7 +345,6 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) info->edev.name = "Headset Jack"; info->edev.supported_cable = arizona_cable; - info->edev.mutually_exclusive = arizona_exclusions; ret = extcon_dev_register(&info->edev, arizona->dev); if (ret < 0) { -- cgit v1.2.3 From 9ef2224d9f2e684da016c8a29ecf6dc548b66e3e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 28 Jun 2012 13:08:31 +0100 Subject: extcon: arizona: Stop microphone detection if we give up on it There should be no point in continuing to try to detect a microphone any more so stop doing so. Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-arizona.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 0626754d6e0..427a289f32a 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -196,6 +196,8 @@ static irqreturn_t arizona_micdet(int irq, void *data) 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); -- cgit v1.2.3 From db1b9037424b8219449a6754e9b7e97b0523c426 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 17 Jul 2012 13:28:28 +0900 Subject: extcon: MAX77693: Add extcon-max77693 driver to support Maxim MAX77693 MUIC device This patch support Maxim MAX77693 MUIC device by using EXTCON Subsystem to handle various external connector. The extcon-max77693 use regmap method for i2c communication and support irq domain instead of previous method of irq base. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 10 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-max77693.c | 779 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 790 insertions(+) create mode 100644 drivers/extcon/extcon-max77693.c (limited to 'drivers') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index bb385ac629a..16716356d1f 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -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 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index e932caaa311..88961b33234 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o +obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c new file mode 100644 index 00000000000..920a609b2c3 --- /dev/null +++ b/drivers/extcon/extcon-max77693.c @@ -0,0 +1,779 @@ +/* + * extcon-max77693.c - MAX77693 extcon driver to support MAX77693 MUIC + * + * Copyright (C) 2012 Samsung Electrnoics + * Chanwoo Choi + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:extcon-max77693"); -- cgit v1.2.3 From a14af325641794d1ce8e676e9c4967342349195c Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 17 Jul 2012 10:39:10 +0200 Subject: driver core: don't trigger uevent after failure Do not send the uevent if driver_add_groups failed. Reported-by: Ming Lei Signed-off-by: Sebastian Ott Signed-off-by: Greg Kroah-Hartman --- drivers/base/driver.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 1b500d6fcc2..974e301a1ef 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -185,9 +185,10 @@ 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; -- cgit v1.2.3 From bd8a4f06d0310326f89fd58fed74f2db8e345056 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 18 Jul 2012 15:57:25 -0700 Subject: Drivers: hv: Change the hex constant to a decimal constant The hex constant chosen for HV_LINUX_GUEST_ID_HI was offensive, update to use the decimal equivalent instead. Reported-by: Paolo Bonzini Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Acked-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index b9426a6592e..0614ff3a7d7 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -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) -- cgit v1.2.3