diff options
author | Mark Brown <broonie@linaro.org> | 2013-06-30 07:40:01 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-06-30 07:40:01 -0400 |
commit | feff98f5507f98c0422252caec41c3be9e6b6399 (patch) | |
tree | d014206d6ecc095ceb8e03ea2b17b42bf1ce786a /drivers/base/regmap | |
parent | 9e895ace5d82df8929b16f58e9f515f6d54ab82d (diff) | |
parent | d6814a7dafa590ec5fe0597922ea76354f9bec59 (diff) |
Merge remote-tracking branch 'regmap/topic/cache' into regmap-next
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r-- | drivers/base/regmap/internal.h | 2 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 62 | ||||
-rw-r--r-- | drivers/base/regmap/regcache.c | 83 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 4 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 21 |
5 files changed, 145 insertions, 27 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index c130536e0ab0..ae23d8391aa0 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h | |||
@@ -52,6 +52,7 @@ struct regmap_async { | |||
52 | struct regmap { | 52 | struct regmap { |
53 | struct mutex mutex; | 53 | struct mutex mutex; |
54 | spinlock_t spinlock; | 54 | spinlock_t spinlock; |
55 | unsigned long spinlock_flags; | ||
55 | regmap_lock lock; | 56 | regmap_lock lock; |
56 | regmap_unlock unlock; | 57 | regmap_unlock unlock; |
57 | void *lock_arg; /* This is passed to lock/unlock functions */ | 58 | void *lock_arg; /* This is passed to lock/unlock functions */ |
@@ -148,6 +149,7 @@ struct regcache_ops { | |||
148 | int (*read)(struct regmap *map, unsigned int reg, unsigned int *value); | 149 | int (*read)(struct regmap *map, unsigned int reg, unsigned int *value); |
149 | int (*write)(struct regmap *map, unsigned int reg, unsigned int value); | 150 | int (*write)(struct regmap *map, unsigned int reg, unsigned int value); |
150 | int (*sync)(struct regmap *map, unsigned int min, unsigned int max); | 151 | int (*sync)(struct regmap *map, unsigned int min, unsigned int max); |
152 | int (*drop)(struct regmap *map, unsigned int min, unsigned int max); | ||
151 | }; | 153 | }; |
152 | 154 | ||
153 | bool regmap_writeable(struct regmap *map, unsigned int reg); | 155 | bool regmap_writeable(struct regmap *map, unsigned int reg); |
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 02f490bad30f..5c1435c4e210 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c | |||
@@ -304,6 +304,48 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, | |||
304 | return 0; | 304 | return 0; |
305 | } | 305 | } |
306 | 306 | ||
307 | static struct regcache_rbtree_node * | ||
308 | regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) | ||
309 | { | ||
310 | struct regcache_rbtree_node *rbnode; | ||
311 | const struct regmap_range *range; | ||
312 | int i; | ||
313 | |||
314 | rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL); | ||
315 | if (!rbnode) | ||
316 | return NULL; | ||
317 | |||
318 | /* If there is a read table then use it to guess at an allocation */ | ||
319 | if (map->rd_table) { | ||
320 | for (i = 0; i < map->rd_table->n_yes_ranges; i++) { | ||
321 | if (regmap_reg_in_range(reg, | ||
322 | &map->rd_table->yes_ranges[i])) | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | if (i != map->rd_table->n_yes_ranges) { | ||
327 | range = &map->rd_table->yes_ranges[i]; | ||
328 | rbnode->blklen = range->range_max - range->range_min | ||
329 | + 1; | ||
330 | rbnode->base_reg = range->range_min; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | if (!rbnode->blklen) { | ||
335 | rbnode->blklen = sizeof(*rbnode); | ||
336 | rbnode->base_reg = reg; | ||
337 | } | ||
338 | |||
339 | rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, | ||
340 | GFP_KERNEL); | ||
341 | if (!rbnode->block) { | ||
342 | kfree(rbnode); | ||
343 | return NULL; | ||
344 | } | ||
345 | |||
346 | return rbnode; | ||
347 | } | ||
348 | |||
307 | static int regcache_rbtree_write(struct regmap *map, unsigned int reg, | 349 | static int regcache_rbtree_write(struct regmap *map, unsigned int reg, |
308 | unsigned int value) | 350 | unsigned int value) |
309 | { | 351 | { |
@@ -354,23 +396,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, | |||
354 | return 0; | 396 | return 0; |
355 | } | 397 | } |
356 | } | 398 | } |
357 | /* we did not manage to find a place to insert it in an existing | 399 | |
358 | * block so create a new rbnode with a single register in its block. | 400 | /* We did not manage to find a place to insert it in |
359 | * This block will get populated further if any other adjacent | 401 | * an existing block so create a new rbnode. |
360 | * registers get modified in the future. | ||
361 | */ | 402 | */ |
362 | rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); | 403 | rbnode = regcache_rbtree_node_alloc(map, reg); |
363 | if (!rbnode) | 404 | if (!rbnode) |
364 | return -ENOMEM; | 405 | return -ENOMEM; |
365 | rbnode->blklen = sizeof(*rbnode); | 406 | regcache_rbtree_set_register(map, rbnode, |
366 | rbnode->base_reg = reg; | 407 | reg - rbnode->base_reg, value); |
367 | rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, | ||
368 | GFP_KERNEL); | ||
369 | if (!rbnode->block) { | ||
370 | kfree(rbnode); | ||
371 | return -ENOMEM; | ||
372 | } | ||
373 | regcache_rbtree_set_register(map, rbnode, 0, value); | ||
374 | regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); | 408 | regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); |
375 | rbtree_ctx->cached_rbnode = rbnode; | 409 | rbtree_ctx->cached_rbnode = rbnode; |
376 | } | 410 | } |
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 507ee2da0f6e..e69102696533 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c | |||
@@ -250,6 +250,38 @@ int regcache_write(struct regmap *map, | |||
250 | return 0; | 250 | return 0; |
251 | } | 251 | } |
252 | 252 | ||
253 | static int regcache_default_sync(struct regmap *map, unsigned int min, | ||
254 | unsigned int max) | ||
255 | { | ||
256 | unsigned int reg; | ||
257 | |||
258 | for (reg = min; reg <= max; reg++) { | ||
259 | unsigned int val; | ||
260 | int ret; | ||
261 | |||
262 | if (regmap_volatile(map, reg)) | ||
263 | continue; | ||
264 | |||
265 | ret = regcache_read(map, reg, &val); | ||
266 | if (ret) | ||
267 | return ret; | ||
268 | |||
269 | /* Is this the hardware default? If so skip. */ | ||
270 | ret = regcache_lookup_reg(map, reg); | ||
271 | if (ret >= 0 && val == map->reg_defaults[ret].def) | ||
272 | continue; | ||
273 | |||
274 | map->cache_bypass = 1; | ||
275 | ret = _regmap_write(map, reg, val); | ||
276 | map->cache_bypass = 0; | ||
277 | if (ret) | ||
278 | return ret; | ||
279 | dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val); | ||
280 | } | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
253 | /** | 285 | /** |
254 | * regcache_sync: Sync the register cache with the hardware. | 286 | * regcache_sync: Sync the register cache with the hardware. |
255 | * | 287 | * |
@@ -268,7 +300,7 @@ int regcache_sync(struct regmap *map) | |||
268 | const char *name; | 300 | const char *name; |
269 | unsigned int bypass; | 301 | unsigned int bypass; |
270 | 302 | ||
271 | BUG_ON(!map->cache_ops || !map->cache_ops->sync); | 303 | BUG_ON(!map->cache_ops); |
272 | 304 | ||
273 | map->lock(map->lock_arg); | 305 | map->lock(map->lock_arg); |
274 | /* Remember the initial bypass state */ | 306 | /* Remember the initial bypass state */ |
@@ -297,7 +329,10 @@ int regcache_sync(struct regmap *map) | |||
297 | } | 329 | } |
298 | map->cache_bypass = 0; | 330 | map->cache_bypass = 0; |
299 | 331 | ||
300 | ret = map->cache_ops->sync(map, 0, map->max_register); | 332 | if (map->cache_ops->sync) |
333 | ret = map->cache_ops->sync(map, 0, map->max_register); | ||
334 | else | ||
335 | ret = regcache_default_sync(map, 0, map->max_register); | ||
301 | 336 | ||
302 | if (ret == 0) | 337 | if (ret == 0) |
303 | map->cache_dirty = false; | 338 | map->cache_dirty = false; |
@@ -331,7 +366,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, | |||
331 | const char *name; | 366 | const char *name; |
332 | unsigned int bypass; | 367 | unsigned int bypass; |
333 | 368 | ||
334 | BUG_ON(!map->cache_ops || !map->cache_ops->sync); | 369 | BUG_ON(!map->cache_ops); |
335 | 370 | ||
336 | map->lock(map->lock_arg); | 371 | map->lock(map->lock_arg); |
337 | 372 | ||
@@ -346,7 +381,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min, | |||
346 | if (!map->cache_dirty) | 381 | if (!map->cache_dirty) |
347 | goto out; | 382 | goto out; |
348 | 383 | ||
349 | ret = map->cache_ops->sync(map, min, max); | 384 | if (map->cache_ops->sync) |
385 | ret = map->cache_ops->sync(map, min, max); | ||
386 | else | ||
387 | ret = regcache_default_sync(map, min, max); | ||
350 | 388 | ||
351 | out: | 389 | out: |
352 | trace_regcache_sync(map->dev, name, "stop region"); | 390 | trace_regcache_sync(map->dev, name, "stop region"); |
@@ -359,6 +397,43 @@ out: | |||
359 | EXPORT_SYMBOL_GPL(regcache_sync_region); | 397 | EXPORT_SYMBOL_GPL(regcache_sync_region); |
360 | 398 | ||
361 | /** | 399 | /** |
400 | * regcache_drop_region: Discard part of the register cache | ||
401 | * | ||
402 | * @map: map to operate on | ||
403 | * @min: first register to discard | ||
404 | * @max: last register to discard | ||
405 | * | ||
406 | * Discard part of the register cache. | ||
407 | * | ||
408 | * Return a negative value on failure, 0 on success. | ||
409 | */ | ||
410 | int regcache_drop_region(struct regmap *map, unsigned int min, | ||
411 | unsigned int max) | ||
412 | { | ||
413 | unsigned int reg; | ||
414 | int ret = 0; | ||
415 | |||
416 | if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) | ||
417 | return -EINVAL; | ||
418 | |||
419 | map->lock(map->lock_arg); | ||
420 | |||
421 | trace_regcache_drop_region(map->dev, min, max); | ||
422 | |||
423 | if (map->cache_present) | ||
424 | for (reg = min; reg < max + 1; reg++) | ||
425 | clear_bit(reg, map->cache_present); | ||
426 | |||
427 | if (map->cache_ops && map->cache_ops->drop) | ||
428 | ret = map->cache_ops->drop(map, min, max); | ||
429 | |||
430 | map->unlock(map->lock_arg); | ||
431 | |||
432 | return ret; | ||
433 | } | ||
434 | EXPORT_SYMBOL_GPL(regcache_drop_region); | ||
435 | |||
436 | /** | ||
362 | * regcache_cache_only: Put a register map into cache only mode | 437 | * regcache_cache_only: Put a register map into cache only mode |
363 | * | 438 | * |
364 | * @map: map to configure | 439 | * @map: map to configure |
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 975719bc3450..98ee9c79678d 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c | |||
@@ -84,6 +84,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | |||
84 | unsigned int fpos_offset; | 84 | unsigned int fpos_offset; |
85 | unsigned int reg_offset; | 85 | unsigned int reg_offset; |
86 | 86 | ||
87 | /* Suppress the cache if we're using a subrange */ | ||
88 | if (from) | ||
89 | return from; | ||
90 | |||
87 | /* | 91 | /* |
88 | * If we don't have a cache build one so we don't have to do a | 92 | * If we don't have a cache build one so we don't have to do a |
89 | * linear scan each time. | 93 | * linear scan each time. |
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a941dcfe7590..1a01553189b3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c | |||
@@ -65,9 +65,8 @@ bool regmap_reg_in_ranges(unsigned int reg, | |||
65 | } | 65 | } |
66 | EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); | 66 | EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); |
67 | 67 | ||
68 | static bool _regmap_check_range_table(struct regmap *map, | 68 | bool regmap_check_range_table(struct regmap *map, unsigned int reg, |
69 | unsigned int reg, | 69 | const struct regmap_access_table *table) |
70 | const struct regmap_access_table *table) | ||
71 | { | 70 | { |
72 | /* Check "no ranges" first */ | 71 | /* Check "no ranges" first */ |
73 | if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) | 72 | if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) |
@@ -80,6 +79,7 @@ static bool _regmap_check_range_table(struct regmap *map, | |||
80 | return regmap_reg_in_ranges(reg, table->yes_ranges, | 79 | return regmap_reg_in_ranges(reg, table->yes_ranges, |
81 | table->n_yes_ranges); | 80 | table->n_yes_ranges); |
82 | } | 81 | } |
82 | EXPORT_SYMBOL_GPL(regmap_check_range_table); | ||
83 | 83 | ||
84 | bool regmap_writeable(struct regmap *map, unsigned int reg) | 84 | bool regmap_writeable(struct regmap *map, unsigned int reg) |
85 | { | 85 | { |
@@ -90,7 +90,7 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) | |||
90 | return map->writeable_reg(map->dev, reg); | 90 | return map->writeable_reg(map->dev, reg); |
91 | 91 | ||
92 | if (map->wr_table) | 92 | if (map->wr_table) |
93 | return _regmap_check_range_table(map, reg, map->wr_table); | 93 | return regmap_check_range_table(map, reg, map->wr_table); |
94 | 94 | ||
95 | return true; | 95 | return true; |
96 | } | 96 | } |
@@ -107,7 +107,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg) | |||
107 | return map->readable_reg(map->dev, reg); | 107 | return map->readable_reg(map->dev, reg); |
108 | 108 | ||
109 | if (map->rd_table) | 109 | if (map->rd_table) |
110 | return _regmap_check_range_table(map, reg, map->rd_table); | 110 | return regmap_check_range_table(map, reg, map->rd_table); |
111 | 111 | ||
112 | return true; | 112 | return true; |
113 | } | 113 | } |
@@ -121,7 +121,7 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) | |||
121 | return map->volatile_reg(map->dev, reg); | 121 | return map->volatile_reg(map->dev, reg); |
122 | 122 | ||
123 | if (map->volatile_table) | 123 | if (map->volatile_table) |
124 | return _regmap_check_range_table(map, reg, map->volatile_table); | 124 | return regmap_check_range_table(map, reg, map->volatile_table); |
125 | 125 | ||
126 | return true; | 126 | return true; |
127 | } | 127 | } |
@@ -135,7 +135,7 @@ bool regmap_precious(struct regmap *map, unsigned int reg) | |||
135 | return map->precious_reg(map->dev, reg); | 135 | return map->precious_reg(map->dev, reg); |
136 | 136 | ||
137 | if (map->precious_table) | 137 | if (map->precious_table) |
138 | return _regmap_check_range_table(map, reg, map->precious_table); | 138 | return regmap_check_range_table(map, reg, map->precious_table); |
139 | 139 | ||
140 | return false; | 140 | return false; |
141 | } | 141 | } |
@@ -302,13 +302,16 @@ static void regmap_unlock_mutex(void *__map) | |||
302 | static void regmap_lock_spinlock(void *__map) | 302 | static void regmap_lock_spinlock(void *__map) |
303 | { | 303 | { |
304 | struct regmap *map = __map; | 304 | struct regmap *map = __map; |
305 | spin_lock(&map->spinlock); | 305 | unsigned long flags; |
306 | |||
307 | spin_lock_irqsave(&map->spinlock, flags); | ||
308 | map->spinlock_flags = flags; | ||
306 | } | 309 | } |
307 | 310 | ||
308 | static void regmap_unlock_spinlock(void *__map) | 311 | static void regmap_unlock_spinlock(void *__map) |
309 | { | 312 | { |
310 | struct regmap *map = __map; | 313 | struct regmap *map = __map; |
311 | spin_unlock(&map->spinlock); | 314 | spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); |
312 | } | 315 | } |
313 | 316 | ||
314 | static void dev_get_regmap_release(struct device *dev, void *res) | 317 | static void dev_get_regmap_release(struct device *dev, void *res) |