diff options
Diffstat (limited to 'drivers/w1')
-rw-r--r-- | drivers/w1/masters/ds1wm.c | 52 | ||||
-rw-r--r-- | drivers/w1/masters/ds2482.c | 51 | ||||
-rw-r--r-- | drivers/w1/masters/mxc_w1.c | 49 | ||||
-rw-r--r-- | drivers/w1/masters/omap_hdq.c | 8 | ||||
-rw-r--r-- | drivers/w1/masters/w1-gpio.c | 2 | ||||
-rw-r--r-- | drivers/w1/slaves/Kconfig | 13 | ||||
-rw-r--r-- | drivers/w1/slaves/Makefile | 3 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds2413.c | 177 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 36 | ||||
-rw-r--r-- | drivers/w1/w1_family.h | 1 |
10 files changed, 282 insertions, 110 deletions
diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 7c294f4dc0ed..96cab6ac2b4e 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/io.h> #include <linux/irq.h> #include <linux/pm.h> #include <linux/platform_device.h> @@ -459,43 +460,34 @@ static int ds1wm_probe(struct platform_device *pdev) if (!pdev) return -ENODEV; - ds1wm_data = kzalloc(sizeof(*ds1wm_data), GFP_KERNEL); + ds1wm_data = devm_kzalloc(&pdev->dev, sizeof(*ds1wm_data), GFP_KERNEL); if (!ds1wm_data) return -ENOMEM; platform_set_drvdata(pdev, ds1wm_data); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENXIO; - goto err0; - } - ds1wm_data->map = ioremap(res->start, resource_size(res)); - if (!ds1wm_data->map) { - ret = -ENOMEM; - goto err0; - } + if (!res) + return -ENXIO; + ds1wm_data->map = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!ds1wm_data->map) + return -ENOMEM; /* calculate bus shift from mem resource */ ds1wm_data->bus_shift = resource_size(res) >> 3; ds1wm_data->pdev = pdev; ds1wm_data->cell = mfd_get_cell(pdev); - if (!ds1wm_data->cell) { - ret = -ENODEV; - goto err1; - } + if (!ds1wm_data->cell) + return -ENODEV; plat = pdev->dev.platform_data; - if (!plat) { - ret = -ENODEV; - goto err1; - } + if (!plat) + return -ENODEV; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - ret = -ENXIO; - goto err1; - } + if (!res) + return -ENXIO; ds1wm_data->irq = res->start; ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0); ds1wm_data->reset_recover_delay = plat->reset_recover_delay; @@ -505,10 +497,10 @@ static int ds1wm_probe(struct platform_device *pdev) if (res->flags & IORESOURCE_IRQ_LOWEDGE) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); - ret = request_irq(ds1wm_data->irq, ds1wm_isr, + ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data); if (ret) - goto err1; + return ret; ds1wm_up(ds1wm_data); @@ -516,17 +508,12 @@ static int ds1wm_probe(struct platform_device *pdev) ret = w1_add_master_device(&ds1wm_master); if (ret) - goto err2; + goto err; return 0; -err2: +err: ds1wm_down(ds1wm_data); - free_irq(ds1wm_data->irq, ds1wm_data); -err1: - iounmap(ds1wm_data->map); -err0: - kfree(ds1wm_data); return ret; } @@ -560,9 +547,6 @@ static int ds1wm_remove(struct platform_device *pdev) w1_remove_master_device(&ds1wm_master); ds1wm_down(ds1wm_data); - free_irq(ds1wm_data->irq, ds1wm_data); - iounmap(ds1wm_data->map); - kfree(ds1wm_data); return 0; } diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 6429b9e9fb82..e033491fe308 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -51,10 +51,10 @@ * The top 4 bits always read 0. * To write, the top nibble must be the 1's compl. of the low nibble. */ -#define DS2482_REG_CFG_1WS 0x08 -#define DS2482_REG_CFG_SPU 0x04 -#define DS2482_REG_CFG_PPM 0x02 -#define DS2482_REG_CFG_APU 0x01 +#define DS2482_REG_CFG_1WS 0x08 /* 1-wire speed */ +#define DS2482_REG_CFG_SPU 0x04 /* strong pull-up */ +#define DS2482_REG_CFG_PPM 0x02 /* presence pulse masking */ +#define DS2482_REG_CFG_APU 0x01 /* active pull-up */ /** @@ -132,6 +132,17 @@ struct ds2482_data { /** + * Helper to calculate values for configuration register + * @param conf the raw config value + * @return the value w/ complements that can be written to register + */ +static inline u8 ds2482_calculate_config(u8 conf) +{ + return conf | ((~conf & 0x0f) << 4); +} + + +/** * Sets the read pointer. * @param pdev The ds2482 client pointer * @param read_ptr see DS2482_PTR_CODE_xxx above @@ -399,7 +410,7 @@ static u8 ds2482_w1_reset_bus(void *data) /* If the chip did reset since detect, re-config it */ if (err & DS2482_REG_STS_RST) ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, - 0xF0); + ds2482_calculate_config(0x00)); } mutex_unlock(&pdev->access_lock); @@ -407,6 +418,32 @@ static u8 ds2482_w1_reset_bus(void *data) return retval; } +static u8 ds2482_w1_set_pullup(void *data, int delay) +{ + struct ds2482_w1_chan *pchan = data; + struct ds2482_data *pdev = pchan->pdev; + u8 retval = 1; + + /* if delay is non-zero activate the pullup, + * the strong pullup will be automatically deactivated + * by the master, so do not explicitly deactive it + */ + if (delay) { + /* both waits are crucial, otherwise devices might not be + * powered long enough, causing e.g. a w1_therm sensor to + * provide wrong conversion results + */ + ds2482_wait_1wire_idle(pdev); + /* note: it seems like both SPU and APU have to be set! */ + retval = ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, + ds2482_calculate_config(DS2482_REG_CFG_SPU | + DS2482_REG_CFG_APU)); + ds2482_wait_1wire_idle(pdev); + } + + return retval; +} + static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -452,7 +489,8 @@ static int ds2482_probe(struct i2c_client *client, data->w1_count = 8; /* Set all config items to 0 (off) */ - ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, 0xF0); + ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, + ds2482_calculate_config(0x00)); mutex_init(&data->access_lock); @@ -468,6 +506,7 @@ static int ds2482_probe(struct i2c_client *client, data->w1_ch[idx].w1_bm.touch_bit = ds2482_w1_touch_bit; data->w1_ch[idx].w1_bm.triplet = ds2482_w1_triplet; data->w1_ch[idx].w1_bm.reset_bus = ds2482_w1_reset_bus; + data->w1_ch[idx].w1_bm.set_pullup = ds2482_w1_set_pullup; err = w1_add_master_device(&data->w1_ch[idx].w1_bm); if (err) { diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 949e56669548..950d354d50e2 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -109,34 +109,21 @@ static int mxc_w1_probe(struct platform_device *pdev) struct resource *res; int err = 0; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - mdev = kzalloc(sizeof(struct mxc_w1_device), GFP_KERNEL); + mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device), + GFP_KERNEL); if (!mdev) return -ENOMEM; - mdev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(mdev->clk)) { - err = PTR_ERR(mdev->clk); - goto failed_clk; - } + mdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mdev->clk)) + return PTR_ERR(mdev->clk); mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; - res = request_mem_region(res->start, resource_size(res), - "mxc_w1"); - if (!res) { - err = -EBUSY; - goto failed_req; - } - - mdev->regs = ioremap(res->start, resource_size(res)); - if (!mdev->regs) { - dev_err(&pdev->dev, "Cannot map mxc_w1 registers\n"); - goto failed_ioremap; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdev->regs = devm_request_and_ioremap(&pdev->dev, res); + if (!mdev->regs) + return -EBUSY; clk_prepare_enable(mdev->clk); __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); @@ -148,20 +135,10 @@ static int mxc_w1_probe(struct platform_device *pdev) err = w1_add_master_device(&mdev->bus_master); if (err) - goto failed_add; + return err; platform_set_drvdata(pdev, mdev); return 0; - -failed_add: - iounmap(mdev->regs); -failed_ioremap: - release_mem_region(res->start, resource_size(res)); -failed_req: - clk_put(mdev->clk); -failed_clk: - kfree(mdev); - return err; } /* @@ -170,16 +147,10 @@ failed_clk: static int mxc_w1_remove(struct platform_device *pdev) { struct mxc_w1_device *mdev = platform_get_drvdata(pdev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); w1_remove_master_device(&mdev->bus_master); - iounmap(mdev->regs); - release_mem_region(res->start, resource_size(res)); clk_disable_unprepare(mdev->clk); - clk_put(mdev->clk); platform_set_drvdata(pdev, NULL); diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 184dbce4abd1..db2390aed387 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -560,11 +560,9 @@ static int omap_hdq_probe(struct platform_device *pdev) return -ENXIO; } - hdq_data->hdq_base = devm_request_and_ioremap(dev, res); - if (!hdq_data->hdq_base) { - dev_dbg(&pdev->dev, "ioremap failed\n"); - return -ENOMEM; - } + hdq_data->hdq_base = devm_ioremap_resource(dev, res); + if (IS_ERR(hdq_data->hdq_base)) + return PTR_ERR(hdq_data->hdq_base); hdq_data->hdq_usecount = 0; mutex_init(&hdq_data->hdq_mutex); diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index 85b363a5bd0f..d39dfa4cc235 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -72,7 +72,7 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) return 0; } -static int __init w1_gpio_probe(struct platform_device *pdev) +static int w1_gpio_probe(struct platform_device *pdev) { struct w1_bus_master *master; struct w1_gpio_platform_data *pdata; diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 67526690acbc..762561fbabbf 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -17,11 +17,16 @@ config W1_SLAVE_SMEM simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire. config W1_SLAVE_DS2408 - tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" - help - Say Y here if you want to use a 1-wire + tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)" + help + Say Y here if you want to use a 1-wire + DS2408 8-Channel Addressable Switch device support - DS2408 8-Channel Addressable Switch device support +config W1_SLAVE_DS2413 + tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)" + help + Say Y here if you want to use a 1-wire + DS2413 Dual Channel Addressable Switch device support config W1_SLAVE_DS2423 tristate "Counter 1-wire device (DS2423)" diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 05188f6aab5a..06529f3157ab 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o -obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o +obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c new file mode 100644 index 000000000000..829786252c6b --- /dev/null +++ b/drivers/w1/slaves/w1_ds2413.c @@ -0,0 +1,177 @@ +/* + * w1_ds2413.c - w1 family 3a (DS2413) driver + * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com> + * + * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); +MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); + +#define W1_F3A_RETRIES 3 +#define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 +#define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A +#define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA + +static ssize_t w1_f3a_read_state( + 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); + dev_dbg(&sl->dev, + "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", + bin_attr->attr.name, kobj, (unsigned int)off, count, buf); + + if (off != 0) + return 0; + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) { + mutex_unlock(&sl->master->bus_mutex); + return -EIO; + } + + w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); + *buf = w1_read_8(sl->master); + + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked"); + + /* check for correct complement */ + if ((*buf & 0x0F) != ((~*buf >> 4) & 0x0F)) + return -EIO; + else + return 1; +} + +static ssize_t w1_f3a_write_output( + 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 w1_buf[3]; + unsigned int retries = W1_F3A_RETRIES; + + if (count != 1 || off != 0) + return -EFAULT; + + dev_dbg(&sl->dev, "locking mutex for write_output"); + mutex_lock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex locked"); + + if (w1_reset_select_slave(sl)) + goto error; + + /* according to the DS2413 datasheet the most significant 6 bits + should be set to "1"s, so do it now */ + *buf = *buf | 0xFC; + + while (retries--) { + w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE; + w1_buf[1] = *buf; + w1_buf[2] = ~(*buf); + w1_write_block(sl->master, w1_buf, 3); + + if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked, retries:%d", retries); + return 1; + } + if (w1_reset_resume_command(sl->master)) + goto error; + } + +error: + mutex_unlock(&sl->master->bus_mutex); + dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries); + return -EIO; +} + +#define NB_SYSFS_BIN_FILES 2 +static struct bin_attribute w1_f3a_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { + { + .attr = { + .name = "state", + .mode = S_IRUGO, + }, + .size = 1, + .read = w1_f3a_read_state, + }, + { + .attr = { + .name = "output", + .mode = S_IRUGO | S_IWUSR | S_IWGRP, + }, + .size = 1, + .write = w1_f3a_write_output, + } +}; + +static int w1_f3a_add_slave(struct w1_slave *sl) +{ + int err = 0; + int i; + + for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) + err = sysfs_create_bin_file( + &sl->dev.kobj, + &(w1_f3a_sysfs_bin_files[i])); + if (err) + while (--i >= 0) + sysfs_remove_bin_file(&sl->dev.kobj, + &(w1_f3a_sysfs_bin_files[i])); + return err; +} + +static void w1_f3a_remove_slave(struct w1_slave *sl) +{ + int i; + for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) + sysfs_remove_bin_file(&sl->dev.kobj, + &(w1_f3a_sysfs_bin_files[i])); +} + +static struct w1_family_ops w1_f3a_fops = { + .add_slave = w1_f3a_add_slave, + .remove_slave = w1_f3a_remove_slave, +}; + +static struct w1_family w1_family_3a = { + .fid = W1_FAMILY_DS2413, + .fops = &w1_f3a_fops, +}; + +static int __init w1_f3a_init(void) +{ + return w1_register_family(&w1_family_3a); +} + +static void __exit w1_f3a_exit(void) +{ + w1_unregister_family(&w1_family_3a); +} + +module_init(w1_f3a_init); +module_exit(w1_f3a_exit); diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 92d08e7fcba2..c1a702f8c803 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -41,14 +41,18 @@ MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature famil * If it was disabled a parasite powered device might not get the require * current to do a temperature conversion. If it is enabled parasite powered * devices have a better chance of getting the current required. + * In case the parasite power-detection is not working (seems to be the case + * for some DS18S20) the strong pullup can also be forced, regardless of the + * power state of the devices. + * + * Summary of options: + * - strong_pullup = 0 Disable strong pullup completely + * - strong_pullup = 1 Enable automatic strong pullup detection + * - strong_pullup = 2 Force strong pullup */ static int w1_strong_pullup = 1; module_param_named(strong_pullup, w1_strong_pullup, int, 0); -static u8 bad_roms[][9] = { - {0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87}, - {} - }; static ssize_t w1_therm_read(struct device *device, struct device_attribute *attr, char *buf); @@ -168,16 +172,6 @@ static inline int w1_convert_temp(u8 rom[9], u8 fid) return 0; } -static int w1_therm_check_rom(u8 rom[9]) -{ - int i; - - for (i=0; i<sizeof(bad_roms)/9; ++i) - if (!memcmp(bad_roms[i], rom, 9)) - return 1; - - return 0; -} static ssize_t w1_therm_read(struct device *device, struct device_attribute *attr, char *buf) @@ -194,10 +188,11 @@ static ssize_t w1_therm_read(struct device *device, memset(rom, 0, sizeof(rom)); - verdict = 0; - crc = 0; - while (max_trying--) { + + verdict = 0; + crc = 0; + if (!w1_reset_select_slave(sl)) { int count = 0; unsigned int tm = 750; @@ -210,7 +205,8 @@ static ssize_t w1_therm_read(struct device *device, continue; /* 750ms strong pullup (or delay) after the convert */ - if (!external_power && w1_strong_pullup) + if (w1_strong_pullup == 2 || + (!external_power && w1_strong_pullup)) w1_next_pullup(dev, tm); w1_write_8(dev, W1_CONVERT_TEMP); @@ -249,7 +245,7 @@ static ssize_t w1_therm_read(struct device *device, } } - if (!w1_therm_check_rom(rom)) + if (verdict) break; } @@ -260,7 +256,7 @@ static ssize_t w1_therm_read(struct device *device, if (verdict) memcpy(sl->rom, rom, sizeof(sl->rom)); else - dev_warn(device, "18S20 doesn't respond to CONVERT_TEMP.\n"); + dev_warn(device, "Read failed CRC check\n"); for (i = 0; i < 9; ++i) c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index a1f0ce151d53..625dd08f775f 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -39,6 +39,7 @@ #define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 #define W1_FAMILY_DS2780 0x32 +#define W1_FAMILY_DS2413 0x3A #define W1_THERM_DS1825 0x3B #define W1_FAMILY_DS2781 0x3D #define W1_THERM_DS28EA00 0x42 |