#include <linux/crc16.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#define CRC16_INIT 0
#include <linux/w1.h>
#define W1_FAMILY_DS28E17 0x19
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jan Kandziora <jjj@gmx.de>");
MODULE_DESCRIPTION("w1 family 19 driver for DS28E17, 1-wire to I2C master bridge");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E17));
static int i2c_speed = 100;
module_param_named(speed, i2c_speed, int, 0600);
MODULE_PARM_DESC(speed, "Default I2C speed to be set when a DS28E17 is detected");
static char i2c_stretch = 1;
module_param_named(stretch, i2c_stretch, byte, 0600);
MODULE_PARM_DESC(stretch, "Default I2C stretch value to be set when a DS28E17 is detected");
#define W1_F19_WRITE_DATA_WITH_STOP 0x4B
#define W1_F19_WRITE_DATA_NO_STOP 0x5A
#define W1_F19_WRITE_DATA_ONLY 0x69
#define W1_F19_WRITE_DATA_ONLY_WITH_STOP 0x78
#define W1_F19_READ_DATA_WITH_STOP 0x87
#define W1_F19_WRITE_READ_DATA_WITH_STOP 0x2D
#define W1_F19_WRITE_CONFIGURATION 0xD2
#define W1_F19_READ_CONFIGURATION 0xE1
#define W1_F19_ENABLE_SLEEP_MODE 0x1E
#define W1_F19_READ_DEVICE_REVISION 0xC4
#define W1_F19_STATUS_CRC 0x01
#define W1_F19_STATUS_ADDRESS 0x02
#define W1_F19_STATUS_START 0x08
#define W1_F19_WRITE_DATA_LIMIT 255
#define W1_F19_READ_DATA_LIMIT 255
#define W1_F19_BUSY_TIMEBASES { 90, 23, 10 }
#define W1_F19_BUSY_GRATUITY 1000
#define W1_F19_BUSY_CHECKS 1000
struct w1_f19_data {
u8 speed;
u8 stretch;
struct i2c_adapter adapter;
};
static int w1_f19_i2c_busy_wait(struct w1_slave *sl, size_t count)
{
const unsigned long timebases[3] = W1_F19_BUSY_TIMEBASES;
struct w1_f19_data *data = sl->family_data;
unsigned int checks;
if (w1_touch_bit(sl->master, 1) == 0)
return 0;
usleep_range(timebases[data->speed] * (data->stretch) * count,
timebases[data->speed] * (data->stretch) * count
+ W1_F19_BUSY_GRATUITY);
checks = W1_F19_BUSY_CHECKS;
while ((checks--) > 0) {
if (w1_touch_bit(sl->master, 1) == 0)
return 0;
udelay(timebases[data->speed]);
}
dev_warn(&sl->dev, "busy timeout\n");
return -ETIMEDOUT;
}
static size_t w1_f19_error(struct w1_slave *sl, u8 w1_buf[])
{
if (w1_buf[0] & W1_F19_STATUS_CRC)
dev_warn(&sl->dev, "crc16 mismatch\n");
if (w1_buf[0] & W1_F19_STATUS_ADDRESS)
dev_warn(&sl->dev, "i2c device not responding\n");
if ((w1_buf[0] & (W1_F19_STATUS_CRC | W1_F19_STATUS_ADDRESS)) == 0
&& w1_buf[1] != 0) {
dev_warn(&sl->dev, "i2c short write, %d bytes not acknowledged\n",
w1_buf[1]);
}
if (w1_buf[0] & W1_F19_STATUS_ADDRESS)
return -ENXIO;
if (w1_buf[0] & W1_F19_STATUS_START)
return -EAGAIN;
if (w1_buf[0] != 0 || w1_buf[1] != 0)
return -EIO;
return 0;
}
static int __w1_f19_i2c_write(struct w1_slave *sl,
const u8 *command, size_t command_count,
const u8 *buffer, size_t count)
{
u16 crc;
int error;
u8 w1_buf[2];
crc = crc16(CRC16_INIT, command, command_count);
w1_write_block(sl->master, command, command_count);
w1_buf[0] = count;
crc = crc16(crc, w1_buf, 1);
w1_write_8(sl->master, w1_buf[0]);
crc = crc16(crc, buffer, count);
w1_write_block(sl->master, buffer, count);
w1_buf[0] = ~(crc & 0xFF);
w1_buf[1] = ~((crc >> 8) & 0xFF);
w1_write_block(sl->master, w1_buf, 2);
if (w1_f19_i2c_busy_wait(sl, count + 1) < 0)
return -ETIMEDOUT;
w1_read_block(sl->master, w1_buf, 2);
error = w1_f19_error(sl, w1_buf);
if (error < 0)
return error;
return count;
}
static int w1_f19_i2c_write(struct w1_slave *sl, u16 i2c_address,
const u8 *buffer, size_t count, bool stop)
{
int result;
int remaining = count;
const u8 *p;
u8 command[2];
if (count == 0)
return -EOPNOTSUPP;
if (count <= W1_F19_WRITE_DATA_LIMIT) {
command[0] = (stop ? W1_F19_WRITE_DATA_WITH_STOP
: W1_F19_WRITE_DATA_NO_STOP);
command[1] = i2c_address << 1;
result = __w1_f19_i2c_write(sl, command, 2, buffer, count);
} else {
p = buffer;
command[0] = W1_F19_WRITE_DATA_NO_STOP;
command[1] = i2c_address << 1;
result = __w1_f19_i2c_write(sl, command, 2, p,
W1_F19_WRITE_DATA_LIMIT);
if (result < 0)
return result;
if (w1_reset_resume_command(sl->master))
return -EIO;
p += W1_F19_WRITE_DATA_LIMIT;
remaining -= W1_F19_WRITE_DATA_LIMIT;
while (remaining > W1_F19_WRITE_DATA_LIMIT) {
command[0] = W1_F19_WRITE_DATA_ONLY;
result = __w1_f19_i2c_write(sl, command, 1, p,
W1_F19_WRITE_DATA_LIMIT);
if (result < 0)
return result;
if (w1_reset_resume_command(sl->master))
return -EIO;
p += W1_F19_WRITE_DATA_LIMIT;
remaining -= W1_F19_WRITE_DATA_LIMIT;
}
command[0] = (stop ? W1_F19_WRITE_DATA_ONLY_WITH_STOP
: W1_F19_WRITE_DATA_ONLY);
result = __w1_f19_i2c_write(sl, command, 1, p, remaining);
}
return result;
}
static int w1_f19_i2c_read(struct w1_slave *sl, u16 i2c_address,
u8 *buffer, size_t count)
{
u16 crc;
int error;
u8 w1_buf[5];
if (count == 0)
return -EOPNOTSUPP;
w1_buf[0] = W1_F19_READ_DATA_WITH_STOP;
w1_buf[1] = i2c_address << 1 | 0x01;
w1_buf[2] = count;
crc = crc16(CRC16_INIT, w1_buf, 3);
w1_buf[3] = ~(crc & 0xFF);
w1_buf[4] = ~((crc >> 8) & 0xFF);
w1_write_block(sl->master, w1_buf, 5);
if (w1_f19_i2c_busy_wait(sl, count + 1) < 0)
return -ETIMEDOUT;
w1_buf[0] = w1_read_8(sl->master);
w1_buf[1] = 0;
error = w1_f19_error(sl, w1_buf);
if (error < 0)
return error;
return w1_read_block(sl->master, buffer, count);
}
static int w1_f19_i2c_write_read(struct w1_slave *sl, u16 i2c_address,
const u8 *wbuffer, size_t wcount, u8 *rbuffer, size_t rcount)
{
u16 crc;
int error;
u8 w1_buf[3];
if (wcount == 0 || rcount == 0)
return -EOPNOTSUPP;
w1_buf[0] = W1_F19_WRITE_READ_DATA_WITH_STOP;
w1_buf[1] = i2c_address << 1;
w1_buf[2] = wcount;
crc = crc16(CRC16_INIT, w1_buf, 3);
w1_write_block(sl->master, w1_buf, 3);
crc = crc16(crc, wbuffer, wcount);
w1_write_block(sl->master, wbuffer, wcount);
w1_buf[0] = rcount;
crc = crc16(crc, w1_buf, 1);
w1_buf[1] = ~(crc & 0xFF);
w1_buf[2] = ~((crc >> 8) & 0xFF);
w1_write_block(sl->master, w1_buf, 3);
if (w1_f19_i2c_busy_wait(sl, wcount + rcount + 2) < 0)
return -ETIMEDOUT;
w1_read_block(sl->master, w1_buf, 2);
error = w1_f19_error(sl, w1_buf);
if (error < 0)
return error;
return w1_read_block(sl->master, rbuffer, rcount);
}
static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
struct w1_slave *sl = (struct w1_slave *) adapter->algo_data;
int i = 0;
int result = 0;
mutex_lock(&sl->master->bus_mutex);
if (w1_reset_select_slave(sl)) {
i = -EIO;
goto error;
}
while (i < num) {
if (i < (num-1)
&& msgs[i].addr == msgs[i+1].addr
&& !(msgs[i].flags & I2C_M_RD)
&& (msgs[i+1].flags & I2C_M_RD)
&& (msgs[i].len <= W1_F19_WRITE_DATA_LIMIT)) {
result = w1_f19_i2c_write_read(sl, msgs[i].addr,
msgs[i].buf, msgs[i].len,
msgs[i+1].buf, msgs[i+1].len);
if (result < 0) {
i = result;
goto error;
}
if (msgs[i+1].flags & I2C_M_RECV_LEN) {
result = w1_f19_i2c_read(sl, msgs[i+1].addr,
&(msgs[i+1].buf[1]), msgs[i+1].buf[0]);
if (result < 0) {
i = result;
goto error;
}
}
i++;
} else if (msgs[i].flags & I2C_M_RD) {
result = w1_f19_i2c_read(sl, msgs[i].addr,
msgs[i].buf, msgs[i].len);
if (result < 0) {
i = result;
goto error;
}
if (msgs[i].flags & I2C_M_RECV_LEN) {
result = w1_f19_i2c_read(sl,
msgs[i].addr,
&(msgs[i].buf[1]),
msgs[i].buf[0]);
if (result < 0) {
i = result;
goto error;
}
}
} else {
result = w1_f19_i2c_write(sl,
msgs[i].addr,
msgs[i].buf,
msgs[i].len,
i == (num-1));
if (result < 0) {
i = result;
goto error;
}
}
i++;
if (i < num) {
if (w1_reset_resume_command(sl->master)) {
i = -EIO;
goto error;
}
}
}
error:
mutex_unlock(&sl->master->bus_mutex);
return i;
}
static u32 w1_f19_i2c_functionality(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK |
I2C_FUNC_SMBUS_PEC;
}
static const struct i2c_adapter_quirks w1_f19_i2c_adapter_quirks = {
.max_read_len = W1_F19_READ_DATA_LIMIT,
};
static const struct i2c_algorithm w1_f19_i2c_algorithm = {
.master_xfer = w1_f19_i2c_master_transfer,
.functionality = w1_f19_i2c_functionality,
};
static int w1_f19_get_i2c_speed(struct w1_slave *sl)
{
struct w1_f19_data *data = sl->family_data;
int result = -EIO;
mutex_lock(&sl->master->bus_mutex);
if (w1_reset_select_slave(sl))
goto error;
w1_write_8(sl->master, W1_F19_READ_CONFIGURATION);
result = w1_read_8(sl->master);
if (result < 0 || result > 2) {
result = -EIO;
goto error;
}
data->speed = result;
error:
mutex_unlock(&sl->master->bus_mutex);
return result;
}
static int __w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed)
{
struct w1_f19_data *data = sl->family_data;
const int i2c_speeds[3] = { 100, 400, 900 };
u8 w1_buf[2];
if (w1_reset_select_slave(sl))
return -EIO;
w1_buf[0] = W1_F19_WRITE_CONFIGURATION;
w1_buf[1] = speed;
w1_write_block(sl->master, w1_buf, 2);
data->speed = speed;
dev_info(&sl->dev, "i2c speed set to %d kBaud\n", i2c_speeds[speed]);
return 0;
}
static int w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed)
{
int result;
mutex_lock(&sl->master->bus_mutex);
result = __w1_f19_set_i2c_speed(sl, speed);
mutex_unlock(&sl->master->bus_mutex);
return result;
}
static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct w1_slave *sl = dev_to_w1_slave(dev);
int result;
result = w1_f19_get_i2c_speed(sl);
if (result < 0)
return result;
return sprintf(buf, "%d\n", result);
}
static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct w1_slave *sl = dev_to_w1_slave(dev);
int error;
if (count < 3 || count > 4 || !buf)
return -EINVAL;
if (count == 4 && buf[3] != '\n')
return -EINVAL;
if (buf[1] != '0' || buf[2] != '0')
return -EINVAL;
switch (buf[0]) {
case '1':
error = w1_f19_set_i2c_speed(sl, 0);
break;
case '4':
error = w1_f19_set_i2c_speed(sl, 1);
break;
case '9':
error = w1_f19_set_i2c_speed(sl, 2);
break;
default:
return -EINVAL;
}
if (error < 0)
return error;
return count;
}
static DEVICE_ATTR_RW(speed);
static ssize_t stretch_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct w1_slave *sl = dev_to_w1_slave(dev);
struct w1_f19_data *data = sl->family_data;
return sprintf(buf, "%d\n", data->stretch);
}
static ssize_t stretch_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct w1_slave *sl = dev_to_w1_slave(dev);
struct w1_f19_data *data = sl->family_data;
if (count < 1 || count > 2 || !buf)
return -EINVAL;
if (count == 2 && buf[1] != '\n')
return -EINVAL;
if (buf[0] < '1' || buf[0] > '9')
return -EINVAL;
data->stretch = buf[0] & 0x0F;
return count;
}
static DEVICE_ATTR_RW(stretch);
static struct attribute *w1_f19_attrs[] = {
&dev_attr_speed.attr,
&dev_attr_stretch.attr,
NULL,
};
static const struct attribute_group w1_f19_group = {
.attrs = w1_f19_attrs,
};
static const struct attribute_group *w1_f19_groups[] = {
&w1_f19_group,
NULL,
};
static int w1_f19_add_slave(struct w1_slave *sl)
{
struct w1_f19_data *data = NULL;
data = devm_kzalloc(&sl->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
sl->family_data = data;
switch (i2c_speed) {
case 100:
__w1_f19_set_i2c_speed(sl, 0);
break;
case 400:
__w1_f19_set_i2c_speed(sl, 1);
break;
case 900:
__w1_f19_set_i2c_speed(sl, 2);
break;
default:
data->speed = 1;
}
data->stretch = i2c_stretch;
data->adapter.owner = THIS_MODULE;
data->adapter.algo = &w1_f19_i2c_algorithm;
data->adapter.algo_data = sl;
strcpy(data->adapter.name, "w1-");
strcat(data->adapter.name, sl->name);
data->adapter.dev.parent = &sl->dev;
data->adapter.quirks = &w1_f19_i2c_adapter_quirks;
return i2c_add_adapter(&data->adapter);
}
static void w1_f19_remove_slave(struct w1_slave *sl)
{
struct w1_f19_data *family_data = sl->family_data;
i2c_del_adapter(&family_data->adapter);
devm_kfree(&sl->dev, family_data);
sl->family_data = NULL;
}
static const struct w1_family_ops w1_f19_fops = {
.add_slave = w1_f19_add_slave,
.remove_slave = w1_f19_remove_slave,
.groups = w1_f19_groups,
};
static struct w1_family w1_family_19 = {
.fid = W1_FAMILY_DS28E17,
.fops = &w1_f19_fops,
};
module_w1_family