regmap: A cache type of _NONE behaves like a bypassed cache
[pandora-kernel.git] / drivers / base / regmap / regcache.c
1 /*
2  * Register cache access API
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 <trace/events/regmap.h>
15
16 #include "internal.h"
17
18 static const struct regcache_ops *cache_types[] = {
19         &regcache_indexed_ops,
20         &regcache_rbtree_ops,
21         &regcache_lzo_ops,
22 };
23
24 static int regcache_hw_init(struct regmap *map)
25 {
26         int i, j;
27         int ret;
28         int count;
29         unsigned int val;
30         void *tmp_buf;
31
32         if (!map->num_reg_defaults_raw)
33                 return -EINVAL;
34
35         if (!map->reg_defaults_raw) {
36                 dev_warn(map->dev, "No cache defaults, reading back from HW\n");
37                 tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
38                 if (!tmp_buf)
39                         return -EINVAL;
40                 ret = regmap_bulk_read(map, 0, tmp_buf,
41                                        map->num_reg_defaults_raw);
42                 if (ret < 0) {
43                         kfree(tmp_buf);
44                         return ret;
45                 }
46                 map->reg_defaults_raw = tmp_buf;
47                 map->cache_free = 1;
48         }
49
50         /* calculate the size of reg_defaults */
51         for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
52                 val = regcache_get_val(map->reg_defaults_raw,
53                                        i, map->cache_word_size);
54                 if (!val)
55                         continue;
56                 count++;
57         }
58
59         map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
60                                       GFP_KERNEL);
61         if (!map->reg_defaults)
62                 return -ENOMEM;
63
64         /* fill the reg_defaults */
65         map->num_reg_defaults = count;
66         for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
67                 val = regcache_get_val(map->reg_defaults_raw,
68                                        i, map->cache_word_size);
69                 if (!val)
70                         continue;
71                 map->reg_defaults[j].reg = i;
72                 map->reg_defaults[j].def = val;
73                 j++;
74         }
75
76         return 0;
77 }
78
79 int regcache_init(struct regmap *map)
80 {
81         int ret;
82         int i;
83         void *tmp_buf;
84
85         if (map->cache_type == REGCACHE_NONE) {
86                 map->cache_bypass = true;
87                 return 0;
88         }
89
90         for (i = 0; i < ARRAY_SIZE(cache_types); i++)
91                 if (cache_types[i]->type == map->cache_type)
92                         break;
93
94         if (i == ARRAY_SIZE(cache_types)) {
95                 dev_err(map->dev, "Could not match compress type: %d\n",
96                         map->cache_type);
97                 return -EINVAL;
98         }
99
100         map->cache = NULL;
101         map->cache_ops = cache_types[i];
102
103         if (!map->cache_ops->read ||
104             !map->cache_ops->write ||
105             !map->cache_ops->name)
106                 return -EINVAL;
107
108         /* We still need to ensure that the reg_defaults
109          * won't vanish from under us.  We'll need to make
110          * a copy of it.
111          */
112         if (map->reg_defaults) {
113                 if (!map->num_reg_defaults)
114                         return -EINVAL;
115                 tmp_buf = kmemdup(map->reg_defaults, map->num_reg_defaults *
116                                   sizeof(struct reg_default), GFP_KERNEL);
117                 if (!tmp_buf)
118                         return -ENOMEM;
119                 map->reg_defaults = tmp_buf;
120         } else {
121                 /* Some devices such as PMIC's don't have cache defaults,
122                  * we cope with this by reading back the HW registers and
123                  * crafting the cache defaults by hand.
124                  */
125                 ret = regcache_hw_init(map);
126                 if (ret < 0)
127                         return ret;
128         }
129
130         if (!map->max_register)
131                 map->max_register = map->num_reg_defaults_raw;
132
133         if (map->cache_ops->init) {
134                 dev_dbg(map->dev, "Initializing %s cache\n",
135                         map->cache_ops->name);
136                 return map->cache_ops->init(map);
137         }
138         return 0;
139 }
140
141 void regcache_exit(struct regmap *map)
142 {
143         if (map->cache_type == REGCACHE_NONE)
144                 return;
145
146         BUG_ON(!map->cache_ops);
147
148         kfree(map->reg_defaults);
149         if (map->cache_free)
150                 kfree(map->reg_defaults_raw);
151
152         if (map->cache_ops->exit) {
153                 dev_dbg(map->dev, "Destroying %s cache\n",
154                         map->cache_ops->name);
155                 map->cache_ops->exit(map);
156         }
157 }
158
159 /**
160  * regcache_read: Fetch the value of a given register from the cache.
161  *
162  * @map: map to configure.
163  * @reg: The register index.
164  * @value: The value to be returned.
165  *
166  * Return a negative value on failure, 0 on success.
167  */
168 int regcache_read(struct regmap *map,
169                   unsigned int reg, unsigned int *value)
170 {
171         if (map->cache_type == REGCACHE_NONE)
172                 return -ENOSYS;
173
174         BUG_ON(!map->cache_ops);
175
176         if (!regmap_readable(map, reg))
177                 return -EIO;
178
179         if (!regmap_volatile(map, reg))
180                 return map->cache_ops->read(map, reg, value);
181
182         return -EINVAL;
183 }
184 EXPORT_SYMBOL_GPL(regcache_read);
185
186 /**
187  * regcache_write: Set the value of a given register in the cache.
188  *
189  * @map: map to configure.
190  * @reg: The register index.
191  * @value: The new register value.
192  *
193  * Return a negative value on failure, 0 on success.
194  */
195 int regcache_write(struct regmap *map,
196                    unsigned int reg, unsigned int value)
197 {
198         if (map->cache_type == REGCACHE_NONE)
199                 return 0;
200
201         BUG_ON(!map->cache_ops);
202
203         if (!regmap_writeable(map, reg))
204                 return -EIO;
205
206         if (!regmap_volatile(map, reg))
207                 return map->cache_ops->write(map, reg, value);
208
209         return 0;
210 }
211 EXPORT_SYMBOL_GPL(regcache_write);
212
213 /**
214  * regcache_sync: Sync the register cache with the hardware.
215  *
216  * @map: map to configure.
217  *
218  * Any registers that should not be synced should be marked as
219  * volatile.  In general drivers can choose not to use the provided
220  * syncing functionality if they so require.
221  *
222  * Return a negative value on failure, 0 on success.
223  */
224 int regcache_sync(struct regmap *map)
225 {
226         int ret;
227         const char *name;
228
229         BUG_ON(!map->cache_ops);
230
231         if (map->cache_ops->sync) {
232                 dev_dbg(map->dev, "Syncing %s cache\n",
233                         map->cache_ops->name);
234                 name = map->cache_ops->name;
235                 trace_regcache_sync(map->dev, name, "start");
236                 ret = map->cache_ops->sync(map);
237                 trace_regcache_sync(map->dev, name, "stop");
238         }
239         return 0;
240 }
241 EXPORT_SYMBOL_GPL(regcache_sync);
242
243 bool regcache_set_val(void *base, unsigned int idx,
244                       unsigned int val, unsigned int word_size)
245 {
246         switch (word_size) {
247         case 1: {
248                 u8 *cache = base;
249                 if (cache[idx] == val)
250                         return true;
251                 cache[idx] = val;
252                 break;
253         }
254         case 2: {
255                 u16 *cache = base;
256                 if (cache[idx] == val)
257                         return true;
258                 cache[idx] = val;
259                 break;
260         }
261         default:
262                 BUG();
263         }
264         /* unreachable */
265         return false;
266 }
267
268 unsigned int regcache_get_val(const void *base, unsigned int idx,
269                               unsigned int word_size)
270 {
271         if (!base)
272                 return -EINVAL;
273
274         switch (word_size) {
275         case 1: {
276                 const u8 *cache = base;
277                 return cache[idx];
278         }
279         case 2: {
280                 const u16 *cache = base;
281                 return cache[idx];
282         }
283         default:
284                 BUG();
285         }
286         /* unreachable */
287         return -1;
288 }
289
290 int regcache_lookup_reg(struct regmap *map, unsigned int reg)
291 {
292         unsigned int i;
293
294         for (i = 0; i < map->num_reg_defaults; i++)
295                 if (map->reg_defaults[i].reg == reg)
296                         return i;
297         return -1;
298 }
299
300 int regcache_insert_reg(struct regmap *map, unsigned int reg,
301                         unsigned int val)
302 {
303         void *tmp;
304
305         tmp = krealloc(map->reg_defaults,
306                        (map->num_reg_defaults + 1) * sizeof(struct reg_default),
307                        GFP_KERNEL);
308         if (!tmp)
309                 return -ENOMEM;
310         map->reg_defaults = tmp;
311         map->num_reg_defaults++;
312         map->reg_defaults[map->num_reg_defaults - 1].reg = reg;
313         map->reg_defaults[map->num_reg_defaults - 1].def = val;
314         return 0;
315 }