forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
9.1 KiB
353 lines
9.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* w1_ds2408.c - w1 family 29 (DS2408) driver |
|
* |
|
* Copyright (c) 2010 Jean-Francois Dagenais <[email protected]> |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/moduleparam.h> |
|
#include <linux/device.h> |
|
#include <linux/types.h> |
|
#include <linux/delay.h> |
|
#include <linux/slab.h> |
|
|
|
#include <linux/w1.h> |
|
|
|
#define W1_FAMILY_DS2408 0x29 |
|
|
|
#define W1_F29_RETRIES 3 |
|
|
|
#define W1_F29_REG_LOGIG_STATE 0x88 /* R */ |
|
#define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */ |
|
#define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */ |
|
#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */ |
|
#define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */ |
|
#define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */ |
|
|
|
#define W1_F29_FUNC_READ_PIO_REGS 0xF0 |
|
#define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5 |
|
#define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A |
|
/* also used to write the control/status reg (0x8D): */ |
|
#define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC |
|
#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3 |
|
|
|
#define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA |
|
|
|
static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf) |
|
{ |
|
u8 wrbuf[3]; |
|
dev_dbg(&sl->dev, |
|
"Reading with slave: %p, reg addr: %0#4x, buff addr: %p", |
|
sl, (unsigned int)address, buf); |
|
|
|
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; |
|
} |
|
|
|
wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS; |
|
wrbuf[1] = address; |
|
wrbuf[2] = 0; |
|
w1_write_block(sl->master, wrbuf, 3); |
|
*buf = w1_read_8(sl->master); |
|
|
|
mutex_unlock(&sl->master->bus_mutex); |
|
dev_dbg(&sl->dev, "mutex unlocked"); |
|
return 1; |
|
} |
|
|
|
static ssize_t state_read(struct file *filp, struct kobject *kobj, |
|
struct bin_attribute *bin_attr, char *buf, loff_t off, |
|
size_t count) |
|
{ |
|
dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
|
"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", |
|
bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf); |
|
} |
|
|
|
static ssize_t output_read(struct file *filp, struct kobject *kobj, |
|
struct bin_attribute *bin_attr, char *buf, |
|
loff_t off, size_t count) |
|
{ |
|
dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
|
"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", |
|
bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
return _read_reg(kobj_to_w1_slave(kobj), |
|
W1_F29_REG_OUTPUT_LATCH_STATE, buf); |
|
} |
|
|
|
static ssize_t activity_read(struct file *filp, struct kobject *kobj, |
|
struct bin_attribute *bin_attr, char *buf, |
|
loff_t off, size_t count) |
|
{ |
|
dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
|
"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", |
|
bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
return _read_reg(kobj_to_w1_slave(kobj), |
|
W1_F29_REG_ACTIVITY_LATCH_STATE, buf); |
|
} |
|
|
|
static ssize_t cond_search_mask_read(struct file *filp, struct kobject *kobj, |
|
struct bin_attribute *bin_attr, char *buf, |
|
loff_t off, size_t count) |
|
{ |
|
dev_dbg(&kobj_to_w1_slave(kobj)->dev, |
|
"Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", |
|
bin_attr->attr.name, kobj, (unsigned int)off, count, buf); |
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
return _read_reg(kobj_to_w1_slave(kobj), |
|
W1_F29_REG_COND_SEARCH_SELECT_MASK, buf); |
|
} |
|
|
|
static ssize_t cond_search_polarity_read(struct file *filp, |
|
struct kobject *kobj, |
|
struct bin_attribute *bin_attr, |
|
char *buf, loff_t off, size_t count) |
|
{ |
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
return _read_reg(kobj_to_w1_slave(kobj), |
|
W1_F29_REG_COND_SEARCH_POL_SELECT, buf); |
|
} |
|
|
|
static ssize_t status_control_read(struct file *filp, struct kobject *kobj, |
|
struct bin_attribute *bin_attr, char *buf, |
|
loff_t off, size_t count) |
|
{ |
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
return _read_reg(kobj_to_w1_slave(kobj), |
|
W1_F29_REG_CONTROL_AND_STATUS, buf); |
|
} |
|
|
|
#ifdef CONFIG_W1_SLAVE_DS2408_READBACK |
|
static bool optional_read_back_valid(struct w1_slave *sl, u8 expected) |
|
{ |
|
u8 w1_buf[3]; |
|
|
|
if (w1_reset_resume_command(sl->master)) |
|
return false; |
|
|
|
w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; |
|
w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE; |
|
w1_buf[2] = 0; |
|
|
|
w1_write_block(sl->master, w1_buf, 3); |
|
|
|
return (w1_read_8(sl->master) == expected); |
|
} |
|
#else |
|
static bool optional_read_back_valid(struct w1_slave *sl, u8 expected) |
|
{ |
|
return true; |
|
} |
|
#endif |
|
|
|
static ssize_t output_write(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_F29_RETRIES; |
|
ssize_t bytes_written = -EIO; |
|
|
|
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 out; |
|
|
|
do { |
|
w1_buf[0] = W1_F29_FUNC_CHANN_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_F29_SUCCESS_CONFIRM_BYTE && |
|
optional_read_back_valid(sl, *buf)) { |
|
bytes_written = 1; |
|
goto out; |
|
} |
|
|
|
if (w1_reset_resume_command(sl->master)) |
|
goto out; /* unrecoverable error */ |
|
/* try again, the slave is ready for a command */ |
|
} while (--retries); |
|
|
|
out: |
|
mutex_unlock(&sl->master->bus_mutex); |
|
|
|
dev_dbg(&sl->dev, "%s, mutex unlocked retries:%d\n", |
|
(bytes_written > 0) ? "succeeded" : "error", retries); |
|
|
|
return bytes_written; |
|
} |
|
|
|
|
|
/** |
|
* Writing to the activity file resets the activity latches. |
|
*/ |
|
static ssize_t activity_write(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); |
|
unsigned int retries = W1_F29_RETRIES; |
|
|
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
|
|
mutex_lock(&sl->master->bus_mutex); |
|
|
|
if (w1_reset_select_slave(sl)) |
|
goto error; |
|
|
|
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->bus_mutex); |
|
return 1; |
|
} |
|
if (w1_reset_resume_command(sl->master)) |
|
goto error; |
|
} |
|
|
|
error: |
|
mutex_unlock(&sl->master->bus_mutex); |
|
return -EIO; |
|
} |
|
|
|
static ssize_t status_control_write(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[4]; |
|
unsigned int retries = W1_F29_RETRIES; |
|
|
|
if (count != 1 || off != 0) |
|
return -EFAULT; |
|
|
|
mutex_lock(&sl->master->bus_mutex); |
|
|
|
if (w1_reset_select_slave(sl)) |
|
goto error; |
|
|
|
while (retries--) { |
|
w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG; |
|
w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; |
|
w1_buf[2] = 0; |
|
w1_buf[3] = *buf; |
|
|
|
w1_write_block(sl->master, w1_buf, 4); |
|
if (w1_reset_resume_command(sl->master)) |
|
goto error; |
|
|
|
w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS; |
|
w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS; |
|
w1_buf[2] = 0; |
|
|
|
w1_write_block(sl->master, w1_buf, 3); |
|
if (w1_read_8(sl->master) == *buf) { |
|
/* success! */ |
|
mutex_unlock(&sl->master->bus_mutex); |
|
return 1; |
|
} |
|
} |
|
error: |
|
mutex_unlock(&sl->master->bus_mutex); |
|
|
|
return -EIO; |
|
} |
|
|
|
/* |
|
* This is a special sequence we must do to ensure the P0 output is not stuck |
|
* in test mode. This is described in rev 2 of the ds2408's datasheet |
|
* (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under |
|
* "APPLICATION INFORMATION/Power-up timing". |
|
*/ |
|
static int w1_f29_disable_test_mode(struct w1_slave *sl) |
|
{ |
|
int res; |
|
u8 magic[10] = {0x96, }; |
|
u64 rn = le64_to_cpu(*((u64*)&sl->reg_num)); |
|
|
|
memcpy(&magic[1], &rn, 8); |
|
magic[9] = 0x3C; |
|
|
|
mutex_lock(&sl->master->bus_mutex); |
|
|
|
res = w1_reset_bus(sl->master); |
|
if (res) |
|
goto out; |
|
w1_write_block(sl->master, magic, ARRAY_SIZE(magic)); |
|
|
|
res = w1_reset_bus(sl->master); |
|
out: |
|
mutex_unlock(&sl->master->bus_mutex); |
|
return res; |
|
} |
|
|
|
static BIN_ATTR_RO(state, 1); |
|
static BIN_ATTR_RW(output, 1); |
|
static BIN_ATTR_RW(activity, 1); |
|
static BIN_ATTR_RO(cond_search_mask, 1); |
|
static BIN_ATTR_RO(cond_search_polarity, 1); |
|
static BIN_ATTR_RW(status_control, 1); |
|
|
|
static struct bin_attribute *w1_f29_bin_attrs[] = { |
|
&bin_attr_state, |
|
&bin_attr_output, |
|
&bin_attr_activity, |
|
&bin_attr_cond_search_mask, |
|
&bin_attr_cond_search_polarity, |
|
&bin_attr_status_control, |
|
NULL, |
|
}; |
|
|
|
static const struct attribute_group w1_f29_group = { |
|
.bin_attrs = w1_f29_bin_attrs, |
|
}; |
|
|
|
static const struct attribute_group *w1_f29_groups[] = { |
|
&w1_f29_group, |
|
NULL, |
|
}; |
|
|
|
static const struct w1_family_ops w1_f29_fops = { |
|
.add_slave = w1_f29_disable_test_mode, |
|
.groups = w1_f29_groups, |
|
}; |
|
|
|
static struct w1_family w1_family_29 = { |
|
.fid = W1_FAMILY_DS2408, |
|
.fops = &w1_f29_fops, |
|
}; |
|
module_w1_family(w1_family_29); |
|
|
|
MODULE_AUTHOR("Jean-Francois Dagenais <[email protected]>"); |
|
MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO"); |
|
MODULE_LICENSE("GPL"); |
|
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408));
|
|
|