1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Register map access API - I2C support |
4 | // |
5 | // Copyright 2011 Wolfson Microelectronics plc |
6 | // |
7 | // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | |
9 | #include <linux/regmap.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/module.h> |
12 | |
13 | #include "internal.h" |
14 | |
15 | static int regmap_smbus_byte_reg_read(void *context, unsigned int reg, |
16 | unsigned int *val) |
17 | { |
18 | struct device *dev = context; |
19 | struct i2c_client *i2c = to_i2c_client(dev); |
20 | int ret; |
21 | |
22 | if (reg > 0xff) |
23 | return -EINVAL; |
24 | |
25 | ret = i2c_smbus_read_byte_data(client: i2c, command: reg); |
26 | if (ret < 0) |
27 | return ret; |
28 | |
29 | *val = ret; |
30 | |
31 | return 0; |
32 | } |
33 | |
34 | static int regmap_smbus_byte_reg_write(void *context, unsigned int reg, |
35 | unsigned int val) |
36 | { |
37 | struct device *dev = context; |
38 | struct i2c_client *i2c = to_i2c_client(dev); |
39 | |
40 | if (val > 0xff || reg > 0xff) |
41 | return -EINVAL; |
42 | |
43 | return i2c_smbus_write_byte_data(client: i2c, command: reg, value: val); |
44 | } |
45 | |
46 | static const struct regmap_bus regmap_smbus_byte = { |
47 | .reg_write = regmap_smbus_byte_reg_write, |
48 | .reg_read = regmap_smbus_byte_reg_read, |
49 | }; |
50 | |
51 | static int regmap_smbus_word_reg_read(void *context, unsigned int reg, |
52 | unsigned int *val) |
53 | { |
54 | struct device *dev = context; |
55 | struct i2c_client *i2c = to_i2c_client(dev); |
56 | int ret; |
57 | |
58 | if (reg > 0xff) |
59 | return -EINVAL; |
60 | |
61 | ret = i2c_smbus_read_word_data(client: i2c, command: reg); |
62 | if (ret < 0) |
63 | return ret; |
64 | |
65 | *val = ret; |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | static int regmap_smbus_word_reg_write(void *context, unsigned int reg, |
71 | unsigned int val) |
72 | { |
73 | struct device *dev = context; |
74 | struct i2c_client *i2c = to_i2c_client(dev); |
75 | |
76 | if (val > 0xffff || reg > 0xff) |
77 | return -EINVAL; |
78 | |
79 | return i2c_smbus_write_word_data(client: i2c, command: reg, value: val); |
80 | } |
81 | |
82 | static const struct regmap_bus regmap_smbus_word = { |
83 | .reg_write = regmap_smbus_word_reg_write, |
84 | .reg_read = regmap_smbus_word_reg_read, |
85 | }; |
86 | |
87 | static int regmap_smbus_word_read_swapped(void *context, unsigned int reg, |
88 | unsigned int *val) |
89 | { |
90 | struct device *dev = context; |
91 | struct i2c_client *i2c = to_i2c_client(dev); |
92 | int ret; |
93 | |
94 | if (reg > 0xff) |
95 | return -EINVAL; |
96 | |
97 | ret = i2c_smbus_read_word_swapped(client: i2c, command: reg); |
98 | if (ret < 0) |
99 | return ret; |
100 | |
101 | *val = ret; |
102 | |
103 | return 0; |
104 | } |
105 | |
106 | static int regmap_smbus_word_write_swapped(void *context, unsigned int reg, |
107 | unsigned int val) |
108 | { |
109 | struct device *dev = context; |
110 | struct i2c_client *i2c = to_i2c_client(dev); |
111 | |
112 | if (val > 0xffff || reg > 0xff) |
113 | return -EINVAL; |
114 | |
115 | return i2c_smbus_write_word_swapped(client: i2c, command: reg, value: val); |
116 | } |
117 | |
118 | static const struct regmap_bus regmap_smbus_word_swapped = { |
119 | .reg_write = regmap_smbus_word_write_swapped, |
120 | .reg_read = regmap_smbus_word_read_swapped, |
121 | }; |
122 | |
123 | static int regmap_i2c_write(void *context, const void *data, size_t count) |
124 | { |
125 | struct device *dev = context; |
126 | struct i2c_client *i2c = to_i2c_client(dev); |
127 | int ret; |
128 | |
129 | ret = i2c_master_send(client: i2c, buf: data, count); |
130 | if (ret == count) |
131 | return 0; |
132 | else if (ret < 0) |
133 | return ret; |
134 | else |
135 | return -EIO; |
136 | } |
137 | |
138 | static int regmap_i2c_gather_write(void *context, |
139 | const void *reg, size_t reg_size, |
140 | const void *val, size_t val_size) |
141 | { |
142 | struct device *dev = context; |
143 | struct i2c_client *i2c = to_i2c_client(dev); |
144 | struct i2c_msg xfer[2]; |
145 | int ret; |
146 | |
147 | /* If the I2C controller can't do a gather tell the core, it |
148 | * will substitute in a linear write for us. |
149 | */ |
150 | if (!i2c_check_functionality(adap: i2c->adapter, I2C_FUNC_NOSTART)) |
151 | return -ENOTSUPP; |
152 | |
153 | xfer[0].addr = i2c->addr; |
154 | xfer[0].flags = 0; |
155 | xfer[0].len = reg_size; |
156 | xfer[0].buf = (void *)reg; |
157 | |
158 | xfer[1].addr = i2c->addr; |
159 | xfer[1].flags = I2C_M_NOSTART; |
160 | xfer[1].len = val_size; |
161 | xfer[1].buf = (void *)val; |
162 | |
163 | ret = i2c_transfer(adap: i2c->adapter, msgs: xfer, num: 2); |
164 | if (ret == 2) |
165 | return 0; |
166 | if (ret < 0) |
167 | return ret; |
168 | else |
169 | return -EIO; |
170 | } |
171 | |
172 | static int regmap_i2c_read(void *context, |
173 | const void *reg, size_t reg_size, |
174 | void *val, size_t val_size) |
175 | { |
176 | struct device *dev = context; |
177 | struct i2c_client *i2c = to_i2c_client(dev); |
178 | struct i2c_msg xfer[2]; |
179 | int ret; |
180 | |
181 | xfer[0].addr = i2c->addr; |
182 | xfer[0].flags = 0; |
183 | xfer[0].len = reg_size; |
184 | xfer[0].buf = (void *)reg; |
185 | |
186 | xfer[1].addr = i2c->addr; |
187 | xfer[1].flags = I2C_M_RD; |
188 | xfer[1].len = val_size; |
189 | xfer[1].buf = val; |
190 | |
191 | ret = i2c_transfer(adap: i2c->adapter, msgs: xfer, num: 2); |
192 | if (ret == 2) |
193 | return 0; |
194 | else if (ret < 0) |
195 | return ret; |
196 | else |
197 | return -EIO; |
198 | } |
199 | |
200 | static const struct regmap_bus regmap_i2c = { |
201 | .write = regmap_i2c_write, |
202 | .gather_write = regmap_i2c_gather_write, |
203 | .read = regmap_i2c_read, |
204 | .reg_format_endian_default = REGMAP_ENDIAN_BIG, |
205 | .val_format_endian_default = REGMAP_ENDIAN_BIG, |
206 | }; |
207 | |
208 | static int regmap_i2c_smbus_i2c_write(void *context, const void *data, |
209 | size_t count) |
210 | { |
211 | struct device *dev = context; |
212 | struct i2c_client *i2c = to_i2c_client(dev); |
213 | |
214 | if (count < 1) |
215 | return -EINVAL; |
216 | |
217 | --count; |
218 | return i2c_smbus_write_i2c_block_data(client: i2c, command: ((u8 *)data)[0], length: count, |
219 | values: ((u8 *)data + 1)); |
220 | } |
221 | |
222 | static int regmap_i2c_smbus_i2c_read(void *context, const void *reg, |
223 | size_t reg_size, void *val, |
224 | size_t val_size) |
225 | { |
226 | struct device *dev = context; |
227 | struct i2c_client *i2c = to_i2c_client(dev); |
228 | int ret; |
229 | |
230 | if (reg_size != 1 || val_size < 1) |
231 | return -EINVAL; |
232 | |
233 | ret = i2c_smbus_read_i2c_block_data(client: i2c, command: ((u8 *)reg)[0], length: val_size, values: val); |
234 | if (ret == val_size) |
235 | return 0; |
236 | else if (ret < 0) |
237 | return ret; |
238 | else |
239 | return -EIO; |
240 | } |
241 | |
242 | static const struct regmap_bus regmap_i2c_smbus_i2c_block = { |
243 | .write = regmap_i2c_smbus_i2c_write, |
244 | .read = regmap_i2c_smbus_i2c_read, |
245 | .max_raw_read = I2C_SMBUS_BLOCK_MAX - 1, |
246 | .max_raw_write = I2C_SMBUS_BLOCK_MAX - 1, |
247 | }; |
248 | |
249 | static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data, |
250 | size_t count) |
251 | { |
252 | struct device *dev = context; |
253 | struct i2c_client *i2c = to_i2c_client(dev); |
254 | |
255 | if (count < 2) |
256 | return -EINVAL; |
257 | |
258 | count--; |
259 | return i2c_smbus_write_i2c_block_data(client: i2c, command: ((u8 *)data)[0], length: count, |
260 | values: (u8 *)data + 1); |
261 | } |
262 | |
263 | static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg, |
264 | size_t reg_size, void *val, |
265 | size_t val_size) |
266 | { |
267 | struct device *dev = context; |
268 | struct i2c_client *i2c = to_i2c_client(dev); |
269 | int ret, count, len = val_size; |
270 | |
271 | if (reg_size != 2) |
272 | return -EINVAL; |
273 | |
274 | ret = i2c_smbus_write_byte_data(client: i2c, command: ((u16 *)reg)[0] & 0xff, |
275 | value: ((u16 *)reg)[0] >> 8); |
276 | if (ret < 0) |
277 | return ret; |
278 | |
279 | count = 0; |
280 | do { |
281 | /* Current Address Read */ |
282 | ret = i2c_smbus_read_byte(client: i2c); |
283 | if (ret < 0) |
284 | break; |
285 | |
286 | *((u8 *)val++) = ret; |
287 | count++; |
288 | len--; |
289 | } while (len > 0); |
290 | |
291 | if (count == val_size) |
292 | return 0; |
293 | else if (ret < 0) |
294 | return ret; |
295 | else |
296 | return -EIO; |
297 | } |
298 | |
299 | static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = { |
300 | .write = regmap_i2c_smbus_i2c_write_reg16, |
301 | .read = regmap_i2c_smbus_i2c_read_reg16, |
302 | .max_raw_read = I2C_SMBUS_BLOCK_MAX - 2, |
303 | .max_raw_write = I2C_SMBUS_BLOCK_MAX - 2, |
304 | }; |
305 | |
306 | static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, |
307 | const struct regmap_config *config) |
308 | { |
309 | const struct i2c_adapter_quirks *quirks; |
310 | const struct regmap_bus *bus = NULL; |
311 | struct regmap_bus *ret_bus; |
312 | u16 max_read = 0, max_write = 0; |
313 | |
314 | if (i2c_check_functionality(adap: i2c->adapter, I2C_FUNC_I2C)) |
315 | bus = ®map_i2c; |
316 | else if (config->val_bits == 8 && config->reg_bits == 8 && |
317 | i2c_check_functionality(adap: i2c->adapter, |
318 | I2C_FUNC_SMBUS_I2C_BLOCK)) |
319 | bus = ®map_i2c_smbus_i2c_block; |
320 | else if (config->val_bits == 8 && config->reg_bits == 16 && |
321 | i2c_check_functionality(adap: i2c->adapter, |
322 | I2C_FUNC_SMBUS_I2C_BLOCK)) |
323 | bus = ®map_i2c_smbus_i2c_block_reg16; |
324 | else if (config->val_bits == 16 && config->reg_bits == 8 && |
325 | i2c_check_functionality(adap: i2c->adapter, |
326 | I2C_FUNC_SMBUS_WORD_DATA)) |
327 | switch (regmap_get_val_endian(dev: &i2c->dev, NULL, config)) { |
328 | case REGMAP_ENDIAN_LITTLE: |
329 | bus = ®map_smbus_word; |
330 | break; |
331 | case REGMAP_ENDIAN_BIG: |
332 | bus = ®map_smbus_word_swapped; |
333 | break; |
334 | default: /* everything else is not supported */ |
335 | break; |
336 | } |
337 | else if (config->val_bits == 8 && config->reg_bits == 8 && |
338 | i2c_check_functionality(adap: i2c->adapter, |
339 | I2C_FUNC_SMBUS_BYTE_DATA)) |
340 | bus = ®map_smbus_byte; |
341 | |
342 | if (!bus) |
343 | return ERR_PTR(error: -ENOTSUPP); |
344 | |
345 | quirks = i2c->adapter->quirks; |
346 | if (quirks) { |
347 | if (quirks->max_read_len && |
348 | (bus->max_raw_read == 0 || bus->max_raw_read > quirks->max_read_len)) |
349 | max_read = quirks->max_read_len; |
350 | |
351 | if (quirks->max_write_len && |
352 | (bus->max_raw_write == 0 || bus->max_raw_write > quirks->max_write_len)) |
353 | max_write = quirks->max_write_len; |
354 | |
355 | if (max_read || max_write) { |
356 | ret_bus = kmemdup(p: bus, size: sizeof(*bus), GFP_KERNEL); |
357 | if (!ret_bus) |
358 | return ERR_PTR(error: -ENOMEM); |
359 | ret_bus->free_on_exit = true; |
360 | ret_bus->max_raw_read = max_read; |
361 | ret_bus->max_raw_write = max_write; |
362 | bus = ret_bus; |
363 | } |
364 | } |
365 | |
366 | return bus; |
367 | } |
368 | |
369 | struct regmap *__regmap_init_i2c(struct i2c_client *i2c, |
370 | const struct regmap_config *config, |
371 | struct lock_class_key *lock_key, |
372 | const char *lock_name) |
373 | { |
374 | const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); |
375 | |
376 | if (IS_ERR(ptr: bus)) |
377 | return ERR_CAST(ptr: bus); |
378 | |
379 | return __regmap_init(dev: &i2c->dev, bus, bus_context: &i2c->dev, config, |
380 | lock_key, lock_name); |
381 | } |
382 | EXPORT_SYMBOL_GPL(__regmap_init_i2c); |
383 | |
384 | struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c, |
385 | const struct regmap_config *config, |
386 | struct lock_class_key *lock_key, |
387 | const char *lock_name) |
388 | { |
389 | const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config); |
390 | |
391 | if (IS_ERR(ptr: bus)) |
392 | return ERR_CAST(ptr: bus); |
393 | |
394 | return __devm_regmap_init(dev: &i2c->dev, bus, bus_context: &i2c->dev, config, |
395 | lock_key, lock_name); |
396 | } |
397 | EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c); |
398 | |
399 | MODULE_LICENSE("GPL" ); |
400 | |