]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/w1/slaves/w1_ds2405.c
w1: add DS2405 addressable switch driver
[mirror_ubuntu-artful-kernel.git] / drivers / w1 / slaves / w1_ds2405.c
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 static int __init w1_ds2405_init(void)
228 {
229 return w1_register_family(&w1_family_ds2405);
230 }
231
232 static void __exit w1_ds2405_fini(void)
233 {
234 w1_unregister_family(&w1_family_ds2405);
235 }
236
237 module_init(w1_ds2405_init);
238 module_exit(w1_ds2405_fini);