Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903// SPDX-License-Identifier: GPL-2.0//// Register cache access API//// Copyright 2011 Wolfson Microelectronics plc//// Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
#include <linux/bsearch.h>#include <linux/device.h>#include <linux/export.h>#include <linux/slab.h>#include <linux/sort.h>
#include "trace.h"#include "internal.h"
static const struct regcache_ops *cache_types[] = { ®cache_flat_sparse_ops, ®cache_rbtree_ops, ®cache_maple_ops, ®cache_flat_ops,};
static int regcache_default_sync(struct regmap *map, unsigned int min, unsigned int max);
static int regcache_defaults_cmp(const void *a, const void *b){ const struct reg_default *x = a; const struct reg_default *y = b;
if (x->reg > y->reg) return 1; else if (x->reg < y->reg) return -1; else return 0;}
void regcache_sort_defaults(struct reg_default *defaults, unsigned int ndefaults){ sort(defaults, ndefaults, sizeof(*defaults), regcache_defaults_cmp, NULL);}EXPORT_SYMBOL_GPL(regcache_sort_defaults);
static int regcache_count_cacheable_registers(struct regmap *map){ unsigned int count;
/* calculate the size of reg_defaults */ count = 0; for (unsigned int i = 0; i < map->num_reg_defaults_raw; i++) if (regmap_readable(map, i * map->reg_stride) && !regmap_volatile(map, i * map->reg_stride)) count++;
return count;}
static int regcache_hw_init(struct regmap *map, int count){ int ret; unsigned int reg, val; void *tmp_buf;
if (!count) return 0;
if (!map->reg_defaults_raw) { bool cache_bypass = map->cache_bypass; dev_dbg(map->dev, "No cache defaults, reading back from HW\n");
/* Bypass the cache access till data read from HW */ map->cache_bypass = true; tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL); if (!tmp_buf) return -ENOMEM; ret = regmap_raw_read(map, 0, tmp_buf, map->cache_size_raw); map->cache_bypass = cache_bypass; if (ret == 0) { map->reg_defaults_raw = tmp_buf; map->cache_free = true; } else { kfree(tmp_buf); } }
/* fill the reg_defaults */ for (unsigned int i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { reg = i * map->reg_stride;
if (!regmap_readable(map, reg)) continue;
if (regmap_volatile(map, reg)) continue;
if (map->reg_defaults_raw) { val = regcache_get_val(map, map->reg_defaults_raw, i); } else { bool cache_bypass = map->cache_bypass;
map->cache_bypass = true; ret = regmap_read(map, reg, &val); map->cache_bypass = cache_bypass; if (ret != 0) { dev_err(map->dev, "Failed to read %x: %d\n", reg, ret); return ret; } }
map->reg_defaults[j].reg = reg; map->reg_defaults[j].def = val; j++; }
return 0;}
static void regcache_hw_exit(struct regmap *map){ if (map->cache_free) kfree(map->reg_defaults_raw);}
static int regcache_locked_op(struct regmap *map, int (*op)(struct regmap *map), const char *action){ if (!op) return 0;
dev_dbg(map->dev, "%s %s cache\n", action, map->cache_ops->name); guard(regmap)(map); return op(map);}
static int __regcache_sync(struct regmap *map, unsigned int min, unsigned int max){ if (!map->cache_dirty) return 0;
if (map->cache_ops->sync) return map->cache_ops->sync(map, min, max);
return regcache_default_sync(map, min, max);}
int regcache_init(struct regmap *map, const struct regmap_config *config){ bool sort_defaults = false; unsigned int reg_prev = 0; int count = 0; int ret; int i; void *tmp_buf;
if (map->cache_type == REGCACHE_NONE) { if (config->reg_defaults || config->num_reg_defaults_raw) dev_warn(map->dev, "No cache used with register defaults set!\n");
map->cache_bypass = true; return 0; }
if (!!config->reg_defaults != !!config->num_reg_defaults) { dev_err(map->dev, "reg_defaults and num_reg_defaults must both be specified\n"); return -EINVAL; }
for (i = 0; i < config->num_reg_defaults; i++) { if (config->reg_defaults[i].reg % map->reg_stride) return -EINVAL;
if (reg_prev > config->reg_defaults[i].reg) sort_defaults = true;
reg_prev = config->reg_defaults[i].reg; }
for (i = 0; i < ARRAY_SIZE(cache_types); i++) if (cache_types[i]->type == map->cache_type) break;
if (i == ARRAY_SIZE(cache_types)) { dev_err(map->dev, "Could not match cache type: %d\n", map->cache_type); return -EINVAL; }
map->num_reg_defaults = config->num_reg_defaults; map->num_reg_defaults_raw = config->num_reg_defaults_raw; map->reg_defaults_raw = config->reg_defaults_raw; map->cache_word_size = BITS_TO_BYTES(config->val_bits); map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
map->cache = NULL; map->cache_ops = cache_types[i];
if (!map->cache_ops->read || !map->cache_ops->write || !map->cache_ops->name) return -EINVAL;
/* We still need to ensure that the reg_defaults * won't vanish from under us. We'll need to make * a copy of it. */ if (config->reg_defaults) { tmp_buf = kmemdup_array(config->reg_defaults, map->num_reg_defaults, sizeof(*map->reg_defaults), GFP_KERNEL); if (!tmp_buf) return -ENOMEM;
/* regcache_lookup_reg() bsearch()es this array */ if (sort_defaults) { dev_warn(map->dev, "Driver needs fixing: Unsorted reg_defaults, sorting the copy\n"); regcache_sort_defaults(tmp_buf, map->num_reg_defaults); } map->reg_defaults = tmp_buf; } else if (map->num_reg_defaults_raw) { count = regcache_count_cacheable_registers(map); if (!count) map->cache_bypass = true;
/* All registers are unreadable or volatile, so just bypass */ if (map->cache_bypass) return 0;
map->num_reg_defaults = count; map->reg_defaults = kmalloc_objs(struct reg_default, count); if (!map->reg_defaults) return -ENOMEM; }
if (!map->max_register_is_set && map->num_reg_defaults_raw) { map->max_register = (map->num_reg_defaults_raw - 1) * map->reg_stride; map->max_register_is_set = true; }
if (map->cache_ops->init) { dev_dbg(map->dev, "Initializing %s cache\n", map->cache_ops->name); ret = map->cache_ops->init(map); if (ret) goto err_free_reg_defaults; }
/* * Some devices such as PMICs don't have cache defaults, * we cope with this by reading back the HW registers and * crafting the cache defaults by hand. */ ret = regcache_hw_init(map, count); if (ret) goto err_exit;
if (!map->num_reg_defaults && !map->reg_default_cb) return 0;
ret = regcache_locked_op(map, map->cache_ops->populate, "Populating"); if (ret) goto err_free;
return 0;
err_free: regcache_hw_exit(map);err_exit: if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); map->cache_ops->exit(map); }err_free_reg_defaults: kfree(map->reg_defaults);
return ret;}
void regcache_exit(struct regmap *map){ if (map->cache_type == REGCACHE_NONE) return;
BUG_ON(!map->cache_ops);
regcache_hw_exit(map); if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); map->cache_ops->exit(map); }
kfree(map->reg_defaults); map->reg_defaults = NULL;}
/** * regcache_read - Fetch the value of a given register from the cache. * * @map: map to configure. * @reg: The register index. * @value: The value to be returned. * * Return a negative value on failure, 0 on success. */int regcache_read(struct regmap *map, unsigned int reg, unsigned int *value){ int ret;
if (map->cache_type == REGCACHE_NONE) return -EINVAL;
BUG_ON(!map->cache_ops);
if (!regmap_volatile(map, reg)) { ret = map->cache_ops->read(map, reg, value);
if (ret == 0) trace_regmap_reg_read_cache(map, reg, *value);
return ret; }
return -EINVAL;}
/** * regcache_write - Set the value of a given register in the cache. * * @map: map to configure. * @reg: The register index. * @value: The new register value. * * Return a negative value on failure, 0 on success. */int regcache_write(struct regmap *map, unsigned int reg, unsigned int value){ if (map->cache_type == REGCACHE_NONE) return 0;
BUG_ON(!map->cache_ops);
if (!regmap_volatile(map, reg)) return map->cache_ops->write(map, reg, value);
return 0;}
bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg, unsigned int val){ int ret;
if (!regmap_writeable(map, reg)) return false;
/* If we don't know the chip just got reset, then sync everything. */ if (!map->no_sync_defaults) return true;
/* Is this the hardware default? If so skip. */ ret = regcache_lookup_reg(map, reg); if (ret >= 0 && val == map->reg_defaults[ret].def) return false; return true;}
static int regcache_default_sync(struct regmap *map, unsigned int min, unsigned int max){ unsigned int reg;
for (reg = min; reg <= max; reg += map->reg_stride) { unsigned int val; int ret;
if (regmap_volatile(map, reg) || !regmap_writeable(map, reg)) continue;
ret = regcache_read(map, reg, &val); if (ret == -ENOENT) continue; if (ret) return ret;
if (!regcache_reg_needs_sync(map, reg, val)) continue;
map->cache_bypass = true; ret = _regmap_write(map, reg, val); map->cache_bypass = false; if (ret) { dev_err(map->dev, "Unable to sync register %#x. %d\n", reg, ret); return ret; } dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val); }
return 0;}
static int rbtree_all(const void *key, const struct rb_node *node){ return 0;}
/** * regcache_sync - Sync the register cache with the hardware. * * @map: map to configure. * * Any registers that should not be synced should be marked as * volatile. In general drivers can choose not to use the provided * syncing functionality if they so require. * * This pushes cached changes made while cache_only (e.g. suspend) down * to hardware. The caller must disable cache_only before calling this * function. * * Return a negative value on failure, 0 on success. */int regcache_sync(struct regmap *map){ int sync_ret = 0; int selector_ret = 0; unsigned int i; const char *name; bool bypass; struct rb_node *node;
if (WARN_ON(map->cache_type == REGCACHE_NONE)) return -EINVAL;
BUG_ON(!map->cache_ops);
map->lock(map->lock_arg);
if (WARN_ON(map->cache_only)) { map->unlock(map->lock_arg); return -EINVAL; }
/* Remember the initial bypass state */ bypass = map->cache_bypass; dev_dbg(map->dev, "Syncing %s cache\n", map->cache_ops->name); name = map->cache_ops->name; trace_regcache_sync(map, name, "start");
if (!map->cache_dirty) goto out;
/* Apply any patch first */ map->cache_bypass = true; for (i = 0; i < map->patch_regs; i++) { sync_ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (sync_ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", map->patch[i].reg, map->patch[i].def, sync_ret); goto out; } } map->cache_bypass = false;
sync_ret = __regcache_sync(map, 0, map->max_register); if (sync_ret == 0) map->cache_dirty = false;
out: /* Restore the bypass state */ map->cache_bypass = bypass; map->no_sync_defaults = false;
/* * If we did any paging with cache bypassed and a cached * paging register then the register and cache state might * have gone out of sync, force writes of all the paging * registers. */ rb_for_each(node, NULL, &map->range_tree, rbtree_all) { struct regmap_range_node *this = rb_entry(node, struct regmap_range_node, node);
/* If there's nothing in the cache there's nothing to sync */ if (regcache_read(map, this->selector_reg, &i) != 0) continue;
selector_ret = _regmap_write(map, this->selector_reg, i); if (selector_ret != 0) { map->cache_dirty = true; dev_err(map->dev, "Failed to write %x = %x: %d\n", this->selector_reg, i, selector_ret); break; } }
map->unlock(map->lock_arg);
regmap_async_complete(map);
trace_regcache_sync(map, name, "stop");
return sync_ret ? sync_ret : selector_ret;}EXPORT_SYMBOL_GPL(regcache_sync);
/** * regcache_sync_region - Sync part of the register cache with the hardware. * * @map: map to sync. * @min: first register to sync * @max: last register to sync * * Write all non-default register values in the specified region to * the hardware. * * Return a negative value on failure, 0 on success. */int regcache_sync_region(struct regmap *map, unsigned int min, unsigned int max){ int ret = 0; const char *name; bool bypass;
if (WARN_ON(map->cache_type == REGCACHE_NONE)) return -EINVAL;
BUG_ON(!map->cache_ops);
map->lock(map->lock_arg);
if (WARN_ON(map->cache_only)) { map->unlock(map->lock_arg); return -EINVAL; } /* Remember the initial bypass state */ bypass = map->cache_bypass;
name = map->cache_ops->name; dev_dbg(map->dev, "Syncing %s cache from %#x-%#x\n", name, min, max);
trace_regcache_sync(map, name, "start region");
map->async = true;
ret = __regcache_sync(map, min, max);
/* Restore the bypass state */ map->cache_bypass = bypass; map->async = false; map->no_sync_defaults = false; map->unlock(map->lock_arg);
regmap_async_complete(map);
trace_regcache_sync(map, name, "stop region");
return ret;}EXPORT_SYMBOL_GPL(regcache_sync_region);
/** * regcache_drop_region - Discard part of the register cache * * @map: map to operate on * @min: first register to discard * @max: last register to discard * * Discard part of the register cache. * * Return a negative value on failure, 0 on success. */int regcache_drop_region(struct regmap *map, unsigned int min, unsigned int max){ if (!map->cache_ops || !map->cache_ops->drop) return -EINVAL;
guard(regmap)(map);
trace_regcache_drop_region(map, min, max);
return map->cache_ops->drop(map, min, max);}EXPORT_SYMBOL_GPL(regcache_drop_region);
/** * regcache_cache_only - Put a register map into cache only mode * * @map: map to configure * @enable: flag if changes should be written to the hardware * * When a register map is marked as cache only writes to the register * map API will only update the register cache, they will not cause * any hardware changes. This is useful for allowing portions of * drivers to act as though the device were functioning as normal when * it is disabled for power saving reasons. */void regcache_cache_only(struct regmap *map, bool enable){ guard(regmap)(map); WARN_ON(map->cache_type != REGCACHE_NONE && map->cache_bypass && enable); map->cache_only = enable; trace_regmap_cache_only(map, enable);}EXPORT_SYMBOL_GPL(regcache_cache_only);
/** * regcache_mark_dirty - Indicate that HW registers were reset to default values * * @map: map to mark * * Inform regcache that the device has been powered down or reset, so that * on resume, regcache_sync() knows to write out all non-default values * stored in the cache. * * If this function is not called, regcache_sync() will assume that * the hardware state still matches the cache state, modulo any writes that * happened when cache_only was true. */void regcache_mark_dirty(struct regmap *map){ guard(regmap)(map); map->cache_dirty = true; map->no_sync_defaults = true;}EXPORT_SYMBOL_GPL(regcache_mark_dirty);
/** * regcache_cache_bypass - Put a register map into cache bypass mode * * @map: map to configure * @enable: flag if changes should not be written to the cache * * When a register map is marked with the cache bypass option, writes * to the register map API will only update the hardware and not * the cache directly. This is useful when syncing the cache back to * the hardware. */void regcache_cache_bypass(struct regmap *map, bool enable){ guard(regmap)(map); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; trace_regmap_cache_bypass(map, enable);}EXPORT_SYMBOL_GPL(regcache_cache_bypass);
/** * regcache_reg_cached - Check if a register is cached * * @map: map to check * @reg: register to check * * Reports if a register is cached. */bool regcache_reg_cached(struct regmap *map, unsigned int reg){ unsigned int val; int ret;
guard(regmap)(map);
ret = regcache_read(map, reg, &val);
return ret == 0;}EXPORT_SYMBOL_GPL(regcache_reg_cached);
void regcache_set_val(struct regmap *map, void *base, unsigned int idx, unsigned int val){ /* Use device native format if possible */ if (map->format.format_val) { map->format.format_val(base + (map->cache_word_size * idx), val, 0); return; }
switch (map->cache_word_size) { case 1: { u8 *cache = base;
cache[idx] = val; break; } case 2: { u16 *cache = base;
cache[idx] = val; break; } case 4: { u32 *cache = base;
cache[idx] = val; break; } default: BUG(); }}
unsigned int regcache_get_val(struct regmap *map, const void *base, unsigned int idx){ if (!base) return -EINVAL;
/* Use device native format if possible */ if (map->format.parse_val) return map->format.parse_val(regcache_get_val_addr(map, base, idx));
switch (map->cache_word_size) { case 1: { const u8 *cache = base;
return cache[idx]; } case 2: { const u16 *cache = base;
return cache[idx]; } case 4: { const u32 *cache = base;
return cache[idx]; } default: BUG(); } /* unreachable */ return -1;}
int regcache_lookup_reg(struct regmap *map, unsigned int reg){ struct reg_default key; struct reg_default *r;
key.reg = reg; key.def = 0;
r = bsearch(&key, map->reg_defaults, map->num_reg_defaults, sizeof(struct reg_default), regcache_defaults_cmp);
if (r) return r - map->reg_defaults;
return -ENOENT;}
static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx){ if (!cache_present) return true;
return test_bit(idx, cache_present);}
int regcache_sync_val(struct regmap *map, unsigned int reg, unsigned int val){ int ret;
if (!regcache_reg_needs_sync(map, reg, val)) return 0;
map->cache_bypass = true;
ret = _regmap_write(map, reg, val);
map->cache_bypass = false;
if (ret != 0) { dev_err(map->dev, "Unable to sync register %#x. %d\n", reg, ret); return ret; } dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val);
return 0;}
static int regcache_sync_block_single(struct regmap *map, void *block, unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end){ unsigned int i, regtmp, val; int ret;
for (i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(cache_present, i) || !regmap_writeable(map, regtmp)) continue;
val = regcache_get_val(map, block, i); ret = regcache_sync_val(map, regtmp, val); if (ret != 0) return ret; }
return 0;}
static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, unsigned int base, unsigned int cur){ size_t val_bytes = map->format.val_bytes; int ret, count;
if (*data == NULL) return 0;
count = (cur - base) / map->reg_stride;
dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", count * val_bytes, count, base, cur - map->reg_stride);
map->cache_bypass = true;
ret = _regmap_raw_write(map, base, *data, count * val_bytes, false); if (ret) dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n", base, cur - map->reg_stride, ret);
map->cache_bypass = false;
*data = NULL;
return ret;}
static int regcache_sync_block_raw(struct regmap *map, void *block, unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end){ unsigned int regtmp = 0; unsigned int base = 0; const void *data = NULL; unsigned int val; int ret;
for (unsigned int i = start; i < end; i++) { regtmp = block_base + (i * map->reg_stride);
if (!regcache_reg_present(cache_present, i) || !regmap_writeable(map, regtmp)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) return ret; continue; }
val = regcache_get_val(map, block, i); if (!regcache_reg_needs_sync(map, regtmp, val)) { ret = regcache_sync_block_raw_flush(map, &data, base, regtmp); if (ret != 0) return ret; continue; }
if (!data) { data = regcache_get_val_addr(map, block, i); base = regtmp; } }
return regcache_sync_block_raw_flush(map, &data, base, regtmp + map->reg_stride);}
int regcache_sync_block(struct regmap *map, void *block, unsigned long *cache_present, unsigned int block_base, unsigned int start, unsigned int end){ if (regmap_can_raw_write(map) && !map->use_single_write) return regcache_sync_block_raw(map, block, cache_present, block_base, start, end);
return regcache_sync_block_single(map, block, cache_present, block_base, start, end);}