xref: /linux/drivers/base/regmap/regmap-raw-ram.c (revision 021bc4b9)
1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Register map access API - Memory region with raw access
4 //
5 // This is intended for testing only
6 //
7 // Copyright (c) 2023, Arm Ltd
8 
9 #include <linux/clk.h>
10 #include <linux/err.h>
11 #include <linux/io.h>
12 #include <linux/module.h>
13 #include <linux/regmap.h>
14 #include <linux/slab.h>
15 #include <linux/swab.h>
16 
17 #include "internal.h"
18 
19 static unsigned int decode_reg(enum regmap_endian endian, const void *reg)
20 {
21 	const u16 *r = reg;
22 
23 	if (endian == REGMAP_ENDIAN_BIG)
24 		return be16_to_cpu(*r);
25 	else
26 		return le16_to_cpu(*r);
27 }
28 
29 static int regmap_raw_ram_gather_write(void *context,
30 				       const void *reg, size_t reg_len,
31 				       const void *val, size_t val_len)
32 {
33 	struct regmap_ram_data *data = context;
34 	unsigned int r;
35 	u16 *our_buf = (u16 *)data->vals;
36 	int i;
37 
38 	if (reg_len != 2)
39 		return -EINVAL;
40 	if (val_len % 2)
41 		return -EINVAL;
42 
43 	r = decode_reg(data->reg_endian, reg);
44 	if (data->noinc_reg && data->noinc_reg(data, r)) {
45 		memcpy(&our_buf[r], val + val_len - 2, 2);
46 		data->written[r] = true;
47 	} else {
48 		memcpy(&our_buf[r], val, val_len);
49 
50 		for (i = 0; i < val_len / 2; i++)
51 			data->written[r + i] = true;
52 	}
53 
54 	return 0;
55 }
56 
57 static int regmap_raw_ram_write(void *context, const void *data, size_t count)
58 {
59 	return regmap_raw_ram_gather_write(context, data, 2,
60 					   data + 2, count - 2);
61 }
62 
63 static int regmap_raw_ram_read(void *context,
64 			       const void *reg, size_t reg_len,
65 			       void *val, size_t val_len)
66 {
67 	struct regmap_ram_data *data = context;
68 	unsigned int r;
69 	u16 *our_buf = (u16 *)data->vals;
70 	int i;
71 
72 	if (reg_len != 2)
73 		return -EINVAL;
74 	if (val_len % 2)
75 		return -EINVAL;
76 
77 	r = decode_reg(data->reg_endian, reg);
78 	if (data->noinc_reg && data->noinc_reg(data, r)) {
79 		for (i = 0; i < val_len; i += 2)
80 			memcpy(val + i, &our_buf[r], 2);
81 		data->read[r] = true;
82 	} else {
83 		memcpy(val, &our_buf[r], val_len);
84 
85 		for (i = 0; i < val_len / 2; i++)
86 			data->read[r + i] = true;
87 	}
88 
89 	return 0;
90 }
91 
92 static void regmap_raw_ram_free_context(void *context)
93 {
94 	struct regmap_ram_data *data = context;
95 
96 	kfree(data->vals);
97 	kfree(data->read);
98 	kfree(data->written);
99 	kfree(data);
100 }
101 
102 static const struct regmap_bus regmap_raw_ram = {
103 	.fast_io = true,
104 	.write = regmap_raw_ram_write,
105 	.gather_write = regmap_raw_ram_gather_write,
106 	.read = regmap_raw_ram_read,
107 	.free_context = regmap_raw_ram_free_context,
108 };
109 
110 struct regmap *__regmap_init_raw_ram(const struct regmap_config *config,
111 				     struct regmap_ram_data *data,
112 				     struct lock_class_key *lock_key,
113 				     const char *lock_name)
114 {
115 	struct regmap *map;
116 
117 	if (config->reg_bits != 16)
118 		return ERR_PTR(-EINVAL);
119 
120 	if (!config->max_register) {
121 		pr_crit("No max_register specified for RAM regmap\n");
122 		return ERR_PTR(-EINVAL);
123 	}
124 
125 	data->read = kcalloc(config->max_register + 1, sizeof(bool),
126 			     GFP_KERNEL);
127 	if (!data->read)
128 		return ERR_PTR(-ENOMEM);
129 
130 	data->written = kcalloc(config->max_register + 1, sizeof(bool),
131 				GFP_KERNEL);
132 	if (!data->written)
133 		return ERR_PTR(-ENOMEM);
134 
135 	data->reg_endian = config->reg_format_endian;
136 
137 	map = __regmap_init(NULL, &regmap_raw_ram, data, config,
138 			    lock_key, lock_name);
139 
140 	return map;
141 }
142 EXPORT_SYMBOL_GPL(__regmap_init_raw_ram);
143 
144 MODULE_LICENSE("GPL v2");
145