aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/regmap
diff options
context:
space:
mode:
authorDimitris Papastamos <dp@opensource.wolfsonmicro.com>2011-09-19 09:34:03 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-09-19 14:06:33 -0400
commit2cbbb579bcbe3e11baf1c59920dcd5a780b80447 (patch)
tree43fca4dcf12a9d2c4690977096d88c873bef9100 /drivers/base/regmap
parent28644c809f44498b8cd91d00b4cdb09e63b99843 (diff)
regmap: Add the LZO cache support
This patch adds support for LZO compression when storing the register cache. For a typical device whose register map would normally occupy 25kB or 50kB by using the LZO compression technique, one can get down to ~5-7kB. There might be a performance penalty associated with each individual read/write due to decompressing/compressing the underlying cache, however that should not be noticeable. These memory benefits depend on whether the target architecture can get rid of the memory occupied by the original register defaults cache which is marked as __devinitconst. Nevertheless there will be some memory gain even if the target architecture can't get rid of the original register map, this should be around ~30-32kB instead of 50kB. Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r--drivers/base/regmap/Kconfig2
-rw-r--r--drivers/base/regmap/Makefile2
-rw-r--r--drivers/base/regmap/internal.h2
-rw-r--r--drivers/base/regmap/regcache-lzo.c361
-rw-r--r--drivers/base/regmap/regcache.c1
5 files changed, 367 insertions, 1 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index fabbf6cc536..2fc6a66f39a 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -4,6 +4,8 @@
4 4
5config REGMAP 5config 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
9config REGMAP_I2C 11config REGMAP_I2C
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 20cd65035fc..0573c8a9dac 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,4 +1,4 @@
1obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o 1obj-$(CONFIG_REGMAP) += regmap.o regcache.o regcache-indexed.o regcache-rbtree.o regcache-lzo.o
2obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o 2obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
3obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o 3obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
4obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o 4obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 7ef8afc77e7..2d51b1b099f 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
121extern struct regcache_ops regcache_indexed_ops; 121extern struct regcache_ops regcache_indexed_ops;
122extern struct regcache_ops regcache_rbtree_ops; 122extern struct regcache_ops regcache_rbtree_ops;
123extern 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 00000000000..9079cb50b0b
--- /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
18struct 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
30static int regcache_lzo_block_count(void)
31{
32 return LZO_BLOCK_NUM;
33}
34
35static 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
43static 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
56static 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
69static 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
87static 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
105static 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
112static 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
119static 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
124static 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;
192err:
193 regcache_exit(map);
194 return ret;
195}
196
197static 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
227static 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
267static 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;
323out:
324 lzo_block->dst = tmp_dst;
325 lzo_block->dst_len = tmp_dst_len;
326 return ret;
327}
328
329static 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
353struct 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 b870a668b77..142d9cdfef3 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -18,6 +18,7 @@
18static const struct regcache_ops *cache_types[] = { 18static const struct regcache_ops *cache_types[] = {
19 &regcache_indexed_ops, 19 &regcache_indexed_ops,
20 &regcache_rbtree_ops, 20 &regcache_rbtree_ops,
21 &regcache_lzo_ops,
21}; 22};
22 23
23static int regcache_hw_init(struct regmap *map) 24static int regcache_hw_init(struct regmap *map)