18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * w1_ds2405.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Maciej S. Szmigiero <mail@maciej.szmigiero.name> 68c2ecf20Sopenharmony_ci * Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/w1.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define W1_FAMILY_DS2405 0x05 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>"); 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for 1-wire Dallas DS2405 PIO."); 248c2ecf20Sopenharmony_ciMODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2405)); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int w1_ds2405_select(struct w1_slave *sl, bool only_active) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct w1_master *dev = sl->master; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num); 318c2ecf20Sopenharmony_ci unsigned int bit_ctr; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (w1_reset_bus(dev) != 0) 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* 378c2ecf20Sopenharmony_ci * We cannot use a normal Match ROM command 388c2ecf20Sopenharmony_ci * since doing so would toggle PIO state 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci w1_write_8(dev, only_active ? W1_ALARM_SEARCH : W1_SEARCH); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci for (bit_ctr = 0; bit_ctr < 64; bit_ctr++) { 438c2ecf20Sopenharmony_ci int bit2send = !!(dev_addr & BIT(bit_ctr)); 448c2ecf20Sopenharmony_ci u8 ret; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci ret = w1_triplet(dev, bit2send); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if ((ret & (BIT(0) | BIT(1))) == 498c2ecf20Sopenharmony_ci (BIT(0) | BIT(1))) /* no devices found */ 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!!(ret & BIT(2)) != bit2send) 538c2ecf20Sopenharmony_ci /* wrong direction taken - no such device */ 548c2ecf20Sopenharmony_ci return 0; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return 1; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int w1_ds2405_read_pio(struct w1_slave *sl) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci if (w1_ds2405_select(sl, true)) 638c2ecf20Sopenharmony_ci return 0; /* "active" means PIO is low */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (w1_ds2405_select(sl, false)) 668c2ecf20Sopenharmony_ci return 1; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return -ENODEV; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *device, 728c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct w1_slave *sl = dev_to_w1_slave(device); 758c2ecf20Sopenharmony_ci struct w1_master *dev = sl->master; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci ssize_t f_retval; 798c2ecf20Sopenharmony_ci u8 state; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&dev->bus_mutex); 828c2ecf20Sopenharmony_ci if (ret) 838c2ecf20Sopenharmony_ci return ret; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!w1_ds2405_select(sl, false)) { 868c2ecf20Sopenharmony_ci f_retval = -ENODEV; 878c2ecf20Sopenharmony_ci goto out_unlock; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci state = w1_read_8(dev); 918c2ecf20Sopenharmony_ci if (state != 0 && 928c2ecf20Sopenharmony_ci state != 0xff) { 938c2ecf20Sopenharmony_ci dev_err(device, "non-consistent state %x\n", state); 948c2ecf20Sopenharmony_ci f_retval = -EIO; 958c2ecf20Sopenharmony_ci goto out_unlock; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci *buf = state ? '1' : '0'; 998c2ecf20Sopenharmony_ci f_retval = 1; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciout_unlock: 1028c2ecf20Sopenharmony_ci w1_reset_bus(dev); 1038c2ecf20Sopenharmony_ci mutex_unlock(&dev->bus_mutex); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return f_retval; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic ssize_t output_show(struct device *device, 1098c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct w1_slave *sl = dev_to_w1_slave(device); 1128c2ecf20Sopenharmony_ci struct w1_master *dev = sl->master; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci int ret; 1158c2ecf20Sopenharmony_ci ssize_t f_retval; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&dev->bus_mutex); 1188c2ecf20Sopenharmony_ci if (ret) 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ret = w1_ds2405_read_pio(sl); 1228c2ecf20Sopenharmony_ci if (ret < 0) { 1238c2ecf20Sopenharmony_ci f_retval = ret; 1248c2ecf20Sopenharmony_ci goto out_unlock; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci *buf = ret ? '1' : '0'; 1288c2ecf20Sopenharmony_ci f_retval = 1; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciout_unlock: 1318c2ecf20Sopenharmony_ci w1_reset_bus(dev); 1328c2ecf20Sopenharmony_ci mutex_unlock(&dev->bus_mutex); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return f_retval; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic ssize_t output_store(struct device *device, 1388c2ecf20Sopenharmony_ci struct device_attribute *attr, 1398c2ecf20Sopenharmony_ci const char *buf, size_t count) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct w1_slave *sl = dev_to_w1_slave(device); 1428c2ecf20Sopenharmony_ci struct w1_master *dev = sl->master; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci int ret, current_pio; 1458c2ecf20Sopenharmony_ci unsigned int val; 1468c2ecf20Sopenharmony_ci ssize_t f_retval; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (count < 1) 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (sscanf(buf, " %u%n", &val, &ret) < 1) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (val != 0 && val != 1) 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci f_retval = ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&dev->bus_mutex); 1608c2ecf20Sopenharmony_ci if (ret) 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci current_pio = w1_ds2405_read_pio(sl); 1648c2ecf20Sopenharmony_ci if (current_pio < 0) { 1658c2ecf20Sopenharmony_ci f_retval = current_pio; 1668c2ecf20Sopenharmony_ci goto out_unlock; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (current_pio == val) 1708c2ecf20Sopenharmony_ci goto out_unlock; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (w1_reset_bus(dev) != 0) { 1738c2ecf20Sopenharmony_ci f_retval = -ENODEV; 1748c2ecf20Sopenharmony_ci goto out_unlock; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * can't use w1_reset_select_slave() here since it uses Skip ROM if 1798c2ecf20Sopenharmony_ci * there is only one device on bus 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci do { 1828c2ecf20Sopenharmony_ci u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num); 1838c2ecf20Sopenharmony_ci u8 cmd[9]; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci cmd[0] = W1_MATCH_ROM; 1868c2ecf20Sopenharmony_ci memcpy(&cmd[1], &dev_addr, sizeof(dev_addr)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci w1_write_block(dev, cmd, sizeof(cmd)); 1898c2ecf20Sopenharmony_ci } while (0); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciout_unlock: 1928c2ecf20Sopenharmony_ci w1_reset_bus(dev); 1938c2ecf20Sopenharmony_ci mutex_unlock(&dev->bus_mutex); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return f_retval; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(state); 1998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(output); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic struct attribute *w1_ds2405_attrs[] = { 2028c2ecf20Sopenharmony_ci &dev_attr_state.attr, 2038c2ecf20Sopenharmony_ci &dev_attr_output.attr, 2048c2ecf20Sopenharmony_ci NULL 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(w1_ds2405); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic const struct w1_family_ops w1_ds2405_fops = { 2108c2ecf20Sopenharmony_ci .groups = w1_ds2405_groups 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic struct w1_family w1_family_ds2405 = { 2148c2ecf20Sopenharmony_ci .fid = W1_FAMILY_DS2405, 2158c2ecf20Sopenharmony_ci .fops = &w1_ds2405_fops 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cimodule_w1_family(w1_family_ds2405); 219