1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Register map access API - W1 (1-Wire) support |
4 | // |
5 | // Copyright (c) 2017 Radioavionica Corporation |
6 | // Author: Alex A. Mihaylov <minimumlaw@rambler.ru> |
7 | |
8 | #include <linux/regmap.h> |
9 | #include <linux/module.h> |
10 | #include <linux/w1.h> |
11 | |
12 | #include "internal.h" |
13 | |
14 | #define W1_CMD_READ_DATA 0x69 |
15 | #define W1_CMD_WRITE_DATA 0x6C |
16 | |
17 | /* |
18 | * 1-Wire slaves registers with addess 8 bit and data 8 bit |
19 | */ |
20 | |
21 | static int w1_reg_a8_v8_read(void *context, unsigned int reg, unsigned int *val) |
22 | { |
23 | struct device *dev = context; |
24 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
25 | int ret = 0; |
26 | |
27 | if (reg > 255) |
28 | return -EINVAL; |
29 | |
30 | mutex_lock(&sl->master->bus_mutex); |
31 | if (!w1_reset_select_slave(sl)) { |
32 | w1_write_8(sl->master, W1_CMD_READ_DATA); |
33 | w1_write_8(sl->master, reg); |
34 | *val = w1_read_8(sl->master); |
35 | } else { |
36 | ret = -ENODEV; |
37 | } |
38 | mutex_unlock(lock: &sl->master->bus_mutex); |
39 | |
40 | return ret; |
41 | } |
42 | |
43 | static int w1_reg_a8_v8_write(void *context, unsigned int reg, unsigned int val) |
44 | { |
45 | struct device *dev = context; |
46 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
47 | int ret = 0; |
48 | |
49 | if (reg > 255) |
50 | return -EINVAL; |
51 | |
52 | mutex_lock(&sl->master->bus_mutex); |
53 | if (!w1_reset_select_slave(sl)) { |
54 | w1_write_8(sl->master, W1_CMD_WRITE_DATA); |
55 | w1_write_8(sl->master, reg); |
56 | w1_write_8(sl->master, val); |
57 | } else { |
58 | ret = -ENODEV; |
59 | } |
60 | mutex_unlock(lock: &sl->master->bus_mutex); |
61 | |
62 | return ret; |
63 | } |
64 | |
65 | /* |
66 | * 1-Wire slaves registers with addess 8 bit and data 16 bit |
67 | */ |
68 | |
69 | static int w1_reg_a8_v16_read(void *context, unsigned int reg, |
70 | unsigned int *val) |
71 | { |
72 | struct device *dev = context; |
73 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
74 | int ret = 0; |
75 | |
76 | if (reg > 255) |
77 | return -EINVAL; |
78 | |
79 | mutex_lock(&sl->master->bus_mutex); |
80 | if (!w1_reset_select_slave(sl)) { |
81 | w1_write_8(sl->master, W1_CMD_READ_DATA); |
82 | w1_write_8(sl->master, reg); |
83 | *val = w1_read_8(sl->master); |
84 | *val |= w1_read_8(sl->master)<<8; |
85 | } else { |
86 | ret = -ENODEV; |
87 | } |
88 | mutex_unlock(lock: &sl->master->bus_mutex); |
89 | |
90 | return ret; |
91 | } |
92 | |
93 | static int w1_reg_a8_v16_write(void *context, unsigned int reg, |
94 | unsigned int val) |
95 | { |
96 | struct device *dev = context; |
97 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
98 | int ret = 0; |
99 | |
100 | if (reg > 255) |
101 | return -EINVAL; |
102 | |
103 | mutex_lock(&sl->master->bus_mutex); |
104 | if (!w1_reset_select_slave(sl)) { |
105 | w1_write_8(sl->master, W1_CMD_WRITE_DATA); |
106 | w1_write_8(sl->master, reg); |
107 | w1_write_8(sl->master, val & 0x00FF); |
108 | w1_write_8(sl->master, val>>8 & 0x00FF); |
109 | } else { |
110 | ret = -ENODEV; |
111 | } |
112 | mutex_unlock(lock: &sl->master->bus_mutex); |
113 | |
114 | return ret; |
115 | } |
116 | |
117 | /* |
118 | * 1-Wire slaves registers with addess 16 bit and data 16 bit |
119 | */ |
120 | |
121 | static int w1_reg_a16_v16_read(void *context, unsigned int reg, |
122 | unsigned int *val) |
123 | { |
124 | struct device *dev = context; |
125 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
126 | int ret = 0; |
127 | |
128 | if (reg > 65535) |
129 | return -EINVAL; |
130 | |
131 | mutex_lock(&sl->master->bus_mutex); |
132 | if (!w1_reset_select_slave(sl)) { |
133 | w1_write_8(sl->master, W1_CMD_READ_DATA); |
134 | w1_write_8(sl->master, reg & 0x00FF); |
135 | w1_write_8(sl->master, reg>>8 & 0x00FF); |
136 | *val = w1_read_8(sl->master); |
137 | *val |= w1_read_8(sl->master)<<8; |
138 | } else { |
139 | ret = -ENODEV; |
140 | } |
141 | mutex_unlock(lock: &sl->master->bus_mutex); |
142 | |
143 | return ret; |
144 | } |
145 | |
146 | static int w1_reg_a16_v16_write(void *context, unsigned int reg, |
147 | unsigned int val) |
148 | { |
149 | struct device *dev = context; |
150 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
151 | int ret = 0; |
152 | |
153 | if (reg > 65535) |
154 | return -EINVAL; |
155 | |
156 | mutex_lock(&sl->master->bus_mutex); |
157 | if (!w1_reset_select_slave(sl)) { |
158 | w1_write_8(sl->master, W1_CMD_WRITE_DATA); |
159 | w1_write_8(sl->master, reg & 0x00FF); |
160 | w1_write_8(sl->master, reg>>8 & 0x00FF); |
161 | w1_write_8(sl->master, val & 0x00FF); |
162 | w1_write_8(sl->master, val>>8 & 0x00FF); |
163 | } else { |
164 | ret = -ENODEV; |
165 | } |
166 | mutex_unlock(lock: &sl->master->bus_mutex); |
167 | |
168 | return ret; |
169 | } |
170 | |
171 | /* |
172 | * Various types of supported bus addressing |
173 | */ |
174 | |
175 | static const struct regmap_bus regmap_w1_bus_a8_v8 = { |
176 | .reg_read = w1_reg_a8_v8_read, |
177 | .reg_write = w1_reg_a8_v8_write, |
178 | }; |
179 | |
180 | static const struct regmap_bus regmap_w1_bus_a8_v16 = { |
181 | .reg_read = w1_reg_a8_v16_read, |
182 | .reg_write = w1_reg_a8_v16_write, |
183 | }; |
184 | |
185 | static const struct regmap_bus regmap_w1_bus_a16_v16 = { |
186 | .reg_read = w1_reg_a16_v16_read, |
187 | .reg_write = w1_reg_a16_v16_write, |
188 | }; |
189 | |
190 | static const struct regmap_bus *regmap_get_w1_bus(struct device *w1_dev, |
191 | const struct regmap_config *config) |
192 | { |
193 | if (config->reg_bits == 8 && config->val_bits == 8) |
194 | return ®map_w1_bus_a8_v8; |
195 | |
196 | if (config->reg_bits == 8 && config->val_bits == 16) |
197 | return ®map_w1_bus_a8_v16; |
198 | |
199 | if (config->reg_bits == 16 && config->val_bits == 16) |
200 | return ®map_w1_bus_a16_v16; |
201 | |
202 | return ERR_PTR(error: -ENOTSUPP); |
203 | } |
204 | |
205 | struct regmap *__regmap_init_w1(struct device *w1_dev, |
206 | const struct regmap_config *config, |
207 | struct lock_class_key *lock_key, |
208 | const char *lock_name) |
209 | { |
210 | |
211 | const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config); |
212 | |
213 | if (IS_ERR(ptr: bus)) |
214 | return ERR_CAST(ptr: bus); |
215 | |
216 | return __regmap_init(dev: w1_dev, bus, bus_context: w1_dev, config, |
217 | lock_key, lock_name); |
218 | } |
219 | EXPORT_SYMBOL_GPL(__regmap_init_w1); |
220 | |
221 | struct regmap *__devm_regmap_init_w1(struct device *w1_dev, |
222 | const struct regmap_config *config, |
223 | struct lock_class_key *lock_key, |
224 | const char *lock_name) |
225 | { |
226 | |
227 | const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config); |
228 | |
229 | if (IS_ERR(ptr: bus)) |
230 | return ERR_CAST(ptr: bus); |
231 | |
232 | return __devm_regmap_init(dev: w1_dev, bus, bus_context: w1_dev, config, |
233 | lock_key, lock_name); |
234 | } |
235 | EXPORT_SYMBOL_GPL(__devm_regmap_init_w1); |
236 | |
237 | MODULE_LICENSE("GPL" ); |
238 | |