blob: e71320f61d18e90e5fa404ed2810e408c6165ccb [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>
14#include <linux/rbtree.h>
15
16#include "internal.h"
17
18static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
19 unsigned int value);
Lars-Peter Clausen462a1852011-11-15 13:34:40 +010020static int regcache_rbtree_exit(struct regmap *map);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010021
22struct regcache_rbtree_node {
23 /* the actual rbtree node holding this block */
24 struct rb_node node;
25 /* base register handled by this block */
26 unsigned int base_reg;
Dimitris Papastamos28644c802011-09-19 14:34:02 +010027 /* block of adjacent registers */
28 void *block;
29 /* number of registers available in the block */
30 unsigned int blklen;
31} __attribute__ ((packed));
32
33struct regcache_rbtree_ctx {
34 struct rb_root root;
35 struct regcache_rbtree_node *cached_rbnode;
36};
37
38static inline void regcache_rbtree_get_base_top_reg(
39 struct regcache_rbtree_node *rbnode,
40 unsigned int *base, unsigned int *top)
41{
42 *base = rbnode->base_reg;
43 *top = rbnode->base_reg + rbnode->blklen - 1;
44}
45
46static unsigned int regcache_rbtree_get_register(
Dimitris Papastamos25ed1152011-09-27 11:25:07 +010047 struct regcache_rbtree_node *rbnode, unsigned int idx,
48 unsigned int word_size)
Dimitris Papastamos28644c802011-09-19 14:34:02 +010049{
Lars-Peter Clausenc5713002011-09-27 20:15:37 +020050 return regcache_get_val(rbnode->block, idx, word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010051}
52
53static void regcache_rbtree_set_register(struct regcache_rbtree_node *rbnode,
Dimitris Papastamos25ed1152011-09-27 11:25:07 +010054 unsigned int idx, unsigned int val,
55 unsigned int word_size)
Dimitris Papastamos28644c802011-09-19 14:34:02 +010056{
Lars-Peter Clausenc5713002011-09-27 20:15:37 +020057 regcache_set_val(rbnode->block, idx, val, word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +010058}
59
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020060static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
61 unsigned int reg)
Dimitris Papastamos28644c802011-09-19 14:34:02 +010062{
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020063 struct regcache_rbtree_ctx *rbtree_ctx = map->cache;
Dimitris Papastamos28644c802011-09-19 14:34:02 +010064 struct rb_node *node;
65 struct regcache_rbtree_node *rbnode;
66 unsigned int base_reg, top_reg;
67
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020068 rbnode = rbtree_ctx->cached_rbnode;
69 if (rbnode) {
Dimitris Papastamos28644c802011-09-19 14:34:02 +010070 regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
71 if (reg >= base_reg && reg <= top_reg)
72 return rbnode;
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020073 }
74
75 node = rbtree_ctx->root.rb_node;
76 while (node) {
77 rbnode = container_of(node, struct regcache_rbtree_node, node);
78 regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
79 if (reg >= base_reg && reg <= top_reg) {
80 rbtree_ctx->cached_rbnode = rbnode;
81 return rbnode;
82 } else if (reg > top_reg) {
Dimitris Papastamos28644c802011-09-19 14:34:02 +010083 node = node->rb_right;
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020084 } else if (reg < base_reg) {
Dimitris Papastamos28644c802011-09-19 14:34:02 +010085 node = node->rb_left;
Lars-Peter Clausen3405add2011-09-27 20:15:38 +020086 }
Dimitris Papastamos28644c802011-09-19 14:34:02 +010087 }
88
89 return NULL;
90}
91
92static int regcache_rbtree_insert(struct rb_root *root,
93 struct regcache_rbtree_node *rbnode)
94{
95 struct rb_node **new, *parent;
96 struct regcache_rbtree_node *rbnode_tmp;
97 unsigned int base_reg_tmp, top_reg_tmp;
98 unsigned int base_reg;
99
100 parent = NULL;
101 new = &root->rb_node;
102 while (*new) {
103 rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
104 node);
105 /* base and top registers of the current rbnode */
106 regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
107 &top_reg_tmp);
108 /* base register of the rbnode to be added */
109 base_reg = rbnode->base_reg;
110 parent = *new;
111 /* if this register has already been inserted, just return */
112 if (base_reg >= base_reg_tmp &&
113 base_reg <= top_reg_tmp)
114 return 0;
115 else if (base_reg > top_reg_tmp)
116 new = &((*new)->rb_right);
117 else if (base_reg < base_reg_tmp)
118 new = &((*new)->rb_left);
119 }
120
121 /* insert the node into the rbtree */
122 rb_link_node(&rbnode->node, parent, new);
123 rb_insert_color(&rbnode->node, root);
124
125 return 1;
126}
127
128static int regcache_rbtree_init(struct regmap *map)
129{
130 struct regcache_rbtree_ctx *rbtree_ctx;
131 int i;
132 int ret;
133
134 map->cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
135 if (!map->cache)
136 return -ENOMEM;
137
138 rbtree_ctx = map->cache;
139 rbtree_ctx->root = RB_ROOT;
140 rbtree_ctx->cached_rbnode = NULL;
141
142 for (i = 0; i < map->num_reg_defaults; i++) {
143 ret = regcache_rbtree_write(map,
144 map->reg_defaults[i].reg,
145 map->reg_defaults[i].def);
146 if (ret)
147 goto err;
148 }
149
150 return 0;
151
152err:
Lars-Peter Clausen462a1852011-11-15 13:34:40 +0100153 regcache_rbtree_exit(map);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100154 return ret;
155}
156
157static int regcache_rbtree_exit(struct regmap *map)
158{
159 struct rb_node *next;
160 struct regcache_rbtree_ctx *rbtree_ctx;
161 struct regcache_rbtree_node *rbtree_node;
162
163 /* if we've already been called then just return */
164 rbtree_ctx = map->cache;
165 if (!rbtree_ctx)
166 return 0;
167
168 /* free up the rbtree */
169 next = rb_first(&rbtree_ctx->root);
170 while (next) {
171 rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
172 next = rb_next(&rbtree_node->node);
173 rb_erase(&rbtree_node->node, &rbtree_ctx->root);
174 kfree(rbtree_node->block);
175 kfree(rbtree_node);
176 }
177
178 /* release the resources */
179 kfree(map->cache);
180 map->cache = NULL;
181
182 return 0;
183}
184
185static int regcache_rbtree_read(struct regmap *map,
186 unsigned int reg, unsigned int *value)
187{
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100188 struct regcache_rbtree_node *rbnode;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100189 unsigned int reg_tmp;
190
Lars-Peter Clausen3405add2011-09-27 20:15:38 +0200191 rbnode = regcache_rbtree_lookup(map, reg);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100192 if (rbnode) {
193 reg_tmp = reg - rbnode->base_reg;
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100194 *value = regcache_rbtree_get_register(rbnode, reg_tmp,
195 map->cache_word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100196 } else {
Mark Brown6e6ace02011-10-09 13:23:31 +0100197 return -ENOENT;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100198 }
199
200 return 0;
201}
202
203
204static int regcache_rbtree_insert_to_block(struct regcache_rbtree_node *rbnode,
205 unsigned int pos, unsigned int reg,
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100206 unsigned int value, unsigned int word_size)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100207{
208 u8 *blk;
209
210 blk = krealloc(rbnode->block,
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100211 (rbnode->blklen + 1) * word_size, GFP_KERNEL);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100212 if (!blk)
213 return -ENOMEM;
214
215 /* insert the register value in the correct place in the rbnode block */
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100216 memmove(blk + (pos + 1) * word_size,
217 blk + pos * word_size,
218 (rbnode->blklen - pos) * word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100219
220 /* update the rbnode block, its size and the base register */
221 rbnode->block = blk;
222 rbnode->blklen++;
223 if (!pos)
224 rbnode->base_reg = reg;
225
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100226 regcache_rbtree_set_register(rbnode, pos, value, word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100227 return 0;
228}
229
230static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
231 unsigned int value)
232{
233 struct regcache_rbtree_ctx *rbtree_ctx;
234 struct regcache_rbtree_node *rbnode, *rbnode_tmp;
235 struct rb_node *node;
236 unsigned int val;
237 unsigned int reg_tmp;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100238 unsigned int pos;
239 int i;
240 int ret;
241
242 rbtree_ctx = map->cache;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100243 /* if we can't locate it in the cached rbnode we'll have
244 * to traverse the rbtree looking for it.
245 */
Lars-Peter Clausen3405add2011-09-27 20:15:38 +0200246 rbnode = regcache_rbtree_lookup(map, reg);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100247 if (rbnode) {
248 reg_tmp = reg - rbnode->base_reg;
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100249 val = regcache_rbtree_get_register(rbnode, reg_tmp,
250 map->cache_word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100251 if (val == value)
252 return 0;
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100253 regcache_rbtree_set_register(rbnode, reg_tmp, value,
254 map->cache_word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100255 } else {
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100256 /* look for an adjacent register to the one we are about to add */
257 for (node = rb_first(&rbtree_ctx->root); node;
258 node = rb_next(node)) {
259 rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
260 for (i = 0; i < rbnode_tmp->blklen; i++) {
261 reg_tmp = rbnode_tmp->base_reg + i;
262 if (abs(reg_tmp - reg) != 1)
263 continue;
264 /* decide where in the block to place our register */
265 if (reg_tmp + 1 == reg)
266 pos = i + 1;
267 else
268 pos = i;
269 ret = regcache_rbtree_insert_to_block(rbnode_tmp, pos,
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100270 reg, value,
271 map->cache_word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100272 if (ret)
273 return ret;
274 rbtree_ctx->cached_rbnode = rbnode_tmp;
275 return 0;
276 }
277 }
278 /* we did not manage to find a place to insert it in an existing
279 * block so create a new rbnode with a single register in its block.
280 * This block will get populated further if any other adjacent
281 * registers get modified in the future.
282 */
283 rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
284 if (!rbnode)
285 return -ENOMEM;
286 rbnode->blklen = 1;
287 rbnode->base_reg = reg;
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100288 rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100289 GFP_KERNEL);
290 if (!rbnode->block) {
291 kfree(rbnode);
292 return -ENOMEM;
293 }
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100294 regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100295 regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
296 rbtree_ctx->cached_rbnode = rbnode;
297 }
298
299 return 0;
300}
301
302static int regcache_rbtree_sync(struct regmap *map)
303{
304 struct regcache_rbtree_ctx *rbtree_ctx;
305 struct rb_node *node;
306 struct regcache_rbtree_node *rbnode;
307 unsigned int regtmp;
Mark Brownb03622a2011-10-09 12:54:25 +0100308 unsigned int val;
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100309 int ret;
310 int i;
311
312 rbtree_ctx = map->cache;
313 for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
314 rbnode = rb_entry(node, struct regcache_rbtree_node, node);
315 for (i = 0; i < rbnode->blklen; i++) {
316 regtmp = rbnode->base_reg + i;
Dimitris Papastamos25ed1152011-09-27 11:25:07 +0100317 val = regcache_rbtree_get_register(rbnode, i,
318 map->cache_word_size);
Mark Brownb03622a2011-10-09 12:54:25 +0100319
320 /* Is this the hardware default? If so skip. */
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100321 ret = regcache_lookup_reg(map, i);
Mark Brownb03622a2011-10-09 12:54:25 +0100322 if (ret > 0 && val == map->reg_defaults[ret].def)
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100323 continue;
Mark Brownb03622a2011-10-09 12:54:25 +0100324
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100325 map->cache_bypass = 1;
Dimitris Papastamos13753a92011-09-29 14:36:25 +0100326 ret = _regmap_write(map, regtmp, val);
Dimitris Papastamos28644c802011-09-19 14:34:02 +0100327 map->cache_bypass = 0;
328 if (ret)
329 return ret;
330 dev_dbg(map->dev, "Synced register %#x, value %#x\n",
331 regtmp, val);
332 }
333 }
334
335 return 0;
336}
337
338struct regcache_ops regcache_rbtree_ops = {
339 .type = REGCACHE_RBTREE,
340 .name = "rbtree",
341 .init = regcache_rbtree_init,
342 .exit = regcache_rbtree_exit,
343 .read = regcache_rbtree_read,
344 .write = regcache_rbtree_write,
345 .sync = regcache_rbtree_sync
346};