Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# 1-wire slaves configuration
#
menu "1-wire Slaves"
config W1_SLAVE_THERM
tristate "Thermal family implementation"
help
Say Y here if you want to connect 1-wire thermal sensors to your
wire.
config W1_SLAVE_SMEM
tristate "Simple 64bit memory family implementation"
help
Say Y here if you want to connect 1-wire
simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
config W1_SLAVE_DS2405
tristate "DS2405 Addressable Switch"
help
Say Y or M here if you want to use a DS2405 1-wire
single-channel addressable switch.
This device can also work as a single-channel
binary remote sensor.
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
DS2408 8-Channel Addressable Switch device support
config W1_SLAVE_DS2408_READBACK
bool "Read-back values written to DS2408's output register"
depends on W1_SLAVE_DS2408
default y
help
Enabling this will cause the driver to read back the values written
to the chip's output register in order to detect errors.
This is slower but useful when debugging chips and/or busses.
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_DS2406
tristate "Dual Channel Addressable Switch 0x12 family support (DS2406)"
select CRC16
help
Say Y or M here if you want to use a 1-wire
DS2406 Dual Channel Addressable Switch. EPROM read/write
support for these devices is not implemented.
config W1_SLAVE_DS2423
tristate "Counter 1-wire device (DS2423)"
select CRC16
help
If you enable this you can read the counter values available
in the DS2423 chipset from the w1_slave file under the
sys file system.
Say Y here if you want to use a 1-wire
counter family device (DS2423).
config W1_SLAVE_DS2805
tristate "112-byte EEPROM support (DS28E05)"
help
Say Y here if you want to use a 1-wire
is a 112-byte user-programmable EEPROM is
organized as 7 pages of 16 bytes each with 64bit
unique number. Requires OverDrive Speed to talk to.
config W1_SLAVE_DS2430
tristate "256b EEPROM family support (DS2430)"
help
Say Y here if you want to use a 1-wire 256bit EEPROM
family device (DS2430).
This EEPROM is organized as one page of 32 bytes for random
access.
config W1_SLAVE_DS2431
tristate "1kb EEPROM family support (DS2431)"
help
Say Y here if you want to use a 1-wire
1kb EEPROM family device (DS2431)
config W1_SLAVE_DS2433
tristate "4kb EEPROM family support (DS2433)"
help
Say Y here if you want to use a 1-wire
4kb EEPROM family device (DS2433).
config W1_SLAVE_DS2433_CRC
bool "Protect DS2433 data with a CRC16"
depends on W1_SLAVE_DS2433
select CRC16
help
Say Y here to protect DS2433 data with a CRC16.
Each block has 30 bytes of data and a two byte CRC16.
Full block writes are only allowed if the CRC is valid.
config W1_SLAVE_DS2438
tristate "DS2438 Smart Battery Monitor 0x26 family support"
help
Say Y here if you want to use a 1-wire
DS2438 Smart Battery Monitor device support
config W1_SLAVE_DS250X
tristate "512b/1kb/16kb EPROM family support"
select CRC16
help
Say Y here if you want to use a 1-wire
512b/1kb/16kb EPROM family device (DS250x).
config W1_SLAVE_DS2780
tristate "Dallas 2780 battery monitor chip"
help
If you enable this you will have the DS2780 battery monitor
chip support.
The battery monitor chip is used in many batteries/devices
as the one who is responsible for charging/discharging/monitoring
Li+ batteries.
If you are unsure, say N.
config W1_SLAVE_DS2781
tristate "Dallas 2781 battery monitor chip"
help
If you enable this you will have the DS2781 battery monitor
chip support.
The battery monitor chip is used in many batteries/devices
as the one who is responsible for charging/discharging/monitoring
Li+ batteries.
If you are unsure, say N.
config W1_SLAVE_DS28E04
tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)"
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_DS28E17
tristate "1-wire-to-I2C master bridge (DS28E17)"
select CRC16
depends on I2C
help
Say Y here if you want to use the DS28E17 1-wire-to-I2C master bridge.
For each DS28E17 detected, a new I2C adapter is created within the
kernel. I2C devices on that bus can be configured to be used by the
kernel and userspace tools as on any other "native" I2C bus.
This driver is also available as a module. If so, the module
will be called w1_ds28e17.
If you are unsure, say N.
endmenu
+22
View File
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Dallas's 1-wire slaves.
#
obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
obj-$(CONFIG_W1_SLAVE_DS2405) += w1_ds2405.o
obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o
obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o
obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o
obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o
obj-$(CONFIG_W1_SLAVE_DS2430) += w1_ds2430.o
obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2805) += w1_ds2805.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o
obj-$(CONFIG_W1_SLAVE_DS250X) += w1_ds250x.o
obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o
obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o
obj-$(CONFIG_W1_SLAVE_DS28E17) += w1_ds28e17.o
+218
View File
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* w1_ds2405.c
*
* Copyright (c) 2017 Maciej S. Szmigiero <mail@maciej.szmigiero.name>
* Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/w1.h>
#define W1_FAMILY_DS2405 0x05
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>");
MODULE_DESCRIPTION("Driver for 1-wire Dallas DS2405 PIO.");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2405));
static int w1_ds2405_select(struct w1_slave *sl, bool only_active)
{
struct w1_master *dev = sl->master;
u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num);
unsigned int bit_ctr;
if (w1_reset_bus(dev) != 0)
return 0;
/*
* We cannot use a normal Match ROM command
* since doing so would toggle PIO state
*/
w1_write_8(dev, only_active ? W1_ALARM_SEARCH : W1_SEARCH);
for (bit_ctr = 0; bit_ctr < 64; bit_ctr++) {
int bit2send = !!(dev_addr & BIT(bit_ctr));
u8 ret;
ret = w1_triplet(dev, bit2send);
if ((ret & (BIT(0) | BIT(1))) ==
(BIT(0) | BIT(1))) /* no devices found */
return 0;
if (!!(ret & BIT(2)) != bit2send)
/* wrong direction taken - no such device */
return 0;
}
return 1;
}
static int w1_ds2405_read_pio(struct w1_slave *sl)
{
if (w1_ds2405_select(sl, true))
return 0; /* "active" means PIO is low */
if (w1_ds2405_select(sl, false))
return 1;
return -ENODEV;
}
static ssize_t state_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
int ret;
ssize_t f_retval;
u8 state;
ret = mutex_lock_interruptible(&dev->bus_mutex);
if (ret)
return ret;
if (!w1_ds2405_select(sl, false)) {
f_retval = -ENODEV;
goto out_unlock;
}
state = w1_read_8(dev);
if (state != 0 &&
state != 0xff) {
dev_err(device, "non-consistent state %x\n", state);
f_retval = -EIO;
goto out_unlock;
}
*buf = state ? '1' : '0';
f_retval = 1;
out_unlock:
w1_reset_bus(dev);
mutex_unlock(&dev->bus_mutex);
return f_retval;
}
static ssize_t output_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
int ret;
ssize_t f_retval;
ret = mutex_lock_interruptible(&dev->bus_mutex);
if (ret)
return ret;
ret = w1_ds2405_read_pio(sl);
if (ret < 0) {
f_retval = ret;
goto out_unlock;
}
*buf = ret ? '1' : '0';
f_retval = 1;
out_unlock:
w1_reset_bus(dev);
mutex_unlock(&dev->bus_mutex);
return f_retval;
}
static ssize_t output_store(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
int ret, current_pio;
unsigned int val;
ssize_t f_retval;
if (count < 1)
return -EINVAL;
if (sscanf(buf, " %u%n", &val, &ret) < 1)
return -EINVAL;
if (val != 0 && val != 1)
return -EINVAL;
f_retval = ret;
ret = mutex_lock_interruptible(&dev->bus_mutex);
if (ret)
return ret;
current_pio = w1_ds2405_read_pio(sl);
if (current_pio < 0) {
f_retval = current_pio;
goto out_unlock;
}
if (current_pio == val)
goto out_unlock;
if (w1_reset_bus(dev) != 0) {
f_retval = -ENODEV;
goto out_unlock;
}
/*
* can't use w1_reset_select_slave() here since it uses Skip ROM if
* there is only one device on bus
*/
do {
u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num);
u8 cmd[9];
cmd[0] = W1_MATCH_ROM;
memcpy(&cmd[1], &dev_addr, sizeof(dev_addr));
w1_write_block(dev, cmd, sizeof(cmd));
} while (0);
out_unlock:
w1_reset_bus(dev);
mutex_unlock(&dev->bus_mutex);
return f_retval;
}
static DEVICE_ATTR_RO(state);
static DEVICE_ATTR_RW(output);
static struct attribute *w1_ds2405_attrs[] = {
&dev_attr_state.attr,
&dev_attr_output.attr,
NULL
};
ATTRIBUTE_GROUPS(w1_ds2405);
static const struct w1_family_ops w1_ds2405_fops = {
.groups = w1_ds2405_groups
};
static struct w1_family w1_family_ds2405 = {
.fid = W1_FAMILY_DS2405,
.fops = &w1_ds2405_fops
};
module_w1_family(w1_family_ds2405);
+147
View File
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds2406.c - w1 family 12 (DS2406) driver
* based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net>
*
* Copyright (c) 2014 Scott Alfter <scott@alfter.us>
*/
#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/crc16.h>
#include <linux/w1.h>
#define W1_FAMILY_DS2406 0x12
#define W1_F12_FUNC_READ_STATUS 0xAA
#define W1_F12_FUNC_WRITE_STATUS 0x55
static ssize_t w1_f12_read_state(
struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
u8 w1_buf[6] = {W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0};
struct w1_slave *sl = kobj_to_w1_slave(kobj);
ssize_t rtnval = 1;
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (w1_reset_select_slave(sl)) {
mutex_unlock(&sl->master->bus_mutex);
return -EIO;
}
w1_write_block(sl->master, w1_buf, 3);
w1_read_block(sl->master, w1_buf+3, 3);
if (crc16(0, w1_buf, sizeof(w1_buf)) == 0xb001) /* good read? */
*buf = ((w1_buf[3]>>5)&3)|0x30;
else
rtnval = -EIO;
mutex_unlock(&sl->master->bus_mutex);
return rtnval;
}
static ssize_t w1_f12_write_output(
struct file *filp, struct kobject *kobj,
const 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[6] = {W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0};
ssize_t rtnval = 1;
if (count != 1 || off != 0)
return -EFAULT;
mutex_lock(&sl->master->bus_mutex);
if (w1_reset_select_slave(sl)) {
mutex_unlock(&sl->master->bus_mutex);
return -EIO;
}
w1_buf[3] = (((*buf)&3)<<5)|0x1F;
w1_write_block(sl->master, w1_buf, 4);
w1_read_block(sl->master, w1_buf+4, 2);
if (crc16(0, w1_buf, sizeof(w1_buf)) == 0xb001) /* good read? */
w1_write_8(sl->master, 0xFF);
else
rtnval = -EIO;
mutex_unlock(&sl->master->bus_mutex);
return rtnval;
}
#define NB_SYSFS_BIN_FILES 2
static const struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
{
.attr = {
.name = "state",
.mode = 0444,
},
.size = 1,
.read = w1_f12_read_state,
},
{
.attr = {
.name = "output",
.mode = 0664,
},
.size = 1,
.write = w1_f12_write_output,
}
};
static int w1_f12_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_f12_sysfs_bin_files[i]));
if (err)
while (--i >= 0)
sysfs_remove_bin_file(&sl->dev.kobj,
&(w1_f12_sysfs_bin_files[i]));
return err;
}
static void w1_f12_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_f12_sysfs_bin_files[i]));
}
static const struct w1_family_ops w1_f12_fops = {
.add_slave = w1_f12_add_slave,
.remove_slave = w1_f12_remove_slave,
};
static struct w1_family w1_family_12 = {
.fid = W1_FAMILY_DS2406,
.fops = &w1_f12_fops,
};
module_w1_family(w1_family_12);
MODULE_AUTHOR("Scott Alfter <scott@alfter.us>");
MODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO");
MODULE_LICENSE("GPL");
+353
View File
@@ -0,0 +1,353 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds2408.c - w1 family 29 (DS2408) driver
*
* Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com>
*/
#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,
const 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,
const 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,
const 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,
const 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,
const 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,
const 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,
const 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,
const 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,
const 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 const BIN_ATTR_RO(state, 1);
static const BIN_ATTR_RW(output, 1);
static const BIN_ATTR_RW(activity, 1);
static const BIN_ATTR_RO(cond_search_mask, 1);
static const BIN_ATTR_RO(cond_search_polarity, 1);
static const BIN_ATTR_RW(status_control, 1);
static const struct bin_attribute *const 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 <dagenaisj@sonatest.com>");
MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2408));
+161
View File
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*/
#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_DS2413 0x3A
#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
#define W1_F3A_INVALID_PIO_STATE 0xFF
static ssize_t state_read(struct file *filp, struct kobject *kobj,
const 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_F3A_RETRIES;
ssize_t bytes_read = -EIO;
u8 state;
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");
next:
if (w1_reset_select_slave(sl))
goto out;
while (retries--) {
w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ);
state = w1_read_8(sl->master);
if ((state & 0x0F) == ((~state >> 4) & 0x0F)) {
/* complement is correct */
*buf = state;
bytes_read = 1;
goto out;
} else if (state == W1_F3A_INVALID_PIO_STATE) {
/* slave didn't respond, try to select it again */
dev_warn(&sl->dev, "slave device did not respond to PIO_ACCESS_READ, " \
"reselecting, retries left: %d\n", retries);
goto next;
}
if (w1_reset_resume_command(sl->master))
goto out; /* unrecoverable error */
dev_warn(&sl->dev, "PIO_ACCESS_READ error, retries left: %d\n", retries);
}
out:
mutex_unlock(&sl->master->bus_mutex);
dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n",
(bytes_read > 0) ? "succeeded" : "error", retries);
return bytes_read;
}
static const BIN_ATTR_RO(state, 1);
static ssize_t output_write(struct file *filp, struct kobject *kobj,
const 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;
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;
/*
* 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) {
bytes_written = 1;
goto out;
}
if (w1_reset_resume_command(sl->master))
goto out; /* unrecoverable error */
dev_warn(&sl->dev, "PIO_ACCESS_WRITE error, retries left: %d\n", 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;
}
static const BIN_ATTR(output, 0664, NULL, output_write, 1);
static const struct bin_attribute *const w1_f3a_bin_attrs[] = {
&bin_attr_state,
&bin_attr_output,
NULL,
};
static const struct attribute_group w1_f3a_group = {
.bin_attrs = w1_f3a_bin_attrs,
};
static const struct attribute_group *w1_f3a_groups[] = {
&w1_f3a_group,
NULL,
};
static const struct w1_family_ops w1_f3a_fops = {
.groups = w1_f3a_groups,
};
static struct w1_family w1_family_3a = {
.fid = W1_FAMILY_DS2413,
.fops = &w1_f3a_fops,
};
module_w1_family(w1_family_3a);
MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
MODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413));
+133
View File
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* w1_ds2423.c
*
* Copyright (c) 2010 Mika Laitio <lamikr@pilppa.org>
*
* This driver will read and write the value of 4 counters to w1_slave file in
* sys filesystem.
* Inspired by the w1_therm and w1_ds2431 drivers.
*/
#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/crc16.h>
#include <linux/w1.h>
#define W1_COUNTER_DS2423 0x1D
#define CRC16_VALID 0xb001
#define CRC16_INIT 0
#define COUNTER_COUNT 4
#define READ_BYTE_COUNT 42
static ssize_t w1_slave_show(struct device *device,
struct device_attribute *attr, char *out_buf)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
u8 rbuf[COUNTER_COUNT * READ_BYTE_COUNT];
u8 wrbuf[3];
int rom_addr;
int read_byte_count;
int result;
ssize_t c;
int ii;
int p;
int crc;
c = PAGE_SIZE;
rom_addr = (12 << 5) + 31;
wrbuf[0] = 0xA5;
wrbuf[1] = rom_addr & 0xFF;
wrbuf[2] = rom_addr >> 8;
mutex_lock(&dev->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_block(dev, wrbuf, 3);
read_byte_count = 0;
for (p = 0; p < 4; p++) {
/*
* 1 byte for first bytes in ram page read
* 4 bytes for counter
* 4 bytes for zero bits
* 2 bytes for crc
* 31 remaining bytes from the ram page
*/
read_byte_count += w1_read_block(dev,
rbuf + (p * READ_BYTE_COUNT), READ_BYTE_COUNT);
for (ii = 0; ii < READ_BYTE_COUNT; ++ii)
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "%02x ",
rbuf[(p * READ_BYTE_COUNT) + ii]);
if (read_byte_count != (p + 1) * READ_BYTE_COUNT) {
dev_warn(device,
"w1_counter_read() returned %u bytes "
"instead of %d bytes wanted.\n",
read_byte_count,
READ_BYTE_COUNT);
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "crc=NO\n");
} else {
if (p == 0) {
crc = crc16(CRC16_INIT, wrbuf, 3);
crc = crc16(crc, rbuf, 11);
} else {
/*
* DS2423 calculates crc from all bytes
* read after the previous crc bytes.
*/
crc = crc16(CRC16_INIT,
(rbuf + 11) +
((p - 1) * READ_BYTE_COUNT),
READ_BYTE_COUNT);
}
if (crc == CRC16_VALID) {
result = 0;
for (ii = 4; ii > 0; ii--) {
result <<= 8;
result |= rbuf[(p *
READ_BYTE_COUNT) + ii];
}
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "crc=YES c=%d\n", result);
} else {
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "crc=NO\n");
}
}
}
} else {
c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error");
}
mutex_unlock(&dev->bus_mutex);
return PAGE_SIZE - c;
}
static DEVICE_ATTR_RO(w1_slave);
static struct attribute *w1_f1d_attrs[] = {
&dev_attr_w1_slave.attr,
NULL,
};
ATTRIBUTE_GROUPS(w1_f1d);
static const struct w1_family_ops w1_f1d_fops = {
.groups = w1_f1d_groups,
};
static struct w1_family w1_family_1d = {
.fid = W1_COUNTER_DS2423,
.fops = &w1_f1d_fops,
};
module_w1_family(w1_family_1d);
MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>");
MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_COUNTER_DS2423));
+295
View File
@@ -0,0 +1,295 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds2430.c - w1 family 14 (DS2430) driver
**
* Copyright (c) 2019 Angelo Dureghello <angelo.dureghello@timesys.com>
*
* Cloned and modified from ds2431
* Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
*
*/
#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/w1.h>
#define W1_EEPROM_DS2430 0x14
#define W1_F14_EEPROM_SIZE 32
#define W1_F14_PAGE_COUNT 1
#define W1_F14_PAGE_BITS 5
#define W1_F14_PAGE_SIZE (1 << W1_F14_PAGE_BITS)
#define W1_F14_PAGE_MASK 0x1F
#define W1_F14_SCRATCH_BITS 5
#define W1_F14_SCRATCH_SIZE (1 << W1_F14_SCRATCH_BITS)
#define W1_F14_SCRATCH_MASK (W1_F14_SCRATCH_SIZE-1)
#define W1_F14_READ_EEPROM 0xF0
#define W1_F14_WRITE_SCRATCH 0x0F
#define W1_F14_READ_SCRATCH 0xAA
#define W1_F14_COPY_SCRATCH 0x55
#define W1_F14_VALIDATION_KEY 0xa5
#define W1_F14_TPROG_MS 11
#define W1_F14_READ_RETRIES 10
#define W1_F14_READ_MAXLEN W1_F14_SCRATCH_SIZE
/*
* 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_f14_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;
}
/*
* Read a block from W1 ROM two times and compares the results.
* If they are equal they are returned, otherwise the read
* is repeated W1_F14_READ_RETRIES times.
*
* count must not exceed W1_F14_READ_MAXLEN.
*/
static int w1_f14_readblock(struct w1_slave *sl, int off, int count, char *buf)
{
u8 wrbuf[2];
u8 cmp[W1_F14_READ_MAXLEN];
int tries = W1_F14_READ_RETRIES;
do {
wrbuf[0] = W1_F14_READ_EEPROM;
wrbuf[1] = off & 0xff;
if (w1_reset_select_slave(sl))
return -1;
w1_write_block(sl->master, wrbuf, 2);
w1_read_block(sl->master, buf, count);
if (w1_reset_select_slave(sl))
return -1;
w1_write_block(sl->master, wrbuf, 2);
w1_read_block(sl->master, cmp, count);
if (!memcmp(cmp, buf, count))
return 0;
} while (--tries);
dev_err(&sl->dev, "proof reading failed %d times\n",
W1_F14_READ_RETRIES);
return -1;
}
static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int todo = count;
count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE);
if (count == 0)
return 0;
mutex_lock(&sl->master->bus_mutex);
/* read directly from the EEPROM in chunks of W1_F14_READ_MAXLEN */
while (todo > 0) {
int block_read;
if (todo >= W1_F14_READ_MAXLEN)
block_read = W1_F14_READ_MAXLEN;
else
block_read = todo;
if (w1_f14_readblock(sl, off, block_read, buf) < 0)
count = -EIO;
todo -= W1_F14_READ_MAXLEN;
buf += W1_F14_READ_MAXLEN;
off += W1_F14_READ_MAXLEN;
}
mutex_unlock(&sl->master->bus_mutex);
return count;
}
/*
* Writes to the scratchpad and reads it back for verification.
* Then copies the scratchpad to EEPROM.
* The data must be aligned at W1_F14_SCRATCH_SIZE bytes and
* must be W1_F14_SCRATCH_SIZE bytes long.
* The master must be locked.
*
* @param sl The slave structure
* @param addr Address for the write
* @param len length must be <= (W1_F14_PAGE_SIZE - (addr & W1_F14_PAGE_MASK))
* @param data The data to write
* @return 0=Success -1=failure
*/
static int w1_f14_write(struct w1_slave *sl, int addr, int len, const u8 *data)
{
int tries = W1_F14_READ_RETRIES;
u8 wrbuf[2];
u8 rdbuf[W1_F14_SCRATCH_SIZE + 3];
retry:
/* Write the data to the scratchpad */
if (w1_reset_select_slave(sl))
return -1;
wrbuf[0] = W1_F14_WRITE_SCRATCH;
wrbuf[1] = addr & 0xff;
w1_write_block(sl->master, wrbuf, 2);
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_F14_READ_SCRATCH);
w1_read_block(sl->master, rdbuf, len + 2);
/*
* Compare what was read against the data written
* Note: on read scratchpad, device returns 2 bulk 0xff bytes,
* to be discarded.
*/
if ((memcmp(data, &rdbuf[2], len) != 0)) {
if (--tries)
goto retry;
dev_err(&sl->dev,
"could not write to eeprom, scratchpad compare failed %d times\n",
W1_F14_READ_RETRIES);
return -1;
}
/* Copy the scratchpad to EEPROM */
if (w1_reset_select_slave(sl))
return -1;
wrbuf[0] = W1_F14_COPY_SCRATCH;
wrbuf[1] = W1_F14_VALIDATION_KEY;
w1_write_block(sl->master, wrbuf, 2);
/* Sleep for tprog ms to wait for the write to complete */
msleep(W1_F14_TPROG_MS);
/* Reset the bus to wake up the EEPROM */
w1_reset_bus(sl->master);
return 0;
}
static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
const 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;
int copy;
count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE);
if (count == 0)
return 0;
mutex_lock(&sl->master->bus_mutex);
/* Can only write data in blocks of the size of the scratchpad */
addr = off;
len = count;
while (len > 0) {
/* if len too short or addr not aligned */
if (len < W1_F14_SCRATCH_SIZE || addr & W1_F14_SCRATCH_MASK) {
char tmp[W1_F14_SCRATCH_SIZE];
/* read the block and update the parts to be written */
if (w1_f14_readblock(sl, addr & ~W1_F14_SCRATCH_MASK,
W1_F14_SCRATCH_SIZE, tmp)) {
count = -EIO;
goto out_up;
}
/* copy at most to the boundary of the PAGE or len */
copy = W1_F14_SCRATCH_SIZE -
(addr & W1_F14_SCRATCH_MASK);
if (copy > len)
copy = len;
memcpy(&tmp[addr & W1_F14_SCRATCH_MASK], buf, copy);
if (w1_f14_write(sl, addr & ~W1_F14_SCRATCH_MASK,
W1_F14_SCRATCH_SIZE, tmp) < 0) {
count = -EIO;
goto out_up;
}
} else {
copy = W1_F14_SCRATCH_SIZE;
if (w1_f14_write(sl, addr, copy, buf) < 0) {
count = -EIO;
goto out_up;
}
}
buf += copy;
addr += copy;
len -= copy;
}
out_up:
mutex_unlock(&sl->master->bus_mutex);
return count;
}
static const BIN_ATTR_RW(eeprom, W1_F14_EEPROM_SIZE);
static const struct bin_attribute *const w1_f14_bin_attrs[] = {
&bin_attr_eeprom,
NULL,
};
static const struct attribute_group w1_f14_group = {
.bin_attrs = w1_f14_bin_attrs,
};
static const struct attribute_group *w1_f14_groups[] = {
&w1_f14_group,
NULL,
};
static const struct w1_family_ops w1_f14_fops = {
.groups = w1_f14_groups,
};
static struct w1_family w1_family_14 = {
.fid = W1_EEPROM_DS2430,
.fops = &w1_f14_fops,
};
module_w1_family(w1_family_14);
MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
MODULE_DESCRIPTION("w1 family 14 driver for DS2430, 256b EEPROM");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2430));
+294
View File
@@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds2431.c - w1 family 2d (DS2431) driver
*
* Copyright (c) 2008 Bernhard Weirich <bernhard.weirich@riedel.net>
*
* Heavily inspired by w1_DS2433 driver from Ben Gardner <bgardner@wabtec.com>
*/
#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/w1.h>
#define W1_EEPROM_DS2431 0x2D
#define W1_F2D_EEPROM_SIZE 128
#define W1_F2D_PAGE_COUNT 4
#define W1_F2D_PAGE_BITS 5
#define W1_F2D_PAGE_SIZE (1<<W1_F2D_PAGE_BITS)
#define W1_F2D_PAGE_MASK 0x1F
#define W1_F2D_SCRATCH_BITS 3
#define W1_F2D_SCRATCH_SIZE (1<<W1_F2D_SCRATCH_BITS)
#define W1_F2D_SCRATCH_MASK (W1_F2D_SCRATCH_SIZE-1)
#define W1_F2D_READ_EEPROM 0xF0
#define W1_F2D_WRITE_SCRATCH 0x0F
#define W1_F2D_READ_SCRATCH 0xAA
#define W1_F2D_COPY_SCRATCH 0x55
#define W1_F2D_TPROG_MS 11
#define W1_F2D_READ_RETRIES 10
#define W1_F2D_READ_MAXLEN 8
/*
* 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_f2d_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;
}
/*
* Read a block from W1 ROM two times and compares the results.
* If they are equal they are returned, otherwise the read
* is repeated W1_F2D_READ_RETRIES times.
*
* count must not exceed W1_F2D_READ_MAXLEN.
*/
static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf)
{
u8 wrbuf[3];
u8 cmp[W1_F2D_READ_MAXLEN];
int tries = W1_F2D_READ_RETRIES;
do {
wrbuf[0] = W1_F2D_READ_EEPROM;
wrbuf[1] = off & 0xff;
wrbuf[2] = off >> 8;
if (w1_reset_select_slave(sl))
return -1;
w1_write_block(sl->master, wrbuf, 3);
w1_read_block(sl->master, buf, count);
if (w1_reset_select_slave(sl))
return -1;
w1_write_block(sl->master, wrbuf, 3);
w1_read_block(sl->master, cmp, count);
if (!memcmp(cmp, buf, count))
return 0;
} while (--tries);
dev_err(&sl->dev, "proof reading failed %d times\n",
W1_F2D_READ_RETRIES);
return -1;
}
static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int todo = count;
count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE);
if (count == 0)
return 0;
mutex_lock(&sl->master->bus_mutex);
/* read directly from the EEPROM in chunks of W1_F2D_READ_MAXLEN */
while (todo > 0) {
int block_read;
if (todo >= W1_F2D_READ_MAXLEN)
block_read = W1_F2D_READ_MAXLEN;
else
block_read = todo;
if (w1_f2d_readblock(sl, off, block_read, buf) < 0)
count = -EIO;
todo -= W1_F2D_READ_MAXLEN;
buf += W1_F2D_READ_MAXLEN;
off += W1_F2D_READ_MAXLEN;
}
mutex_unlock(&sl->master->bus_mutex);
return count;
}
/*
* Writes to the scratchpad and reads it back for verification.
* Then copies the scratchpad to EEPROM.
* The data must be aligned at W1_F2D_SCRATCH_SIZE bytes and
* must be W1_F2D_SCRATCH_SIZE bytes long.
* The master must be locked.
*
* @param sl The slave structure
* @param addr Address for the write
* @param len length must be <= (W1_F2D_PAGE_SIZE - (addr & W1_F2D_PAGE_MASK))
* @param data The data to write
* @return 0=Success -1=failure
*/
static int w1_f2d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
{
int tries = W1_F2D_READ_RETRIES;
u8 wrbuf[4];
u8 rdbuf[W1_F2D_SCRATCH_SIZE + 3];
u8 es = (addr + len - 1) % W1_F2D_SCRATCH_SIZE;
retry:
/* Write the data to the scratchpad */
if (w1_reset_select_slave(sl))
return -1;
wrbuf[0] = W1_F2D_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_F2D_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)) {
if (--tries)
goto retry;
dev_err(&sl->dev,
"could not write to eeprom, scratchpad compare failed %d times\n",
W1_F2D_READ_RETRIES);
return -1;
}
/* Copy the scratchpad to EEPROM */
if (w1_reset_select_slave(sl))
return -1;
wrbuf[0] = W1_F2D_COPY_SCRATCH;
wrbuf[3] = es;
w1_write_block(sl->master, wrbuf, 4);
/* Sleep for tprog ms to wait for the write to complete */
msleep(W1_F2D_TPROG_MS);
/* Reset the bus to wake up the EEPROM */
w1_reset_bus(sl->master);
return 0;
}
static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
const 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;
int copy;
count = w1_f2d_fix_count(off, count, W1_F2D_EEPROM_SIZE);
if (count == 0)
return 0;
mutex_lock(&sl->master->bus_mutex);
/* Can only write data in blocks of the size of the scratchpad */
addr = off;
len = count;
while (len > 0) {
/* if len too short or addr not aligned */
if (len < W1_F2D_SCRATCH_SIZE || addr & W1_F2D_SCRATCH_MASK) {
char tmp[W1_F2D_SCRATCH_SIZE];
/* read the block and update the parts to be written */
if (w1_f2d_readblock(sl, addr & ~W1_F2D_SCRATCH_MASK,
W1_F2D_SCRATCH_SIZE, tmp)) {
count = -EIO;
goto out_up;
}
/* copy at most to the boundary of the PAGE or len */
copy = W1_F2D_SCRATCH_SIZE -
(addr & W1_F2D_SCRATCH_MASK);
if (copy > len)
copy = len;
memcpy(&tmp[addr & W1_F2D_SCRATCH_MASK], buf, copy);
if (w1_f2d_write(sl, addr & ~W1_F2D_SCRATCH_MASK,
W1_F2D_SCRATCH_SIZE, tmp) < 0) {
count = -EIO;
goto out_up;
}
} else {
copy = W1_F2D_SCRATCH_SIZE;
if (w1_f2d_write(sl, addr, copy, buf) < 0) {
count = -EIO;
goto out_up;
}
}
buf += copy;
addr += copy;
len -= copy;
}
out_up:
mutex_unlock(&sl->master->bus_mutex);
return count;
}
static const BIN_ATTR_RW(eeprom, W1_F2D_EEPROM_SIZE);
static const struct bin_attribute *const w1_f2d_bin_attrs[] = {
&bin_attr_eeprom,
NULL,
};
static const struct attribute_group w1_f2d_group = {
.bin_attrs = w1_f2d_bin_attrs,
};
static const struct attribute_group *w1_f2d_groups[] = {
&w1_f2d_group,
NULL,
};
static const struct w1_family_ops w1_f2d_fops = {
.groups = w1_f2d_groups,
};
static struct w1_family w1_family_2d = {
.fid = W1_EEPROM_DS2431,
.fops = &w1_f2d_fops,
};
module_w1_family(w1_family_2d);
MODULE_AUTHOR("Bernhard Weirich <bernhard.weirich@riedel.net>");
MODULE_DESCRIPTION("w1 family 2d driver for DS2431, 1kb EEPROM");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2431));
+419
View File
@@ -0,0 +1,419 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds2433.c - w1 family 23 (DS2433) & 43 (DS28EC20) eeprom driver
*
* Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
* Copyright (c) 2023 Marc Ferland <marc.ferland@sonatest.com>
*/
#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>
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
#include <linux/crc16.h>
#define CRC16_INIT 0
#define CRC16_VALID 0xb001
#endif
#include <linux/w1.h>
#define W1_EEPROM_DS2433 0x23
#define W1_EEPROM_DS28EC20 0x43
#define W1_EEPROM_DS2433_SIZE 512
#define W1_EEPROM_DS28EC20_SIZE 2560
#define W1_PAGE_SIZE 32
#define W1_PAGE_BITS 5
#define W1_PAGE_MASK 0x1F
#define W1_VALIDCRC_MAX 96
#define W1_F23_READ_EEPROM 0xF0
#define W1_F23_WRITE_SCRATCH 0x0F
#define W1_F23_READ_SCRATCH 0xAA
#define W1_F23_COPY_SCRATCH 0x55
struct ds2433_config {
size_t eeprom_size; /* eeprom size in bytes */
unsigned int page_count; /* number of 256 bits pages */
unsigned int tprog; /* time in ms for page programming */
};
static const struct ds2433_config config_f23 = {
.eeprom_size = W1_EEPROM_DS2433_SIZE,
.page_count = 16,
.tprog = 5,
};
static const struct ds2433_config config_f43 = {
.eeprom_size = W1_EEPROM_DS28EC20_SIZE,
.page_count = 80,
.tprog = 10,
};
struct w1_f23_data {
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
u8 *memory;
DECLARE_BITMAP(validcrc, W1_VALIDCRC_MAX);
#endif
const struct ds2433_config *cfg;
};
/*
* 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_f23_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;
}
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,
int block)
{
u8 wrbuf[3];
int off = block * W1_PAGE_SIZE;
if (test_bit(block, data->validcrc))
return 0;
if (w1_reset_select_slave(sl)) {
bitmap_zero(data->validcrc, data->cfg->page_count);
return -EIO;
}
wrbuf[0] = W1_F23_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)
set_bit(block, data->validcrc);
return 0;
}
#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
struct w1_f23_data *data = sl->family_data;
int i, min_page, max_page;
#else
u8 wrbuf[3];
#endif
count = w1_f23_fix_count(off, count, bin_attr->size);
if (!count)
return 0;
mutex_lock(&sl->master->bus_mutex);
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
min_page = (off >> W1_PAGE_BITS);
max_page = (off + count - 1) >> W1_PAGE_BITS;
for (i = min_page; i <= max_page; i++) {
if (w1_f23_refresh_block(sl, data, i)) {
count = -EIO;
goto out_up;
}
}
memcpy(buf, &data->memory[off], count);
#else /* CONFIG_W1_SLAVE_DS2433_CRC */
/* read directly from the EEPROM */
if (w1_reset_select_slave(sl)) {
count = -EIO;
goto out_up;
}
wrbuf[0] = W1_F23_READ_EEPROM;
wrbuf[1] = off & 0xff;
wrbuf[2] = off >> 8;
w1_write_block(sl->master, wrbuf, 3);
w1_read_block(sl->master, buf, count);
#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
out_up:
mutex_unlock(&sl->master->bus_mutex);
return count;
}
/**
* w1_f23_write() - Writes to the scratchpad and reads it back for verification.
* @sl: The slave structure
* @addr: Address for the write
* @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
* @data: The data to write
*
* Then copies the scratchpad to EEPROM.
* The data must be on one page.
* The master must be locked.
*
* Return: 0=Success, -1=failure
*/
static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
{
struct w1_f23_data *f23 = sl->family_data;
u8 wrbuf[4];
u8 rdbuf[W1_PAGE_SIZE + 3];
u8 es = (addr + len - 1) & 0x1f;
/* Write the data to the scratchpad */
if (w1_reset_select_slave(sl))
return -1;
wrbuf[0] = W1_F23_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_F23_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_F23_COPY_SCRATCH;
wrbuf[3] = es;
w1_write_block(sl->master, wrbuf, 4);
/* Sleep for tprog ms to wait for the write to complete */
msleep(f23->cfg->tprog);
/* Reset the bus to wake up the EEPROM (this may not be needed) */
w1_reset_bus(sl->master);
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
clear_bit(addr >> W1_PAGE_BITS, f23->validcrc);
#endif
return 0;
}
static ssize_t eeprom_write(struct file *filp, struct kobject *kobj,
const 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_f23_fix_count(off, count, bin_attr->size);
if (!count)
return 0;
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
/* 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;
}
}
#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
mutex_lock(&sl->master->bus_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_f23_write(sl, addr, len, &buf[idx]) < 0) {
count = -EIO;
goto out_up;
}
idx += len;
}
out_up:
mutex_unlock(&sl->master->bus_mutex);
return count;
}
static const struct bin_attribute bin_attr_f23_eeprom = {
.attr = { .name = "eeprom", .mode = 0644 },
.read = eeprom_read,
.write = eeprom_write,
.size = W1_EEPROM_DS2433_SIZE,
};
static const struct bin_attribute bin_attr_f43_eeprom = {
.attr = { .name = "eeprom", .mode = 0644 },
.read = eeprom_read,
.write = eeprom_write,
.size = W1_EEPROM_DS28EC20_SIZE,
};
static const struct bin_attribute *const w1_f23_bin_attributes[] = {
&bin_attr_f23_eeprom,
NULL,
};
static const struct attribute_group w1_f23_group = {
.bin_attrs = w1_f23_bin_attributes,
};
static const struct attribute_group *w1_f23_groups[] = {
&w1_f23_group,
NULL,
};
static const struct bin_attribute *const w1_f43_bin_attributes[] = {
&bin_attr_f43_eeprom,
NULL,
};
static const struct attribute_group w1_f43_group = {
.bin_attrs = w1_f43_bin_attributes,
};
static const struct attribute_group *w1_f43_groups[] = {
&w1_f43_group,
NULL,
};
static int w1_f23_add_slave(struct w1_slave *sl)
{
struct w1_f23_data *data;
data = kzalloc_obj(struct w1_f23_data);
if (!data)
return -ENOMEM;
switch (sl->family->fid) {
case W1_EEPROM_DS2433:
data->cfg = &config_f23;
break;
case W1_EEPROM_DS28EC20:
data->cfg = &config_f43;
break;
}
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
if (data->cfg->page_count > W1_VALIDCRC_MAX) {
dev_err(&sl->dev, "page count too big for crc bitmap\n");
kfree(data);
return -EINVAL;
}
data->memory = kzalloc(data->cfg->eeprom_size, GFP_KERNEL);
if (!data->memory) {
kfree(data);
return -ENOMEM;
}
bitmap_zero(data->validcrc, data->cfg->page_count);
#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
sl->family_data = data;
return 0;
}
static void w1_f23_remove_slave(struct w1_slave *sl)
{
struct w1_f23_data *data = sl->family_data;
sl->family_data = NULL;
#ifdef CONFIG_W1_SLAVE_DS2433_CRC
kfree(data->memory);
#endif /* CONFIG_W1_SLAVE_DS2433_CRC */
kfree(data);
}
static const struct w1_family_ops w1_f23_fops = {
.add_slave = w1_f23_add_slave,
.remove_slave = w1_f23_remove_slave,
.groups = w1_f23_groups,
};
static const struct w1_family_ops w1_f43_fops = {
.add_slave = w1_f23_add_slave,
.remove_slave = w1_f23_remove_slave,
.groups = w1_f43_groups,
};
static struct w1_family w1_family_23 = {
.fid = W1_EEPROM_DS2433,
.fops = &w1_f23_fops,
};
static struct w1_family w1_family_43 = {
.fid = W1_EEPROM_DS28EC20,
.fops = &w1_f43_fops,
};
static int __init w1_ds2433_init(void)
{
int err;
err = w1_register_family(&w1_family_23);
if (err)
return err;
err = w1_register_family(&w1_family_43);
if (err)
goto err_43;
return 0;
err_43:
w1_unregister_family(&w1_family_23);
return err;
}
static void __exit w1_ds2433_exit(void)
{
w1_unregister_family(&w1_family_23);
w1_unregister_family(&w1_family_43);
}
module_init(w1_ds2433_init);
module_exit(w1_ds2433_exit);
MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_AUTHOR("Marc Ferland <marc.ferland@sonatest.com>");
MODULE_DESCRIPTION("w1 family 23/43 driver for DS2433 (4kb) and DS28EC20 (20kb)");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2433));
MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS28EC20));
+516
View File
@@ -0,0 +1,516 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 1-Wire implementation for the ds2438 chip
*
* Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/w1.h>
#define W1_FAMILY_DS2438 0x26
#define W1_DS2438_RETRIES 3
/* Memory commands */
#define W1_DS2438_READ_SCRATCH 0xBE
#define W1_DS2438_WRITE_SCRATCH 0x4E
#define W1_DS2438_COPY_SCRATCH 0x48
#define W1_DS2438_RECALL_MEMORY 0xB8
/* Register commands */
#define W1_DS2438_CONVERT_TEMP 0x44
#define W1_DS2438_CONVERT_VOLTAGE 0xB4
#define DS2438_PAGE_SIZE 8
#define DS2438_ADC_INPUT_VAD 0
#define DS2438_ADC_INPUT_VDD 1
#define DS2438_MAX_CONVERSION_TIME 10 /* ms */
/* Page #0 definitions */
#define DS2438_STATUS_REG 0x00 /* Status/Configuration Register */
#define DS2438_STATUS_IAD (1 << 0) /* Current A/D Control Bit */
#define DS2438_STATUS_CA (1 << 1) /* Current Accumulator Configuration */
#define DS2438_STATUS_EE (1 << 2) /* Current Accumulator Shadow Selector bit */
#define DS2438_STATUS_AD (1 << 3) /* Voltage A/D Input Select Bit */
#define DS2438_STATUS_TB (1 << 4) /* Temperature Busy Flag */
#define DS2438_STATUS_NVB (1 << 5) /* Nonvolatile Memory Busy Flag */
#define DS2438_STATUS_ADB (1 << 6) /* A/D Converter Busy Flag */
#define DS2438_TEMP_LSB 0x01
#define DS2438_TEMP_MSB 0x02
#define DS2438_VOLTAGE_LSB 0x03
#define DS2438_VOLTAGE_MSB 0x04
#define DS2438_CURRENT_LSB 0x05
#define DS2438_CURRENT_MSB 0x06
#define DS2438_THRESHOLD 0x07
/* Page #1 definitions */
#define DS2438_ETM_0 0x00
#define DS2438_ETM_1 0x01
#define DS2438_ETM_2 0x02
#define DS2438_ETM_3 0x03
#define DS2438_ICA 0x04
#define DS2438_OFFSET_LSB 0x05
#define DS2438_OFFSET_MSB 0x06
static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
{
unsigned int retries = W1_DS2438_RETRIES;
u8 w1_buf[2];
u8 crc;
size_t count;
while (retries--) {
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_RECALL_MEMORY;
w1_buf[1] = (u8)pageno;
w1_write_block(sl->master, w1_buf, 2);
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_READ_SCRATCH;
w1_buf[1] = (u8)pageno;
w1_write_block(sl->master, w1_buf, 2);
count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);
if (count == DS2438_PAGE_SIZE + 1) {
crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE);
/* check for correct CRC */
if ((u8)buf[DS2438_PAGE_SIZE] == crc)
return 0;
}
}
return -1;
}
static int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature)
{
unsigned int retries = W1_DS2438_RETRIES;
u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
unsigned int tm = DS2438_MAX_CONVERSION_TIME;
unsigned long sleep_rem;
int ret;
mutex_lock(&sl->master->bus_mutex);
while (retries--) {
if (w1_reset_select_slave(sl))
continue;
w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP);
mutex_unlock(&sl->master->bus_mutex);
sleep_rem = msleep_interruptible(tm);
if (sleep_rem != 0) {
ret = -1;
goto post_unlock;
}
if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
ret = -1;
goto post_unlock;
}
break;
}
if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
*temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]);
ret = 0;
} else
ret = -1;
mutex_unlock(&sl->master->bus_mutex);
post_unlock:
return ret;
}
static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
{
unsigned int retries = W1_DS2438_RETRIES;
u8 w1_buf[3];
u8 status;
int perform_write = 0;
while (retries--) {
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_RECALL_MEMORY;
w1_buf[1] = 0x00;
w1_write_block(sl->master, w1_buf, 2);
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_READ_SCRATCH;
w1_buf[1] = 0x00;
w1_write_block(sl->master, w1_buf, 2);
/* reading one byte of result */
status = w1_read_8(sl->master);
/* if bit0=1, set a value to a mask for easy compare */
if (value)
value = mask;
if ((status & mask) == value)
return 0; /* already set as requested */
/* changing bit */
status ^= mask;
perform_write = 1;
break;
}
if (perform_write) {
retries = W1_DS2438_RETRIES;
while (retries--) {
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
w1_buf[1] = 0x00;
w1_buf[2] = status;
w1_write_block(sl->master, w1_buf, 3);
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_COPY_SCRATCH;
w1_buf[1] = 0x00;
w1_write_block(sl->master, w1_buf, 2);
return 0;
}
}
return -1;
}
static int w1_ds2438_change_offset_register(struct w1_slave *sl, u8 *value)
{
unsigned int retries = W1_DS2438_RETRIES;
u8 w1_buf[9];
u8 w1_page1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
if (w1_ds2438_get_page(sl, 1, w1_page1_buf) == 0) {
memcpy(&w1_buf[2], w1_page1_buf, DS2438_PAGE_SIZE - 1); /* last register reserved */
w1_buf[7] = value[0]; /* change only offset register */
w1_buf[8] = value[1];
while (retries--) {
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
w1_buf[1] = 0x01; /* write to page 1 */
w1_write_block(sl->master, w1_buf, 9);
if (w1_reset_select_slave(sl))
continue;
w1_buf[0] = W1_DS2438_COPY_SCRATCH;
w1_buf[1] = 0x01;
w1_write_block(sl->master, w1_buf, 2);
return 0;
}
}
return -1;
}
static int w1_ds2438_get_voltage(struct w1_slave *sl,
int adc_input, uint16_t *voltage)
{
unsigned int retries = W1_DS2438_RETRIES;
u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
unsigned int tm = DS2438_MAX_CONVERSION_TIME;
unsigned long sleep_rem;
int ret;
mutex_lock(&sl->master->bus_mutex);
if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) {
ret = -1;
goto pre_unlock;
}
while (retries--) {
if (w1_reset_select_slave(sl))
continue;
w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE);
mutex_unlock(&sl->master->bus_mutex);
sleep_rem = msleep_interruptible(tm);
if (sleep_rem != 0) {
ret = -1;
goto post_unlock;
}
if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
ret = -1;
goto post_unlock;
}
break;
}
if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
*voltage = (((uint16_t) w1_buf[DS2438_VOLTAGE_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_VOLTAGE_LSB]);
ret = 0;
} else
ret = -1;
pre_unlock:
mutex_unlock(&sl->master->bus_mutex);
post_unlock:
return ret;
}
static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage)
{
u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
int ret;
mutex_lock(&sl->master->bus_mutex);
if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
/* The voltage measured across current sense resistor RSENS. */
*voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]);
ret = 0;
} else
ret = -1;
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static ssize_t iad_write(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
if (count != 1 || off != 0)
return -EFAULT;
mutex_lock(&sl->master->bus_mutex);
if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0)
ret = 1;
else
ret = -EIO;
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static ssize_t iad_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
int16_t voltage;
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
if (w1_ds2438_get_current(sl, &voltage) == 0)
ret = snprintf(buf, count, "%i\n", voltage);
else
ret = -EIO;
return ret;
}
static ssize_t page0_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
/* Read no more than page0 size */
if (count > DS2438_PAGE_SIZE)
count = DS2438_PAGE_SIZE;
if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
memcpy(buf, &w1_buf, count);
ret = count;
} else
ret = -EIO;
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static ssize_t page1_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
/* Read no more than page1 size */
if (count > DS2438_PAGE_SIZE)
count = DS2438_PAGE_SIZE;
if (w1_ds2438_get_page(sl, 1, w1_buf) == 0) {
memcpy(buf, &w1_buf, count);
ret = count;
} else
ret = -EIO;
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static ssize_t offset_write(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
mutex_lock(&sl->master->bus_mutex);
if (w1_ds2438_change_offset_register(sl, buf) == 0)
ret = count;
else
ret = -EIO;
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
int16_t temp;
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
if (w1_ds2438_get_temperature(sl, &temp) == 0)
ret = snprintf(buf, count, "%i\n", temp);
else
ret = -EIO;
return ret;
}
static ssize_t vad_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
uint16_t voltage;
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0)
ret = snprintf(buf, count, "%u\n", voltage);
else
ret = -EIO;
return ret;
}
static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int ret;
uint16_t voltage;
if (off != 0)
return 0;
if (!buf)
return -EINVAL;
if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0)
ret = snprintf(buf, count, "%u\n", voltage);
else
ret = -EIO;
return ret;
}
static const BIN_ATTR_RW(iad, 0);
static const BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
static const BIN_ATTR_RO(page1, DS2438_PAGE_SIZE);
static const BIN_ATTR_WO(offset, 2);
static const BIN_ATTR_RO(temperature, 0/* real length varies */);
static const BIN_ATTR_RO(vad, 0/* real length varies */);
static const BIN_ATTR_RO(vdd, 0/* real length varies */);
static const struct bin_attribute *const w1_ds2438_bin_attrs[] = {
&bin_attr_iad,
&bin_attr_page0,
&bin_attr_page1,
&bin_attr_offset,
&bin_attr_temperature,
&bin_attr_vad,
&bin_attr_vdd,
NULL,
};
static const struct attribute_group w1_ds2438_group = {
.bin_attrs = w1_ds2438_bin_attrs,
};
static const struct attribute_group *w1_ds2438_groups[] = {
&w1_ds2438_group,
NULL,
};
static const struct w1_family_ops w1_ds2438_fops = {
.groups = w1_ds2438_groups,
};
static struct w1_family w1_ds2438_family = {
.fid = W1_FAMILY_DS2438,
.fops = &w1_ds2438_fops,
};
module_w1_family(w1_ds2438_family);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));
+291
View File
@@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0
/*
* w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver
*/
#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/crc16.h>
#include <linux/w1.h>
#include <linux/nvmem-provider.h>
#define W1_DS2501_UNW_FAMILY 0x91
#define W1_DS2501_SIZE 64
#define W1_DS2502_FAMILY 0x09
#define W1_DS2502_UNW_FAMILY 0x89
#define W1_DS2502_SIZE 128
#define W1_DS2505_FAMILY 0x0b
#define W1_DS2505_SIZE 2048
#define W1_PAGE_SIZE 32
#define W1_EXT_READ_MEMORY 0xA5
#define W1_READ_DATA_CRC 0xC3
#define OFF2PG(off) ((off) / W1_PAGE_SIZE)
#define CRC16_INIT 0
#define CRC16_VALID 0xb001
struct w1_eprom_data {
size_t size;
int (*read)(struct w1_slave *sl, int pageno);
u8 eprom[W1_DS2505_SIZE];
DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE);
char nvmem_name[64];
};
static int w1_ds2502_read_page(struct w1_slave *sl, int pageno)
{
struct w1_eprom_data *data = sl->family_data;
int pgoff = pageno * W1_PAGE_SIZE;
int ret = -EIO;
u8 buf[3];
u8 crc8;
if (test_bit(pageno, data->page_present))
return 0; /* page already present */
mutex_lock(&sl->master->bus_mutex);
if (w1_reset_select_slave(sl))
goto err;
buf[0] = W1_READ_DATA_CRC;
buf[1] = pgoff & 0xff;
buf[2] = pgoff >> 8;
w1_write_block(sl->master, buf, 3);
crc8 = w1_read_8(sl->master);
if (w1_calc_crc8(buf, 3) != crc8)
goto err;
w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE);
crc8 = w1_read_8(sl->master);
if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8)
goto err;
set_bit(pageno, data->page_present); /* mark page present */
ret = 0;
err:
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_ds2505_read_page(struct w1_slave *sl, int pageno)
{
struct w1_eprom_data *data = sl->family_data;
int redir_retries = 16;
int pgoff, epoff;
int ret = -EIO;
u8 buf[6];
u8 redir;
u16 crc;
if (test_bit(pageno, data->page_present))
return 0; /* page already present */
epoff = pgoff = pageno * W1_PAGE_SIZE;
mutex_lock(&sl->master->bus_mutex);
retry:
if (w1_reset_select_slave(sl))
goto err;
buf[0] = W1_EXT_READ_MEMORY;
buf[1] = pgoff & 0xff;
buf[2] = pgoff >> 8;
w1_write_block(sl->master, buf, 3);
w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */
redir = buf[3];
crc = crc16(CRC16_INIT, buf, 6);
if (crc != CRC16_VALID)
goto err;
if (redir != 0xff) {
redir_retries--;
if (redir_retries < 0)
goto err;
pgoff = (redir ^ 0xff) * W1_PAGE_SIZE;
goto retry;
}
w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE);
w1_read_block(sl->master, buf, 2); /* crc16 */
crc = crc16(CRC16_INIT, &data->eprom[epoff], W1_PAGE_SIZE);
crc = crc16(crc, buf, 2);
if (crc != CRC16_VALID)
goto err;
set_bit(pageno, data->page_present);
ret = 0;
err:
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)
{
struct w1_slave *sl = priv;
struct w1_eprom_data *data = sl->family_data;
size_t eprom_size = data->size;
int ret;
int i;
if (off > eprom_size)
return -EINVAL;
if ((off + count) > eprom_size)
count = eprom_size - off;
i = OFF2PG(off);
do {
ret = data->read(sl, i++);
if (ret < 0)
return ret;
} while (i < OFF2PG(off + count));
memcpy(buf, &data->eprom[off], count);
return 0;
}
static int w1_eprom_add_slave(struct w1_slave *sl)
{
struct w1_eprom_data *data;
struct nvmem_device *nvmem;
struct nvmem_config nvmem_cfg = {
.dev = &sl->dev,
.add_legacy_fixed_of_cells = true,
.reg_read = w1_nvmem_read,
.type = NVMEM_TYPE_OTP,
.read_only = true,
.word_size = 1,
.priv = sl,
.id = -1
};
data = devm_kzalloc(&sl->dev, sizeof(struct w1_eprom_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
sl->family_data = data;
switch (sl->family->fid) {
case W1_DS2501_UNW_FAMILY:
data->size = W1_DS2501_SIZE;
data->read = w1_ds2502_read_page;
break;
case W1_DS2502_FAMILY:
case W1_DS2502_UNW_FAMILY:
data->size = W1_DS2502_SIZE;
data->read = w1_ds2502_read_page;
break;
case W1_DS2505_FAMILY:
data->size = W1_DS2505_SIZE;
data->read = w1_ds2505_read_page;
break;
}
if (sl->master->bus_master->dev_id)
snprintf(data->nvmem_name, sizeof(data->nvmem_name),
"%s-%02x-%012llx",
sl->master->bus_master->dev_id, sl->reg_num.family,
(unsigned long long)sl->reg_num.id);
else
snprintf(data->nvmem_name, sizeof(data->nvmem_name),
"%02x-%012llx",
sl->reg_num.family,
(unsigned long long)sl->reg_num.id);
nvmem_cfg.name = data->nvmem_name;
nvmem_cfg.size = data->size;
nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);
return PTR_ERR_OR_ZERO(nvmem);
}
static const struct w1_family_ops w1_eprom_fops = {
.add_slave = w1_eprom_add_slave,
};
static struct w1_family w1_family_09 = {
.fid = W1_DS2502_FAMILY,
.fops = &w1_eprom_fops,
};
static struct w1_family w1_family_0b = {
.fid = W1_DS2505_FAMILY,
.fops = &w1_eprom_fops,
};
static struct w1_family w1_family_89 = {
.fid = W1_DS2502_UNW_FAMILY,
.fops = &w1_eprom_fops,
};
static struct w1_family w1_family_91 = {
.fid = W1_DS2501_UNW_FAMILY,
.fops = &w1_eprom_fops,
};
static int __init w1_ds250x_init(void)
{
int err;
err = w1_register_family(&w1_family_09);
if (err)
return err;
err = w1_register_family(&w1_family_0b);
if (err)
goto err_0b;
err = w1_register_family(&w1_family_89);
if (err)
goto err_89;
err = w1_register_family(&w1_family_91);
if (err)
goto err_91;
return 0;
err_91:
w1_unregister_family(&w1_family_89);
err_89:
w1_unregister_family(&w1_family_0b);
err_0b:
w1_unregister_family(&w1_family_09);
return err;
}
static void __exit w1_ds250x_exit(void)
{
w1_unregister_family(&w1_family_09);
w1_unregister_family(&w1_family_0b);
w1_unregister_family(&w1_family_89);
w1_unregister_family(&w1_family_91);
}
module_init(w1_ds250x_init);
module_exit(w1_ds250x_exit);
MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfe@suse.de>");
MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY));
MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY));
MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY));
MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY));
+160
View File
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 1-Wire implementation for the ds2780 chip
*
* Copyright (C) 2010 Indesign, LLC
*
* Author: Clifton Barnes <cabarnes@indesign-llc.com>
*
* Based on w1-ds2760 driver
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/idr.h>
#include <linux/w1.h>
#include "w1_ds2780.h"
#define W1_FAMILY_DS2780 0x32
static int w1_ds2780_do_io(struct device *dev, char *buf, int addr,
size_t count, int io)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
if (addr > DS2780_DATA_SIZE || addr < 0)
return 0;
count = min_t(int, count, DS2780_DATA_SIZE - addr);
if (w1_reset_select_slave(sl) == 0) {
if (io) {
w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
w1_write_8(sl->master, addr);
w1_write_block(sl->master, buf, count);
} else {
w1_write_8(sl->master, W1_DS2780_READ_DATA);
w1_write_8(sl->master, addr);
count = w1_read_block(sl->master, buf, count);
}
}
return count;
}
int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
int io)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret;
if (!dev)
return -ENODEV;
mutex_lock(&sl->master->bus_mutex);
ret = w1_ds2780_do_io(dev, buf, addr, count, io);
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
EXPORT_SYMBOL(w1_ds2780_io);
int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
if (!dev)
return -EINVAL;
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->bus_mutex);
return 0;
}
EXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
return w1_ds2780_io(dev, buf, off, count, 0);
}
static const BIN_ATTR_RO(w1_slave, DS2780_DATA_SIZE);
static const struct bin_attribute *const w1_ds2780_bin_attrs[] = {
&bin_attr_w1_slave,
NULL,
};
static const struct attribute_group w1_ds2780_group = {
.bin_attrs = w1_ds2780_bin_attrs,
};
static const struct attribute_group *w1_ds2780_groups[] = {
&w1_ds2780_group,
NULL,
};
static int w1_ds2780_add_slave(struct w1_slave *sl)
{
int ret;
struct platform_device *pdev;
pdev = platform_device_alloc("ds2780-battery", PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = &sl->dev;
ret = platform_device_add(pdev);
if (ret)
goto pdev_add_failed;
dev_set_drvdata(&sl->dev, pdev);
return 0;
pdev_add_failed:
platform_device_put(pdev);
return ret;
}
static void w1_ds2780_remove_slave(struct w1_slave *sl)
{
struct platform_device *pdev = dev_get_drvdata(&sl->dev);
platform_device_unregister(pdev);
}
static const struct w1_family_ops w1_ds2780_fops = {
.add_slave = w1_ds2780_add_slave,
.remove_slave = w1_ds2780_remove_slave,
.groups = w1_ds2780_groups,
};
static struct w1_family w1_ds2780_family = {
.fid = W1_FAMILY_DS2780,
.fops = &w1_ds2780_fops,
};
module_w1_family(w1_ds2780_family);
MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2780));
+125
View File
@@ -0,0 +1,125 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 1-Wire implementation for the ds2780 chip
*
* Copyright (C) 2010 Indesign, LLC
*
* Author: Clifton Barnes <cabarnes@indesign-llc.com>
*
* Based on w1-ds2760 driver
*/
#ifndef _W1_DS2780_H
#define _W1_DS2780_H
/* Function commands */
#define W1_DS2780_READ_DATA 0x69
#define W1_DS2780_WRITE_DATA 0x6C
#define W1_DS2780_COPY_DATA 0x48
#define W1_DS2780_RECALL_DATA 0xB8
#define W1_DS2780_LOCK 0x6A
/* Register map */
/* Register 0x00 Reserved */
#define DS2780_STATUS_REG 0x01
#define DS2780_RAAC_MSB_REG 0x02
#define DS2780_RAAC_LSB_REG 0x03
#define DS2780_RSAC_MSB_REG 0x04
#define DS2780_RSAC_LSB_REG 0x05
#define DS2780_RARC_REG 0x06
#define DS2780_RSRC_REG 0x07
#define DS2780_IAVG_MSB_REG 0x08
#define DS2780_IAVG_LSB_REG 0x09
#define DS2780_TEMP_MSB_REG 0x0A
#define DS2780_TEMP_LSB_REG 0x0B
#define DS2780_VOLT_MSB_REG 0x0C
#define DS2780_VOLT_LSB_REG 0x0D
#define DS2780_CURRENT_MSB_REG 0x0E
#define DS2780_CURRENT_LSB_REG 0x0F
#define DS2780_ACR_MSB_REG 0x10
#define DS2780_ACR_LSB_REG 0x11
#define DS2780_ACRL_MSB_REG 0x12
#define DS2780_ACRL_LSB_REG 0x13
#define DS2780_AS_REG 0x14
#define DS2780_SFR_REG 0x15
#define DS2780_FULL_MSB_REG 0x16
#define DS2780_FULL_LSB_REG 0x17
#define DS2780_AE_MSB_REG 0x18
#define DS2780_AE_LSB_REG 0x19
#define DS2780_SE_MSB_REG 0x1A
#define DS2780_SE_LSB_REG 0x1B
/* Register 0x1C - 0x1E Reserved */
#define DS2780_EEPROM_REG 0x1F
#define DS2780_EEPROM_BLOCK0_START 0x20
/* Register 0x20 - 0x2F User EEPROM */
#define DS2780_EEPROM_BLOCK0_END 0x2F
/* Register 0x30 - 0x5F Reserved */
#define DS2780_EEPROM_BLOCK1_START 0x60
#define DS2780_CONTROL_REG 0x60
#define DS2780_AB_REG 0x61
#define DS2780_AC_MSB_REG 0x62
#define DS2780_AC_LSB_REG 0x63
#define DS2780_VCHG_REG 0x64
#define DS2780_IMIN_REG 0x65
#define DS2780_VAE_REG 0x66
#define DS2780_IAE_REG 0x67
#define DS2780_AE_40_REG 0x68
#define DS2780_RSNSP_REG 0x69
#define DS2780_FULL_40_MSB_REG 0x6A
#define DS2780_FULL_40_LSB_REG 0x6B
#define DS2780_FULL_3040_SLOPE_REG 0x6C
#define DS2780_FULL_2030_SLOPE_REG 0x6D
#define DS2780_FULL_1020_SLOPE_REG 0x6E
#define DS2780_FULL_0010_SLOPE_REG 0x6F
#define DS2780_AE_3040_SLOPE_REG 0x70
#define DS2780_AE_2030_SLOPE_REG 0x71
#define DS2780_AE_1020_SLOPE_REG 0x72
#define DS2780_AE_0010_SLOPE_REG 0x73
#define DS2780_SE_3040_SLOPE_REG 0x74
#define DS2780_SE_2030_SLOPE_REG 0x75
#define DS2780_SE_1020_SLOPE_REG 0x76
#define DS2780_SE_0010_SLOPE_REG 0x77
#define DS2780_RSGAIN_MSB_REG 0x78
#define DS2780_RSGAIN_LSB_REG 0x79
#define DS2780_RSTC_REG 0x7A
#define DS2780_FRSGAIN_MSB_REG 0x7B
#define DS2780_FRSGAIN_LSB_REG 0x7C
#define DS2780_EEPROM_BLOCK1_END 0x7C
/* Register 0x7D - 0xFF Reserved */
/* Number of valid register addresses */
#define DS2780_DATA_SIZE 0x80
/* Status register bits */
#define DS2780_STATUS_REG_CHGTF (1 << 7)
#define DS2780_STATUS_REG_AEF (1 << 6)
#define DS2780_STATUS_REG_SEF (1 << 5)
#define DS2780_STATUS_REG_LEARNF (1 << 4)
/* Bit 3 Reserved */
#define DS2780_STATUS_REG_UVF (1 << 2)
#define DS2780_STATUS_REG_PORF (1 << 1)
/* Bit 0 Reserved */
/* Control register bits */
/* Bit 7 Reserved */
#define DS2780_CONTROL_REG_UVEN (1 << 6)
#define DS2780_CONTROL_REG_PMOD (1 << 5)
#define DS2780_CONTROL_REG_RNAOP (1 << 4)
/* Bit 0 - 3 Reserved */
/* Special feature register bits */
/* Bit 1 - 7 Reserved */
#define DS2780_SFR_REG_PIOSC (1 << 0)
/* EEPROM register bits */
#define DS2780_EEPROM_REG_EEC (1 << 7)
#define DS2780_EEPROM_REG_LOCK (1 << 6)
/* Bit 2 - 6 Reserved */
#define DS2780_EEPROM_REG_BL1 (1 << 1)
#define DS2780_EEPROM_REG_BL0 (1 << 0)
extern int w1_ds2780_io(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 */
+157
View File
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 1-Wire implementation for the ds2781 chip
*
* Author: Renata Sayakhova <renata@oktetlabs.ru>
*
* Based on w1-ds2780 driver
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/w1.h>
#include "w1_ds2781.h"
#define W1_FAMILY_DS2781 0x3D
static int w1_ds2781_do_io(struct device *dev, char *buf, int addr,
size_t count, int io)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
if (addr > DS2781_DATA_SIZE || addr < 0)
return 0;
count = min_t(int, count, DS2781_DATA_SIZE - addr);
if (w1_reset_select_slave(sl) == 0) {
if (io) {
w1_write_8(sl->master, W1_DS2781_WRITE_DATA);
w1_write_8(sl->master, addr);
w1_write_block(sl->master, buf, count);
} else {
w1_write_8(sl->master, W1_DS2781_READ_DATA);
w1_write_8(sl->master, addr);
count = w1_read_block(sl->master, buf, count);
}
}
return count;
}
int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count,
int io)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret;
if (!dev)
return -ENODEV;
mutex_lock(&sl->master->bus_mutex);
ret = w1_ds2781_do_io(dev, buf, addr, count, io);
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
EXPORT_SYMBOL(w1_ds2781_io);
int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd)
{
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
if (!dev)
return -EINVAL;
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->bus_mutex);
return 0;
}
EXPORT_SYMBOL(w1_ds2781_eeprom_cmd);
static ssize_t w1_slave_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
return w1_ds2781_io(dev, buf, off, count, 0);
}
static const BIN_ATTR_RO(w1_slave, DS2781_DATA_SIZE);
static const struct bin_attribute *const w1_ds2781_bin_attrs[] = {
&bin_attr_w1_slave,
NULL,
};
static const struct attribute_group w1_ds2781_group = {
.bin_attrs = w1_ds2781_bin_attrs,
};
static const struct attribute_group *w1_ds2781_groups[] = {
&w1_ds2781_group,
NULL,
};
static int w1_ds2781_add_slave(struct w1_slave *sl)
{
int ret;
struct platform_device *pdev;
pdev = platform_device_alloc("ds2781-battery", PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = &sl->dev;
ret = platform_device_add(pdev);
if (ret)
goto pdev_add_failed;
dev_set_drvdata(&sl->dev, pdev);
return 0;
pdev_add_failed:
platform_device_put(pdev);
return ret;
}
static void w1_ds2781_remove_slave(struct w1_slave *sl)
{
struct platform_device *pdev = dev_get_drvdata(&sl->dev);
platform_device_unregister(pdev);
}
static const struct w1_family_ops w1_ds2781_fops = {
.add_slave = w1_ds2781_add_slave,
.remove_slave = w1_ds2781_remove_slave,
.groups = w1_ds2781_groups,
};
static struct w1_family w1_ds2781_family = {
.fid = W1_FAMILY_DS2781,
.fops = &w1_ds2781_fops,
};
module_w1_family(w1_ds2781_family);
MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2781));
+130
View File
@@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 1-Wire implementation for the ds2780 chip
*
* Author: Renata Sayakhova <renata@oktetlabs.ru>
*
* Based on w1-ds2760 driver
*/
#ifndef _W1_DS2781_H
#define _W1_DS2781_H
/* Function commands */
#define W1_DS2781_READ_DATA 0x69
#define W1_DS2781_WRITE_DATA 0x6C
#define W1_DS2781_COPY_DATA 0x48
#define W1_DS2781_RECALL_DATA 0xB8
#define W1_DS2781_LOCK 0x6A
/* Register map */
/* Register 0x00 Reserved */
#define DS2781_STATUS 0x01
#define DS2781_RAAC_MSB 0x02
#define DS2781_RAAC_LSB 0x03
#define DS2781_RSAC_MSB 0x04
#define DS2781_RSAC_LSB 0x05
#define DS2781_RARC 0x06
#define DS2781_RSRC 0x07
#define DS2781_IAVG_MSB 0x08
#define DS2781_IAVG_LSB 0x09
#define DS2781_TEMP_MSB 0x0A
#define DS2781_TEMP_LSB 0x0B
#define DS2781_VOLT_MSB 0x0C
#define DS2781_VOLT_LSB 0x0D
#define DS2781_CURRENT_MSB 0x0E
#define DS2781_CURRENT_LSB 0x0F
#define DS2781_ACR_MSB 0x10
#define DS2781_ACR_LSB 0x11
#define DS2781_ACRL_MSB 0x12
#define DS2781_ACRL_LSB 0x13
#define DS2781_AS 0x14
#define DS2781_SFR 0x15
#define DS2781_FULL_MSB 0x16
#define DS2781_FULL_LSB 0x17
#define DS2781_AE_MSB 0x18
#define DS2781_AE_LSB 0x19
#define DS2781_SE_MSB 0x1A
#define DS2781_SE_LSB 0x1B
/* Register 0x1C - 0x1E Reserved */
#define DS2781_EEPROM 0x1F
#define DS2781_EEPROM_BLOCK0_START 0x20
/* Register 0x20 - 0x2F User EEPROM */
#define DS2781_EEPROM_BLOCK0_END 0x2F
/* Register 0x30 - 0x5F Reserved */
#define DS2781_EEPROM_BLOCK1_START 0x60
#define DS2781_CONTROL 0x60
#define DS2781_AB 0x61
#define DS2781_AC_MSB 0x62
#define DS2781_AC_LSB 0x63
#define DS2781_VCHG 0x64
#define DS2781_IMIN 0x65
#define DS2781_VAE 0x66
#define DS2781_IAE 0x67
#define DS2781_AE_40 0x68
#define DS2781_RSNSP 0x69
#define DS2781_FULL_40_MSB 0x6A
#define DS2781_FULL_40_LSB 0x6B
#define DS2781_FULL_4_SLOPE 0x6C
#define DS2781_FULL_3_SLOPE 0x6D
#define DS2781_FULL_2_SLOPE 0x6E
#define DS2781_FULL_1_SLOPE 0x6F
#define DS2781_AE_4_SLOPE 0x70
#define DS2781_AE_3_SLOPE 0x71
#define DS2781_AE_2_SLOPE 0x72
#define DS2781_AE_1_SLOPE 0x73
#define DS2781_SE_4_SLOPE 0x74
#define DS2781_SE_3_SLOPE 0x75
#define DS2781_SE_2_SLOPE 0x76
#define DS2781_SE_1_SLOPE 0x77
#define DS2781_RSGAIN_MSB 0x78
#define DS2781_RSGAIN_LSB 0x79
#define DS2781_RSTC 0x7A
#define DS2781_COB 0x7B
#define DS2781_TBP34 0x7C
#define DS2781_TBP23 0x7D
#define DS2781_TBP12 0x7E
#define DS2781_EEPROM_BLOCK1_END 0x7F
/* Register 0x7D - 0xFF Reserved */
#define DS2781_FSGAIN_MSB 0xB0
#define DS2781_FSGAIN_LSB 0xB1
/* Number of valid register addresses */
#define DS2781_DATA_SIZE 0xB2
/* Status register bits */
#define DS2781_STATUS_CHGTF (1 << 7)
#define DS2781_STATUS_AEF (1 << 6)
#define DS2781_STATUS_SEF (1 << 5)
#define DS2781_STATUS_LEARNF (1 << 4)
/* Bit 3 Reserved */
#define DS2781_STATUS_UVF (1 << 2)
#define DS2781_STATUS_PORF (1 << 1)
/* Bit 0 Reserved */
/* Control register bits */
/* Bit 7 Reserved */
#define DS2781_CONTROL_NBEN (1 << 7)
#define DS2781_CONTROL_UVEN (1 << 6)
#define DS2781_CONTROL_PMOD (1 << 5)
#define DS2781_CONTROL_RNAOP (1 << 4)
#define DS1781_CONTROL_UVTH (1 << 3)
/* Bit 0 - 2 Reserved */
/* Special feature register bits */
/* Bit 1 - 7 Reserved */
#define DS2781_SFR_PIOSC (1 << 0)
/* EEPROM register bits */
#define DS2781_EEPROM_EEC (1 << 7)
#define DS2781_EEPROM_LOCK (1 << 6)
/* Bit 2 - 6 Reserved */
#define DS2781_EEPROM_BL1 (1 << 1)
#define DS2781_EEPROM_BL0 (1 << 0)
extern int w1_ds2781_io(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 */
+298
View File
@@ -0,0 +1,298 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds2805 - w1 family 0d (DS28E05) driver
*
* Copyright (c) 2016 Andrew Worsley amworsley@gmail.com
*/
#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/w1.h>
#define W1_EEPROM_DS2805 0x0D
#define W1_F0D_EEPROM_SIZE 128
#define W1_F0D_PAGE_BITS 3
#define W1_F0D_PAGE_SIZE (1<<W1_F0D_PAGE_BITS)
#define W1_F0D_PAGE_MASK 0x0F
#define W1_F0D_SCRATCH_BITS 1
#define W1_F0D_SCRATCH_SIZE (1<<W1_F0D_SCRATCH_BITS)
#define W1_F0D_SCRATCH_MASK (W1_F0D_SCRATCH_SIZE-1)
#define W1_F0D_READ_EEPROM 0xF0
#define W1_F0D_WRITE_EEPROM 0x55
#define W1_F0D_RELEASE 0xFF
#define W1_F0D_CS_OK 0xAA /* Chip Status Ok */
#define W1_F0D_TPROG_MS 16
#define W1_F0D_READ_RETRIES 10
#define W1_F0D_READ_MAXLEN W1_F0D_EEPROM_SIZE
/*
* 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_f0d_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;
}
/*
* Read a block from W1 ROM two times and compares the results.
* If they are equal they are returned, otherwise the read
* is repeated W1_F0D_READ_RETRIES times.
*
* count must not exceed W1_F0D_READ_MAXLEN.
*/
static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf)
{
u8 wrbuf[3];
u8 cmp[W1_F0D_READ_MAXLEN];
int tries = W1_F0D_READ_RETRIES;
do {
wrbuf[0] = W1_F0D_READ_EEPROM;
wrbuf[1] = off & 0x7f;
wrbuf[2] = 0;
if (w1_reset_select_slave(sl))
return -1;
w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
w1_read_block(sl->master, buf, count);
if (w1_reset_select_slave(sl))
return -1;
w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
w1_read_block(sl->master, cmp, count);
if (!memcmp(cmp, buf, count))
return 0;
} while (--tries);
dev_err(&sl->dev, "proof reading failed %d times\n",
W1_F0D_READ_RETRIES);
return -1;
}
static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct w1_slave *sl = kobj_to_w1_slave(kobj);
int todo = count;
count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
if (count == 0)
return 0;
mutex_lock(&sl->master->mutex);
/* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */
while (todo > 0) {
int block_read;
if (todo >= W1_F0D_READ_MAXLEN)
block_read = W1_F0D_READ_MAXLEN;
else
block_read = todo;
if (w1_f0d_readblock(sl, off, block_read, buf) < 0) {
count = -EIO;
break;
}
todo -= W1_F0D_READ_MAXLEN;
buf += W1_F0D_READ_MAXLEN;
off += W1_F0D_READ_MAXLEN;
}
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 aligned at W1_F0D_SCRATCH_SIZE bytes and
* must be W1_F0D_SCRATCH_SIZE bytes long.
* The master must be locked.
*
* @param sl The slave structure
* @param addr Address for the write
* @param len length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK))
* @param data The data to write
* @return 0=Success -1=failure
*/
static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data)
{
int tries = W1_F0D_READ_RETRIES;
u8 wrbuf[3];
u8 rdbuf[W1_F0D_SCRATCH_SIZE];
u8 cs;
if ((addr & 1) || (len != 2)) {
dev_err(&sl->dev, "%s: bad addr/len - addr=%#x len=%d\n",
__func__, addr, len);
return -1;
}
retry:
/* Write the data to the scratchpad */
if (w1_reset_select_slave(sl))
return -1;
wrbuf[0] = W1_F0D_WRITE_EEPROM;
wrbuf[1] = addr & 0xff;
wrbuf[2] = 0xff; /* ?? from Example */
w1_write_block(sl->master, wrbuf, sizeof(wrbuf));
w1_write_block(sl->master, data, len);
w1_read_block(sl->master, rdbuf, sizeof(rdbuf));
/* Compare what was read against the data written */
if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) {
if (--tries)
goto retry;
dev_err(&sl->dev,
"could not write to eeprom, scratchpad compare failed %d times\n",
W1_F0D_READ_RETRIES);
pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n",
__func__, rdbuf[0], rdbuf[1], data[0], data[1]);
return -1;
}
/* Trigger write out to EEPROM */
w1_write_8(sl->master, W1_F0D_RELEASE);
/* Sleep for tprog ms to wait for the write to complete */
msleep(W1_F0D_TPROG_MS);
/* Check CS (Command Status) == 0xAA ? */
cs = w1_read_8(sl->master);
if (cs != W1_F0D_CS_OK) {
dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs);
return -1;
}
return 0;
}
static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj,
const 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;
int copy;
count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE);
if (count == 0)
return 0;
mutex_lock(&sl->master->mutex);
/* Can only write data in blocks of the size of the scratchpad */
addr = off;
len = count;
while (len > 0) {
/* if len too short or addr not aligned */
if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) {
char tmp[W1_F0D_SCRATCH_SIZE];
/* read the block and update the parts to be written */
if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK,
W1_F0D_SCRATCH_SIZE, tmp)) {
count = -EIO;
goto out_up;
}
/* copy at most to the boundary of the PAGE or len */
copy = W1_F0D_SCRATCH_SIZE -
(addr & W1_F0D_SCRATCH_MASK);
if (copy > len)
copy = len;
memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy);
if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK,
W1_F0D_SCRATCH_SIZE, tmp) < 0) {
count = -EIO;
goto out_up;
}
} else {
copy = W1_F0D_SCRATCH_SIZE;
if (w1_f0d_write(sl, addr, copy, buf) < 0) {
count = -EIO;
goto out_up;
}
}
buf += copy;
addr += copy;
len -= copy;
}
out_up:
mutex_unlock(&sl->master->mutex);
return count;
}
static const struct bin_attribute w1_f0d_bin_attr = {
.attr = {
.name = "eeprom",
.mode = 0644,
},
.size = W1_F0D_EEPROM_SIZE,
.read = w1_f0d_read_bin,
.write = w1_f0d_write_bin,
};
static int w1_f0d_add_slave(struct w1_slave *sl)
{
return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
}
static void w1_f0d_remove_slave(struct w1_slave *sl)
{
sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr);
}
static const struct w1_family_ops w1_f0d_fops = {
.add_slave = w1_f0d_add_slave,
.remove_slave = w1_f0d_remove_slave,
};
static struct w1_family w1_family_0d = {
.fid = W1_EEPROM_DS2805,
.fops = &w1_f0d_fops,
};
module_w1_family(w1_family_0d);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com");
MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM");
+417
View File
@@ -0,0 +1,417 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds28e04.c - w1 family 1C (DS28E04) driver
*
* Copyright (c) 2012 Markus Franke <franke.m@sebakmt.com>
*/
#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/crc16.h>
#include <linux/uaccess.h>
#define CRC16_INIT 0
#define CRC16_VALID 0xb001
#include <linux/w1.h>
#define W1_FAMILY_DS28E04 0x1C
/* 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 bool w1_enable_crccheck = true;
#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 eeprom_read(struct file *filp, struct kobject *kobj,
const 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;
}
/**
* w1_f1C_write() - Writes to the scratchpad and reads it back for verification.
* @sl: The slave structure
* @addr: Address for the write
* @len: length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
* @data: The data to write
*
* Then copies the scratchpad to EEPROM.
* The data must be on one page.
* The master must be locked.
*
* 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 eeprom_write(struct file *filp, struct kobject *kobj,
const 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 const BIN_ATTR_RW(eeprom, W1_EEPROM_SIZE);
static ssize_t pio_read(struct file *filp, struct kobject *kobj,
const 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 pio_write(struct file *filp, struct kobject *kobj,
const 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 const BIN_ATTR_RW(pio, 1);
static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%d\n", w1_enable_crccheck);
}
static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int err = kstrtobool(buf, &w1_enable_crccheck);
if (err)
return err;
return count;
}
static DEVICE_ATTR_RW(crccheck);
static struct attribute *w1_f1C_attrs[] = {
&dev_attr_crccheck.attr,
NULL,
};
static const struct bin_attribute *const w1_f1C_bin_attrs[] = {
&bin_attr_eeprom,
&bin_attr_pio,
NULL,
};
static const struct attribute_group w1_f1C_group = {
.attrs = w1_f1C_attrs,
.bin_attrs = w1_f1C_bin_attrs,
};
static const struct attribute_group *w1_f1C_groups[] = {
&w1_f1C_group,
NULL,
};
static int w1_f1C_add_slave(struct w1_slave *sl)
{
struct w1_f1C_data *data = NULL;
if (w1_enable_crccheck) {
data = kzalloc_obj(struct w1_f1C_data);
if (!data)
return -ENOMEM;
sl->family_data = data;
}
return 0;
}
static void w1_f1C_remove_slave(struct w1_slave *sl)
{
kfree(sl->family_data);
sl->family_data = NULL;
}
static const struct w1_family_ops w1_f1C_fops = {
.add_slave = w1_f1C_add_slave,
.remove_slave = w1_f1C_remove_slave,
.groups = w1_f1C_groups,
};
static struct w1_family w1_family_1C = {
.fid = W1_FAMILY_DS28E04,
.fops = &w1_f1C_fops,
};
module_w1_family(w1_family_1C);
MODULE_AUTHOR("Markus Franke <franke.m@sebakmt.com>, <franm@hrz.tu-chemnitz.de>");
MODULE_DESCRIPTION("w1 family 1C driver for DS28E04, 4kb EEPROM and PIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E04));
+755
View File
@@ -0,0 +1,755 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* w1_ds28e17.c - w1 family 19 (DS28E17) driver
*
* Copyright (c) 2016 Jan Kandziora <jjj@gmx.de>
*/
#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 setup. */
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));
/* Default I2C speed to be set when a DS28E17 is detected. */
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");
/* Default I2C stretch value 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");
/* DS28E17 device command codes. */
#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
/* DS28E17 status bits */
#define W1_F19_STATUS_CRC 0x01
#define W1_F19_STATUS_ADDRESS 0x02
#define W1_F19_STATUS_START 0x08
/*
* Maximum number of I2C bytes to transfer within one CRC16 protected onewire
* command.
*/
#define W1_F19_WRITE_DATA_LIMIT 255
/* Maximum number of I2C bytes to read with one onewire command. */
#define W1_F19_READ_DATA_LIMIT 255
/* Constants for calculating the busy sleep. */
#define W1_F19_BUSY_TIMEBASES { 90, 23, 10 }
#define W1_F19_BUSY_GRATUITY 1000
/* Number of checks for the busy flag before timeout. */
#define W1_F19_BUSY_CHECKS 1000
/* Slave specific data. */
struct w1_f19_data {
u8 speed;
u8 stretch;
struct i2c_adapter adapter;
};
/* Wait a while until the busy flag clears. */
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;
/* Check the busy flag first in any case.*/
if (w1_touch_bit(sl->master, 1) == 0)
return 0;
/*
* Do a generously long sleep in the beginning,
* as we have to wait at least this time for all
* the I2C bytes at the given speed to be transferred.
*/
usleep_range(timebases[data->speed] * (data->stretch) * count,
timebases[data->speed] * (data->stretch) * count
+ W1_F19_BUSY_GRATUITY);
/* Now continusly check the busy flag sent by the DS28E17. */
checks = W1_F19_BUSY_CHECKS;
while ((checks--) > 0) {
/* Return success if the busy flag is cleared. */
if (w1_touch_bit(sl->master, 1) == 0)
return 0;
/* Wait one non-streched byte timeslot. */
udelay(timebases[data->speed]);
}
/* Timeout. */
dev_warn(&sl->dev, "busy timeout\n");
return -ETIMEDOUT;
}
/* Utility function: result. */
static size_t w1_f19_error(struct w1_slave *sl, u8 w1_buf[])
{
/* Warnings. */
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]);
}
/* Check error conditions. */
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;
/* All ok. */
return 0;
}
/* Utility function: write data to I2C slave, single chunk. */
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];
/* Send command and I2C data to DS28E17. */
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);
/* Wait until busy flag clears (or timeout). */
if (w1_f19_i2c_busy_wait(sl, count + 1) < 0)
return -ETIMEDOUT;
/* Read status from DS28E17. */
w1_read_block(sl->master, w1_buf, 2);
/* Check error conditions. */
error = w1_f19_error(sl, w1_buf);
if (error < 0)
return error;
/* Return number of bytes written. */
return count;
}
/* Write data to I2C slave. */
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];
/* Check input. */
if (count == 0)
return -EOPNOTSUPP;
/* Check whether we need multiple commands. */
if (count <= W1_F19_WRITE_DATA_LIMIT) {
/*
* Small data amount. Data can be sent with
* a single onewire command.
*/
/* Send all data to DS28E17. */
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 {
/* Large data amount. Data has to be sent in multiple chunks. */
/* Send first chunk to DS28E17. */
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;
/* Resume to same DS28E17. */
if (w1_reset_resume_command(sl->master))
return -EIO;
/* Next data chunk. */
p += W1_F19_WRITE_DATA_LIMIT;
remaining -= W1_F19_WRITE_DATA_LIMIT;
while (remaining > W1_F19_WRITE_DATA_LIMIT) {
/* Send intermediate chunk to DS28E17. */
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;
/* Resume to same DS28E17. */
if (w1_reset_resume_command(sl->master))
return -EIO;
/* Next data chunk. */
p += W1_F19_WRITE_DATA_LIMIT;
remaining -= W1_F19_WRITE_DATA_LIMIT;
}
/* Send final chunk to DS28E17. */
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;
}
/* Read data from I2C slave. */
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];
/* Check input. */
if (count == 0)
return -EOPNOTSUPP;
/* Send command to DS28E17. */
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);
/* Wait until busy flag clears (or timeout). */
if (w1_f19_i2c_busy_wait(sl, count + 1) < 0)
return -ETIMEDOUT;
/* Read status from DS28E17. */
w1_buf[0] = w1_read_8(sl->master);
w1_buf[1] = 0;
/* Check error conditions. */
error = w1_f19_error(sl, w1_buf);
if (error < 0)
return error;
/* Read received I2C data from DS28E17. */
return w1_read_block(sl->master, buffer, count);
}
/* Write to, then read data from I2C slave. */
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];
/* Check input. */
if (wcount == 0 || rcount == 0)
return -EOPNOTSUPP;
/* Send command and I2C data to DS28E17. */
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);
/* Wait until busy flag clears (or timeout). */
if (w1_f19_i2c_busy_wait(sl, wcount + rcount + 2) < 0)
return -ETIMEDOUT;
/* Read status from DS28E17. */
w1_read_block(sl->master, w1_buf, 2);
/* Check error conditions. */
error = w1_f19_error(sl, w1_buf);
if (error < 0)
return error;
/* Read received I2C data from DS28E17. */
return w1_read_block(sl->master, rbuffer, rcount);
}
/* Do an I2C master transfer. */
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;
/* Start onewire transaction. */
mutex_lock(&sl->master->bus_mutex);
/* Select DS28E17. */
if (w1_reset_select_slave(sl)) {
i = -EIO;
goto error;
}
/* Loop while there are still messages to transfer. */
while (i < num) {
/*
* Check for special case: Small write followed
* by read to same I2C device.
*/
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)) {
/*
* The DS28E17 has a combined transfer
* for small write+read.
*/
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;
}
/*
* Check if we should interpret the read data
* as a length byte. The DS28E17 unfortunately
* has no read without stop, so we can just do
* another simple read in that case.
*/
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;
}
}
/* Eat up read message, too. */
i++;
} else if (msgs[i].flags & I2C_M_RD) {
/* Read transfer. */
result = w1_f19_i2c_read(sl, msgs[i].addr,
msgs[i].buf, msgs[i].len);
if (result < 0) {
i = result;
goto error;
}
/*
* Check if we should interpret the read data
* as a length byte. The DS28E17 unfortunately
* has no read without stop, so we can just do
* another simple read in that case.
*/
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 {
/*
* Write transfer.
* Stop condition only for last
* transfer.
*/
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;
}
}
/* Next message. */
i++;
/* Are there still messages to send/receive? */
if (i < num) {
/* Yes. Resume to same DS28E17. */
if (w1_reset_resume_command(sl->master)) {
i = -EIO;
goto error;
}
}
}
error:
/* End onewire transaction. */
mutex_unlock(&sl->master->bus_mutex);
/* Return number of messages processed or error. */
return i;
}
/* Get I2C adapter functionality. */
static u32 w1_f19_i2c_functionality(struct i2c_adapter *adapter)
{
/*
* Plain I2C functions only.
* SMBus is emulated by the kernel's I2C layer.
* No "I2C_FUNC_SMBUS_QUICK"
* No "I2C_FUNC_SMBUS_READ_BLOCK_DATA"
* No "I2C_FUNC_SMBUS_BLOCK_PROC_CALL"
*/
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;
}
/* I2C adapter quirks. */
static const struct i2c_adapter_quirks w1_f19_i2c_adapter_quirks = {
.max_read_len = W1_F19_READ_DATA_LIMIT,
};
/* I2C algorithm. */
static const struct i2c_algorithm w1_f19_i2c_algorithm = {
.master_xfer = w1_f19_i2c_master_transfer,
.functionality = w1_f19_i2c_functionality,
};
/* Read I2C speed from DS28E17. */
static int w1_f19_get_i2c_speed(struct w1_slave *sl)
{
struct w1_f19_data *data = sl->family_data;
int result = -EIO;
/* Start onewire transaction. */
mutex_lock(&sl->master->bus_mutex);
/* Select slave. */
if (w1_reset_select_slave(sl))
goto error;
/* Read slave configuration byte. */
w1_write_8(sl->master, W1_F19_READ_CONFIGURATION);
result = w1_read_8(sl->master);
if (result < 0 || result > 2) {
result = -EIO;
goto error;
}
/* Update speed in slave specific data. */
data->speed = result;
error:
/* End onewire transaction. */
mutex_unlock(&sl->master->bus_mutex);
return result;
}
/* Set I2C speed on DS28E17. */
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];
/* Select slave. */
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);
/* Update speed in slave specific data. */
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;
/* Start onewire transaction. */
mutex_lock(&sl->master->bus_mutex);
/* Set I2C speed on DS28E17. */
result = __w1_f19_set_i2c_speed(sl, speed);
/* End onewire transaction. */
mutex_unlock(&sl->master->bus_mutex);
return result;
}
/* Sysfs attributes. */
/* I2C speed attribute for a single chip. */
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;
/* Read current speed from slave. Updates data->speed. */
result = w1_f19_get_i2c_speed(sl);
if (result < 0)
return result;
/* Return current speed value. */
return sysfs_emit(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;
/* Valid values are: "100", "400", "900" */
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;
/* Set speed on slave. */
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 bytes written. */
return count;
}
static DEVICE_ATTR_RW(speed);
/* Busy stretch attribute for a single chip. */
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 current stretch value. */
return sysfs_emit(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;
/* Valid values are '1' to '9' */
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;
/* Set busy stretch value. */
data->stretch = buf[0] & 0x0F;
/* Return bytes written. */
return count;
}
static DEVICE_ATTR_RW(stretch);
/* All attributes. */
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,
};
/* Slave add and remove functions. */
static int w1_f19_add_slave(struct w1_slave *sl)
{
struct w1_f19_data *data = NULL;
/* Allocate memory for slave specific data. */
data = devm_kzalloc(&sl->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
sl->family_data = data;
/* Setup default I2C speed on slave. */
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:
/*
* A i2c_speed module parameter of anything else
* than 100, 400, 900 means not to touch the
* speed of the DS28E17.
* We assume 400kBaud, the power-on value.
*/
data->speed = 1;
}
/*
* Setup default busy stretch
* configuration for the DS28E17.
*/
data->stretch = i2c_stretch;
/* Setup I2C adapter. */
data->adapter.owner = THIS_MODULE;
data->adapter.algo = &w1_f19_i2c_algorithm;
data->adapter.algo_data = sl;
scnprintf(data->adapter.name, sizeof(data->adapter.name), "w1-%s",
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;
/* Delete I2C adapter. */
i2c_del_adapter(&family_data->adapter);
/* Free slave specific data. */
devm_kfree(&sl->dev, family_data);
sl->family_data = NULL;
}
/* Declarations within the w1 subsystem. */
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(w1_family_19);
+59
View File
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* w1_smem.c
*
* Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
*/
#include <asm/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/w1.h>
#define W1_FAMILY_SMEM_01 0x01
#define W1_FAMILY_SMEM_81 0x81
static struct w1_family w1_smem_family_01 = {
.fid = W1_FAMILY_SMEM_01,
};
static struct w1_family w1_smem_family_81 = {
.fid = W1_FAMILY_SMEM_81,
};
static int __init w1_smem_init(void)
{
int err;
err = w1_register_family(&w1_smem_family_01);
if (err)
return err;
err = w1_register_family(&w1_smem_family_81);
if (err) {
w1_unregister_family(&w1_smem_family_01);
return err;
}
return 0;
}
static void __exit w1_smem_fini(void)
{
w1_unregister_family(&w1_smem_family_01);
w1_unregister_family(&w1_smem_family_81);
}
module_init(w1_smem_init);
module_exit(w1_smem_fini);
MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family.");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_01));
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_SMEM_81));
File diff suppressed because it is too large Load Diff