| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 1 | /* | 
 | 2 |  * Register map access API | 
 | 3 |  * | 
 | 4 |  * Copyright 2011 Wolfson Microelectronics plc | 
 | 5 |  * | 
 | 6 |  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | 
 | 7 |  * | 
 | 8 |  * This program is free software; you can redistribute it and/or modify | 
 | 9 |  * it under the terms of the GNU General Public License version 2 as | 
 | 10 |  * published by the Free Software Foundation. | 
 | 11 |  */ | 
 | 12 |  | 
 | 13 | #include <linux/slab.h> | 
 | 14 | #include <linux/module.h> | 
 | 15 | #include <linux/mutex.h> | 
 | 16 | #include <linux/err.h> | 
 | 17 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 18 | #define CREATE_TRACE_POINTS | 
 | 19 | #include <trace/events/regmap.h> | 
 | 20 |  | 
| Mark Brown | 93de912 | 2011-07-20 22:35:37 +0100 | [diff] [blame] | 21 | #include "internal.h" | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 22 |  | 
| Mark Brown | 8de2f08 | 2011-08-10 17:14:41 +0900 | [diff] [blame] | 23 | bool regmap_writeable(struct regmap *map, unsigned int reg) | 
 | 24 | { | 
 | 25 | 	if (map->max_register && reg > map->max_register) | 
 | 26 | 		return false; | 
 | 27 |  | 
 | 28 | 	if (map->writeable_reg) | 
 | 29 | 		return map->writeable_reg(map->dev, reg); | 
 | 30 |  | 
 | 31 | 	return true; | 
 | 32 | } | 
 | 33 |  | 
 | 34 | bool regmap_readable(struct regmap *map, unsigned int reg) | 
 | 35 | { | 
 | 36 | 	if (map->max_register && reg > map->max_register) | 
 | 37 | 		return false; | 
 | 38 |  | 
 | 39 | 	if (map->readable_reg) | 
 | 40 | 		return map->readable_reg(map->dev, reg); | 
 | 41 |  | 
 | 42 | 	return true; | 
 | 43 | } | 
 | 44 |  | 
 | 45 | bool regmap_volatile(struct regmap *map, unsigned int reg) | 
 | 46 | { | 
 | 47 | 	if (map->max_register && reg > map->max_register) | 
 | 48 | 		return false; | 
 | 49 |  | 
 | 50 | 	if (map->volatile_reg) | 
 | 51 | 		return map->volatile_reg(map->dev, reg); | 
 | 52 |  | 
 | 53 | 	return true; | 
 | 54 | } | 
 | 55 |  | 
 | 56 | bool regmap_precious(struct regmap *map, unsigned int reg) | 
 | 57 | { | 
 | 58 | 	if (map->max_register && reg > map->max_register) | 
 | 59 | 		return false; | 
 | 60 |  | 
 | 61 | 	if (map->precious_reg) | 
 | 62 | 		return map->precious_reg(map->dev, reg); | 
 | 63 |  | 
 | 64 | 	return false; | 
 | 65 | } | 
 | 66 |  | 
| Lars-Peter Clausen | 82cd996 | 2011-11-08 18:37:25 +0100 | [diff] [blame] | 67 | static bool regmap_volatile_range(struct regmap *map, unsigned int reg, | 
 | 68 | 	unsigned int num) | 
 | 69 | { | 
 | 70 | 	unsigned int i; | 
 | 71 |  | 
 | 72 | 	for (i = 0; i < num; i++) | 
 | 73 | 		if (!regmap_volatile(map, reg + i)) | 
 | 74 | 			return false; | 
 | 75 |  | 
 | 76 | 	return true; | 
 | 77 | } | 
 | 78 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 79 | static void regmap_format_4_12_write(struct regmap *map, | 
 | 80 | 				     unsigned int reg, unsigned int val) | 
 | 81 | { | 
 | 82 | 	__be16 *out = map->work_buf; | 
 | 83 | 	*out = cpu_to_be16((reg << 12) | val); | 
 | 84 | } | 
 | 85 |  | 
 | 86 | static void regmap_format_7_9_write(struct regmap *map, | 
 | 87 | 				    unsigned int reg, unsigned int val) | 
 | 88 | { | 
 | 89 | 	__be16 *out = map->work_buf; | 
 | 90 | 	*out = cpu_to_be16((reg << 9) | val); | 
 | 91 | } | 
 | 92 |  | 
| Lars-Peter Clausen | 7e5ec63 | 2011-11-16 16:28:21 +0100 | [diff] [blame] | 93 | static void regmap_format_10_14_write(struct regmap *map, | 
 | 94 | 				    unsigned int reg, unsigned int val) | 
 | 95 | { | 
 | 96 | 	u8 *out = map->work_buf; | 
 | 97 |  | 
 | 98 | 	out[2] = val; | 
 | 99 | 	out[1] = (val >> 8) | (reg << 6); | 
 | 100 | 	out[0] = reg >> 2; | 
 | 101 | } | 
 | 102 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 103 | static void regmap_format_8(void *buf, unsigned int val) | 
 | 104 | { | 
 | 105 | 	u8 *b = buf; | 
 | 106 |  | 
 | 107 | 	b[0] = val; | 
 | 108 | } | 
 | 109 |  | 
 | 110 | static void regmap_format_16(void *buf, unsigned int val) | 
 | 111 | { | 
 | 112 | 	__be16 *b = buf; | 
 | 113 |  | 
 | 114 | 	b[0] = cpu_to_be16(val); | 
 | 115 | } | 
 | 116 |  | 
 | 117 | static unsigned int regmap_parse_8(void *buf) | 
 | 118 | { | 
 | 119 | 	u8 *b = buf; | 
 | 120 |  | 
 | 121 | 	return b[0]; | 
 | 122 | } | 
 | 123 |  | 
 | 124 | static unsigned int regmap_parse_16(void *buf) | 
 | 125 | { | 
 | 126 | 	__be16 *b = buf; | 
 | 127 |  | 
 | 128 | 	b[0] = be16_to_cpu(b[0]); | 
 | 129 |  | 
 | 130 | 	return b[0]; | 
 | 131 | } | 
 | 132 |  | 
 | 133 | /** | 
 | 134 |  * regmap_init(): Initialise register map | 
 | 135 |  * | 
 | 136 |  * @dev: Device that will be interacted with | 
 | 137 |  * @bus: Bus-specific callbacks to use with device | 
 | 138 |  * @config: Configuration for register map | 
 | 139 |  * | 
 | 140 |  * The return value will be an ERR_PTR() on error or a valid pointer to | 
 | 141 |  * a struct regmap.  This function should generally not be called | 
 | 142 |  * directly, it should be called by bus-specific init functions. | 
 | 143 |  */ | 
 | 144 | struct regmap *regmap_init(struct device *dev, | 
 | 145 | 			   const struct regmap_bus *bus, | 
 | 146 | 			   const struct regmap_config *config) | 
 | 147 | { | 
 | 148 | 	struct regmap *map; | 
 | 149 | 	int ret = -EINVAL; | 
 | 150 |  | 
 | 151 | 	if (!bus || !config) | 
| Lars-Peter Clausen | abbb18f | 2011-11-14 10:40:15 +0100 | [diff] [blame] | 152 | 		goto err; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 153 |  | 
 | 154 | 	map = kzalloc(sizeof(*map), GFP_KERNEL); | 
 | 155 | 	if (map == NULL) { | 
 | 156 | 		ret = -ENOMEM; | 
 | 157 | 		goto err; | 
 | 158 | 	} | 
 | 159 |  | 
 | 160 | 	mutex_init(&map->lock); | 
 | 161 | 	map->format.buf_size = (config->reg_bits + config->val_bits) / 8; | 
 | 162 | 	map->format.reg_bytes = config->reg_bits / 8; | 
 | 163 | 	map->format.val_bytes = config->val_bits / 8; | 
 | 164 | 	map->dev = dev; | 
 | 165 | 	map->bus = bus; | 
| Mark Brown | 2e2ae66 | 2011-07-20 22:33:39 +0100 | [diff] [blame] | 166 | 	map->max_register = config->max_register; | 
 | 167 | 	map->writeable_reg = config->writeable_reg; | 
 | 168 | 	map->readable_reg = config->readable_reg; | 
 | 169 | 	map->volatile_reg = config->volatile_reg; | 
| Mark Brown | 2efe164 | 2011-08-08 15:41:46 +0900 | [diff] [blame] | 170 | 	map->precious_reg = config->precious_reg; | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 171 | 	map->cache_type = config->cache_type; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 172 |  | 
| Lars-Peter Clausen | 6f30644 | 2011-09-05 20:46:32 +0200 | [diff] [blame] | 173 | 	if (config->read_flag_mask || config->write_flag_mask) { | 
 | 174 | 		map->read_flag_mask = config->read_flag_mask; | 
 | 175 | 		map->write_flag_mask = config->write_flag_mask; | 
 | 176 | 	} else { | 
 | 177 | 		map->read_flag_mask = bus->read_flag_mask; | 
 | 178 | 	} | 
 | 179 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 180 | 	switch (config->reg_bits) { | 
 | 181 | 	case 4: | 
 | 182 | 		switch (config->val_bits) { | 
 | 183 | 		case 12: | 
 | 184 | 			map->format.format_write = regmap_format_4_12_write; | 
 | 185 | 			break; | 
 | 186 | 		default: | 
 | 187 | 			goto err_map; | 
 | 188 | 		} | 
 | 189 | 		break; | 
 | 190 |  | 
 | 191 | 	case 7: | 
 | 192 | 		switch (config->val_bits) { | 
 | 193 | 		case 9: | 
 | 194 | 			map->format.format_write = regmap_format_7_9_write; | 
 | 195 | 			break; | 
 | 196 | 		default: | 
 | 197 | 			goto err_map; | 
 | 198 | 		} | 
 | 199 | 		break; | 
 | 200 |  | 
| Lars-Peter Clausen | 7e5ec63 | 2011-11-16 16:28:21 +0100 | [diff] [blame] | 201 | 	case 10: | 
 | 202 | 		switch (config->val_bits) { | 
 | 203 | 		case 14: | 
 | 204 | 			map->format.format_write = regmap_format_10_14_write; | 
 | 205 | 			break; | 
 | 206 | 		default: | 
 | 207 | 			goto err_map; | 
 | 208 | 		} | 
 | 209 | 		break; | 
 | 210 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 211 | 	case 8: | 
 | 212 | 		map->format.format_reg = regmap_format_8; | 
 | 213 | 		break; | 
 | 214 |  | 
 | 215 | 	case 16: | 
 | 216 | 		map->format.format_reg = regmap_format_16; | 
 | 217 | 		break; | 
 | 218 |  | 
 | 219 | 	default: | 
 | 220 | 		goto err_map; | 
 | 221 | 	} | 
 | 222 |  | 
 | 223 | 	switch (config->val_bits) { | 
 | 224 | 	case 8: | 
 | 225 | 		map->format.format_val = regmap_format_8; | 
 | 226 | 		map->format.parse_val = regmap_parse_8; | 
 | 227 | 		break; | 
 | 228 | 	case 16: | 
 | 229 | 		map->format.format_val = regmap_format_16; | 
 | 230 | 		map->format.parse_val = regmap_parse_16; | 
 | 231 | 		break; | 
 | 232 | 	} | 
 | 233 |  | 
 | 234 | 	if (!map->format.format_write && | 
 | 235 | 	    !(map->format.format_reg && map->format.format_val)) | 
 | 236 | 		goto err_map; | 
 | 237 |  | 
 | 238 | 	map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL); | 
 | 239 | 	if (map->work_buf == NULL) { | 
 | 240 | 		ret = -ENOMEM; | 
| Mark Brown | 5204f5e | 2011-09-05 08:07:47 -0700 | [diff] [blame] | 241 | 		goto err_map; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 242 | 	} | 
 | 243 |  | 
| Mark Brown | 052d2cd | 2011-11-21 19:05:13 +0000 | [diff] [blame] | 244 | 	regmap_debugfs_init(map); | 
 | 245 |  | 
| Lars-Peter Clausen | e5e3b8a | 2011-11-16 16:28:16 +0100 | [diff] [blame] | 246 | 	ret = regcache_init(map, config); | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 247 | 	if (ret < 0) | 
| Lars-Peter Clausen | 58072cb | 2011-11-10 18:15:15 +0100 | [diff] [blame] | 248 | 		goto err_free_workbuf; | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 249 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 250 | 	return map; | 
 | 251 |  | 
| Lars-Peter Clausen | 58072cb | 2011-11-10 18:15:15 +0100 | [diff] [blame] | 252 | err_free_workbuf: | 
 | 253 | 	kfree(map->work_buf); | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 254 | err_map: | 
 | 255 | 	kfree(map); | 
 | 256 | err: | 
 | 257 | 	return ERR_PTR(ret); | 
 | 258 | } | 
 | 259 | EXPORT_SYMBOL_GPL(regmap_init); | 
 | 260 |  | 
 | 261 | /** | 
| Mark Brown | bf31517 | 2011-12-03 17:06:20 +0000 | [diff] [blame] | 262 |  * regmap_reinit_cache(): Reinitialise the current register cache | 
 | 263 |  * | 
 | 264 |  * @map: Register map to operate on. | 
 | 265 |  * @config: New configuration.  Only the cache data will be used. | 
 | 266 |  * | 
 | 267 |  * Discard any existing register cache for the map and initialize a | 
 | 268 |  * new cache.  This can be used to restore the cache to defaults or to | 
 | 269 |  * update the cache configuration to reflect runtime discovery of the | 
 | 270 |  * hardware. | 
 | 271 |  */ | 
 | 272 | int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) | 
 | 273 | { | 
 | 274 | 	int ret; | 
 | 275 |  | 
 | 276 | 	mutex_lock(&map->lock); | 
 | 277 |  | 
 | 278 | 	regcache_exit(map); | 
 | 279 |  | 
 | 280 | 	map->max_register = config->max_register; | 
 | 281 | 	map->writeable_reg = config->writeable_reg; | 
 | 282 | 	map->readable_reg = config->readable_reg; | 
 | 283 | 	map->volatile_reg = config->volatile_reg; | 
 | 284 | 	map->precious_reg = config->precious_reg; | 
 | 285 | 	map->cache_type = config->cache_type; | 
 | 286 |  | 
| Mark Brown | 421e8d2 | 2012-01-20 13:39:37 +0000 | [diff] [blame] | 287 | 	map->cache_bypass = false; | 
 | 288 | 	map->cache_only = false; | 
 | 289 |  | 
| Mark Brown | bf31517 | 2011-12-03 17:06:20 +0000 | [diff] [blame] | 290 | 	ret = regcache_init(map, config); | 
 | 291 |  | 
 | 292 | 	mutex_unlock(&map->lock); | 
 | 293 |  | 
 | 294 | 	return ret; | 
 | 295 | } | 
 | 296 |  | 
 | 297 | /** | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 298 |  * regmap_exit(): Free a previously allocated register map | 
 | 299 |  */ | 
 | 300 | void regmap_exit(struct regmap *map) | 
 | 301 | { | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 302 | 	regcache_exit(map); | 
| Mark Brown | 31244e3 | 2011-07-20 22:56:53 +0100 | [diff] [blame] | 303 | 	regmap_debugfs_exit(map); | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 304 | 	kfree(map->work_buf); | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 305 | 	kfree(map); | 
 | 306 | } | 
 | 307 | EXPORT_SYMBOL_GPL(regmap_exit); | 
 | 308 |  | 
 | 309 | static int _regmap_raw_write(struct regmap *map, unsigned int reg, | 
 | 310 | 			     const void *val, size_t val_len) | 
 | 311 | { | 
| Lars-Peter Clausen | 6f30644 | 2011-09-05 20:46:32 +0200 | [diff] [blame] | 312 | 	u8 *u8 = map->work_buf; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 313 | 	void *buf; | 
 | 314 | 	int ret = -ENOTSUPP; | 
 | 315 | 	size_t len; | 
| Mark Brown | 7330478 | 2011-07-24 11:46:20 +0100 | [diff] [blame] | 316 | 	int i; | 
 | 317 |  | 
 | 318 | 	/* Check for unwritable registers before we start */ | 
 | 319 | 	if (map->writeable_reg) | 
 | 320 | 		for (i = 0; i < val_len / map->format.val_bytes; i++) | 
 | 321 | 			if (!map->writeable_reg(map->dev, reg + i)) | 
 | 322 | 				return -EINVAL; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 323 |  | 
 | 324 | 	map->format.format_reg(map->work_buf, reg); | 
 | 325 |  | 
| Lars-Peter Clausen | 6f30644 | 2011-09-05 20:46:32 +0200 | [diff] [blame] | 326 | 	u8[0] |= map->write_flag_mask; | 
 | 327 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 328 | 	trace_regmap_hw_write_start(map->dev, reg, | 
 | 329 | 				    val_len / map->format.val_bytes); | 
 | 330 |  | 
| Mark Brown | 2547e20 | 2011-07-20 21:47:22 +0100 | [diff] [blame] | 331 | 	/* If we're doing a single register write we can probably just | 
 | 332 | 	 * send the work_buf directly, otherwise try to do a gather | 
 | 333 | 	 * write. | 
 | 334 | 	 */ | 
 | 335 | 	if (val == map->work_buf + map->format.reg_bytes) | 
 | 336 | 		ret = map->bus->write(map->dev, map->work_buf, | 
 | 337 | 				      map->format.reg_bytes + val_len); | 
 | 338 | 	else if (map->bus->gather_write) | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 339 | 		ret = map->bus->gather_write(map->dev, map->work_buf, | 
 | 340 | 					     map->format.reg_bytes, | 
 | 341 | 					     val, val_len); | 
 | 342 |  | 
| Mark Brown | 2547e20 | 2011-07-20 21:47:22 +0100 | [diff] [blame] | 343 | 	/* If that didn't work fall back on linearising by hand. */ | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 344 | 	if (ret == -ENOTSUPP) { | 
 | 345 | 		len = map->format.reg_bytes + val_len; | 
 | 346 | 		buf = kmalloc(len, GFP_KERNEL); | 
 | 347 | 		if (!buf) | 
 | 348 | 			return -ENOMEM; | 
 | 349 |  | 
 | 350 | 		memcpy(buf, map->work_buf, map->format.reg_bytes); | 
 | 351 | 		memcpy(buf + map->format.reg_bytes, val, val_len); | 
 | 352 | 		ret = map->bus->write(map->dev, buf, len); | 
 | 353 |  | 
 | 354 | 		kfree(buf); | 
 | 355 | 	} | 
 | 356 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 357 | 	trace_regmap_hw_write_done(map->dev, reg, | 
 | 358 | 				   val_len / map->format.val_bytes); | 
 | 359 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 360 | 	return ret; | 
 | 361 | } | 
 | 362 |  | 
| Dimitris Papastamos | 4d2dc09 | 2011-09-29 10:39:07 +0100 | [diff] [blame] | 363 | int _regmap_write(struct regmap *map, unsigned int reg, | 
 | 364 | 		  unsigned int val) | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 365 | { | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 366 | 	int ret; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 367 | 	BUG_ON(!map->format.format_write && !map->format.format_val); | 
 | 368 |  | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 369 | 	if (!map->cache_bypass) { | 
 | 370 | 		ret = regcache_write(map, reg, val); | 
 | 371 | 		if (ret != 0) | 
 | 372 | 			return ret; | 
| Mark Brown | 8ae0d7e | 2011-10-26 10:34:22 +0200 | [diff] [blame] | 373 | 		if (map->cache_only) { | 
 | 374 | 			map->cache_dirty = true; | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 375 | 			return 0; | 
| Mark Brown | 8ae0d7e | 2011-10-26 10:34:22 +0200 | [diff] [blame] | 376 | 		} | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 377 | 	} | 
 | 378 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 379 | 	trace_regmap_reg_write(map->dev, reg, val); | 
 | 380 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 381 | 	if (map->format.format_write) { | 
 | 382 | 		map->format.format_write(map, reg, val); | 
 | 383 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 384 | 		trace_regmap_hw_write_start(map->dev, reg, 1); | 
 | 385 |  | 
 | 386 | 		ret = map->bus->write(map->dev, map->work_buf, | 
 | 387 | 				      map->format.buf_size); | 
 | 388 |  | 
 | 389 | 		trace_regmap_hw_write_done(map->dev, reg, 1); | 
 | 390 |  | 
 | 391 | 		return ret; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 392 | 	} else { | 
 | 393 | 		map->format.format_val(map->work_buf + map->format.reg_bytes, | 
 | 394 | 				       val); | 
 | 395 | 		return _regmap_raw_write(map, reg, | 
 | 396 | 					 map->work_buf + map->format.reg_bytes, | 
 | 397 | 					 map->format.val_bytes); | 
 | 398 | 	} | 
 | 399 | } | 
 | 400 |  | 
 | 401 | /** | 
 | 402 |  * regmap_write(): Write a value to a single register | 
 | 403 |  * | 
 | 404 |  * @map: Register map to write to | 
 | 405 |  * @reg: Register to write to | 
 | 406 |  * @val: Value to be written | 
 | 407 |  * | 
 | 408 |  * A value of zero will be returned on success, a negative errno will | 
 | 409 |  * be returned in error cases. | 
 | 410 |  */ | 
 | 411 | int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) | 
 | 412 | { | 
 | 413 | 	int ret; | 
 | 414 |  | 
 | 415 | 	mutex_lock(&map->lock); | 
 | 416 |  | 
 | 417 | 	ret = _regmap_write(map, reg, val); | 
 | 418 |  | 
 | 419 | 	mutex_unlock(&map->lock); | 
 | 420 |  | 
 | 421 | 	return ret; | 
 | 422 | } | 
 | 423 | EXPORT_SYMBOL_GPL(regmap_write); | 
 | 424 |  | 
 | 425 | /** | 
 | 426 |  * regmap_raw_write(): Write raw values to one or more registers | 
 | 427 |  * | 
 | 428 |  * @map: Register map to write to | 
 | 429 |  * @reg: Initial register to write to | 
 | 430 |  * @val: Block of data to be written, laid out for direct transmission to the | 
 | 431 |  *       device | 
 | 432 |  * @val_len: Length of data pointed to by val. | 
 | 433 |  * | 
 | 434 |  * This function is intended to be used for things like firmware | 
 | 435 |  * download where a large block of data needs to be transferred to the | 
 | 436 |  * device.  No formatting will be done on the data provided. | 
 | 437 |  * | 
 | 438 |  * A value of zero will be returned on success, a negative errno will | 
 | 439 |  * be returned in error cases. | 
 | 440 |  */ | 
 | 441 | int regmap_raw_write(struct regmap *map, unsigned int reg, | 
 | 442 | 		     const void *val, size_t val_len) | 
 | 443 | { | 
| Lars-Peter Clausen | c48a9d7 | 2011-11-08 18:37:26 +0100 | [diff] [blame] | 444 | 	size_t val_count = val_len / map->format.val_bytes; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 445 | 	int ret; | 
 | 446 |  | 
| Lars-Peter Clausen | c48a9d7 | 2011-11-08 18:37:26 +0100 | [diff] [blame] | 447 | 	WARN_ON(!regmap_volatile_range(map, reg, val_count) && | 
 | 448 | 		map->cache_type != REGCACHE_NONE); | 
| Mark Brown | 04e016a | 2011-10-09 13:35:43 +0100 | [diff] [blame] | 449 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 450 | 	mutex_lock(&map->lock); | 
 | 451 |  | 
 | 452 | 	ret = _regmap_raw_write(map, reg, val, val_len); | 
 | 453 |  | 
 | 454 | 	mutex_unlock(&map->lock); | 
 | 455 |  | 
 | 456 | 	return ret; | 
 | 457 | } | 
 | 458 | EXPORT_SYMBOL_GPL(regmap_raw_write); | 
 | 459 |  | 
 | 460 | static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, | 
 | 461 | 			    unsigned int val_len) | 
 | 462 | { | 
 | 463 | 	u8 *u8 = map->work_buf; | 
 | 464 | 	int ret; | 
 | 465 |  | 
 | 466 | 	map->format.format_reg(map->work_buf, reg); | 
 | 467 |  | 
 | 468 | 	/* | 
| Lars-Peter Clausen | 6f30644 | 2011-09-05 20:46:32 +0200 | [diff] [blame] | 469 | 	 * Some buses or devices flag reads by setting the high bits in the | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 470 | 	 * register addresss; since it's always the high bits for all | 
 | 471 | 	 * current formats we can do this here rather than in | 
 | 472 | 	 * formatting.  This may break if we get interesting formats. | 
 | 473 | 	 */ | 
| Lars-Peter Clausen | 6f30644 | 2011-09-05 20:46:32 +0200 | [diff] [blame] | 474 | 	u8[0] |= map->read_flag_mask; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 475 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 476 | 	trace_regmap_hw_read_start(map->dev, reg, | 
 | 477 | 				   val_len / map->format.val_bytes); | 
 | 478 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 479 | 	ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes, | 
| Mark Brown | 40c5cc2 | 2011-07-24 22:39:12 +0100 | [diff] [blame] | 480 | 			     val, val_len); | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 481 |  | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 482 | 	trace_regmap_hw_read_done(map->dev, reg, | 
 | 483 | 				  val_len / map->format.val_bytes); | 
 | 484 |  | 
 | 485 | 	return ret; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 486 | } | 
 | 487 |  | 
 | 488 | static int _regmap_read(struct regmap *map, unsigned int reg, | 
 | 489 | 			unsigned int *val) | 
 | 490 | { | 
 | 491 | 	int ret; | 
 | 492 |  | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 493 | 	if (!map->cache_bypass) { | 
 | 494 | 		ret = regcache_read(map, reg, val); | 
 | 495 | 		if (ret == 0) | 
 | 496 | 			return 0; | 
 | 497 | 	} | 
 | 498 |  | 
| Lars-Peter Clausen | 1925441 | 2011-11-16 16:28:19 +0100 | [diff] [blame] | 499 | 	if (!map->format.parse_val) | 
 | 500 | 		return -EINVAL; | 
 | 501 |  | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 502 | 	if (map->cache_only) | 
 | 503 | 		return -EBUSY; | 
 | 504 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 505 | 	ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 506 | 	if (ret == 0) { | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 507 | 		*val = map->format.parse_val(map->work_buf); | 
| Mark Brown | fb2736b | 2011-07-24 21:30:55 +0100 | [diff] [blame] | 508 | 		trace_regmap_reg_read(map->dev, reg, *val); | 
 | 509 | 	} | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 510 |  | 
 | 511 | 	return ret; | 
 | 512 | } | 
 | 513 |  | 
 | 514 | /** | 
 | 515 |  * regmap_read(): Read a value from a single register | 
 | 516 |  * | 
 | 517 |  * @map: Register map to write to | 
 | 518 |  * @reg: Register to be read from | 
 | 519 |  * @val: Pointer to store read value | 
 | 520 |  * | 
 | 521 |  * A value of zero will be returned on success, a negative errno will | 
 | 522 |  * be returned in error cases. | 
 | 523 |  */ | 
 | 524 | int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) | 
 | 525 | { | 
 | 526 | 	int ret; | 
 | 527 |  | 
 | 528 | 	mutex_lock(&map->lock); | 
 | 529 |  | 
 | 530 | 	ret = _regmap_read(map, reg, val); | 
 | 531 |  | 
 | 532 | 	mutex_unlock(&map->lock); | 
 | 533 |  | 
 | 534 | 	return ret; | 
 | 535 | } | 
 | 536 | EXPORT_SYMBOL_GPL(regmap_read); | 
 | 537 |  | 
 | 538 | /** | 
 | 539 |  * regmap_raw_read(): Read raw data from the device | 
 | 540 |  * | 
 | 541 |  * @map: Register map to write to | 
 | 542 |  * @reg: First register to be read from | 
 | 543 |  * @val: Pointer to store read value | 
 | 544 |  * @val_len: Size of data to read | 
 | 545 |  * | 
 | 546 |  * A value of zero will be returned on success, a negative errno will | 
 | 547 |  * be returned in error cases. | 
 | 548 |  */ | 
 | 549 | int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, | 
 | 550 | 		    size_t val_len) | 
 | 551 | { | 
| Lars-Peter Clausen | 82cd996 | 2011-11-08 18:37:25 +0100 | [diff] [blame] | 552 | 	size_t val_count = val_len / map->format.val_bytes; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 553 | 	int ret; | 
 | 554 |  | 
| Lars-Peter Clausen | 82cd996 | 2011-11-08 18:37:25 +0100 | [diff] [blame] | 555 | 	WARN_ON(!regmap_volatile_range(map, reg, val_count) && | 
 | 556 | 		map->cache_type != REGCACHE_NONE); | 
| Mark Brown | 04e016a | 2011-10-09 13:35:43 +0100 | [diff] [blame] | 557 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 558 | 	mutex_lock(&map->lock); | 
 | 559 |  | 
 | 560 | 	ret = _regmap_raw_read(map, reg, val, val_len); | 
 | 561 |  | 
 | 562 | 	mutex_unlock(&map->lock); | 
 | 563 |  | 
 | 564 | 	return ret; | 
 | 565 | } | 
 | 566 | EXPORT_SYMBOL_GPL(regmap_raw_read); | 
 | 567 |  | 
 | 568 | /** | 
 | 569 |  * regmap_bulk_read(): Read multiple registers from the device | 
 | 570 |  * | 
 | 571 |  * @map: Register map to write to | 
 | 572 |  * @reg: First register to be read from | 
 | 573 |  * @val: Pointer to store read value, in native register size for device | 
 | 574 |  * @val_count: Number of registers to read | 
 | 575 |  * | 
 | 576 |  * A value of zero will be returned on success, a negative errno will | 
 | 577 |  * be returned in error cases. | 
 | 578 |  */ | 
 | 579 | int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, | 
 | 580 | 		     size_t val_count) | 
 | 581 | { | 
 | 582 | 	int ret, i; | 
 | 583 | 	size_t val_bytes = map->format.val_bytes; | 
| Lars-Peter Clausen | 82cd996 | 2011-11-08 18:37:25 +0100 | [diff] [blame] | 584 | 	bool vol = regmap_volatile_range(map, reg, val_count); | 
| Dimitris Papastamos | 5d1729e | 2011-09-19 14:34:05 +0100 | [diff] [blame] | 585 |  | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 586 | 	if (!map->format.parse_val) | 
 | 587 | 		return -EINVAL; | 
 | 588 |  | 
| Mark Brown | de2d808 | 2011-10-10 13:24:52 +0100 | [diff] [blame] | 589 | 	if (vol || map->cache_type == REGCACHE_NONE) { | 
 | 590 | 		ret = regmap_raw_read(map, reg, val, val_bytes * val_count); | 
 | 591 | 		if (ret != 0) | 
 | 592 | 			return ret; | 
 | 593 |  | 
 | 594 | 		for (i = 0; i < val_count * val_bytes; i += val_bytes) | 
 | 595 | 			map->format.parse_val(val + i); | 
 | 596 | 	} else { | 
 | 597 | 		for (i = 0; i < val_count; i++) { | 
 | 598 | 			ret = regmap_read(map, reg + i, val + (i * val_bytes)); | 
 | 599 | 			if (ret != 0) | 
 | 600 | 				return ret; | 
 | 601 | 		} | 
 | 602 | 	} | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 603 |  | 
 | 604 | 	return 0; | 
 | 605 | } | 
 | 606 | EXPORT_SYMBOL_GPL(regmap_bulk_read); | 
 | 607 |  | 
| Mark Brown | 018690d | 2011-11-29 20:10:36 +0000 | [diff] [blame] | 608 | static int _regmap_update_bits(struct regmap *map, unsigned int reg, | 
 | 609 | 			       unsigned int mask, unsigned int val, | 
 | 610 | 			       bool *change) | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 611 | { | 
 | 612 | 	int ret; | 
| Mark Brown | d91e8db | 2011-11-18 16:03:50 +0000 | [diff] [blame] | 613 | 	unsigned int tmp, orig; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 614 |  | 
 | 615 | 	mutex_lock(&map->lock); | 
 | 616 |  | 
| Mark Brown | d91e8db | 2011-11-18 16:03:50 +0000 | [diff] [blame] | 617 | 	ret = _regmap_read(map, reg, &orig); | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 618 | 	if (ret != 0) | 
 | 619 | 		goto out; | 
 | 620 |  | 
| Mark Brown | d91e8db | 2011-11-18 16:03:50 +0000 | [diff] [blame] | 621 | 	tmp = orig & ~mask; | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 622 | 	tmp |= val & mask; | 
 | 623 |  | 
| Mark Brown | 018690d | 2011-11-29 20:10:36 +0000 | [diff] [blame] | 624 | 	if (tmp != orig) { | 
| Mark Brown | d91e8db | 2011-11-18 16:03:50 +0000 | [diff] [blame] | 625 | 		ret = _regmap_write(map, reg, tmp); | 
| Mark Brown | 018690d | 2011-11-29 20:10:36 +0000 | [diff] [blame] | 626 | 		*change = true; | 
 | 627 | 	} else { | 
 | 628 | 		*change = false; | 
 | 629 | 	} | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 630 |  | 
 | 631 | out: | 
 | 632 | 	mutex_unlock(&map->lock); | 
 | 633 |  | 
 | 634 | 	return ret; | 
 | 635 | } | 
| Mark Brown | 018690d | 2011-11-29 20:10:36 +0000 | [diff] [blame] | 636 |  | 
 | 637 | /** | 
 | 638 |  * regmap_update_bits: Perform a read/modify/write cycle on the register map | 
 | 639 |  * | 
 | 640 |  * @map: Register map to update | 
 | 641 |  * @reg: Register to update | 
 | 642 |  * @mask: Bitmask to change | 
 | 643 |  * @val: New value for bitmask | 
 | 644 |  * | 
 | 645 |  * Returns zero for success, a negative number on error. | 
 | 646 |  */ | 
 | 647 | int regmap_update_bits(struct regmap *map, unsigned int reg, | 
 | 648 | 		       unsigned int mask, unsigned int val) | 
 | 649 | { | 
 | 650 | 	bool change; | 
 | 651 | 	return _regmap_update_bits(map, reg, mask, val, &change); | 
 | 652 | } | 
| Mark Brown | b83a313 | 2011-05-11 19:59:58 +0200 | [diff] [blame] | 653 | EXPORT_SYMBOL_GPL(regmap_update_bits); | 
| Mark Brown | 31244e3 | 2011-07-20 22:56:53 +0100 | [diff] [blame] | 654 |  | 
| Mark Brown | 018690d | 2011-11-29 20:10:36 +0000 | [diff] [blame] | 655 | /** | 
 | 656 |  * regmap_update_bits_check: Perform a read/modify/write cycle on the | 
 | 657 |  *                           register map and report if updated | 
 | 658 |  * | 
 | 659 |  * @map: Register map to update | 
 | 660 |  * @reg: Register to update | 
 | 661 |  * @mask: Bitmask to change | 
 | 662 |  * @val: New value for bitmask | 
 | 663 |  * @change: Boolean indicating if a write was done | 
 | 664 |  * | 
 | 665 |  * Returns zero for success, a negative number on error. | 
 | 666 |  */ | 
 | 667 | int regmap_update_bits_check(struct regmap *map, unsigned int reg, | 
 | 668 | 			     unsigned int mask, unsigned int val, | 
 | 669 | 			     bool *change) | 
 | 670 | { | 
 | 671 | 	return _regmap_update_bits(map, reg, mask, val, change); | 
 | 672 | } | 
 | 673 | EXPORT_SYMBOL_GPL(regmap_update_bits_check); | 
 | 674 |  | 
| Mark Brown | 31244e3 | 2011-07-20 22:56:53 +0100 | [diff] [blame] | 675 | static int __init regmap_initcall(void) | 
 | 676 | { | 
 | 677 | 	regmap_debugfs_initcall(); | 
 | 678 |  | 
 | 679 | 	return 0; | 
 | 680 | } | 
 | 681 | postcore_initcall(regmap_initcall); |