diff options
-rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 70 |
1 files changed, 69 insertions, 1 deletions
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 045319615608..792a760c8ab1 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c | |||
@@ -36,6 +36,8 @@ struct regcache_rbtree_node { | |||
36 | struct regcache_rbtree_ctx { | 36 | struct regcache_rbtree_ctx { |
37 | struct rb_root root; | 37 | struct rb_root root; |
38 | struct regcache_rbtree_node *cached_rbnode; | 38 | struct regcache_rbtree_node *cached_rbnode; |
39 | unsigned long *reg_present; | ||
40 | unsigned int reg_present_nbits; | ||
39 | }; | 41 | }; |
40 | 42 | ||
41 | static inline void regcache_rbtree_get_base_top_reg( | 43 | static inline void regcache_rbtree_get_base_top_reg( |
@@ -146,6 +148,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) | |||
146 | map->lock(map); | 148 | map->lock(map); |
147 | 149 | ||
148 | mem_size = sizeof(*rbtree_ctx); | 150 | mem_size = sizeof(*rbtree_ctx); |
151 | mem_size += BITS_TO_LONGS(rbtree_ctx->reg_present_nbits) * sizeof(long); | ||
149 | 152 | ||
150 | for (node = rb_first(&rbtree_ctx->root); node != NULL; | 153 | for (node = rb_first(&rbtree_ctx->root); node != NULL; |
151 | node = rb_next(node)) { | 154 | node = rb_next(node)) { |
@@ -196,6 +199,44 @@ static void rbtree_debugfs_init(struct regmap *map) | |||
196 | } | 199 | } |
197 | #endif | 200 | #endif |
198 | 201 | ||
202 | static int enlarge_reg_present_bitmap(struct regmap *map, unsigned int reg) | ||
203 | { | ||
204 | struct regcache_rbtree_ctx *rbtree_ctx; | ||
205 | unsigned long *reg_present; | ||
206 | unsigned int reg_present_size; | ||
207 | unsigned int nregs; | ||
208 | int i; | ||
209 | |||
210 | rbtree_ctx = map->cache; | ||
211 | nregs = reg + 1; | ||
212 | reg_present_size = BITS_TO_LONGS(nregs); | ||
213 | reg_present_size *= sizeof(long); | ||
214 | |||
215 | if (!rbtree_ctx->reg_present) { | ||
216 | reg_present = kmalloc(reg_present_size, GFP_KERNEL); | ||
217 | if (!reg_present) | ||
218 | return -ENOMEM; | ||
219 | bitmap_zero(reg_present, nregs); | ||
220 | rbtree_ctx->reg_present = reg_present; | ||
221 | rbtree_ctx->reg_present_nbits = nregs; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | if (nregs > rbtree_ctx->reg_present_nbits) { | ||
226 | reg_present = krealloc(rbtree_ctx->reg_present, | ||
227 | reg_present_size, GFP_KERNEL); | ||
228 | if (!reg_present) | ||
229 | return -ENOMEM; | ||
230 | for (i = 0; i < nregs; i++) | ||
231 | if (i >= rbtree_ctx->reg_present_nbits) | ||
232 | clear_bit(i, reg_present); | ||
233 | rbtree_ctx->reg_present = reg_present; | ||
234 | rbtree_ctx->reg_present_nbits = nregs; | ||
235 | } | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
199 | static int regcache_rbtree_init(struct regmap *map) | 240 | static int regcache_rbtree_init(struct regmap *map) |
200 | { | 241 | { |
201 | struct regcache_rbtree_ctx *rbtree_ctx; | 242 | struct regcache_rbtree_ctx *rbtree_ctx; |
@@ -209,6 +250,8 @@ static int regcache_rbtree_init(struct regmap *map) | |||
209 | rbtree_ctx = map->cache; | 250 | rbtree_ctx = map->cache; |
210 | rbtree_ctx->root = RB_ROOT; | 251 | rbtree_ctx->root = RB_ROOT; |
211 | rbtree_ctx->cached_rbnode = NULL; | 252 | rbtree_ctx->cached_rbnode = NULL; |
253 | rbtree_ctx->reg_present = NULL; | ||
254 | rbtree_ctx->reg_present_nbits = 0; | ||
212 | 255 | ||
213 | for (i = 0; i < map->num_reg_defaults; i++) { | 256 | for (i = 0; i < map->num_reg_defaults; i++) { |
214 | ret = regcache_rbtree_write(map, | 257 | ret = regcache_rbtree_write(map, |
@@ -238,6 +281,8 @@ static int regcache_rbtree_exit(struct regmap *map) | |||
238 | if (!rbtree_ctx) | 281 | if (!rbtree_ctx) |
239 | return 0; | 282 | return 0; |
240 | 283 | ||
284 | kfree(rbtree_ctx->reg_present); | ||
285 | |||
241 | /* free up the rbtree */ | 286 | /* free up the rbtree */ |
242 | next = rb_first(&rbtree_ctx->root); | 287 | next = rb_first(&rbtree_ctx->root); |
243 | while (next) { | 288 | while (next) { |
@@ -255,6 +300,17 @@ static int regcache_rbtree_exit(struct regmap *map) | |||
255 | return 0; | 300 | return 0; |
256 | } | 301 | } |
257 | 302 | ||
303 | static int regcache_reg_present(struct regmap *map, unsigned int reg) | ||
304 | { | ||
305 | struct regcache_rbtree_ctx *rbtree_ctx; | ||
306 | |||
307 | rbtree_ctx = map->cache; | ||
308 | if (!(rbtree_ctx->reg_present[BIT_WORD(reg)] & BIT_MASK(reg))) | ||
309 | return 0; | ||
310 | return 1; | ||
311 | |||
312 | } | ||
313 | |||
258 | static int regcache_rbtree_read(struct regmap *map, | 314 | static int regcache_rbtree_read(struct regmap *map, |
259 | unsigned int reg, unsigned int *value) | 315 | unsigned int reg, unsigned int *value) |
260 | { | 316 | { |
@@ -264,6 +320,8 @@ static int regcache_rbtree_read(struct regmap *map, | |||
264 | rbnode = regcache_rbtree_lookup(map, reg); | 320 | rbnode = regcache_rbtree_lookup(map, reg); |
265 | if (rbnode) { | 321 | if (rbnode) { |
266 | reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; | 322 | reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; |
323 | if (!regcache_reg_present(map, reg)) | ||
324 | return -ENOENT; | ||
267 | *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); | 325 | *value = regcache_rbtree_get_register(map, rbnode, reg_tmp); |
268 | } else { | 326 | } else { |
269 | return -ENOENT; | 327 | return -ENOENT; |
@@ -313,6 +371,12 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, | |||
313 | int ret; | 371 | int ret; |
314 | 372 | ||
315 | rbtree_ctx = map->cache; | 373 | rbtree_ctx = map->cache; |
374 | /* update the reg_present bitmap, make space if necessary */ | ||
375 | ret = enlarge_reg_present_bitmap(map, reg); | ||
376 | if (ret < 0) | ||
377 | return ret; | ||
378 | set_bit(reg, rbtree_ctx->reg_present); | ||
379 | |||
316 | /* if we can't locate it in the cached rbnode we'll have | 380 | /* if we can't locate it in the cached rbnode we'll have |
317 | * to traverse the rbtree looking for it. | 381 | * to traverse the rbtree looking for it. |
318 | */ | 382 | */ |
@@ -354,7 +418,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, | |||
354 | rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); | 418 | rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); |
355 | if (!rbnode) | 419 | if (!rbnode) |
356 | return -ENOMEM; | 420 | return -ENOMEM; |
357 | rbnode->blklen = 1; | 421 | rbnode->blklen = sizeof(*rbnode); |
358 | rbnode->base_reg = reg; | 422 | rbnode->base_reg = reg; |
359 | rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, | 423 | rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, |
360 | GFP_KERNEL); | 424 | GFP_KERNEL); |
@@ -404,6 +468,10 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, | |||
404 | 468 | ||
405 | for (i = base; i < end; i++) { | 469 | for (i = base; i < end; i++) { |
406 | regtmp = rbnode->base_reg + (i * map->reg_stride); | 470 | regtmp = rbnode->base_reg + (i * map->reg_stride); |
471 | |||
472 | if (!regcache_reg_present(map, regtmp)) | ||
473 | continue; | ||
474 | |||
407 | val = regcache_rbtree_get_register(map, rbnode, i); | 475 | val = regcache_rbtree_get_register(map, rbnode, i); |
408 | 476 | ||
409 | /* Is this the hardware default? If so skip. */ | 477 | /* Is this the hardware default? If so skip. */ |