1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * w1_ds2406.c - w1 family 12 (DS2406) driver |
4 | * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net> |
5 | * |
6 | * Copyright (c) 2014 Scott Alfter <scott@alfter.us> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/moduleparam.h> |
12 | #include <linux/device.h> |
13 | #include <linux/types.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/crc16.h> |
17 | |
18 | #include <linux/w1.h> |
19 | |
20 | #define W1_FAMILY_DS2406 0x12 |
21 | |
22 | #define W1_F12_FUNC_READ_STATUS 0xAA |
23 | #define W1_F12_FUNC_WRITE_STATUS 0x55 |
24 | |
25 | static ssize_t w1_f12_read_state( |
26 | struct file *filp, struct kobject *kobj, |
27 | struct bin_attribute *bin_attr, |
28 | char *buf, loff_t off, size_t count) |
29 | { |
30 | u8 w1_buf[6] = {W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; |
31 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
32 | u16 crc = 0; |
33 | int i; |
34 | ssize_t rtnval = 1; |
35 | |
36 | if (off != 0) |
37 | return 0; |
38 | if (!buf) |
39 | return -EINVAL; |
40 | |
41 | mutex_lock(&sl->master->bus_mutex); |
42 | |
43 | if (w1_reset_select_slave(sl)) { |
44 | mutex_unlock(lock: &sl->master->bus_mutex); |
45 | return -EIO; |
46 | } |
47 | |
48 | w1_write_block(sl->master, w1_buf, 3); |
49 | w1_read_block(sl->master, w1_buf+3, 3); |
50 | for (i = 0; i < 6; i++) |
51 | crc = crc16_byte(crc, data: w1_buf[i]); |
52 | if (crc == 0xb001) /* good read? */ |
53 | *buf = ((w1_buf[3]>>5)&3)|0x30; |
54 | else |
55 | rtnval = -EIO; |
56 | |
57 | mutex_unlock(lock: &sl->master->bus_mutex); |
58 | |
59 | return rtnval; |
60 | } |
61 | |
62 | static ssize_t w1_f12_write_output( |
63 | struct file *filp, struct kobject *kobj, |
64 | struct bin_attribute *bin_attr, |
65 | char *buf, loff_t off, size_t count) |
66 | { |
67 | struct w1_slave *sl = kobj_to_w1_slave(kobj); |
68 | u8 w1_buf[6] = {W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; |
69 | u16 crc = 0; |
70 | int i; |
71 | ssize_t rtnval = 1; |
72 | |
73 | if (count != 1 || off != 0) |
74 | return -EFAULT; |
75 | |
76 | mutex_lock(&sl->master->bus_mutex); |
77 | |
78 | if (w1_reset_select_slave(sl)) { |
79 | mutex_unlock(lock: &sl->master->bus_mutex); |
80 | return -EIO; |
81 | } |
82 | |
83 | w1_buf[3] = (((*buf)&3)<<5)|0x1F; |
84 | w1_write_block(sl->master, w1_buf, 4); |
85 | w1_read_block(sl->master, w1_buf+4, 2); |
86 | for (i = 0; i < 6; i++) |
87 | crc = crc16_byte(crc, data: w1_buf[i]); |
88 | if (crc == 0xb001) /* good read? */ |
89 | w1_write_8(sl->master, 0xFF); |
90 | else |
91 | rtnval = -EIO; |
92 | |
93 | mutex_unlock(lock: &sl->master->bus_mutex); |
94 | return rtnval; |
95 | } |
96 | |
97 | #define NB_SYSFS_BIN_FILES 2 |
98 | static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { |
99 | { |
100 | .attr = { |
101 | .name = "state" , |
102 | .mode = 0444, |
103 | }, |
104 | .size = 1, |
105 | .read = w1_f12_read_state, |
106 | }, |
107 | { |
108 | .attr = { |
109 | .name = "output" , |
110 | .mode = 0664, |
111 | }, |
112 | .size = 1, |
113 | .write = w1_f12_write_output, |
114 | } |
115 | }; |
116 | |
117 | static int w1_f12_add_slave(struct w1_slave *sl) |
118 | { |
119 | int err = 0; |
120 | int i; |
121 | |
122 | for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) |
123 | err = sysfs_create_bin_file( |
124 | kobj: &sl->dev.kobj, |
125 | attr: &(w1_f12_sysfs_bin_files[i])); |
126 | if (err) |
127 | while (--i >= 0) |
128 | sysfs_remove_bin_file(kobj: &sl->dev.kobj, |
129 | attr: &(w1_f12_sysfs_bin_files[i])); |
130 | return err; |
131 | } |
132 | |
133 | static void w1_f12_remove_slave(struct w1_slave *sl) |
134 | { |
135 | int i; |
136 | |
137 | for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) |
138 | sysfs_remove_bin_file(kobj: &sl->dev.kobj, |
139 | attr: &(w1_f12_sysfs_bin_files[i])); |
140 | } |
141 | |
142 | static const struct w1_family_ops w1_f12_fops = { |
143 | .add_slave = w1_f12_add_slave, |
144 | .remove_slave = w1_f12_remove_slave, |
145 | }; |
146 | |
147 | static struct w1_family w1_family_12 = { |
148 | .fid = W1_FAMILY_DS2406, |
149 | .fops = &w1_f12_fops, |
150 | }; |
151 | module_w1_family(w1_family_12); |
152 | |
153 | MODULE_AUTHOR("Scott Alfter <scott@alfter.us>" ); |
154 | MODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO" ); |
155 | MODULE_LICENSE("GPL" ); |
156 | |