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