Commit | Line | Data |
---|---|---|
37613fa5 GKH |
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 | ||
a01779f8 JC |
11 | #include <linux/regmap.h> |
12 | #include <linux/spmi.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/init.h> | |
15 | ||
c9afbb05 JC |
16 | static int regmap_spmi_base_read(void *context, |
17 | const void *reg, size_t reg_size, | |
18 | void *val, size_t val_size) | |
a01779f8 | 19 | { |
c9afbb05 JC |
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(context, addr++, 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(context, *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(context, addr, *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, data, 1, data + 1, | |
74 | count - 1); | |
75 | } | |
76 | ||
9c2e5cb3 | 77 | static const struct regmap_bus regmap_spmi_base = { |
c9afbb05 JC |
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 | ||
3cfe7a74 NB |
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) | |
c9afbb05 | 89 | { |
3cfe7a74 NB |
90 | return __regmap_init(&sdev->dev, ®map_spmi_base, sdev, config, |
91 | lock_key, lock_name); | |
c9afbb05 | 92 | } |
3cfe7a74 | 93 | EXPORT_SYMBOL_GPL(__regmap_init_spmi_base); |
c9afbb05 | 94 | |
3cfe7a74 NB |
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) | |
c9afbb05 | 99 | { |
3cfe7a74 NB |
100 | return __devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config, |
101 | lock_key, lock_name); | |
c9afbb05 | 102 | } |
3cfe7a74 | 103 | EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_base); |
c9afbb05 JC |
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 | ||
a01779f8 | 113 | BUG_ON(reg_size != 2); |
c9afbb05 JC |
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(context, addr, 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 | ||
dec8e8f6 | 136 | err = spmi_ext_register_readl(context, addr, val, len); |
c9afbb05 JC |
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; | |
a01779f8 JC |
147 | } |
148 | ||
c9afbb05 JC |
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) | |
a01779f8 | 152 | { |
c9afbb05 JC |
153 | int err = 0; |
154 | size_t len; | |
155 | u16 addr; | |
156 | ||
a01779f8 | 157 | BUG_ON(reg_size != 2); |
c9afbb05 JC |
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(context, addr, 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(context, addr, 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; | |
a01779f8 JC |
187 | } |
188 | ||
c9afbb05 JC |
189 | static int regmap_spmi_ext_write(void *context, const void *data, |
190 | size_t count) | |
a01779f8 JC |
191 | { |
192 | BUG_ON(count < 2); | |
c9afbb05 JC |
193 | return regmap_spmi_ext_gather_write(context, data, 2, data + 2, |
194 | count - 2); | |
a01779f8 JC |
195 | } |
196 | ||
9c2e5cb3 | 197 | static const struct regmap_bus regmap_spmi_ext = { |
c9afbb05 JC |
198 | .read = regmap_spmi_ext_read, |
199 | .write = regmap_spmi_ext_write, | |
200 | .gather_write = regmap_spmi_ext_gather_write, | |
a01779f8 JC |
201 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, |
202 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, | |
203 | }; | |
204 | ||
3cfe7a74 NB |
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) | |
a01779f8 | 209 | { |
3cfe7a74 NB |
210 | return __regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config, |
211 | lock_key, lock_name); | |
a01779f8 | 212 | } |
3cfe7a74 | 213 | EXPORT_SYMBOL_GPL(__regmap_init_spmi_ext); |
a01779f8 | 214 | |
3cfe7a74 NB |
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) | |
a01779f8 | 219 | { |
3cfe7a74 NB |
220 | return __devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config, |
221 | lock_key, lock_name); | |
a01779f8 | 222 | } |
3cfe7a74 | 223 | EXPORT_SYMBOL_GPL(__devm_regmap_init_spmi_ext); |
a01779f8 JC |
224 | |
225 | MODULE_LICENSE("GPL"); |