1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Register map access API - SPMI support |
4 | // |
5 | // Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
6 | // |
7 | // Based on regmap-i2c.c: |
8 | // Copyright 2011 Wolfson Microelectronics plc |
9 | // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
10 | |
11 | #include <linux/regmap.h> |
12 | #include <linux/spmi.h> |
13 | #include <linux/module.h> |
14 | #include <linux/init.h> |
15 | |
16 | static int regmap_spmi_base_read(void *context, |
17 | const void *reg, size_t reg_size, |
18 | void *val, size_t val_size) |
19 | { |
20 | u8 addr = *(u8 *)reg; |
21 | int err = 0; |
22 | |
23 | BUG_ON(reg_size != 1); |
24 | |
25 | while (val_size-- && !err) |
26 | err = spmi_register_read(sdev: context, addr: addr++, buf: val++); |
27 | |
28 | return err; |
29 | } |
30 | |
31 | static int regmap_spmi_base_gather_write(void *context, |
32 | const void *reg, size_t reg_size, |
33 | const void *val, size_t val_size) |
34 | { |
35 | const u8 *data = val; |
36 | u8 addr = *(u8 *)reg; |
37 | int err = 0; |
38 | |
39 | BUG_ON(reg_size != 1); |
40 | |
41 | /* |
42 | * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence, |
43 | * use it when possible. |
44 | */ |
45 | if (addr == 0 && val_size) { |
46 | err = spmi_register_zero_write(sdev: context, data: *data); |
47 | if (err) |
48 | goto err_out; |
49 | |
50 | data++; |
51 | addr++; |
52 | val_size--; |
53 | } |
54 | |
55 | while (val_size) { |
56 | err = spmi_register_write(sdev: context, addr, data: *data); |
57 | if (err) |
58 | goto err_out; |
59 | |
60 | data++; |
61 | addr++; |
62 | val_size--; |
63 | } |
64 | |
65 | err_out: |
66 | return err; |
67 | } |
68 | |
69 | static int regmap_spmi_base_write(void *context, const void *data, |
70 | size_t count) |
71 | { |
72 | BUG_ON(count < 1); |
73 | return regmap_spmi_base_gather_write(context, reg: data, reg_size: 1, val: data + 1, |
74 | val_size: count - 1); |
75 | } |
76 | |
77 | static const struct regmap_bus regmap_spmi_base = { |
78 | .read = regmap_spmi_base_read, |
79 | .write = regmap_spmi_base_write, |
80 | .gather_write = regmap_spmi_base_gather_write, |
81 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, |
82 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, |
83 | }; |
84 | |
85 | struct regmap *__regmap_init_spmi_base(struct spmi_device *sdev, |
86 | const struct regmap_config *config, |
87 | struct lock_class_key *lock_key, |
88 | const char *lock_name) |
89 | { |
90 | return __regmap_init(dev: &sdev->dev, bus: ®map_spmi_base, bus_context: sdev, config, |
91 | lock_key, lock_name); |
92 | } |
93 | EXPORT_SYMBOL_GPL(__regmap_init_spmi_base); |
94 | |
95 | struct regmap *__devm_regmap_init_spmi_base(struct spmi_device *sdev, |
96 | const struct regmap_config *config, |
97 | struct lock_class_key *lock_key, |
98 | const char *lock_name) |
99 | { |
100 | return __devm_regmap_init(dev: &sdev->dev, bus: ®map_spmi_base, bus_context: sdev, config, |
101 | lock_key, lock_name); |
102 | } |
103 | EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base); |
104 | |
105 | static int regmap_spmi_ext_read(void *context, |
106 | const void *reg, size_t reg_size, |
107 | void *val, size_t val_size) |
108 | { |
109 | int err = 0; |
110 | size_t len; |
111 | u16 addr; |
112 | |
113 | BUG_ON(reg_size != 2); |
114 | |
115 | addr = *(u16 *)reg; |
116 | |
117 | /* |
118 | * Split accesses into two to take advantage of the more |
119 | * bandwidth-efficient 'Extended Register Read' command when possible |
120 | */ |
121 | while (addr <= 0xFF && val_size) { |
122 | len = min_t(size_t, val_size, 16); |
123 | |
124 | err = spmi_ext_register_read(sdev: context, addr, buf: val, len); |
125 | if (err) |
126 | goto err_out; |
127 | |
128 | addr += len; |
129 | val += len; |
130 | val_size -= len; |
131 | } |
132 | |
133 | while (val_size) { |
134 | len = min_t(size_t, val_size, 8); |
135 | |
136 | err = spmi_ext_register_readl(sdev: context, addr, buf: val, len); |
137 | if (err) |
138 | goto err_out; |
139 | |
140 | addr += len; |
141 | val += len; |
142 | val_size -= len; |
143 | } |
144 | |
145 | err_out: |
146 | return err; |
147 | } |
148 | |
149 | static int regmap_spmi_ext_gather_write(void *context, |
150 | const void *reg, size_t reg_size, |
151 | const void *val, size_t val_size) |
152 | { |
153 | int err = 0; |
154 | size_t len; |
155 | u16 addr; |
156 | |
157 | BUG_ON(reg_size != 2); |
158 | |
159 | addr = *(u16 *)reg; |
160 | |
161 | while (addr <= 0xFF && val_size) { |
162 | len = min_t(size_t, val_size, 16); |
163 | |
164 | err = spmi_ext_register_write(sdev: context, addr, buf: val, len); |
165 | if (err) |
166 | goto err_out; |
167 | |
168 | addr += len; |
169 | val += len; |
170 | val_size -= len; |
171 | } |
172 | |
173 | while (val_size) { |
174 | len = min_t(size_t, val_size, 8); |
175 | |
176 | err = spmi_ext_register_writel(sdev: context, addr, buf: val, len); |
177 | if (err) |
178 | goto err_out; |
179 | |
180 | addr += len; |
181 | val += len; |
182 | val_size -= len; |
183 | } |
184 | |
185 | err_out: |
186 | return err; |
187 | } |
188 | |
189 | static int regmap_spmi_ext_write(void *context, const void *data, |
190 | size_t count) |
191 | { |
192 | BUG_ON(count < 2); |
193 | return regmap_spmi_ext_gather_write(context, reg: data, reg_size: 2, val: data + 2, |
194 | val_size: count - 2); |
195 | } |
196 | |
197 | static const struct regmap_bus regmap_spmi_ext = { |
198 | .read = regmap_spmi_ext_read, |
199 | .write = regmap_spmi_ext_write, |
200 | .gather_write = regmap_spmi_ext_gather_write, |
201 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, |
202 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, |
203 | }; |
204 | |
205 | struct regmap *__regmap_init_spmi_ext(struct spmi_device *sdev, |
206 | const struct regmap_config *config, |
207 | struct lock_class_key *lock_key, |
208 | const char *lock_name) |
209 | { |
210 | return __regmap_init(dev: &sdev->dev, bus: ®map_spmi_ext, bus_context: sdev, config, |
211 | lock_key, lock_name); |
212 | } |
213 | EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext); |
214 | |
215 | struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *sdev, |
216 | const struct regmap_config *config, |
217 | struct lock_class_key *lock_key, |
218 | const char *lock_name) |
219 | { |
220 | return __devm_regmap_init(dev: &sdev->dev, bus: ®map_spmi_ext, bus_context: sdev, config, |
221 | lock_key, lock_name); |
222 | } |
223 | EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext); |
224 | |
225 | MODULE_LICENSE("GPL" ); |
226 | |