xref: /linux/drivers/w1/slaves/w1_ds2405.c (revision 905e46acd3272d04566fec49afbd7ad9e2ed9ae3)
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