diff options
author | Josh Cartwright <joshc@codeaurora.org> | 2014-02-12 14:44:27 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-15 14:55:28 -0500 |
commit | c9afbb05a9ffbef0976242ab1135af6814ccff0f (patch) | |
tree | 2fc72c3dabe1bf6963661cdf6797a434464dfe1f | |
parent | 2c9e577d979b87b1c500a736b68319830a984a2f (diff) |
regmap: spmi: support base and extended register spaces
SPMI states that a slave may contain two register spaces, the Base
register space is a 5-bit byte-addressable space accessed via the
Register Read/Write and Register Zero Write command sequences, and the
Extended register space: a 16-bit byte-addressable space accessed via
the Extended Read/Write and Extended Read/Write Long command sequences.
Provide support for accessing both of these spaces, taking advantage of
the more bandwidth-efficient commands ('Register 0 Write' vs 'Register
Write', and 'Extended Register Read/Write' vs 'Extended Register
Read/Write Long') when possible.
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
Acked-by: Mark Brown <broonie@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/base/regmap/regmap-spmi.c | 228 | ||||
-rw-r--r-- | include/linux/regmap.h | 12 |
2 files changed, 205 insertions, 35 deletions
diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c index ac2391013db1..d7026dc33388 100644 --- a/drivers/base/regmap/regmap-spmi.c +++ b/drivers/base/regmap/regmap-spmi.c | |||
@@ -22,69 +22,235 @@ | |||
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | 24 | ||
25 | static int regmap_spmi_read(void *context, | 25 | static int regmap_spmi_base_read(void *context, |
26 | const void *reg, size_t reg_size, | 26 | const void *reg, size_t reg_size, |
27 | void *val, size_t val_size) | 27 | void *val, size_t val_size) |
28 | { | 28 | { |
29 | u8 addr = *(u8 *)reg; | ||
30 | int err = 0; | ||
31 | |||
32 | BUG_ON(reg_size != 1); | ||
33 | |||
34 | while (val_size-- && !err) | ||
35 | err = spmi_register_read(context, addr++, val++); | ||
36 | |||
37 | return err; | ||
38 | } | ||
39 | |||
40 | static int regmap_spmi_base_gather_write(void *context, | ||
41 | const void *reg, size_t reg_size, | ||
42 | const void *val, size_t val_size) | ||
43 | { | ||
44 | const u8 *data = val; | ||
45 | u8 addr = *(u8 *)reg; | ||
46 | int err = 0; | ||
47 | |||
48 | BUG_ON(reg_size != 1); | ||
49 | |||
50 | /* | ||
51 | * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence, | ||
52 | * use it when possible. | ||
53 | */ | ||
54 | if (addr == 0 && val_size) { | ||
55 | err = spmi_register_zero_write(context, *data); | ||
56 | if (err) | ||
57 | goto err_out; | ||
58 | |||
59 | data++; | ||
60 | addr++; | ||
61 | val_size--; | ||
62 | } | ||
63 | |||
64 | while (val_size) { | ||
65 | err = spmi_register_write(context, addr, *data); | ||
66 | if (err) | ||
67 | goto err_out; | ||
68 | |||
69 | data++; | ||
70 | addr++; | ||
71 | val_size--; | ||
72 | } | ||
73 | |||
74 | err_out: | ||
75 | return err; | ||
76 | } | ||
77 | |||
78 | static int regmap_spmi_base_write(void *context, const void *data, | ||
79 | size_t count) | ||
80 | { | ||
81 | BUG_ON(count < 1); | ||
82 | return regmap_spmi_base_gather_write(context, data, 1, data + 1, | ||
83 | count - 1); | ||
84 | } | ||
85 | |||
86 | static struct regmap_bus regmap_spmi_base = { | ||
87 | .read = regmap_spmi_base_read, | ||
88 | .write = regmap_spmi_base_write, | ||
89 | .gather_write = regmap_spmi_base_gather_write, | ||
90 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, | ||
91 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, | ||
92 | }; | ||
93 | |||
94 | /** | ||
95 | * regmap_init_spmi_base(): Create regmap for the Base register space | ||
96 | * @sdev: SPMI device that will be interacted with | ||
97 | * @config: Configuration for register map | ||
98 | * | ||
99 | * The return value will be an ERR_PTR() on error or a valid pointer to | ||
100 | * a struct regmap. | ||
101 | */ | ||
102 | struct regmap *regmap_init_spmi_base(struct spmi_device *sdev, | ||
103 | const struct regmap_config *config) | ||
104 | { | ||
105 | return regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); | ||
106 | } | ||
107 | EXPORT_SYMBOL_GPL(regmap_init_spmi_base); | ||
108 | |||
109 | /** | ||
110 | * devm_regmap_init_spmi_base(): Create managed regmap for Base register space | ||
111 | * @sdev: SPMI device that will be interacted with | ||
112 | * @config: Configuration for register map | ||
113 | * | ||
114 | * The return value will be an ERR_PTR() on error or a valid pointer | ||
115 | * to a struct regmap. The regmap will be automatically freed by the | ||
116 | * device management code. | ||
117 | */ | ||
118 | struct regmap *devm_regmap_init_spmi_base(struct spmi_device *sdev, | ||
119 | const struct regmap_config *config) | ||
120 | { | ||
121 | return devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); | ||
122 | } | ||
123 | EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base); | ||
124 | |||
125 | static int regmap_spmi_ext_read(void *context, | ||
126 | const void *reg, size_t reg_size, | ||
127 | void *val, size_t val_size) | ||
128 | { | ||
129 | int err = 0; | ||
130 | size_t len; | ||
131 | u16 addr; | ||
132 | |||
29 | BUG_ON(reg_size != 2); | 133 | BUG_ON(reg_size != 2); |
30 | return spmi_ext_register_readl(context, *(u16 *)reg, | 134 | |
31 | val, val_size); | 135 | addr = *(u16 *)reg; |
136 | |||
137 | /* | ||
138 | * Split accesses into two to take advantage of the more | ||
139 | * bandwidth-efficient 'Extended Register Read' command when possible | ||
140 | */ | ||
141 | while (addr <= 0xFF && val_size) { | ||
142 | len = min_t(size_t, val_size, 16); | ||
143 | |||
144 | err = spmi_ext_register_read(context, addr, val, len); | ||
145 | if (err) | ||
146 | goto err_out; | ||
147 | |||
148 | addr += len; | ||
149 | val += len; | ||
150 | val_size -= len; | ||
151 | } | ||
152 | |||
153 | while (val_size) { | ||
154 | len = min_t(size_t, val_size, 8); | ||
155 | |||
156 | err = spmi_ext_register_readl(context, addr, val, val_size); | ||
157 | if (err) | ||
158 | goto err_out; | ||
159 | |||
160 | addr += len; | ||
161 | val += len; | ||
162 | val_size -= len; | ||
163 | } | ||
164 | |||
165 | err_out: | ||
166 | return err; | ||
32 | } | 167 | } |
33 | 168 | ||
34 | static int regmap_spmi_gather_write(void *context, | 169 | static int regmap_spmi_ext_gather_write(void *context, |
35 | const void *reg, size_t reg_size, | 170 | const void *reg, size_t reg_size, |
36 | const void *val, size_t val_size) | 171 | const void *val, size_t val_size) |
37 | { | 172 | { |
173 | int err = 0; | ||
174 | size_t len; | ||
175 | u16 addr; | ||
176 | |||
38 | BUG_ON(reg_size != 2); | 177 | BUG_ON(reg_size != 2); |
39 | return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size); | 178 | |
179 | addr = *(u16 *)reg; | ||
180 | |||
181 | while (addr <= 0xFF && val_size) { | ||
182 | len = min_t(size_t, val_size, 16); | ||
183 | |||
184 | err = spmi_ext_register_write(context, addr, val, len); | ||
185 | if (err) | ||
186 | goto err_out; | ||
187 | |||
188 | addr += len; | ||
189 | val += len; | ||
190 | val_size -= len; | ||
191 | } | ||
192 | |||
193 | while (val_size) { | ||
194 | len = min_t(size_t, val_size, 8); | ||
195 | |||
196 | err = spmi_ext_register_writel(context, addr, val, len); | ||
197 | if (err) | ||
198 | goto err_out; | ||
199 | |||
200 | addr += len; | ||
201 | val += len; | ||
202 | val_size -= len; | ||
203 | } | ||
204 | |||
205 | err_out: | ||
206 | return err; | ||
40 | } | 207 | } |
41 | 208 | ||
42 | static int regmap_spmi_write(void *context, const void *data, | 209 | static int regmap_spmi_ext_write(void *context, const void *data, |
43 | size_t count) | 210 | size_t count) |
44 | { | 211 | { |
45 | BUG_ON(count < 2); | 212 | BUG_ON(count < 2); |
46 | return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2); | 213 | return regmap_spmi_ext_gather_write(context, data, 2, data + 2, |
214 | count - 2); | ||
47 | } | 215 | } |
48 | 216 | ||
49 | static struct regmap_bus regmap_spmi = { | 217 | static struct regmap_bus regmap_spmi_ext = { |
50 | .read = regmap_spmi_read, | 218 | .read = regmap_spmi_ext_read, |
51 | .write = regmap_spmi_write, | 219 | .write = regmap_spmi_ext_write, |
52 | .gather_write = regmap_spmi_gather_write, | 220 | .gather_write = regmap_spmi_ext_gather_write, |
53 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, | 221 | .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, |
54 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, | 222 | .val_format_endian_default = REGMAP_ENDIAN_NATIVE, |
55 | }; | 223 | }; |
56 | 224 | ||
57 | /** | 225 | /** |
58 | * regmap_init_spmi(): Initialize register map | 226 | * regmap_init_spmi_ext(): Create regmap for Ext register space |
59 | * | 227 | * @sdev: Device that will be interacted with |
60 | * @sdev: Device that will be interacted with | 228 | * @config: Configuration for register map |
61 | * @config: Configuration for register map | ||
62 | * | 229 | * |
63 | * The return value will be an ERR_PTR() on error or a valid pointer to | 230 | * The return value will be an ERR_PTR() on error or a valid pointer to |
64 | * a struct regmap. | 231 | * a struct regmap. |
65 | */ | 232 | */ |
66 | struct regmap *regmap_init_spmi(struct spmi_device *sdev, | 233 | struct regmap *regmap_init_spmi_ext(struct spmi_device *sdev, |
67 | const struct regmap_config *config) | 234 | const struct regmap_config *config) |
68 | { | 235 | { |
69 | return regmap_init(&sdev->dev, ®map_spmi, sdev, config); | 236 | return regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); |
70 | } | 237 | } |
71 | EXPORT_SYMBOL_GPL(regmap_init_spmi); | 238 | EXPORT_SYMBOL_GPL(regmap_init_spmi_ext); |
72 | 239 | ||
73 | /** | 240 | /** |
74 | * devm_regmap_init_spmi(): Initialise managed register map | 241 | * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space |
75 | * | 242 | * @sdev: SPMI device that will be interacted with |
76 | * @sdev: Device that will be interacted with | 243 | * @config: Configuration for register map |
77 | * @config: Configuration for register map | ||
78 | * | 244 | * |
79 | * The return value will be an ERR_PTR() on error or a valid pointer | 245 | * The return value will be an ERR_PTR() on error or a valid pointer |
80 | * to a struct regmap. The regmap will be automatically freed by the | 246 | * to a struct regmap. The regmap will be automatically freed by the |
81 | * device management code. | 247 | * device management code. |
82 | */ | 248 | */ |
83 | struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev, | 249 | struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *sdev, |
84 | const struct regmap_config *config) | 250 | const struct regmap_config *config) |
85 | { | 251 | { |
86 | return devm_regmap_init(&sdev->dev, ®map_spmi, sdev, config); | 252 | return devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); |
87 | } | 253 | } |
88 | EXPORT_SYMBOL_GPL(devm_regmap_init_spmi); | 254 | EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext); |
89 | 255 | ||
90 | MODULE_LICENSE("GPL"); | 256 | MODULE_LICENSE("GPL"); |
diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 4149f1a9b003..8cc73ac6f888 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h | |||
@@ -321,8 +321,10 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, | |||
321 | const struct regmap_config *config); | 321 | const struct regmap_config *config); |
322 | struct regmap *regmap_init_spi(struct spi_device *dev, | 322 | struct regmap *regmap_init_spi(struct spi_device *dev, |
323 | const struct regmap_config *config); | 323 | const struct regmap_config *config); |
324 | struct regmap *regmap_init_spmi(struct spmi_device *dev, | 324 | struct regmap *regmap_init_spmi_base(struct spmi_device *dev, |
325 | const struct regmap_config *config); | 325 | const struct regmap_config *config); |
326 | struct regmap *regmap_init_spmi_ext(struct spmi_device *dev, | ||
327 | const struct regmap_config *config); | ||
326 | struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, | 328 | struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, |
327 | void __iomem *regs, | 329 | void __iomem *regs, |
328 | const struct regmap_config *config); | 330 | const struct regmap_config *config); |
@@ -335,8 +337,10 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, | |||
335 | const struct regmap_config *config); | 337 | const struct regmap_config *config); |
336 | struct regmap *devm_regmap_init_spi(struct spi_device *dev, | 338 | struct regmap *devm_regmap_init_spi(struct spi_device *dev, |
337 | const struct regmap_config *config); | 339 | const struct regmap_config *config); |
338 | struct regmap *devm_regmap_init_spmi(struct spmi_device *dev, | 340 | struct regmap *devm_regmap_init_spmi_base(struct spmi_device *dev, |
339 | const struct regmap_config *config); | 341 | const struct regmap_config *config); |
342 | struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev, | ||
343 | const struct regmap_config *config); | ||
340 | struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, | 344 | struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, |
341 | void __iomem *regs, | 345 | void __iomem *regs, |
342 | const struct regmap_config *config); | 346 | const struct regmap_config *config); |