regcache-lzo.c (8900B)
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};