root/drivers/base/regmap/regcache-lzo.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. regcache_lzo_block_count
  2. regcache_lzo_prepare
  3. regcache_lzo_compress
  4. regcache_lzo_decompress
  5. regcache_lzo_compress_cache_block
  6. regcache_lzo_decompress_cache_block
  7. regcache_lzo_get_blkindex
  8. regcache_lzo_get_blkpos
  9. regcache_lzo_get_blksize
  10. regcache_lzo_init
  11. regcache_lzo_exit
  12. regcache_lzo_read
  13. regcache_lzo_write
  14. regcache_lzo_sync

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

/* [<][>][^][v][top][bottom][index][help] */