diff options
author | Lily Zhang <r58066@freescale.com> | 2010-09-25 09:37:00 +0800 |
---|---|---|
committer | Alan Tull <r80115@freescale.com> | 2010-09-25 09:53:59 -0500 |
commit | 1a0d1a8bc4b1e39ab25488ed0e28f0e39eb71982 (patch) | |
tree | c637fb3e321bfe6f4e0e529e8cd8d7c0394ca7b7 /drivers/w1 | |
parent | 27d19e5176a0b235a610d9de564f424d484c4f9b (diff) |
ENGR00131906-1 split w1_ds2438 driver as two drivers
The kernel commit "5f487cd34" changes the function
power_supply_register. When calling power_supply_register
into ds2438_add_slave, it causes slave->master->mutex
dead lock. This patch splits w1_ds2438 driver as
w1_ds2438 (w1 slave) driver and ds2438_battery driver
to avoid dead lock in driver initialization.
Signed-off-by: Lily Zhang <r58066@freescale.com>
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/slaves/Kconfig | 1 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2438.c | 537 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2438.h | 6 |
3 files changed, 85 insertions, 459 deletions
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index ad77126801f5..58f3e64e600a 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -46,6 +46,7 @@ config W1_SLAVE_DS2433 config W1_SLAVE_DS2438 tristate "Smart Battery Monitor (DS2438)" + depends on W1 help Say Y here if you want to use a 1-wire Smart Battery Monitor family device (DS2438). diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c index 251ca0e7f914..4212117d9424 100644 --- a/drivers/w1/slaves/w1_ds2438.c +++ b/drivers/w1/slaves/w1_ds2438.c @@ -14,411 +14,20 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/jiffies.h> -#include <linux/workqueue.h> -#include <linux/pm.h> #include <linux/platform_device.h> -#include <linux/device.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/err.h> #include <linux/idr.h> -#include <linux/power_supply.h> #include "../w1.h" #include "../w1_int.h" #include "../w1_family.h" #include "w1_ds2438.h" -struct ds2438_device_info { - /* DS2438 data, valid after calling ds2438_battery_read_status() */ - unsigned long update_time; /* jiffies when data read */ - char raw[DS2438_PAGE_SIZE]; /* raw DS2438 data */ - int voltage_uV; - int current_uA; - int accum_current_uAh; - int temp_C; - int charge_status; - u8 init:1; - u8 setup:1; - u8 calibrate:1; - u8 input_src:1; - u8 ee_flg:1; - u8 resv_bit:3; - u8 threshold:8; - u16 resv_bytes; - u32 senser; - - struct power_supply bat; - struct device *w1_dev; - struct ds2438_ops ops; - struct workqueue_struct *monitor_wqueue; - struct delayed_work monitor_work; -}; - -#define DS2438_SENSER 25 -#define to_ds2438_device_info(x) container_of((x), struct ds2438_device_info, \ - bat); - - -static enum power_supply_property ds2438_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_CHARGE_NOW, -}; - -static char ds2438_sensers_title[] = "DS2438 senserin thousands of resister:"; -static unsigned int cache_time = 1000; -module_param(cache_time, uint, 0644); -MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); - -static ssize_t ds2438_show_input(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - return sprintf(buf, "%s\n", di->input_src ? "1:VDD" : "0:VAD"); -} - -static ssize_t ds2438_show_senser(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int len; - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - len = sprintf(buf, "%s\n", ds2438_sensers_title); - len += sprintf(buf + len, "%d\n", di->senser); - return len; -} - -static ssize_t ds2438_show_ee(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - return sprintf(buf, "%d\n", di->ee_flg); -} - -static ssize_t ds2438_show_threshold(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - return sprintf(buf, "%d\n", di->threshold); -} - -static ssize_t ds2438_set_input(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - di->input_src = !!simple_strtoul(buf, NULL, 0); - return count; -} - -static ssize_t ds2438_set_senser(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - u32 resister; - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - resister = simple_strtoul(buf, NULL, 0); - if (resister) - di->senser = resister; - return count; -} - -static ssize_t ds2438_set_ee(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - di->ee_flg = !!simple_strtoul(buf, NULL, 0); - di->setup = 1; - return count; -} - -static ssize_t ds2438_set_threshold(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int threshold; - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - threshold = simple_strtoul(buf, NULL, 0); - if (threshold < 256) { - di->threshold = threshold; - di->setup = 1; - return count; - } - return -EINVAL; -} - -static ssize_t ds2438_set_calibrate(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct power_supply *psy = dev_get_drvdata(dev); - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - di->calibrate = !!simple_strtoul(buf, NULL, 0); - return count; -} - -static struct device_attribute ds2438_dev_attr[] = { - __ATTR(input_src, 0664, ds2438_show_input, ds2438_set_input), - __ATTR(senser, 0664, ds2438_show_senser, ds2438_set_senser), - __ATTR(ee_flg, 0664, ds2438_show_ee, ds2438_set_ee), - __ATTR(threshold, 0664, ds2438_show_threshold, ds2438_set_threshold), - __ATTR(calibrate, 0220, NULL, ds2438_set_calibrate), -}; - -static void ds2438_setup(struct ds2438_device_info *di) -{ - di->ops.load_sram(di->w1_dev, PAGE0_CONTROL); - di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw); - if (di->init && di->setup) { - if (di->ee_flg) - di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_EE; - else - di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_EE; - if (di->input_src) - di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_AD; - else - di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_AD; - di->raw[PAGE0_THRESHOLD] = di->threshold; - } else { - di->ee_flg = !!(di->raw[PAGE0_STAT_CTRL] & DS2438_CTRL_EE); - di->input_src = !!(di->raw[PAGE0_STAT_CTRL] & DS2438_CTRL_AD); - di->threshold = di->raw[PAGE0_THRESHOLD]; - di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD | DS2438_CTRL_CA; - } - di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL); - if (!di->init) { - di->calibrate = 1; - di->init = 1; - } - di->setup = 0; -} - -static void ds2438_calibrate(struct ds2438_device_info *di) -{ - int current_raw; - /* disable ICA */ - di->ops.load_sram(di->w1_dev, PAGE0_CONTROL); - di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_IAD; - di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL); - - /* Zero offset */ - di->ops.load_sram(di->w1_dev, PAGE1_ETM); - di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw); - ds2438_writew(di->raw + PAGE1_OFFSET_LSB, 0); - di->ops.drain_sram(di->w1_dev, PAGE1_ETM_BYTE0); - - /* enable ICA & read current */ - di->ops.load_sram(di->w1_dev, PAGE0_CONTROL); - di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD; - di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL); - /*wait current convert about 36HZ */ - mdelay(30); - /* disable ICA */ - di->ops.load_sram(di->w1_dev, PAGE0_CONTROL); - di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_IAD; - di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL); - /* read current value */ - current_raw = ds2438_readw(di->raw + PAGE0_CURRENT_LSB); - /* write offset by current value */ - di->ops.load_sram(di->w1_dev, PAGE1_ETM); - di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw); - ds2438_writew(di->raw + PAGE1_OFFSET_LSB, current_raw << 8); - di->ops.write_page(di->w1_dev, PAGE1_ETM, di->raw); - di->ops.drain_sram(di->w1_dev, PAGE1_ETM); - - /*enable ICA again */ - di->ops.load_sram(di->w1_dev, PAGE0_CONTROL); - di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD; - di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw); - di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL); - di->calibrate = 0; -} - -/* - * power supply temperture is in tenths of degree. - */ -static inline int ds2438_get_temp(u16 raw) -{ - int degree, s; - s = !!(raw & 0x8000); - - if (s) - raw = ((~raw & 0x7FFF) + 1); - degree = ((raw >> 8) * 10) + (((raw & 0xFF) * 5) + 63) / 128; - return s ? -degree : degree; -} - -/* - * power supply current is in uA. - */ -static inline int ds2438_get_current(u32 senser, u16 raw) -{ - int s, current_uA; - s = !!(raw & 0xFC00); - /* (x * 1000 * 1000)uA / (4096 * (Rsens / 1000)) */ - raw &= 0x3FF; - current_uA = raw * 125 * 125 * 125; - current_uA /= (senser << 3); - return s ? -current_uA : current_uA; -} - -/* - * power supply current is in uAh. - */ -static inline int ds2438_get_ica(u32 senser, u8 raw) -{ - int charge_uAh; - /* (x * 1000 * 1000)uA / (2048 * (Rsens / 1000)) */ - charge_uAh = (raw * 125 * 125 * 125) >> 4; - charge_uAh /= (senser << 4); - return charge_uAh; -} - -static int ds2438_battery_update_page1(struct ds2438_device_info *di) -{ - int ica_raw; - di->ops.load_sram(di->w1_dev, PAGE1_ETM); - di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw); - ica_raw = di->raw[PAGE1_ICA]; - di->accum_current_uAh = ds2438_get_ica(di->senser, ica_raw); - return 0; -} - -static int ds2438_battery_read_status(struct ds2438_device_info *di) -{ - u8 status; - int temp_raw, voltage_raw, current_raw; - - if (!(di->init) || di->setup) - ds2438_setup(di); - - if (di->calibrate) - ds2438_calibrate(di); - - if (di->update_time && time_before(jiffies, di->update_time + - msecs_to_jiffies(cache_time))) - return 0; - - di->ops.load_sram(di->w1_dev, PAGE0_CONTROL); - di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw); - status = di->raw[PAGE0_STAT_CTRL]; - temp_raw = ds2438_readw(di->raw + PAGE0_TEMP_LSB); - voltage_raw = ds2438_readw(di->raw + PAGE0_VOLTAGE_LSB); - current_raw = ds2438_readw(di->raw + PAGE0_CURRENT_LSB); - di->temp_C = ds2438_get_temp(temp_raw); - di->voltage_uV = voltage_raw * 10000; - di->current_uA = ds2438_get_current(di->senser, current_raw); - - ds2438_battery_update_page1(di); - - if (!(status & DS2438_STAT_TB)) - di->ops.command(di->w1_dev, DS2438_CONVERT_TEMP, 0); - if (!(status & DS2438_STAT_ADB)) - di->ops.command(di->w1_dev, DS2438_CONVERT_VOLT, 0); - di->update_time = jiffies; - return 0; -} - -static void ds2438_battery_update_status(struct ds2438_device_info *di) -{ - int old_charge_status = di->charge_status; - - ds2438_battery_read_status(di); - - if (di->charge_status != old_charge_status) - power_supply_changed(&di->bat); -} - -static void ds2438_battery_work(struct work_struct *work) -{ - struct ds2438_device_info *di = container_of(work, - struct ds2438_device_info, - monitor_work.work); - const int interval = HZ * 60; - - dev_dbg(di->w1_dev, "%s\n", __func__); - - ds2438_battery_update_status(di); - queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); -} - -static void ds2438_battery_external_power_changed(struct power_supply *psy) -{ - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - dev_dbg(di->w1_dev, "%s\n", __func__); - - cancel_delayed_work(&di->monitor_work); - queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ / 10); -} - -static int ds2438_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct ds2438_device_info *di = to_ds2438_device_info(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = di->charge_status; - return 0; - default: - break; - } - - ds2438_battery_read_status(di); - - switch (psp) { - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = di->voltage_uV; - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = di->current_uA; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = di->temp_C; - break; - case POWER_SUPPLY_PROP_CHARGE_NOW: - val->intval = di->accum_current_uAh; - break; - default: - return -EINVAL; - } - - return 0; -} - /* W1 slave DS2438 famliy operations */ -static int ds2438_read_page(struct device *dev, u8 page, u8 *buf) +int w1_ds2438_read_page(struct device *dev, u8 page, u8 *buf) { struct w1_slave *slave = container_of(dev, struct w1_slave, dev); if ((page >= DS2438_PAGE_NUM) || (buf == NULL)) @@ -433,8 +42,9 @@ static int ds2438_read_page(struct device *dev, u8 page, u8 *buf) mutex_unlock(&slave->master->mutex); return 0; } +EXPORT_SYMBOL(w1_ds2438_read_page); -static int ds2438_write_page(struct device *dev, u8 page, u8 *buf) +int w1_ds2438_write_page(struct device *dev, u8 page, u8 *buf) { struct w1_slave *slave = container_of(dev, struct w1_slave, dev); if ((page >= DS2438_PAGE_NUM) || (buf == NULL)) @@ -449,8 +59,9 @@ static int ds2438_write_page(struct device *dev, u8 page, u8 *buf) mutex_unlock(&slave->master->mutex); return 0; } +EXPORT_SYMBOL(w1_ds2438_write_page); -static int ds2438_command(struct device *dev, u8 command, u8 data) +int w1_ds2438_command(struct device *dev, u8 command, u8 data) { struct w1_slave *slave = container_of(dev, struct w1_slave, dev); @@ -466,94 +77,100 @@ static int ds2438_command(struct device *dev, u8 command, u8 data) mutex_unlock(&slave->master->mutex); return 0; } +EXPORT_SYMBOL(w1_ds2438_command); -static int ds2438_drain_sram(struct device *dev, u8 page) +int w1_ds2438_drain_sram(struct device *dev, u8 page) { - return ds2438_command(dev, DS2438_COPY_SCRATCHPAD, page); + return w1_ds2438_command(dev, DS2438_COPY_SCRATCHPAD, page); } +EXPORT_SYMBOL(w1_ds2438_drain_sram); -static int ds2438_load_sram(struct device *dev, u8 page) +int w1_ds2438_load_sram(struct device *dev, u8 page) { - return ds2438_command(dev, DS2438_RECALL_MEMORY, page); + return w1_ds2438_command(dev, DS2438_RECALL_MEMORY, page); } +EXPORT_SYMBOL(w1_ds2438_load_sram); -static inline void ds2438_defaut_ops(struct ds2438_ops *ops) -{ - ops->read_page = ds2438_read_page; - ops->write_page = ds2438_write_page; - ops->drain_sram = ds2438_drain_sram; - ops->load_sram = ds2438_load_sram; - ops->command = ds2438_command; -} +static DEFINE_IDR(bat_idr); +static DEFINE_MUTEX(bat_idr_lock); -static int ds2438_add_slave(struct w1_slave *slave) +static int new_bat_id(void) { - int i, retval = 0; - struct ds2438_device_info *di; + int ret; - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) { - retval = -ENOMEM; - goto di_alloc_failed; - } + while (1) { + int id; - di->w1_dev = &slave->dev; - di->bat.name = dev_name(&slave->dev); - di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->bat.properties = ds2438_battery_props; - di->bat.num_properties = ARRAY_SIZE(ds2438_battery_props); - di->bat.get_property = ds2438_battery_get_property; - di->bat.external_power_changed = ds2438_battery_external_power_changed; - ds2438_defaut_ops(&di->ops); - di->senser = DS2438_SENSER; - di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; - - retval = power_supply_register(&slave->dev, &di->bat); - if (retval) { - dev_err(&slave->dev, "failed to register battery\n"); - goto batt_failed; - } + ret = idr_pre_get(&bat_idr, GFP_KERNEL); + if (ret == 0) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(ds2438_dev_attr); i++) { - if (device_create_file(di->bat.dev, ds2438_dev_attr + i)) { - printk(KERN_ERR "Customize attribute file fail!\n"); + mutex_lock(&bat_idr_lock); + ret = idr_get_new(&bat_idr, NULL, &id); + mutex_unlock(&bat_idr_lock); + + if (ret == 0) { + ret = id & MAX_ID_MASK; + break; + } else if (ret == -EAGAIN) { + continue; + } else { break; } } - if (i != ARRAY_SIZE(ds2438_dev_attr)) { - for (; i >= 0; i++) - device_remove_file(di->bat.dev, ds2438_dev_attr + i); - goto workqueue_failed; + return ret; +} + +static void release_bat_id(int id) +{ + mutex_lock(&bat_idr_lock); + idr_remove(&bat_idr, id); + mutex_unlock(&bat_idr_lock); +} + +static int ds2438_add_slave(struct w1_slave *slave) +{ + int ret; + int id; + struct platform_device *pdev; + + id = new_bat_id(); + if (id < 0) { + ret = id; + goto noid; } - INIT_DELAYED_WORK(&di->monitor_work, ds2438_battery_work); - di->monitor_wqueue = create_singlethread_workqueue(dev_name(&slave->dev)); - if (!di->monitor_wqueue) { - retval = -ESRCH; - goto workqueue_failed; + + pdev = platform_device_alloc(DS2438_DEV_NAME, id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; } - dev_set_drvdata(&slave->dev, di); - queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ / 2); - - goto success; - - workqueue_failed: - power_supply_unregister(&di->bat); - batt_failed: - kfree(di); - di_alloc_failed: - success: - return retval; + pdev->dev.parent = &slave->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&slave->dev, pdev); + goto success; + +pdev_add_failed: + platform_device_unregister(pdev); +pdev_alloc_failed: + release_bat_id(id); +noid: +success: + return ret; } static void ds2438_remove_slave(struct w1_slave *slave) { - struct ds2438_device_info *di = dev_get_drvdata(&slave->dev); + struct platform_device *pdev = dev_get_drvdata(&slave->dev); + int id = pdev->id; - cancel_rearming_delayed_workqueue(di->monitor_wqueue, - &di->monitor_work); - destroy_workqueue(di->monitor_wqueue); - power_supply_unregister(&di->bat); + platform_device_unregister(pdev); + release_bat_id(id); } static struct w1_family_ops w1_ds2438_fops = { @@ -569,12 +186,14 @@ static struct w1_family w1_family_ds2438 = { static int __init w1_ds2438_init(void) { pr_info("1-wire driver for the DS2438 smart battery monitor\n"); + idr_init(&bat_idr); return w1_register_family(&w1_family_ds2438); } static void __exit w1_ds2438_fini(void) { w1_unregister_family(&w1_family_ds2438); + idr_destroy(&bat_idr); } module_init(w1_ds2438_init); diff --git a/drivers/w1/slaves/w1_ds2438.h b/drivers/w1/slaves/w1_ds2438.h index 40d48f25cc10..b9fb73cafea4 100644 --- a/drivers/w1/slaves/w1_ds2438.h +++ b/drivers/w1/slaves/w1_ds2438.h @@ -116,4 +116,10 @@ static inline void ds2438_writew(u8 *raw, u16 data) *raw++ = data & 0xFF; *raw = (data >> 8) & 0xFF; } + +extern int w1_ds2438_read_page(struct device *dev, u8 page, u8 *buf); +extern int w1_ds2438_write_page(struct device *dev, u8 page, u8 *buf); +extern int w1_ds2438_command(struct device *dev, u8 command, u8 data); +extern int w1_ds2438_drain_sram(struct device *dev, u8 page); +extern int w1_ds2438_load_sram(struct device *dev, u8 page); #endif /* __W1_DS2438_H__ */ |