diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/regmap/Kconfig | 2 | ||||
-rw-r--r-- | drivers/base/regmap/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 2 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-lzo.c | 361 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 1 |
5 files changed, 367 insertions, 1 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index fabbf6cc5367..2fc6a66f39a4 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig | |||
@@ -4,6 +4,8 @@ | |||
4 | 4 | ||
5 | config REGMAP | 5 | config REGMAP |
6 | default y if (REGMAP_I2C || REGMAP_SPI) | 6 | default y if (REGMAP_I2C || REGMAP_SPI) |
7 | select LZO_COMPRESS | ||
8 | select LZO_DECOMPRESS | ||
7 | bool | 9 | bool |
8 | 10 | ||
9 | config REGMAP_I2C | 11 | config REGMAP_I2C |
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 20cd65035fc7..0573c8a9dacb 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o | 1 | obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o |
2 | obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o | 2 | obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o |
3 | obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o | 3 | obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o |
4 | obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o | 4 | obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o |
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 7ef8afc77e7c..2d51b1b099f7 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h | |||
@@ -120,4 +120,6 @@ int regcache_insert_reg(struct regmap *map, unsigned int reg, | |||
120 | 120 | ||
121 | extern struct regcache_ops regcache_indexed_ops; | 121 | extern struct regcache_ops regcache_indexed_ops; |
122 | extern struct regcache_ops regcache_rbtree_ops; | 122 | extern struct regcache_ops regcache_rbtree_ops; |
123 | extern struct regcache_ops regcache_lzo_ops; | ||
124 | |||
123 | #endif | 125 | #endif |
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c new file mode 100644 index 000000000000..9079cb50b0b9 --- /dev/null +++ b/drivers/base/regmap/regcache-lzo.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * Register cache access API - LZO caching support | ||
3 | * | ||
4 | * Copyright 2011 Wolfson Microelectronics plc | ||
5 | * | ||
6 | * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/slab.h> | ||
14 | #include <linux/lzo.h> | ||
15 | |||
16 | #include "internal.h" | ||
17 | |||
18 | struct regcache_lzo_ctx { | ||
19 | void *wmem; | ||
20 | void *dst; | ||
21 | const void *src; | ||
22 | size_t src_len; | ||
23 | size_t dst_len; | ||
24 | size_t decompressed_size; | ||
25 | unsigned long *sync_bmp; | ||
26 | int sync_bmp_nbits; | ||
27 | }; | ||
28 | |||
29 | #define LZO_BLOCK_NUM 8 | ||
30 | static int regcache_lzo_block_count(void) | ||
31 | { | ||
32 | return LZO_BLOCK_NUM; | ||
33 | } | ||
34 | |||
35 | static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx) | ||
36 | { | ||
37 | lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); | ||
38 | if (!lzo_ctx->wmem) | ||
39 | return -ENOMEM; | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx) | ||
44 | { | ||
45 | size_t compress_size; | ||
46 | int ret; | ||
47 | |||
48 | ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len, | ||
49 | lzo_ctx->dst, &compress_size, lzo_ctx->wmem); | ||
50 | if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len) | ||
51 | return -EINVAL; | ||
52 | lzo_ctx->dst_len = compress_size; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx) | ||
57 | { | ||
58 | size_t dst_len; | ||
59 | int ret; | ||
60 | |||
61 | dst_len = lzo_ctx->dst_len; | ||
62 | ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len, | ||
63 | lzo_ctx->dst, &dst_len); | ||
64 | if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len) | ||
65 | return -EINVAL; | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int regcache_lzo_compress_cache_block(struct regmap *map, | ||
70 | struct regcache_lzo_ctx *lzo_ctx) | ||
71 | { | ||
72 | int ret; | ||
73 | |||
74 | lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE); | ||
75 | lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL); | ||
76 | if (!lzo_ctx->dst) { | ||
77 | lzo_ctx->dst_len = 0; | ||
78 | return -ENOMEM; | ||
79 | } | ||
80 | |||
81 | ret = regcache_lzo_compress(lzo_ctx); | ||
82 | if (ret < 0) | ||
83 | return ret; | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int regcache_lzo_decompress_cache_block(struct regmap *map, | ||
88 | struct regcache_lzo_ctx *lzo_ctx) | ||
89 | { | ||
90 | int ret; | ||
91 | |||
92 | lzo_ctx->dst_len = lzo_ctx->decompressed_size; | ||
93 | lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL); | ||
94 | if (!lzo_ctx->dst) { | ||
95 | lzo_ctx->dst_len = 0; | ||
96 | return -ENOMEM; | ||
97 | } | ||
98 | |||
99 | ret = regcache_lzo_decompress(lzo_ctx); | ||
100 | if (ret < 0) | ||
101 | return ret; | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static inline int regcache_lzo_get_blkindex(struct regmap *map, | ||
106 | unsigned int reg) | ||
107 | { | ||
108 | return (reg * map->cache_word_size) / | ||
109 | DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()); | ||
110 | } | ||
111 | |||
112 | static inline int regcache_lzo_get_blkpos(struct regmap *map, | ||
113 | unsigned int reg) | ||
114 | { | ||
115 | return reg % (DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()) / | ||
116 | map->cache_word_size); | ||
117 | } | ||
118 | |||
119 | static inline int regcache_lzo_get_blksize(struct regmap *map) | ||
120 | { | ||
121 | return DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count()); | ||
122 | } | ||
123 | |||
124 | static int regcache_lzo_init(struct regmap *map) | ||
125 | { | ||
126 | struct regcache_lzo_ctx **lzo_blocks; | ||
127 | size_t bmp_size; | ||
128 | int ret, i, blksize, blkcount; | ||
129 | const char *p, *end; | ||
130 | unsigned long *sync_bmp; | ||
131 | |||
132 | ret = 0; | ||
133 | |||
134 | blkcount = regcache_lzo_block_count(); | ||
135 | map->cache = kzalloc(blkcount * sizeof *lzo_blocks, | ||
136 | GFP_KERNEL); | ||
137 | if (!map->cache) | ||
138 | return -ENOMEM; | ||
139 | lzo_blocks = map->cache; | ||
140 | |||
141 | /* | ||
142 | * allocate a bitmap to be used when syncing the cache with | ||
143 | * the hardware. Each time a register is modified, the corresponding | ||
144 | * bit is set in the bitmap, so we know that we have to sync | ||
145 | * that register. | ||
146 | */ | ||
147 | bmp_size = map->num_reg_defaults_raw; | ||
148 | sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long), | ||
149 | GFP_KERNEL); | ||
150 | if (!sync_bmp) { | ||
151 | ret = -ENOMEM; | ||
152 | goto err; | ||
153 | } | ||
154 | bitmap_zero(sync_bmp, bmp_size); | ||
155 | |||
156 | /* allocate the lzo blocks and initialize them */ | ||
157 | for (i = 0; i < blkcount; i++) { | ||
158 | lzo_blocks[i] = kzalloc(sizeof **lzo_blocks, | ||
159 | GFP_KERNEL); | ||
160 | if (!lzo_blocks[i]) { | ||
161 | kfree(sync_bmp); | ||
162 | ret = -ENOMEM; | ||
163 | goto err; | ||
164 | } | ||
165 | lzo_blocks[i]->sync_bmp = sync_bmp; | ||
166 | lzo_blocks[i]->sync_bmp_nbits = bmp_size; | ||
167 | /* alloc the working space for the compressed block */ | ||
168 | ret = regcache_lzo_prepare(lzo_blocks[i]); | ||
169 | if (ret < 0) | ||
170 | goto err; | ||
171 | } | ||
172 | |||
173 | blksize = regcache_lzo_get_blksize(map); | ||
174 | p = map->reg_defaults_raw; | ||
175 | end = map->reg_defaults_raw + map->cache_size_raw; | ||
176 | /* compress the register map and fill the lzo blocks */ | ||
177 | for (i = 0; i < blkcount; i++, p += blksize) { | ||
178 | lzo_blocks[i]->src = p; | ||
179 | if (p + blksize > end) | ||
180 | lzo_blocks[i]->src_len = end - p; | ||
181 | else | ||
182 | lzo_blocks[i]->src_len = blksize; | ||
183 | ret = regcache_lzo_compress_cache_block(map, | ||
184 | lzo_blocks[i]); | ||
185 | if (ret < 0) | ||
186 | goto err; | ||
187 | lzo_blocks[i]->decompressed_size = | ||
188 | lzo_blocks[i]->src_len; | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | err: | ||
193 | regcache_exit(map); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | static int regcache_lzo_exit(struct regmap *map) | ||
198 | { | ||
199 | struct regcache_lzo_ctx **lzo_blocks; | ||
200 | int i, blkcount; | ||
201 | |||
202 | lzo_blocks = map->cache; | ||
203 | if (!lzo_blocks) | ||
204 | return 0; | ||
205 | |||
206 | blkcount = regcache_lzo_block_count(); | ||
207 | /* | ||
208 | * the pointer to the bitmap used for syncing the cache | ||
209 | * is shared amongst all lzo_blocks. Ensure it is freed | ||
210 | * only once. | ||
211 | */ | ||
212 | if (lzo_blocks[0]) | ||
213 | kfree(lzo_blocks[0]->sync_bmp); | ||
214 | for (i = 0; i < blkcount; i++) { | ||
215 | if (lzo_blocks[i]) { | ||
216 | kfree(lzo_blocks[i]->wmem); | ||
217 | kfree(lzo_blocks[i]->dst); | ||
218 | } | ||
219 | /* each lzo_block is a pointer returned by kmalloc or NULL */ | ||
220 | kfree(lzo_blocks[i]); | ||
221 | } | ||
222 | kfree(lzo_blocks); | ||
223 | map->cache = NULL; | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static int regcache_lzo_read(struct regmap *map, | ||
228 | unsigned int reg, unsigned int *value) | ||
229 | { | ||
230 | struct regcache_lzo_ctx *lzo_block, **lzo_blocks; | ||
231 | int ret, blkindex, blkpos; | ||
232 | size_t blksize, tmp_dst_len; | ||
233 | void *tmp_dst; | ||
234 | |||
235 | *value = 0; | ||
236 | /* index of the compressed lzo block */ | ||
237 | blkindex = regcache_lzo_get_blkindex(map, reg); | ||
238 | /* register index within the decompressed block */ | ||
239 | blkpos = regcache_lzo_get_blkpos(map, reg); | ||
240 | /* size of the compressed block */ | ||
241 | blksize = regcache_lzo_get_blksize(map); | ||
242 | lzo_blocks = map->cache; | ||
243 | lzo_block = lzo_blocks[blkindex]; | ||
244 | |||
245 | /* save the pointer and length of the compressed block */ | ||
246 | tmp_dst = lzo_block->dst; | ||
247 | tmp_dst_len = lzo_block->dst_len; | ||
248 | |||
249 | /* prepare the source to be the compressed block */ | ||
250 | lzo_block->src = lzo_block->dst; | ||
251 | lzo_block->src_len = lzo_block->dst_len; | ||
252 | |||
253 | /* decompress the block */ | ||
254 | ret = regcache_lzo_decompress_cache_block(map, lzo_block); | ||
255 | if (ret >= 0) | ||
256 | /* fetch the value from the cache */ | ||
257 | *value = regcache_get_val(lzo_block->dst, blkpos, | ||
258 | map->cache_word_size); | ||
259 | |||
260 | kfree(lzo_block->dst); | ||
261 | /* restore the pointer and length of the compressed block */ | ||
262 | lzo_block->dst = tmp_dst; | ||
263 | lzo_block->dst_len = tmp_dst_len; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int regcache_lzo_write(struct regmap *map, | ||
268 | unsigned int reg, unsigned int value) | ||
269 | { | ||
270 | struct regcache_lzo_ctx *lzo_block, **lzo_blocks; | ||
271 | int ret, blkindex, blkpos; | ||
272 | size_t blksize, tmp_dst_len; | ||
273 | void *tmp_dst; | ||
274 | |||
275 | /* index of the compressed lzo block */ | ||
276 | blkindex = regcache_lzo_get_blkindex(map, reg); | ||
277 | /* register index within the decompressed block */ | ||
278 | blkpos = regcache_lzo_get_blkpos(map, reg); | ||
279 | /* size of the compressed block */ | ||
280 | blksize = regcache_lzo_get_blksize(map); | ||
281 | lzo_blocks = map->cache; | ||
282 | lzo_block = lzo_blocks[blkindex]; | ||
283 | |||
284 | /* save the pointer and length of the compressed block */ | ||
285 | tmp_dst = lzo_block->dst; | ||
286 | tmp_dst_len = lzo_block->dst_len; | ||
287 | |||
288 | /* prepare the source to be the compressed block */ | ||
289 | lzo_block->src = lzo_block->dst; | ||
290 | lzo_block->src_len = lzo_block->dst_len; | ||
291 | |||
292 | /* decompress the block */ | ||
293 | ret = regcache_lzo_decompress_cache_block(map, lzo_block); | ||
294 | if (ret < 0) { | ||
295 | kfree(lzo_block->dst); | ||
296 | goto out; | ||
297 | } | ||
298 | |||
299 | /* write the new value to the cache */ | ||
300 | if (regcache_set_val(lzo_block->dst, blkpos, value, | ||
301 | map->cache_word_size)) { | ||
302 | kfree(lzo_block->dst); | ||
303 | goto out; | ||
304 | } | ||
305 | |||
306 | /* prepare the source to be the decompressed block */ | ||
307 | lzo_block->src = lzo_block->dst; | ||
308 | lzo_block->src_len = lzo_block->dst_len; | ||
309 | |||
310 | /* compress the block */ | ||
311 | ret = regcache_lzo_compress_cache_block(map, lzo_block); | ||
312 | if (ret < 0) { | ||
313 | kfree(lzo_block->dst); | ||
314 | kfree(lzo_block->src); | ||
315 | goto out; | ||
316 | } | ||
317 | |||
318 | /* set the bit so we know we have to sync this register */ | ||
319 | set_bit(reg, lzo_block->sync_bmp); | ||
320 | kfree(tmp_dst); | ||
321 | kfree(lzo_block->src); | ||
322 | return 0; | ||
323 | out: | ||
324 | lzo_block->dst = tmp_dst; | ||
325 | lzo_block->dst_len = tmp_dst_len; | ||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | static int regcache_lzo_sync(struct regmap *map) | ||
330 | { | ||
331 | struct regcache_lzo_ctx **lzo_blocks; | ||
332 | unsigned int val; | ||
333 | int i; | ||
334 | int ret; | ||
335 | |||
336 | lzo_blocks = map->cache; | ||
337 | for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { | ||
338 | ret = regcache_read(map, i, &val); | ||
339 | if (ret) | ||
340 | return ret; | ||
341 | map->cache_bypass = 1; | ||
342 | ret = regmap_write(map, i, val); | ||
343 | map->cache_bypass = 0; | ||
344 | if (ret) | ||
345 | return ret; | ||
346 | dev_dbg(map->dev, "Synced register %#x, value %#x\n", | ||
347 | i, val); | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | struct regcache_ops regcache_lzo_ops = { | ||
354 | .type = REGCACHE_LZO, | ||
355 | .name = "lzo", | ||
356 | .init = regcache_lzo_init, | ||
357 | .exit = regcache_lzo_exit, | ||
358 | .read = regcache_lzo_read, | ||
359 | .write = regcache_lzo_write, | ||
360 | .sync = regcache_lzo_sync | ||
361 | }; | ||
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index b870a668b771..142d9cdfef3a 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c | |||
@@ -18,6 +18,7 @@ | |||
18 | static const struct regcache_ops *cache_types[] = { | 18 | static const struct regcache_ops *cache_types[] = { |
19 | ®cache_indexed_ops, | 19 | ®cache_indexed_ops, |
20 | ®cache_rbtree_ops, | 20 | ®cache_rbtree_ops, |
21 | ®cache_lzo_ops, | ||
21 | }; | 22 | }; |
22 | 23 | ||
23 | static int regcache_hw_init(struct regmap *map) | 24 | static int regcache_hw_init(struct regmap *map) |