blob: 382a6deb3ca8c9b200e17f27491ba5c8cdfbde88 [file] [log] [blame]
Dimitris Papastamos28644c802011-09-19 14:34:02 +01001/*
2 * Register cache access API - rbtree caching support
3 *
4 * Copyright 2011 Wolfson Microelectronics plc
5 *
6 * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/slab.h>
Paul Gortmaker51990e82012-01-22 11:23:42 -050014#include <linux/device.h>
Mark Brownbad2ab42011-11-21 19:44:44 +000015#include <linux/debugfs.h>
Dimitris Papastamos28644c802011-09-19 14:34:02 +010016#include <linux/rbtree.h>
Mark Brownbad2ab42011-11-21 19:44:44 +000017#include <linux/seq_file.h>
Dimitris Papastamos28644c802011-09-19 14:34:02 +010018
19#include "internal.h"
20
21static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
22 unsigned int value);
Lars-Peter Clausen462a1852011-11-15 13:34:40 +010023static int regcache_rbtree_exit(struct regmap *map);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010024
25struct regcache_rbtree_node {
26 /* the actual rbtree node holding this block */
27 struct rb_node node;
28 /* base register handled by this block */
29 unsigned int base_reg;
Dimitris Papastamos28644c802011-09-19 14:34:02 +010030 /* block of adjacent registers */
31 void *block;
32 /* number of registers available in the block */
33 unsigned int blklen;
34} __attribute__ ((packed));
35
36struct regcache_rbtree_ctx {
37 struct rb_root root;
38 struct regcache_rbtree_node *cached_rbnode;
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +000039 unsigned long *reg_present;
40 unsigned int reg_present_nbits;
Dimitris Papastamos28644c802011-09-19 14:34:02 +010041};
42
43static inline void regcache_rbtree_get_base_top_reg(
Stephen Warrenf01ee602012-04-09 13:40:24 -060044 struct regmap *map,
Dimitris Papastamos28644c802011-09-19 14:34:02 +010045 struct regcache_rbtree_node *rbnode,
46 unsigned int *base, unsigned int *top)
47{
48 *base = rbnode->base_reg;
Stephen Warrenf01ee602012-04-09 13:40:24 -060049 *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010050}
51
Mark Brown879082c2013-02-21 18:03:13 +000052static unsigned int regcache_rbtree_get_register(struct regmap *map,
53 struct regcache_rbtree_node *rbnode, unsigned int idx)
Dimitris Papastamos28644c802011-09-19 14:34:02 +010054{
Mark Brown879082c2013-02-21 18:03:13 +000055 return regcache_get_val(map, rbnode->block, idx);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010056}
57
Mark Brown137b8332013-03-26 21:26:19 +000058static const void *regcache_rbtree_get_reg_addr(struct regmap *map,
59 struct regcache_rbtree_node *rbnode, unsigned int idx)
60{
61 return regcache_get_val_addr(map, rbnode->block, idx);
62}
63
Mark Brown879082c2013-02-21 18:03:13 +000064static void regcache_rbtree_set_register(struct regmap *map,
65 struct regcache_rbtree_node *rbnode,
66 unsigned int idx, unsigned int val)
Dimitris Papastamos28644c802011-09-19 14:34:02 +010067{
Mark Brown879082c2013-02-21 18:03:13 +000068 regcache_set_val(map, rbnode->block, idx, val);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010069}
70
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020071static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
Mark Brown879082c2013-02-21 18:03:13 +000072 unsigned int reg)
Dimitris Papastamos28644c802011-09-19 14:34:02 +010073{
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020074 struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
Dimitris Papastamos28644c802011-09-19 14:34:02 +010075 struct rb_node *node;
76 struct regcache_rbtree_node *rbnode;
77 unsigned int base_reg, top_reg;
78
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020079 rbnode = rbtree_ctx->cached_rbnode;
80 if (rbnode) {
Stephen Warrenf01ee602012-04-09 13:40:24 -060081 regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
82 &top_reg);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010083 if (reg >= base_reg && reg <= top_reg)
84 return rbnode;
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020085 }
86
87 node = rbtree_ctx->root.rb_node;
88 while (node) {
89 rbnode = container_of(node, struct regcache_rbtree_node, node);
Stephen Warrenf01ee602012-04-09 13:40:24 -060090 regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
91 &top_reg);
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020092 if (reg >= base_reg && reg <= top_reg) {
93 rbtree_ctx->cached_rbnode = rbnode;
94 return rbnode;
95 } else if (reg > top_reg) {
Dimitris Papastamos28644c802011-09-19 14:34:02 +010096 node = node->rb_right;
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020097 } else if (reg < base_reg) {
Dimitris Papastamos28644c802011-09-19 14:34:02 +010098 node = node->rb_left;
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020099 }
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100100 }
101
102 return NULL;
103}
104
Stephen Warrenf01ee602012-04-09 13:40:24 -0600105static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100106 struct regcache_rbtree_node *rbnode)
107{
108 struct rb_node **new, *parent;
109 struct regcache_rbtree_node *rbnode_tmp;
110 unsigned int base_reg_tmp, top_reg_tmp;
111 unsigned int base_reg;
112
113 parent = NULL;
114 new = &root->rb_node;
115 while (*new) {
116 rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
117 node);
118 /* base and top registers of the current rbnode */
Stephen Warrenf01ee602012-04-09 13:40:24 -0600119 regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100120 &top_reg_tmp);
121 /* base register of the rbnode to be added */
122 base_reg = rbnode->base_reg;
123 parent = *new;
124 /* if this register has already been inserted, just return */
125 if (base_reg >= base_reg_tmp &&
126 base_reg <= top_reg_tmp)
127 return 0;
128 else if (base_reg > top_reg_tmp)
129 new = &((*new)->rb_right);
130 else if (base_reg < base_reg_tmp)
131 new = &((*new)->rb_left);
132 }
133
134 /* insert the node into the rbtree */
135 rb_link_node(&rbnode->node, parent, new);
136 rb_insert_color(&rbnode->node, root);
137
138 return 1;
139}
140
Mark Brownbad2ab42011-11-21 19:44:44 +0000141#ifdef CONFIG_DEBUG_FS
142static int rbtree_show(struct seq_file *s, void *ignored)
143{
144 struct regmap *map = s->private;
145 struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
146 struct regcache_rbtree_node *n;
147 struct rb_node *node;
148 unsigned int base, top;
Dimitris Papastamosa42277c2013-03-12 17:26:49 +0000149 size_t mem_size;
Mark Brownbad2ab42011-11-21 19:44:44 +0000150 int nodes = 0;
151 int registers = 0;
Stephen Warrenf01ee602012-04-09 13:40:24 -0600152 int this_registers, average;
Mark Brownbad2ab42011-11-21 19:44:44 +0000153
Stephen Warrenbacdbe02012-04-04 15:48:28 -0600154 map->lock(map);
Mark Brownbad2ab42011-11-21 19:44:44 +0000155
Dimitris Papastamosa42277c2013-03-12 17:26:49 +0000156 mem_size = sizeof(*rbtree_ctx);
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000157 mem_size += BITS_TO_LONGS(rbtree_ctx->reg_present_nbits) * sizeof(long);
Dimitris Papastamosa42277c2013-03-12 17:26:49 +0000158
Mark Brownbad2ab42011-11-21 19:44:44 +0000159 for (node = rb_first(&rbtree_ctx->root); node != NULL;
160 node = rb_next(node)) {
161 n = container_of(node, struct regcache_rbtree_node, node);
Dimitris Papastamosa42277c2013-03-12 17:26:49 +0000162 mem_size += sizeof(*n);
163 mem_size += (n->blklen * map->cache_word_size);
Mark Brownbad2ab42011-11-21 19:44:44 +0000164
Stephen Warrenf01ee602012-04-09 13:40:24 -0600165 regcache_rbtree_get_base_top_reg(map, n, &base, &top);
166 this_registers = ((top - base) / map->reg_stride) + 1;
167 seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
Mark Brownbad2ab42011-11-21 19:44:44 +0000168
169 nodes++;
Stephen Warrenf01ee602012-04-09 13:40:24 -0600170 registers += this_registers;
Mark Brownbad2ab42011-11-21 19:44:44 +0000171 }
172
Stephen Warrenc04c1b92012-04-04 15:48:33 -0600173 if (nodes)
174 average = registers / nodes;
175 else
176 average = 0;
177
Dimitris Papastamosa42277c2013-03-12 17:26:49 +0000178 seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n",
179 nodes, registers, average, mem_size);
Mark Brownbad2ab42011-11-21 19:44:44 +0000180
Stephen Warrenbacdbe02012-04-04 15:48:28 -0600181 map->unlock(map);
Mark Brownbad2ab42011-11-21 19:44:44 +0000182
183 return 0;
184}
185
186static int rbtree_open(struct inode *inode, struct file *file)
187{
188 return single_open(file, rbtree_show, inode->i_private);
189}
190
191static const struct file_operations rbtree_fops = {
192 .open = rbtree_open,
193 .read = seq_read,
194 .llseek = seq_lseek,
195 .release = single_release,
196};
Mark Browncce585c2011-11-22 11:33:31 +0000197
198static void rbtree_debugfs_init(struct regmap *map)
199{
200 debugfs_create_file("rbtree", 0400, map->debugfs, map, &rbtree_fops);
201}
202#else
203static void rbtree_debugfs_init(struct regmap *map)
204{
205}
Mark Brownbad2ab42011-11-21 19:44:44 +0000206#endif
207
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000208static int enlarge_reg_present_bitmap(struct regmap *map, unsigned int reg)
209{
210 struct regcache_rbtree_ctx *rbtree_ctx;
211 unsigned long *reg_present;
212 unsigned int reg_present_size;
213 unsigned int nregs;
214 int i;
215
216 rbtree_ctx = map->cache;
217 nregs = reg + 1;
218 reg_present_size = BITS_TO_LONGS(nregs);
219 reg_present_size *= sizeof(long);
220
221 if (!rbtree_ctx->reg_present) {
222 reg_present = kmalloc(reg_present_size, GFP_KERNEL);
223 if (!reg_present)
224 return -ENOMEM;
225 bitmap_zero(reg_present, nregs);
226 rbtree_ctx->reg_present = reg_present;
227 rbtree_ctx->reg_present_nbits = nregs;
228 return 0;
229 }
230
231 if (nregs > rbtree_ctx->reg_present_nbits) {
232 reg_present = krealloc(rbtree_ctx->reg_present,
233 reg_present_size, GFP_KERNEL);
234 if (!reg_present)
235 return -ENOMEM;
236 for (i = 0; i < nregs; i++)
237 if (i >= rbtree_ctx->reg_present_nbits)
238 clear_bit(i, reg_present);
239 rbtree_ctx->reg_present = reg_present;
240 rbtree_ctx->reg_present_nbits = nregs;
241 }
242
243 return 0;
244}
245
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100246static int regcache_rbtree_init(struct regmap *map)
247{
248 struct regcache_rbtree_ctx *rbtree_ctx;
249 int i;
250 int ret;
251
252 map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
253 if (!map->cache)
254 return -ENOMEM;
255
256 rbtree_ctx = map->cache;
257 rbtree_ctx->root = RB_ROOT;
258 rbtree_ctx->cached_rbnode = NULL;
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000259 rbtree_ctx->reg_present = NULL;
260 rbtree_ctx->reg_present_nbits = 0;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100261
262 for (i = 0; i < map->num_reg_defaults; i++) {
263 ret = regcache_rbtree_write(map,
264 map->reg_defaults[i].reg,
265 map->reg_defaults[i].def);
266 if (ret)
267 goto err;
268 }
269
Mark Browncce585c2011-11-22 11:33:31 +0000270 rbtree_debugfs_init(map);
Mark Brownbad2ab42011-11-21 19:44:44 +0000271
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100272 return 0;
273
274err:
Lars-Peter Clausen462a1852011-11-15 13:34:40 +0100275 regcache_rbtree_exit(map);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100276 return ret;
277}
278
279static int regcache_rbtree_exit(struct regmap *map)
280{
281 struct rb_node *next;
282 struct regcache_rbtree_ctx *rbtree_ctx;
283 struct regcache_rbtree_node *rbtree_node;
284
285 /* if we've already been called then just return */
286 rbtree_ctx = map->cache;
287 if (!rbtree_ctx)
288 return 0;
289
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000290 kfree(rbtree_ctx->reg_present);
291
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100292 /* free up the rbtree */
293 next = rb_first(&rbtree_ctx->root);
294 while (next) {
295 rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
296 next = rb_next(&rbtree_node->node);
297 rb_erase(&rbtree_node->node, &rbtree_ctx->root);
298 kfree(rbtree_node->block);
299 kfree(rbtree_node);
300 }
301
302 /* release the resources */
303 kfree(map->cache);
304 map->cache = NULL;
305
306 return 0;
307}
308
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000309static int regcache_reg_present(struct regmap *map, unsigned int reg)
310{
311 struct regcache_rbtree_ctx *rbtree_ctx;
312
313 rbtree_ctx = map->cache;
314 if (!(rbtree_ctx->reg_present[BIT_WORD(reg)] & BIT_MASK(reg)))
315 return 0;
316 return 1;
317
318}
319
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100320static int regcache_rbtree_read(struct regmap *map,
321 unsigned int reg, unsigned int *value)
322{
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100323 struct regcache_rbtree_node *rbnode;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100324 unsigned int reg_tmp;
325
Lars-Peter Clausen3405add2011-09-27 20:15:38 +0200326 rbnode = regcache_rbtree_lookup(map, reg);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100327 if (rbnode) {
Stephen Warrenf01ee602012-04-09 13:40:24 -0600328 reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000329 if (!regcache_reg_present(map, reg))
330 return -ENOENT;
Mark Brown879082c2013-02-21 18:03:13 +0000331 *value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100332 } else {
Mark Brown6e6ace02011-10-09 13:23:31 +0100333 return -ENOENT;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100334 }
335
336 return 0;
337}
338
339
Mark Brown879082c2013-02-21 18:03:13 +0000340static int regcache_rbtree_insert_to_block(struct regmap *map,
341 struct regcache_rbtree_node *rbnode,
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100342 unsigned int pos, unsigned int reg,
Mark Brown879082c2013-02-21 18:03:13 +0000343 unsigned int value)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100344{
345 u8 *blk;
346
347 blk = krealloc(rbnode->block,
Mark Brown879082c2013-02-21 18:03:13 +0000348 (rbnode->blklen + 1) * map->cache_word_size,
349 GFP_KERNEL);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100350 if (!blk)
351 return -ENOMEM;
352
353 /* insert the register value in the correct place in the rbnode block */
Mark Brown879082c2013-02-21 18:03:13 +0000354 memmove(blk + (pos + 1) * map->cache_word_size,
355 blk + pos * map->cache_word_size,
356 (rbnode->blklen - pos) * map->cache_word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100357
358 /* update the rbnode block, its size and the base register */
359 rbnode->block = blk;
360 rbnode->blklen++;
361 if (!pos)
362 rbnode->base_reg = reg;
363
Mark Brown879082c2013-02-21 18:03:13 +0000364 regcache_rbtree_set_register(map, rbnode, pos, value);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100365 return 0;
366}
367
368static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
369 unsigned int value)
370{
371 struct regcache_rbtree_ctx *rbtree_ctx;
372 struct regcache_rbtree_node *rbnode, *rbnode_tmp;
373 struct rb_node *node;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100374 unsigned int reg_tmp;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100375 unsigned int pos;
376 int i;
377 int ret;
378
379 rbtree_ctx = map->cache;
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000380 /* update the reg_present bitmap, make space if necessary */
381 ret = enlarge_reg_present_bitmap(map, reg);
382 if (ret < 0)
383 return ret;
384 set_bit(reg, rbtree_ctx->reg_present);
385
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100386 /* if we can't locate it in the cached rbnode we'll have
387 * to traverse the rbtree looking for it.
388 */
Lars-Peter Clausen3405add2011-09-27 20:15:38 +0200389 rbnode = regcache_rbtree_lookup(map, reg);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100390 if (rbnode) {
Stephen Warrenf01ee602012-04-09 13:40:24 -0600391 reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
Mark Brown879082c2013-02-21 18:03:13 +0000392 regcache_rbtree_set_register(map, rbnode, reg_tmp, value);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100393 } else {
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100394 /* look for an adjacent register to the one we are about to add */
395 for (node = rb_first(&rbtree_ctx->root); node;
396 node = rb_next(node)) {
Stephen Warrenf01ee602012-04-09 13:40:24 -0600397 rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
398 node);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100399 for (i = 0; i < rbnode_tmp->blklen; i++) {
Stephen Warrenf01ee602012-04-09 13:40:24 -0600400 reg_tmp = rbnode_tmp->base_reg +
401 (i * map->reg_stride);
402 if (abs(reg_tmp - reg) != map->reg_stride)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100403 continue;
404 /* decide where in the block to place our register */
Stephen Warrenf01ee602012-04-09 13:40:24 -0600405 if (reg_tmp + map->reg_stride == reg)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100406 pos = i + 1;
407 else
408 pos = i;
Mark Brown879082c2013-02-21 18:03:13 +0000409 ret = regcache_rbtree_insert_to_block(map,
410 rbnode_tmp,
411 pos, reg,
412 value);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100413 if (ret)
414 return ret;
415 rbtree_ctx->cached_rbnode = rbnode_tmp;
416 return 0;
417 }
418 }
419 /* we did not manage to find a place to insert it in an existing
420 * block so create a new rbnode with a single register in its block.
421 * This block will get populated further if any other adjacent
422 * registers get modified in the future.
423 */
424 rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
425 if (!rbnode)
426 return -ENOMEM;
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000427 rbnode->blklen = sizeof(*rbnode);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100428 rbnode->base_reg = reg;
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100429 rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100430 GFP_KERNEL);
431 if (!rbnode->block) {
432 kfree(rbnode);
433 return -ENOMEM;
434 }
Mark Brown879082c2013-02-21 18:03:13 +0000435 regcache_rbtree_set_register(map, rbnode, 0, value);
Stephen Warrenf01ee602012-04-09 13:40:24 -0600436 regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100437 rbtree_ctx->cached_rbnode = rbnode;
438 }
439
440 return 0;
441}
442
Mark Brownac8d91c2012-02-23 19:31:04 +0000443static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
444 unsigned int max)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100445{
446 struct regcache_rbtree_ctx *rbtree_ctx;
447 struct rb_node *node;
448 struct regcache_rbtree_node *rbnode;
449 unsigned int regtmp;
Mark Brownb03622a2011-10-09 12:54:25 +0100450 unsigned int val;
Mark Brown137b8332013-03-26 21:26:19 +0000451 const void *addr;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100452 int ret;
Mark Brownac8d91c2012-02-23 19:31:04 +0000453 int i, base, end;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100454
455 rbtree_ctx = map->cache;
456 for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
457 rbnode = rb_entry(node, struct regcache_rbtree_node, node);
Mark Brownac8d91c2012-02-23 19:31:04 +0000458
459 if (rbnode->base_reg < min)
460 continue;
461 if (rbnode->base_reg > max)
462 break;
463 if (rbnode->base_reg + rbnode->blklen < min)
464 continue;
465
Mark Brownf9353e72012-03-05 23:28:49 +0000466 if (min > rbnode->base_reg)
Mark Brownac8d91c2012-02-23 19:31:04 +0000467 base = min - rbnode->base_reg;
468 else
469 base = 0;
470
471 if (max < rbnode->base_reg + rbnode->blklen)
472 end = rbnode->base_reg + rbnode->blklen - max;
473 else
474 end = rbnode->blklen;
475
476 for (i = base; i < end; i++) {
Stephen Warrenf01ee602012-04-09 13:40:24 -0600477 regtmp = rbnode->base_reg + (i * map->reg_stride);
Dimitris Papastamos0c7ed8562013-03-15 14:54:35 +0000478
479 if (!regcache_reg_present(map, regtmp))
480 continue;
481
Mark Brown879082c2013-02-21 18:03:13 +0000482 val = regcache_rbtree_get_register(map, rbnode, i);
Mark Brownb03622a2011-10-09 12:54:25 +0100483
484 /* Is this the hardware default? If so skip. */
Lars-Peter Clausen4b4e9e42012-03-23 11:04:57 +0100485 ret = regcache_lookup_reg(map, regtmp);
Mark Brown994f5db2012-03-05 23:31:39 +0000486 if (ret >= 0 && val == map->reg_defaults[ret].def)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100487 continue;
Mark Brownb03622a2011-10-09 12:54:25 +0100488
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100489 map->cache_bypass = 1;
Mark Brown137b8332013-03-26 21:26:19 +0000490
491 if (regmap_can_raw_write(map)) {
492 addr = regcache_rbtree_get_reg_addr(map,
493 rbnode, i);
494 ret = _regmap_raw_write(map, regtmp, addr,
495 map->format.val_bytes,
496 false);
497 } else {
498 ret = _regmap_write(map, regtmp, val);
499 }
500
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100501 map->cache_bypass = 0;
502 if (ret)
503 return ret;
504 dev_dbg(map->dev, "Synced register %#x, value %#x\n",
505 regtmp, val);
506 }
507 }
508
509 return 0;
510}
511
512struct regcache_ops regcache_rbtree_ops = {
513 .type = REGCACHE_RBTREE,
514 .name = "rbtree",
515 .init = regcache_rbtree_init,
516 .exit = regcache_rbtree_exit,
517 .read = regcache_rbtree_read,
518 .write = regcache_rbtree_write,
519 .sync = regcache_rbtree_sync
520};