w1_ds2405.c (4344B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * w1_ds2405.c 4 * 5 * Copyright (c) 2017 Maciej S. Szmigiero <mail@maciej.szmigiero.name> 6 * Based on w1_therm.c copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> 7 */ 8 9#include <linux/device.h> 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/moduleparam.h> 13#include <linux/mutex.h> 14#include <linux/string.h> 15#include <linux/types.h> 16 17#include <linux/w1.h> 18 19#define W1_FAMILY_DS2405 0x05 20 21MODULE_LICENSE("GPL"); 22MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>"); 23MODULE_DESCRIPTION("Driver for 1-wire Dallas DS2405 PIO."); 24MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2405)); 25 26static int w1_ds2405_select(struct w1_slave *sl, bool only_active) 27{ 28 struct w1_master *dev = sl->master; 29 30 u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num); 31 unsigned int bit_ctr; 32 33 if (w1_reset_bus(dev) != 0) 34 return 0; 35 36 /* 37 * We cannot use a normal Match ROM command 38 * since doing so would toggle PIO state 39 */ 40 w1_write_8(dev, only_active ? W1_ALARM_SEARCH : W1_SEARCH); 41 42 for (bit_ctr = 0; bit_ctr < 64; bit_ctr++) { 43 int bit2send = !!(dev_addr & BIT(bit_ctr)); 44 u8 ret; 45 46 ret = w1_triplet(dev, bit2send); 47 48 if ((ret & (BIT(0) | BIT(1))) == 49 (BIT(0) | BIT(1))) /* no devices found */ 50 return 0; 51 52 if (!!(ret & BIT(2)) != bit2send) 53 /* wrong direction taken - no such device */ 54 return 0; 55 } 56 57 return 1; 58} 59 60static int w1_ds2405_read_pio(struct w1_slave *sl) 61{ 62 if (w1_ds2405_select(sl, true)) 63 return 0; /* "active" means PIO is low */ 64 65 if (w1_ds2405_select(sl, false)) 66 return 1; 67 68 return -ENODEV; 69} 70 71static ssize_t state_show(struct device *device, 72 struct device_attribute *attr, char *buf) 73{ 74 struct w1_slave *sl = dev_to_w1_slave(device); 75 struct w1_master *dev = sl->master; 76 77 int ret; 78 ssize_t f_retval; 79 u8 state; 80 81 ret = mutex_lock_interruptible(&dev->bus_mutex); 82 if (ret) 83 return ret; 84 85 if (!w1_ds2405_select(sl, false)) { 86 f_retval = -ENODEV; 87 goto out_unlock; 88 } 89 90 state = w1_read_8(dev); 91 if (state != 0 && 92 state != 0xff) { 93 dev_err(device, "non-consistent state %x\n", state); 94 f_retval = -EIO; 95 goto out_unlock; 96 } 97 98 *buf = state ? '1' : '0'; 99 f_retval = 1; 100 101out_unlock: 102 w1_reset_bus(dev); 103 mutex_unlock(&dev->bus_mutex); 104 105 return f_retval; 106} 107 108static ssize_t output_show(struct device *device, 109 struct device_attribute *attr, char *buf) 110{ 111 struct w1_slave *sl = dev_to_w1_slave(device); 112 struct w1_master *dev = sl->master; 113 114 int ret; 115 ssize_t f_retval; 116 117 ret = mutex_lock_interruptible(&dev->bus_mutex); 118 if (ret) 119 return ret; 120 121 ret = w1_ds2405_read_pio(sl); 122 if (ret < 0) { 123 f_retval = ret; 124 goto out_unlock; 125 } 126 127 *buf = ret ? '1' : '0'; 128 f_retval = 1; 129 130out_unlock: 131 w1_reset_bus(dev); 132 mutex_unlock(&dev->bus_mutex); 133 134 return f_retval; 135} 136 137static ssize_t output_store(struct device *device, 138 struct device_attribute *attr, 139 const char *buf, size_t count) 140{ 141 struct w1_slave *sl = dev_to_w1_slave(device); 142 struct w1_master *dev = sl->master; 143 144 int ret, current_pio; 145 unsigned int val; 146 ssize_t f_retval; 147 148 if (count < 1) 149 return -EINVAL; 150 151 if (sscanf(buf, " %u%n", &val, &ret) < 1) 152 return -EINVAL; 153 154 if (val != 0 && val != 1) 155 return -EINVAL; 156 157 f_retval = ret; 158 159 ret = mutex_lock_interruptible(&dev->bus_mutex); 160 if (ret) 161 return ret; 162 163 current_pio = w1_ds2405_read_pio(sl); 164 if (current_pio < 0) { 165 f_retval = current_pio; 166 goto out_unlock; 167 } 168 169 if (current_pio == val) 170 goto out_unlock; 171 172 if (w1_reset_bus(dev) != 0) { 173 f_retval = -ENODEV; 174 goto out_unlock; 175 } 176 177 /* 178 * can't use w1_reset_select_slave() here since it uses Skip ROM if 179 * there is only one device on bus 180 */ 181 do { 182 u64 dev_addr = le64_to_cpu(*(u64 *)&sl->reg_num); 183 u8 cmd[9]; 184 185 cmd[0] = W1_MATCH_ROM; 186 memcpy(&cmd[1], &dev_addr, sizeof(dev_addr)); 187 188 w1_write_block(dev, cmd, sizeof(cmd)); 189 } while (0); 190 191out_unlock: 192 w1_reset_bus(dev); 193 mutex_unlock(&dev->bus_mutex); 194 195 return f_retval; 196} 197 198static DEVICE_ATTR_RO(state); 199static DEVICE_ATTR_RW(output); 200 201static struct attribute *w1_ds2405_attrs[] = { 202 &dev_attr_state.attr, 203 &dev_attr_output.attr, 204 NULL 205}; 206 207ATTRIBUTE_GROUPS(w1_ds2405); 208 209static const struct w1_family_ops w1_ds2405_fops = { 210 .groups = w1_ds2405_groups 211}; 212 213static struct w1_family w1_family_ds2405 = { 214 .fid = W1_FAMILY_DS2405, 215 .fops = &w1_ds2405_fops 216}; 217 218module_w1_family(w1_family_ds2405);