2564 lines · 61.4 KB · C
RawDownload
/* * 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_reg_read(void *context, unsigned int reg,				unsigned int *val);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_reg_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_le(void *buf, unsigned int val, unsigned int shift){	__le16 *b = buf; 	b[0] = cpu_to_le16(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_le(void *buf, unsigned int val, unsigned int shift){	__le32 *b = buf; 	b[0] = cpu_to_le32(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 unsigned int regmap_parse_16_le(const void *buf){	const __le16 *b = buf; 	return le16_to_cpu(b[0]);} static void regmap_parse_16_be_inplace(void *buf){	__be16 *b = buf; 	b[0] = be16_to_cpu(b[0]);} static void regmap_parse_16_le_inplace(void *buf){	__le16 *b = buf; 	b[0] = le16_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 unsigned int regmap_parse_32_le(const void *buf){	const __le32 *b = buf; 	return le32_to_cpu(b[0]);} static void regmap_parse_32_be_inplace(void *buf){	__be32 *b = buf; 	b[0] = be32_to_cpu(b[0]);} static void regmap_parse_32_le_inplace(void *buf){	__le32 *b = buf; 	b[0] = le32_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);} int regmap_attach_dev(struct device *dev, struct regmap *map,		      const struct regmap_config *config){	struct regmap **m; 	map->dev = dev; 	regmap_debugfs_init(map, config->name); 	/* Add a devres resource for dev_get_regmap() */	m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL);	if (!m) {		regmap_debugfs_exit(map);		return -ENOMEM;	}	*m = map;	devres_add(dev, m); 	return 0;}EXPORT_SYMBOL_GPL(regmap_attach_dev); /** * 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;	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;

Lines 1–500 of 2564.