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