/* * Register map access API * * Copyright 2011 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/device.h>#include <linux/slab.h>#include <linux/export.h>#include <linux/mutex.h>#include <linux/err.h>#include <linux/rbtree.h>#include <linux/sched.h> #define CREATE_TRACE_POINTS#include <trace/events/regmap.h> #include "internal.h" /* * Sometimes for failures during very early init the trace * infrastructure isn't available early enough to be used. For this * sort of problem defining LOG_DEVICE will add printks for basic * register I/O on a specific device. */#undef LOG_DEVICE static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val);static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val);static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges){ const struct regmap_range *r; int i; for (i = 0, r = ranges; i < nranges; i++, r++) if (regmap_reg_in_range(reg, r)) return true; return false;}EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); bool regmap_check_range_table(struct regmap *map, unsigned int reg, const struct regmap_access_table *table){ /* Check "no ranges" first */ if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) return false; /* In case zero "yes ranges" are supplied, any reg is OK */ if (!table->n_yes_ranges) return true; return regmap_reg_in_ranges(reg, table->yes_ranges, table->n_yes_ranges);}EXPORT_SYMBOL_GPL(regmap_check_range_table); bool regmap_writeable(struct regmap *map, unsigned int reg){ if (map->max_register && reg > map->max_register) return false; if (map->writeable_reg) return map->writeable_reg(map->dev, reg); if (map->wr_table) return regmap_check_range_table(map, reg, map->wr_table); return true;} bool regmap_readable(struct regmap *map, unsigned int reg){ if (map->max_register && reg > map->max_register) return false; if (map->format.format_write) return false; if (map->readable_reg) return map->readable_reg(map->dev, reg); if (map->rd_table) return regmap_check_range_table(map, reg, map->rd_table); return true;} bool regmap_volatile(struct regmap *map, unsigned int reg){ if (!regmap_readable(map, reg)) return false; if (map->volatile_reg) return map->volatile_reg(map->dev, reg); if (map->volatile_table) return regmap_check_range_table(map, reg, map->volatile_table); if (map->cache_ops) return false; else return true;} bool regmap_precious(struct regmap *map, unsigned int reg){ if (!regmap_readable(map, reg)) return false; if (map->precious_reg) return map->precious_reg(map->dev, reg); if (map->precious_table) return regmap_check_range_table(map, reg, map->precious_table); return false;} static bool regmap_volatile_range(struct regmap *map, unsigned int reg, size_t num){ unsigned int i; for (i = 0; i < num; i++) if (!regmap_volatile(map, reg + i)) return false; return true;} static void regmap_format_2_6_write(struct regmap *map, unsigned int reg, unsigned int val){ u8 *out = map->work_buf; *out = (reg << 6) | val;} static void regmap_format_4_12_write(struct regmap *map, unsigned int reg, unsigned int val){ __be16 *out = map->work_buf; *out = cpu_to_be16((reg << 12) | val);} static void regmap_format_7_9_write(struct regmap *map, unsigned int reg, unsigned int val){ __be16 *out = map->work_buf; *out = cpu_to_be16((reg << 9) | val);} static void regmap_format_10_14_write(struct regmap *map, unsigned int reg, unsigned int val){ u8 *out = map->work_buf; out[2] = val; out[1] = (val >> 8) | (reg << 6); out[0] = reg >> 2;} static void regmap_format_8(void *buf, unsigned int val, unsigned int shift){ u8 *b = buf; b[0] = val << shift;} static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift){ __be16 *b = buf; b[0] = cpu_to_be16(val << shift);} static void regmap_format_16_native(void *buf, unsigned int val, unsigned int shift){ *(u16 *)buf = val << shift;} static void regmap_format_24(void *buf, unsigned int val, unsigned int shift){ u8 *b = buf; val <<= shift; b[0] = val >> 16; b[1] = val >> 8; b[2] = val;} static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift){ __be32 *b = buf; b[0] = cpu_to_be32(val << shift);} static void regmap_format_32_native(void *buf, unsigned int val, unsigned int shift){ *(u32 *)buf = val << shift;} static void regmap_parse_inplace_noop(void *buf){} static unsigned int regmap_parse_8(const void *buf){ const u8 *b = buf; return b[0];} static unsigned int regmap_parse_16_be(const void *buf){ const __be16 *b = buf; return be16_to_cpu(b[0]);} static void regmap_parse_16_be_inplace(void *buf){ __be16 *b = buf; b[0] = be16_to_cpu(b[0]);} static unsigned int regmap_parse_16_native(const void *buf){ return *(u16 *)buf;} static unsigned int regmap_parse_24(const void *buf){ const u8 *b = buf; unsigned int ret = b[2]; ret |= ((unsigned int)b[1]) << 8; ret |= ((unsigned int)b[0]) << 16; return ret;} static unsigned int regmap_parse_32_be(const void *buf){ const __be32 *b = buf; return be32_to_cpu(b[0]);} static void regmap_parse_32_be_inplace(void *buf){ __be32 *b = buf; b[0] = be32_to_cpu(b[0]);} static unsigned int regmap_parse_32_native(const void *buf){ return *(u32 *)buf;} static void regmap_lock_mutex(void *__map){ struct regmap *map = __map; mutex_lock(&map->mutex);} static void regmap_unlock_mutex(void *__map){ struct regmap *map = __map; mutex_unlock(&map->mutex);} static void regmap_lock_spinlock(void *__map)__acquires(&map->spinlock){ struct regmap *map = __map; unsigned long flags; spin_lock_irqsave(&map->spinlock, flags); map->spinlock_flags = flags;} static void regmap_unlock_spinlock(void *__map)__releases(&map->spinlock){ struct regmap *map = __map; spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags);} static void dev_get_regmap_release(struct device *dev, void *res){ /* * We don't actually have anything to do here; the goal here * is not to manage the regmap but to provide a simple way to * get the regmap back given a struct device. */} static bool _regmap_range_add(struct regmap *map, struct regmap_range_node *data){ struct rb_root *root = &map->range_tree; struct rb_node **new = &(root->rb_node), *parent = NULL; while (*new) { struct regmap_range_node *this = container_of(*new, struct regmap_range_node, node); parent = *new; if (data->range_max < this->range_min) new = &((*new)->rb_left); else if (data->range_min > this->range_max) new = &((*new)->rb_right); else return false; } rb_link_node(&data->node, parent, new); rb_insert_color(&data->node, root); return true;} static struct regmap_range_node *_regmap_range_lookup(struct regmap *map, unsigned int reg){ struct rb_node *node = map->range_tree.rb_node; while (node) { struct regmap_range_node *this = container_of(node, struct regmap_range_node, node); if (reg < this->range_min) node = node->rb_left; else if (reg > this->range_max) node = node->rb_right; else return this; } return NULL;} static void regmap_range_exit(struct regmap *map){ struct rb_node *next; struct regmap_range_node *range_node; next = rb_first(&map->range_tree); while (next) { range_node = rb_entry(next, struct regmap_range_node, node); next = rb_next(&range_node->node); rb_erase(&range_node->node, &map->range_tree); kfree(range_node); } kfree(map->selector_work_buf);} /** * regmap_init(): Initialise register map * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. This function should generally not be called * directly, it should be called by bus-specific init functions. */struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, const struct regmap_config *config){ struct regmap *map, **m; int ret = -EINVAL; enum regmap_endian reg_endian, val_endian; int i, j; if (!config) goto err; map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) { ret = -ENOMEM; goto err; } if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; map->lock_arg = config->lock_arg; } else { if ((bus && bus->fast_io) || config->fast_io) { spin_lock_init(&map->spinlock); map->lock = regmap_lock_spinlock; map->unlock = regmap_unlock_spinlock; } else { mutex_init(&map->mutex); map->lock = regmap_lock_mutex; map->unlock = regmap_unlock_mutex; } map->lock_arg = map; } map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size = DIV_ROUND_UP(config->reg_bits + config->val_bits + config->pad_bits, 8); map->reg_shift = config->pad_bits % 8; if (config->reg_stride) map->reg_stride = config->reg_stride; else map->reg_stride = 1; map->use_single_rw = config->use_single_rw; map->dev = dev; map->bus = bus; map->bus_context = bus_context; map->max_register = config->max_register; map->wr_table = config->wr_table; map->rd_table = config->rd_table; map->volatile_table = config->volatile_table; map->precious_table = config->precious_table; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; map->name = config->name; spin_lock_init(&map->async_lock); INIT_LIST_HEAD(&map->async_list); INIT_LIST_HEAD(&map->async_free); init_waitqueue_head(&map->async_waitq); if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; } else if (bus) { map->read_flag_mask = bus->read_flag_mask; } if (!bus) { map->reg_read = config->reg_read; map->reg_write = config->reg_write; map->defer_caching = false; goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; } reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = REGMAP_ENDIAN_BIG; val_endian = config->val_format_endian; if (val_endian == REGMAP_ENDIAN_DEFAULT) val_endian = bus->val_format_endian_default; if (val_endian == REGMAP_ENDIAN_DEFAULT) val_endian = REGMAP_ENDIAN_BIG; switch (config->reg_bits + map->reg_shift) { case 2: switch (config->val_bits) { case 6: map->format.format_write = regmap_format_2_6_write; break; default: goto err_map; } break;Lines 1–500 of 2237. Load more
/* * Register map access API * * Copyright 2011 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/device.h> #include <linux/slab.h> #include <linux/export.h> #include <linux/mutex.h> #include <linux/err.h> #include <linux/rbtree.h> #include <linux/sched.h> #define CREATE_TRACE_POINTS #include <trace/events/regmap.h> #include "internal.h" /* * Sometimes for failures during very early init the trace * infrastructure isn't available early enough to be used. For this * sort of problem defining LOG_DEVICE will add printks for basic * register I/O on a specific device. */ #undef LOG_DEVICE static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val); static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val); static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) { const struct regmap_range *r; int i; for (i = 0, r = ranges; i < nranges; i++, r++) if (regmap_reg_in_range(reg, r)) return true; return false; } EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); bool regmap_check_range_table(struct regmap *map, unsigned int reg, const struct regmap_access_table *table) { /* Check "no ranges" first */ if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) return false; /* In case zero "yes ranges" are supplied, any reg is OK */ if (!table->n_yes_ranges) return true; return regmap_reg_in_ranges(reg, table->yes_ranges, table->n_yes_ranges); } EXPORT_SYMBOL_GPL(regmap_check_range_table); bool regmap_writeable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) return false; if (map->writeable_reg) return map->writeable_reg(map->dev, reg); if (map->wr_table) return regmap_check_range_table(map, reg, map->wr_table); return true; } bool regmap_readable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) return false; if (map->format.format_write) return false; if (map->readable_reg) return map->readable_reg(map->dev, reg); if (map->rd_table) return regmap_check_range_table(map, reg, map->rd_table); return true; } bool regmap_volatile(struct regmap *map, unsigned int reg) { if (!regmap_readable(map, reg)) return false; if (map->volatile_reg) return map->volatile_reg(map->dev, reg); if (map->volatile_table) return regmap_check_range_table(map, reg, map->volatile_table); if (map->cache_ops) return false; else return true; } bool regmap_precious(struct regmap *map, unsigned int reg) { if (!regmap_readable(map, reg)) return false; if (map->precious_reg) return map->precious_reg(map->dev, reg); if (map->precious_table) return regmap_check_range_table(map, reg, map->precious_table); return false; } static bool regmap_volatile_range(struct regmap *map, unsigned int reg, size_t num) { unsigned int i; for (i = 0; i < num; i++) if (!regmap_volatile(map, reg + i)) return false; return true; } static void regmap_format_2_6_write(struct regmap *map, unsigned int reg, unsigned int val) { u8 *out = map->work_buf; *out = (reg << 6) | val; } static void regmap_format_4_12_write(struct regmap *map, unsigned int reg, unsigned int val) { __be16 *out = map->work_buf; *out = cpu_to_be16((reg << 12) | val); } static void regmap_format_7_9_write(struct regmap *map, unsigned int reg, unsigned int val) { __be16 *out = map->work_buf; *out = cpu_to_be16((reg << 9) | val); } static void regmap_format_10_14_write(struct regmap *map, unsigned int reg, unsigned int val) { u8 *out = map->work_buf; out[2] = val; out[1] = (val >> 8) | (reg << 6); out[0] = reg >> 2; } static void regmap_format_8(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; b[0] = val << shift; } static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift) { __be16 *b = buf; b[0] = cpu_to_be16(val << shift); } static void regmap_format_16_native(void *buf, unsigned int val, unsigned int shift) { *(u16 *)buf = val << shift; } static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; val <<= shift; b[0] = val >> 16; b[1] = val >> 8; b[2] = val; } static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift) { __be32 *b = buf; b[0] = cpu_to_be32(val << shift); } static void regmap_format_32_native(void *buf, unsigned int val, unsigned int shift) { *(u32 *)buf = val << shift; } static void regmap_parse_inplace_noop(void *buf) { } static unsigned int regmap_parse_8(const void *buf) { const u8 *b = buf; return b[0]; } static unsigned int regmap_parse_16_be(const void *buf) { const __be16 *b = buf; return be16_to_cpu(b[0]); } static void regmap_parse_16_be_inplace(void *buf) { __be16 *b = buf; b[0] = be16_to_cpu(b[0]); } static unsigned int regmap_parse_16_native(const void *buf) { return *(u16 *)buf; } static unsigned int regmap_parse_24(const void *buf) { const u8 *b = buf; unsigned int ret = b[2]; ret |= ((unsigned int)b[1]) << 8; ret |= ((unsigned int)b[0]) << 16; return ret; } static unsigned int regmap_parse_32_be(const void *buf) { const __be32 *b = buf; return be32_to_cpu(b[0]); } static void regmap_parse_32_be_inplace(void *buf) { __be32 *b = buf; b[0] = be32_to_cpu(b[0]); } static unsigned int regmap_parse_32_native(const void *buf) { return *(u32 *)buf; } static void regmap_lock_mutex(void *__map) { struct regmap *map = __map; mutex_lock(&map->mutex); } static void regmap_unlock_mutex(void *__map) { struct regmap *map = __map; mutex_unlock(&map->mutex); } static void regmap_lock_spinlock(void *__map) __acquires(&map->spinlock) { struct regmap *map = __map; unsigned long flags; spin_lock_irqsave(&map->spinlock, flags); map->spinlock_flags = flags; } static void regmap_unlock_spinlock(void *__map) __releases(&map->spinlock) { struct regmap *map = __map; spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } static void dev_get_regmap_release(struct device *dev, void *res) { /* * We don't actually have anything to do here; the goal here * is not to manage the regmap but to provide a simple way to * get the regmap back given a struct device. */ } static bool _regmap_range_add(struct regmap *map, struct regmap_range_node *data) { struct rb_root *root = &map->range_tree; struct rb_node **new = &(root->rb_node), *parent = NULL; while (*new) { struct regmap_range_node *this = container_of(*new, struct regmap_range_node, node); parent = *new; if (data->range_max < this->range_min) new = &((*new)->rb_left); else if (data->range_min > this->range_max) new = &((*new)->rb_right); else return false; } rb_link_node(&data->node, parent, new); rb_insert_color(&data->node, root); return true; } static struct regmap_range_node *_regmap_range_lookup(struct regmap *map, unsigned int reg) { struct rb_node *node = map->range_tree.rb_node; while (node) { struct regmap_range_node *this = container_of(node, struct regmap_range_node, node); if (reg < this->range_min) node = node->rb_left; else if (reg > this->range_max) node = node->rb_right; else return this; } return NULL; } static void regmap_range_exit(struct regmap *map) { struct rb_node *next; struct regmap_range_node *range_node; next = rb_first(&map->range_tree); while (next) { range_node = rb_entry(next, struct regmap_range_node, node); next = rb_next(&range_node->node); rb_erase(&range_node->node, &map->range_tree); kfree(range_node); } kfree(map->selector_work_buf); } /** * regmap_init(): Initialise register map * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. This function should generally not be called * directly, it should be called by bus-specific init functions. */ struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, const struct regmap_config *config) { struct regmap *map, **m; int ret = -EINVAL; enum regmap_endian reg_endian, val_endian; int i, j; if (!config) goto err; map = kzalloc(sizeof(*map), GFP_KERNEL); if (map == NULL) { ret = -ENOMEM; goto err; } if (config->lock && config->unlock) { map->lock = config->lock; map->unlock = config->unlock; map->lock_arg = config->lock_arg; } else { if ((bus && bus->fast_io) || config->fast_io) { spin_lock_init(&map->spinlock); map->lock = regmap_lock_spinlock; map->unlock = regmap_unlock_spinlock; } else { mutex_init(&map->mutex); map->lock = regmap_lock_mutex; map->unlock = regmap_unlock_mutex; } map->lock_arg = map; } map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size = DIV_ROUND_UP(config->reg_bits + config->val_bits + config->pad_bits, 8); map->reg_shift = config->pad_bits % 8; if (config->reg_stride) map->reg_stride = config->reg_stride; else map->reg_stride = 1; map->use_single_rw = config->use_single_rw; map->dev = dev; map->bus = bus; map->bus_context = bus_context; map->max_register = config->max_register; map->wr_table = config->wr_table; map->rd_table = config->rd_table; map->volatile_table = config->volatile_table; map->precious_table = config->precious_table; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; map->name = config->name; spin_lock_init(&map->async_lock); INIT_LIST_HEAD(&map->async_list); INIT_LIST_HEAD(&map->async_free); init_waitqueue_head(&map->async_waitq); if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; } else if (bus) { map->read_flag_mask = bus->read_flag_mask; } if (!bus) { map->reg_read = config->reg_read; map->reg_write = config->reg_write; map->defer_caching = false; goto skip_format_initialization; } else { map->reg_read = _regmap_bus_read; } reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = REGMAP_ENDIAN_BIG; val_endian = config->val_format_endian; if (val_endian == REGMAP_ENDIAN_DEFAULT) val_endian = bus->val_format_endian_default; if (val_endian == REGMAP_ENDIAN_DEFAULT) val_endian = REGMAP_ENDIAN_BIG; switch (config->reg_bits + map->reg_shift) { case 2: switch (config->val_bits) { case 6: map->format.format_write = regmap_format_2_6_write; break; default: goto err_map; } break;