162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * w1_ds2413.c - w1 family 3a (DS2413) driver 462306a36Sopenharmony_ci * based on w1_ds2408.c by Jean-Francois Dagenais <dagenaisj@sonatest.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2013 Mariusz Bialonczyk <manio@skyboo.net> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/w1.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define W1_FAMILY_DS2413 0x3A 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define W1_F3A_RETRIES 3 2262306a36Sopenharmony_ci#define W1_F3A_FUNC_PIO_ACCESS_READ 0xF5 2362306a36Sopenharmony_ci#define W1_F3A_FUNC_PIO_ACCESS_WRITE 0x5A 2462306a36Sopenharmony_ci#define W1_F3A_SUCCESS_CONFIRM_BYTE 0xAA 2562306a36Sopenharmony_ci#define W1_F3A_INVALID_PIO_STATE 0xFF 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic ssize_t state_read(struct file *filp, struct kobject *kobj, 2862306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, loff_t off, 2962306a36Sopenharmony_ci size_t count) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct w1_slave *sl = kobj_to_w1_slave(kobj); 3262306a36Sopenharmony_ci unsigned int retries = W1_F3A_RETRIES; 3362306a36Sopenharmony_ci ssize_t bytes_read = -EIO; 3462306a36Sopenharmony_ci u8 state; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci dev_dbg(&sl->dev, 3762306a36Sopenharmony_ci "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p", 3862306a36Sopenharmony_ci bin_attr->attr.name, kobj, (unsigned int)off, count, buf); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (off != 0) 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci if (!buf) 4362306a36Sopenharmony_ci return -EINVAL; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci mutex_lock(&sl->master->bus_mutex); 4662306a36Sopenharmony_ci dev_dbg(&sl->dev, "mutex locked"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cinext: 4962306a36Sopenharmony_ci if (w1_reset_select_slave(sl)) 5062306a36Sopenharmony_ci goto out; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci while (retries--) { 5362306a36Sopenharmony_ci w1_write_8(sl->master, W1_F3A_FUNC_PIO_ACCESS_READ); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci state = w1_read_8(sl->master); 5662306a36Sopenharmony_ci if ((state & 0x0F) == ((~state >> 4) & 0x0F)) { 5762306a36Sopenharmony_ci /* complement is correct */ 5862306a36Sopenharmony_ci *buf = state; 5962306a36Sopenharmony_ci bytes_read = 1; 6062306a36Sopenharmony_ci goto out; 6162306a36Sopenharmony_ci } else if (state == W1_F3A_INVALID_PIO_STATE) { 6262306a36Sopenharmony_ci /* slave didn't respond, try to select it again */ 6362306a36Sopenharmony_ci dev_warn(&sl->dev, "slave device did not respond to PIO_ACCESS_READ, " \ 6462306a36Sopenharmony_ci "reselecting, retries left: %d\n", retries); 6562306a36Sopenharmony_ci goto next; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (w1_reset_resume_command(sl->master)) 6962306a36Sopenharmony_ci goto out; /* unrecoverable error */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci dev_warn(&sl->dev, "PIO_ACCESS_READ error, retries left: %d\n", retries); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciout: 7562306a36Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 7662306a36Sopenharmony_ci dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n", 7762306a36Sopenharmony_ci (bytes_read > 0) ? "succeeded" : "error", retries); 7862306a36Sopenharmony_ci return bytes_read; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic BIN_ATTR_RO(state, 1); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic ssize_t output_write(struct file *filp, struct kobject *kobj, 8462306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 8562306a36Sopenharmony_ci loff_t off, size_t count) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct w1_slave *sl = kobj_to_w1_slave(kobj); 8862306a36Sopenharmony_ci u8 w1_buf[3]; 8962306a36Sopenharmony_ci unsigned int retries = W1_F3A_RETRIES; 9062306a36Sopenharmony_ci ssize_t bytes_written = -EIO; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (count != 1 || off != 0) 9362306a36Sopenharmony_ci return -EFAULT; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci dev_dbg(&sl->dev, "locking mutex for write_output"); 9662306a36Sopenharmony_ci mutex_lock(&sl->master->bus_mutex); 9762306a36Sopenharmony_ci dev_dbg(&sl->dev, "mutex locked"); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (w1_reset_select_slave(sl)) 10062306a36Sopenharmony_ci goto out; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * according to the DS2413 datasheet the most significant 6 bits 10462306a36Sopenharmony_ci * should be set to "1"s, so do it now 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci *buf = *buf | 0xFC; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci while (retries--) { 10962306a36Sopenharmony_ci w1_buf[0] = W1_F3A_FUNC_PIO_ACCESS_WRITE; 11062306a36Sopenharmony_ci w1_buf[1] = *buf; 11162306a36Sopenharmony_ci w1_buf[2] = ~(*buf); 11262306a36Sopenharmony_ci w1_write_block(sl->master, w1_buf, 3); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (w1_read_8(sl->master) == W1_F3A_SUCCESS_CONFIRM_BYTE) { 11562306a36Sopenharmony_ci bytes_written = 1; 11662306a36Sopenharmony_ci goto out; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci if (w1_reset_resume_command(sl->master)) 11962306a36Sopenharmony_ci goto out; /* unrecoverable error */ 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci dev_warn(&sl->dev, "PIO_ACCESS_WRITE error, retries left: %d\n", retries); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciout: 12562306a36Sopenharmony_ci mutex_unlock(&sl->master->bus_mutex); 12662306a36Sopenharmony_ci dev_dbg(&sl->dev, "%s, mutex unlocked, retries: %d\n", 12762306a36Sopenharmony_ci (bytes_written > 0) ? "succeeded" : "error", retries); 12862306a36Sopenharmony_ci return bytes_written; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic BIN_ATTR(output, 0664, NULL, output_write, 1); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct bin_attribute *w1_f3a_bin_attrs[] = { 13462306a36Sopenharmony_ci &bin_attr_state, 13562306a36Sopenharmony_ci &bin_attr_output, 13662306a36Sopenharmony_ci NULL, 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic const struct attribute_group w1_f3a_group = { 14062306a36Sopenharmony_ci .bin_attrs = w1_f3a_bin_attrs, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const struct attribute_group *w1_f3a_groups[] = { 14462306a36Sopenharmony_ci &w1_f3a_group, 14562306a36Sopenharmony_ci NULL, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic const struct w1_family_ops w1_f3a_fops = { 14962306a36Sopenharmony_ci .groups = w1_f3a_groups, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct w1_family w1_family_3a = { 15362306a36Sopenharmony_ci .fid = W1_FAMILY_DS2413, 15462306a36Sopenharmony_ci .fops = &w1_f3a_fops, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_cimodule_w1_family(w1_family_3a); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciMODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>"); 15962306a36Sopenharmony_ciMODULE_DESCRIPTION("w1 family 3a driver for DS2413 2 Pin IO"); 16062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 16162306a36Sopenharmony_ciMODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2413)); 162