0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/device.h>
0010 #include <linux/lzo.h>
0011 #include <linux/slab.h>
0012
0013 #include "internal.h"
0014
0015 static int regcache_lzo_exit(struct regmap *map);
0016
0017 struct regcache_lzo_ctx {
0018 void *wmem;
0019 void *dst;
0020 const void *src;
0021 size_t src_len;
0022 size_t dst_len;
0023 size_t decompressed_size;
0024 unsigned long *sync_bmp;
0025 int sync_bmp_nbits;
0026 };
0027
0028 #define LZO_BLOCK_NUM 8
0029 static int regcache_lzo_block_count(struct regmap *map)
0030 {
0031 return LZO_BLOCK_NUM;
0032 }
0033
0034 static int regcache_lzo_prepare(struct regcache_lzo_ctx *lzo_ctx)
0035 {
0036 lzo_ctx->wmem = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
0037 if (!lzo_ctx->wmem)
0038 return -ENOMEM;
0039 return 0;
0040 }
0041
0042 static int regcache_lzo_compress(struct regcache_lzo_ctx *lzo_ctx)
0043 {
0044 size_t compress_size;
0045 int ret;
0046
0047 ret = lzo1x_1_compress(lzo_ctx->src, lzo_ctx->src_len,
0048 lzo_ctx->dst, &compress_size, lzo_ctx->wmem);
0049 if (ret != LZO_E_OK || compress_size > lzo_ctx->dst_len)
0050 return -EINVAL;
0051 lzo_ctx->dst_len = compress_size;
0052 return 0;
0053 }
0054
0055 static int regcache_lzo_decompress(struct regcache_lzo_ctx *lzo_ctx)
0056 {
0057 size_t dst_len;
0058 int ret;
0059
0060 dst_len = lzo_ctx->dst_len;
0061 ret = lzo1x_decompress_safe(lzo_ctx->src, lzo_ctx->src_len,
0062 lzo_ctx->dst, &dst_len);
0063 if (ret != LZO_E_OK || dst_len != lzo_ctx->dst_len)
0064 return -EINVAL;
0065 return 0;
0066 }
0067
0068 static int regcache_lzo_compress_cache_block(struct regmap *map,
0069 struct regcache_lzo_ctx *lzo_ctx)
0070 {
0071 int ret;
0072
0073 lzo_ctx->dst_len = lzo1x_worst_compress(PAGE_SIZE);
0074 lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
0075 if (!lzo_ctx->dst) {
0076 lzo_ctx->dst_len = 0;
0077 return -ENOMEM;
0078 }
0079
0080 ret = regcache_lzo_compress(lzo_ctx);
0081 if (ret < 0)
0082 return ret;
0083 return 0;
0084 }
0085
0086 static int regcache_lzo_decompress_cache_block(struct regmap *map,
0087 struct regcache_lzo_ctx *lzo_ctx)
0088 {
0089 int ret;
0090
0091 lzo_ctx->dst_len = lzo_ctx->decompressed_size;
0092 lzo_ctx->dst = kmalloc(lzo_ctx->dst_len, GFP_KERNEL);
0093 if (!lzo_ctx->dst) {
0094 lzo_ctx->dst_len = 0;
0095 return -ENOMEM;
0096 }
0097
0098 ret = regcache_lzo_decompress(lzo_ctx);
0099 if (ret < 0)
0100 return ret;
0101 return 0;
0102 }
0103
0104 static inline int regcache_lzo_get_blkindex(struct regmap *map,
0105 unsigned int reg)
0106 {
0107 return ((reg / map->reg_stride) * map->cache_word_size) /
0108 DIV_ROUND_UP(map->cache_size_raw,
0109 regcache_lzo_block_count(map));
0110 }
0111
0112 static inline int regcache_lzo_get_blkpos(struct regmap *map,
0113 unsigned int reg)
0114 {
0115 return (reg / map->reg_stride) %
0116 (DIV_ROUND_UP(map->cache_size_raw,
0117 regcache_lzo_block_count(map)) /
0118 map->cache_word_size);
0119 }
0120
0121 static inline int regcache_lzo_get_blksize(struct regmap *map)
0122 {
0123 return DIV_ROUND_UP(map->cache_size_raw,
0124 regcache_lzo_block_count(map));
0125 }
0126
0127 static int regcache_lzo_init(struct regmap *map)
0128 {
0129 struct regcache_lzo_ctx **lzo_blocks;
0130 size_t bmp_size;
0131 int ret, i, blksize, blkcount;
0132 const char *p, *end;
0133 unsigned long *sync_bmp;
0134
0135 ret = 0;
0136
0137 blkcount = regcache_lzo_block_count(map);
0138 map->cache = kcalloc(blkcount, sizeof(*lzo_blocks),
0139 GFP_KERNEL);
0140 if (!map->cache)
0141 return -ENOMEM;
0142 lzo_blocks = map->cache;
0143
0144
0145
0146
0147
0148
0149
0150 bmp_size = map->num_reg_defaults_raw;
0151 sync_bmp = bitmap_zalloc(bmp_size, GFP_KERNEL);
0152 if (!sync_bmp) {
0153 ret = -ENOMEM;
0154 goto err;
0155 }
0156
0157
0158 for (i = 0; i < blkcount; i++) {
0159 lzo_blocks[i] = kzalloc(sizeof **lzo_blocks,
0160 GFP_KERNEL);
0161 if (!lzo_blocks[i]) {
0162 bitmap_free(sync_bmp);
0163 ret = -ENOMEM;
0164 goto err;
0165 }
0166 lzo_blocks[i]->sync_bmp = sync_bmp;
0167 lzo_blocks[i]->sync_bmp_nbits = bmp_size;
0168
0169 ret = regcache_lzo_prepare(lzo_blocks[i]);
0170 if (ret < 0)
0171 goto err;
0172 }
0173
0174 blksize = regcache_lzo_get_blksize(map);
0175 p = map->reg_defaults_raw;
0176 end = map->reg_defaults_raw + map->cache_size_raw;
0177
0178 for (i = 0; i < blkcount; i++, p += blksize) {
0179 lzo_blocks[i]->src = p;
0180 if (p + blksize > end)
0181 lzo_blocks[i]->src_len = end - p;
0182 else
0183 lzo_blocks[i]->src_len = blksize;
0184 ret = regcache_lzo_compress_cache_block(map,
0185 lzo_blocks[i]);
0186 if (ret < 0)
0187 goto err;
0188 lzo_blocks[i]->decompressed_size =
0189 lzo_blocks[i]->src_len;
0190 }
0191
0192 return 0;
0193 err:
0194 regcache_lzo_exit(map);
0195 return ret;
0196 }
0197
0198 static int regcache_lzo_exit(struct regmap *map)
0199 {
0200 struct regcache_lzo_ctx **lzo_blocks;
0201 int i, blkcount;
0202
0203 lzo_blocks = map->cache;
0204 if (!lzo_blocks)
0205 return 0;
0206
0207 blkcount = regcache_lzo_block_count(map);
0208
0209
0210
0211
0212
0213 if (lzo_blocks[0])
0214 bitmap_free(lzo_blocks[0]->sync_bmp);
0215 for (i = 0; i < blkcount; i++) {
0216 if (lzo_blocks[i]) {
0217 kfree(lzo_blocks[i]->wmem);
0218 kfree(lzo_blocks[i]->dst);
0219 }
0220
0221 kfree(lzo_blocks[i]);
0222 }
0223 kfree(lzo_blocks);
0224 map->cache = NULL;
0225 return 0;
0226 }
0227
0228 static int regcache_lzo_read(struct regmap *map,
0229 unsigned int reg, unsigned int *value)
0230 {
0231 struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
0232 int ret, blkindex, blkpos;
0233 size_t tmp_dst_len;
0234 void *tmp_dst;
0235
0236
0237 blkindex = regcache_lzo_get_blkindex(map, reg);
0238
0239 blkpos = regcache_lzo_get_blkpos(map, reg);
0240 lzo_blocks = map->cache;
0241 lzo_block = lzo_blocks[blkindex];
0242
0243
0244 tmp_dst = lzo_block->dst;
0245 tmp_dst_len = lzo_block->dst_len;
0246
0247
0248 lzo_block->src = lzo_block->dst;
0249 lzo_block->src_len = lzo_block->dst_len;
0250
0251
0252 ret = regcache_lzo_decompress_cache_block(map, lzo_block);
0253 if (ret >= 0)
0254
0255 *value = regcache_get_val(map, lzo_block->dst, blkpos);
0256
0257 kfree(lzo_block->dst);
0258
0259 lzo_block->dst = tmp_dst;
0260 lzo_block->dst_len = tmp_dst_len;
0261
0262 return ret;
0263 }
0264
0265 static int regcache_lzo_write(struct regmap *map,
0266 unsigned int reg, unsigned int value)
0267 {
0268 struct regcache_lzo_ctx *lzo_block, **lzo_blocks;
0269 int ret, blkindex, blkpos;
0270 size_t tmp_dst_len;
0271 void *tmp_dst;
0272
0273
0274 blkindex = regcache_lzo_get_blkindex(map, reg);
0275
0276 blkpos = regcache_lzo_get_blkpos(map, reg);
0277 lzo_blocks = map->cache;
0278 lzo_block = lzo_blocks[blkindex];
0279
0280
0281 tmp_dst = lzo_block->dst;
0282 tmp_dst_len = lzo_block->dst_len;
0283
0284
0285 lzo_block->src = lzo_block->dst;
0286 lzo_block->src_len = lzo_block->dst_len;
0287
0288
0289 ret = regcache_lzo_decompress_cache_block(map, lzo_block);
0290 if (ret < 0) {
0291 kfree(lzo_block->dst);
0292 goto out;
0293 }
0294
0295
0296 if (regcache_set_val(map, lzo_block->dst, blkpos, value)) {
0297 kfree(lzo_block->dst);
0298 goto out;
0299 }
0300
0301
0302 lzo_block->src = lzo_block->dst;
0303 lzo_block->src_len = lzo_block->dst_len;
0304
0305
0306 ret = regcache_lzo_compress_cache_block(map, lzo_block);
0307 if (ret < 0) {
0308 kfree(lzo_block->dst);
0309 kfree(lzo_block->src);
0310 goto out;
0311 }
0312
0313
0314 set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
0315 kfree(tmp_dst);
0316 kfree(lzo_block->src);
0317 return 0;
0318 out:
0319 lzo_block->dst = tmp_dst;
0320 lzo_block->dst_len = tmp_dst_len;
0321 return ret;
0322 }
0323
0324 static int regcache_lzo_sync(struct regmap *map, unsigned int min,
0325 unsigned int max)
0326 {
0327 struct regcache_lzo_ctx **lzo_blocks;
0328 unsigned int val;
0329 int i;
0330 int ret;
0331
0332 lzo_blocks = map->cache;
0333 i = min;
0334 for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp,
0335 lzo_blocks[0]->sync_bmp_nbits) {
0336 if (i > max)
0337 continue;
0338
0339 ret = regcache_read(map, i, &val);
0340 if (ret)
0341 return ret;
0342
0343
0344 ret = regcache_lookup_reg(map, i);
0345 if (ret > 0 && val == map->reg_defaults[ret].def)
0346 continue;
0347
0348 map->cache_bypass = true;
0349 ret = _regmap_write(map, i, val);
0350 map->cache_bypass = false;
0351 if (ret)
0352 return ret;
0353 dev_dbg(map->dev, "Synced register %#x, value %#x\n",
0354 i, val);
0355 }
0356
0357 return 0;
0358 }
0359
0360 struct regcache_ops regcache_lzo_ops = {
0361 .type = REGCACHE_COMPRESSED,
0362 .name = "lzo",
0363 .init = regcache_lzo_init,
0364 .exit = regcache_lzo_exit,
0365 .read = regcache_lzo_read,
0366 .write = regcache_lzo_write,
0367 .sync = regcache_lzo_sync
0368 };