3301 lines · 78.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0//// Register map access API//// Copyright 2011 Wolfson Microelectronics plc//// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> #include <linux/device.h>#include <linux/slab.h>#include <linux/export.h>#include <linux/mutex.h>#include <linux/err.h>#include <linux/property.h>#include <linux/rbtree.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/log2.h>#include <linux/hwspinlock.h>#include <asm/unaligned.h> #define CREATE_TRACE_POINTS#include "trace.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 #ifdef LOG_DEVICEstatic inline bool regmap_should_log(struct regmap *map){	return (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0);}#elsestatic inline bool regmap_should_log(struct regmap *map) { return false; }#endif  static int _regmap_update_bits(struct regmap *map, unsigned int reg,			       unsigned int mask, unsigned int val,			       bool *change, bool force_write); 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_cached(struct regmap *map, unsigned int reg){	int ret;	unsigned int val; 	if (map->cache_type == REGCACHE_NONE)		return false; 	if (!map->cache_ops)		return false; 	if (map->max_register && reg > map->max_register)		return false; 	map->lock(map->lock_arg);	ret = regcache_read(map, reg, &val);	map->unlock(map->lock_arg);	if (ret)		return false; 	return true;} bool regmap_readable(struct regmap *map, unsigned int reg){	if (!map->reg_read)		return false; 	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 (!map->format.format_write && !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;} bool regmap_writeable_noinc(struct regmap *map, unsigned int reg){	if (map->writeable_noinc_reg)		return map->writeable_noinc_reg(map->dev, reg); 	if (map->wr_noinc_table)		return regmap_check_range_table(map, reg, map->wr_noinc_table); 	return true;} bool regmap_readable_noinc(struct regmap *map, unsigned int reg){	if (map->readable_noinc_reg)		return map->readable_noinc_reg(map->dev, reg); 	if (map->rd_noinc_table)		return regmap_check_range_table(map, reg, map->rd_noinc_table); 	return true;} 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 + regmap_get_offset(map, i)))			return false; 	return true;} static void regmap_format_12_20_write(struct regmap *map,				     unsigned int reg, unsigned int val){	u8 *out = map->work_buf; 	out[0] = reg >> 4;	out[1] = (reg << 4) | (val >> 16);	out[2] = val >> 8;	out[3] = val;}  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){	put_unaligned_be16(val << shift, buf);} static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift){	put_unaligned_le16(val << shift, buf);} static void regmap_format_16_native(void *buf, unsigned int val,				    unsigned int shift){	u16 v = val << shift; 	memcpy(buf, &v, sizeof(v));} 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){	put_unaligned_be32(val << shift, buf);} static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift){	put_unaligned_le32(val << shift, buf);} static void regmap_format_32_native(void *buf, unsigned int val,				    unsigned int shift){	u32 v = val << shift; 	memcpy(buf, &v, sizeof(v));} #ifdef CONFIG_64BITstatic void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift){	put_unaligned_be64((u64) val << shift, buf);} static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift){	put_unaligned_le64((u64) val << shift, buf);} static void regmap_format_64_native(void *buf, unsigned int val,				    unsigned int shift){	u64 v = (u64) val << shift; 	memcpy(buf, &v, sizeof(v));}#endif 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){	return get_unaligned_be16(buf);} static unsigned int regmap_parse_16_le(const void *buf){	return get_unaligned_le16(buf);} static void regmap_parse_16_be_inplace(void *buf){	u16 v = get_unaligned_be16(buf); 	memcpy(buf, &v, sizeof(v));} static void regmap_parse_16_le_inplace(void *buf){	u16 v = get_unaligned_le16(buf); 	memcpy(buf, &v, sizeof(v));} static unsigned int regmap_parse_16_native(const void *buf){	u16 v; 	memcpy(&v, buf, sizeof(v));	return v;} 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){	return get_unaligned_be32(buf);} static unsigned int regmap_parse_32_le(const void *buf){	return get_unaligned_le32(buf);} static void regmap_parse_32_be_inplace(void *buf){	u32 v = get_unaligned_be32(buf); 	memcpy(buf, &v, sizeof(v));} static void regmap_parse_32_le_inplace(void *buf){	u32 v = get_unaligned_le32(buf); 	memcpy(buf, &v, sizeof(v));} static unsigned int regmap_parse_32_native(const void *buf){	u32 v; 	memcpy(&v, buf, sizeof(v));	return v;} #ifdef CONFIG_64BITstatic unsigned int regmap_parse_64_be(const void *buf){	return get_unaligned_be64(buf);} static unsigned int regmap_parse_64_le(const void *buf){	return get_unaligned_le64(buf);} static void regmap_parse_64_be_inplace(void *buf){	u64 v =  get_unaligned_be64(buf); 	memcpy(buf, &v, sizeof(v));} static void regmap_parse_64_le_inplace(void *buf){	u64 v = get_unaligned_le64(buf); 	memcpy(buf, &v, sizeof(v));} static unsigned int regmap_parse_64_native(const void *buf){	u64 v; 	memcpy(&v, buf, sizeof(v));	return v;}#endif static void regmap_lock_hwlock(void *__map){	struct regmap *map = __map; 	hwspin_lock_timeout(map->hwlock, UINT_MAX);} static void regmap_lock_hwlock_irq(void *__map){	struct regmap *map = __map; 	hwspin_lock_timeout_irq(map->hwlock, UINT_MAX);} static void regmap_lock_hwlock_irqsave(void *__map){	struct regmap *map = __map; 	hwspin_lock_timeout_irqsave(map->hwlock, UINT_MAX,				    &map->spinlock_flags);} static void regmap_unlock_hwlock(void *__map){	struct regmap *map = __map; 	hwspin_unlock(map->hwlock);} static void regmap_unlock_hwlock_irq(void *__map){	struct regmap *map = __map; 	hwspin_unlock_irq(map->hwlock);} static void regmap_unlock_hwlock_irqrestore(void *__map){	struct regmap *map = __map; 	hwspin_unlock_irqrestore(map->hwlock, &map->spinlock_flags);} static void regmap_lock_unlock_none(void *__map){ } static void regmap_lock_mutex(void *__map){	struct regmap *map = __map;	mutex_lock(&map->mutex);

Lines 1–500 of 3301.