1124 lines · 25.7 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> #define CREATE_TRACE_POINTS#include <trace/events/regmap.h> #include "internal.h" 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); 	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); 	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); 	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); 	return false;} static bool regmap_volatile_range(struct regmap *map, unsigned int reg,	unsigned int 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(void *buf, unsigned int val, unsigned int shift){	__be16 *b = buf; 	b[0] = cpu_to_be16(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(void *buf, unsigned int val, unsigned int shift){	__be32 *b = buf; 	b[0] = cpu_to_be32(val << shift);} static unsigned int regmap_parse_8(void *buf){	u8 *b = buf; 	return b[0];} static unsigned int regmap_parse_16(void *buf){	__be16 *b = buf; 	b[0] = be16_to_cpu(b[0]); 	return b[0];} static unsigned int regmap_parse_24(void *buf){	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(void *buf){	__be32 *b = buf; 	b[0] = be32_to_cpu(b[0]); 	return b[0];} static void regmap_lock_mutex(struct regmap *map){	mutex_lock(&map->mutex);} static void regmap_unlock_mutex(struct regmap *map){	mutex_unlock(&map->mutex);} static void regmap_lock_spinlock(struct regmap *map){	spin_lock(&map->spinlock);} static void regmap_unlock_spinlock(struct regmap *map){	spin_unlock(&map->spinlock);} 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.	 */} /** * 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; 	if (!bus || !config)		goto err; 	map = kzalloc(sizeof(*map), GFP_KERNEL);	if (map == NULL) {		ret = -ENOMEM;		goto err;	} 	if (bus->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->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->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; 	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 {		map->read_flag_mask = bus->read_flag_mask;	} 	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; 	case 4:		switch (config->val_bits) {		case 12:			map->format.format_write = regmap_format_4_12_write;			break;		default:			goto err_map;		}		break; 	case 7:		switch (config->val_bits) {		case 9:			map->format.format_write = regmap_format_7_9_write;			break;		default:			goto err_map;		}		break; 	case 10:		switch (config->val_bits) {		case 14:			map->format.format_write = regmap_format_10_14_write;			break;		default:			goto err_map;		}		break; 	case 8:		map->format.format_reg = regmap_format_8;		break; 	case 16:		map->format.format_reg = regmap_format_16;		break; 	case 32:		map->format.format_reg = regmap_format_32;		break; 	default:		goto err_map;	} 	switch (config->val_bits) {	case 8:		map->format.format_val = regmap_format_8;		map->format.parse_val = regmap_parse_8;		break;	case 16:		map->format.format_val = regmap_format_16;		map->format.parse_val = regmap_parse_16;		break;	case 24:		map->format.format_val = regmap_format_24;		map->format.parse_val = regmap_parse_24;		break;	case 32:		map->format.format_val = regmap_format_32;		map->format.parse_val = regmap_parse_32;		break;	} 	if (map->format.format_write)		map->use_single_rw = true; 	if (!map->format.format_write &&	    !(map->format.format_reg && map->format.format_val))		goto err_map; 	map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL);	if (map->work_buf == NULL) {		ret = -ENOMEM;		goto err_map;	} 	regmap_debugfs_init(map, config->name); 	ret = regcache_init(map, config);	if (ret < 0)		goto err_debugfs; 	/* Add a devres resource for dev_get_regmap() */	m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL);	if (!m) {		ret = -ENOMEM;		goto err_cache;	}	*m = map;	devres_add(dev, m); 	return map; err_cache:	regcache_exit(map);err_debugfs:	regmap_debugfs_exit(map);	kfree(map->work_buf);err_map:	kfree(map);err:	return ERR_PTR(ret);}EXPORT_SYMBOL_GPL(regmap_init); static void devm_regmap_release(struct device *dev, void *res){	regmap_exit(*(struct regmap **)res);} /** * devm_regmap_init(): Initialise managed 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.  The * map will be automatically freed by the device management code. */struct regmap *devm_regmap_init(struct device *dev,				const struct regmap_bus *bus,				void *bus_context,				const struct regmap_config *config){	struct regmap **ptr, *regmap; 	ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL);	if (!ptr)		return ERR_PTR(-ENOMEM); 	regmap = regmap_init(dev, bus, bus_context, config);	if (!IS_ERR(regmap)) {		*ptr = regmap;		devres_add(dev, ptr);	} else {		devres_free(ptr);	} 	return regmap;}EXPORT_SYMBOL_GPL(devm_regmap_init); /** * regmap_reinit_cache(): Reinitialise the current register cache * * @map: Register map to operate on. * @config: New configuration.  Only the cache data will be used. * * Discard any existing register cache for the map and initialize a * new cache.  This can be used to restore the cache to defaults or to * update the cache configuration to reflect runtime discovery of the * hardware. */int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config){	int ret; 	map->lock(map); 	regcache_exit(map);	regmap_debugfs_exit(map); 	map->max_register = config->max_register;	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; 	regmap_debugfs_init(map, config->name); 	map->cache_bypass = false;	map->cache_only = false; 	ret = regcache_init(map, config); 	map->unlock(map); 	return ret;}EXPORT_SYMBOL_GPL(regmap_reinit_cache); /** * regmap_exit(): Free a previously allocated register map */void regmap_exit(struct regmap *map){	regcache_exit(map);	regmap_debugfs_exit(map);	if (map->bus->free_context)		map->bus->free_context(map->bus_context);	kfree(map->work_buf);	kfree(map);}EXPORT_SYMBOL_GPL(regmap_exit); static int dev_get_regmap_match(struct device *dev, void *res, void *data){	struct regmap **r = res;	if (!r || !*r) {		WARN_ON(!r || !*r);		return 0;	} 	/* If the user didn't specify a name match any */	if (data)

Lines 1–500 of 1124.