linux/drivers/base/regmap/regcache-lzo.c
<<
>>
Prefs
   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
  15static int regcache_lzo_exit(struct regmap *map);
  16
  17struct 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
  29static int regcache_lzo_block_count(struct regmap *map)
  30{
  31        return LZO_BLOCK_NUM;
  32}
  33
  34static 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
  42static 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
  55static 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
  68static 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
  86static 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
 104static 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
 112static 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
 121static 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
 127static 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;
 193err:
 194        regcache_lzo_exit(map);
 195        return ret;
 196}
 197
 198static 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
 228static 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
 265static 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;
 318out:
 319        lzo_block->dst = tmp_dst;
 320        lzo_block->dst_len = tmp_dst_len;
 321        return ret;
 322}
 323
 324static 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
 360struct 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};
 369