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(endian: 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, reg: data, reg_len: 2, |
60 | val: data + 2, val_len: 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(endian: 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(objp: data->vals); |
97 | kfree(objp: data->read); |
98 | kfree(objp: data->written); |
99 | kfree(objp: 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(error: -EINVAL); |
119 | |
120 | if (!config->max_register) { |
121 | pr_crit("No max_register specified for RAM regmap\n" ); |
122 | return ERR_PTR(error: -EINVAL); |
123 | } |
124 | |
125 | data->read = kcalloc(n: config->max_register + 1, size: sizeof(bool), |
126 | GFP_KERNEL); |
127 | if (!data->read) |
128 | return ERR_PTR(error: -ENOMEM); |
129 | |
130 | data->written = kcalloc(n: config->max_register + 1, size: sizeof(bool), |
131 | GFP_KERNEL); |
132 | if (!data->written) |
133 | return ERR_PTR(error: -ENOMEM); |
134 | |
135 | data->reg_endian = config->reg_format_endian; |
136 | |
137 | map = __regmap_init(NULL, bus: ®map_raw_ram, bus_context: 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 | |