1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Mock regmap for cs_dsp KUnit tests.
4 //
5 // Copyright (C) 2024 Cirrus Logic, Inc. and
6 //                    Cirrus Logic International Semiconductor Ltd.
7 
8 #include <kunit/test.h>
9 #include <linux/firmware/cirrus/cs_dsp.h>
10 #include <linux/firmware/cirrus/cs_dsp_test_utils.h>
11 #include <linux/firmware/cirrus/wmfw.h>
12 #include <linux/regmap.h>
13 
cs_dsp_mock_regmap_read(void * context,const void * reg_buf,const size_t reg_size,void * val_buf,size_t val_size)14 static int cs_dsp_mock_regmap_read(void *context, const void *reg_buf,
15 				   const size_t reg_size, void *val_buf,
16 				   size_t val_size)
17 {
18 	struct cs_dsp_test *priv = context;
19 
20 	/* Should never get here because the regmap is cache-only */
21 	KUNIT_FAIL(priv->test, "Unexpected bus read @%#x", *(u32 *)reg_buf);
22 
23 	return -EIO;
24 }
25 
cs_dsp_mock_regmap_gather_write(void * context,const void * reg_buf,size_t reg_size,const void * val_buf,size_t val_size)26 static int cs_dsp_mock_regmap_gather_write(void *context,
27 					   const void *reg_buf, size_t reg_size,
28 					   const void *val_buf, size_t val_size)
29 {
30 	struct cs_dsp_test *priv = context;
31 
32 	priv->saw_bus_write = true;
33 
34 	/* Should never get here because the regmap is cache-only */
35 	KUNIT_FAIL(priv->test, "Unexpected bus gather_write @%#x", *(u32 *)reg_buf);
36 
37 	return -EIO;
38 }
39 
cs_dsp_mock_regmap_write(void * context,const void * val_buf,size_t val_size)40 static int cs_dsp_mock_regmap_write(void *context, const void *val_buf, size_t val_size)
41 {
42 	struct cs_dsp_test *priv = context;
43 
44 	priv->saw_bus_write = true;
45 
46 	/* Should never get here because the regmap is cache-only */
47 	KUNIT_FAIL(priv->test, "Unexpected bus write @%#x", *(u32 *)val_buf);
48 
49 	return -EIO;
50 }
51 
52 static const struct regmap_bus cs_dsp_mock_regmap_bus = {
53 	.read = cs_dsp_mock_regmap_read,
54 	.write = cs_dsp_mock_regmap_write,
55 	.gather_write = cs_dsp_mock_regmap_gather_write,
56 	.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
57 	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
58 };
59 
60 static const struct reg_default adsp2_32bit_register_defaults[] = {
61 	{ 0xffe00, 0x0000 }, /* CONTROL */
62 	{ 0xffe02, 0x0000 }, /* CLOCKING */
63 	{ 0xffe04, 0x0001 }, /* STATUS1: RAM_RDY=1 */
64 	{ 0xffe30, 0x0000 }, /* WDMW_CONFIG_1 */
65 	{ 0xffe32, 0x0000 }, /* WDMA_CONFIG_2 */
66 	{ 0xffe34, 0x0000 }, /* RDMA_CONFIG_1 */
67 	{ 0xffe40, 0x0000 }, /* SCRATCH_0_1 */
68 	{ 0xffe42, 0x0000 }, /* SCRATCH_2_3 */
69 };
70 
71 static const struct regmap_range adsp2_32bit_registers[] = {
72 	regmap_reg_range(0x80000, 0x88ffe), /* PM */
73 	regmap_reg_range(0xa0000, 0xa9ffe), /* XM */
74 	regmap_reg_range(0xc0000, 0xc1ffe), /* YM */
75 	regmap_reg_range(0xe0000, 0xe1ffe), /* ZM */
76 	regmap_reg_range(0xffe00, 0xffe7c), /* CORE CTRL */
77 };
78 
79 const unsigned int cs_dsp_mock_adsp2_32bit_sysbase = 0xffe00;
80 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
81 
82 static const struct regmap_access_table adsp2_32bit_rw = {
83 	.yes_ranges = adsp2_32bit_registers,
84 	.n_yes_ranges = ARRAY_SIZE(adsp2_32bit_registers),
85 };
86 
87 static const struct regmap_config cs_dsp_mock_regmap_adsp2_32bit = {
88 	.reg_bits = 32,
89 	.val_bits = 32,
90 	.reg_stride = 2,
91 	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
92 	.val_format_endian = REGMAP_ENDIAN_BIG,
93 	.wr_table = &adsp2_32bit_rw,
94 	.rd_table = &adsp2_32bit_rw,
95 	.max_register = 0xffe7c,
96 	.reg_defaults = adsp2_32bit_register_defaults,
97 	.num_reg_defaults = ARRAY_SIZE(adsp2_32bit_register_defaults),
98 	.cache_type = REGCACHE_MAPLE,
99 };
100 
101 static const struct reg_default adsp2_16bit_register_defaults[] = {
102 	{ 0x1100, 0x0000 }, /* CONTROL */
103 	{ 0x1101, 0x0000 }, /* CLOCKING */
104 	{ 0x1104, 0x0001 }, /* STATUS1: RAM_RDY=1 */
105 	{ 0x1130, 0x0000 }, /* WDMW_CONFIG_1 */
106 	{ 0x1131, 0x0000 }, /* WDMA_CONFIG_2 */
107 	{ 0x1134, 0x0000 }, /* RDMA_CONFIG_1 */
108 	{ 0x1140, 0x0000 }, /* SCRATCH_0 */
109 	{ 0x1141, 0x0000 }, /* SCRATCH_1 */
110 	{ 0x1142, 0x0000 }, /* SCRATCH_2 */
111 	{ 0x1143, 0x0000 }, /* SCRATCH_3 */
112 };
113 
114 static const struct regmap_range adsp2_16bit_registers[] = {
115 	regmap_reg_range(0x001100, 0x001143), /* CORE CTRL */
116 	regmap_reg_range(0x100000, 0x105fff), /* PM */
117 	regmap_reg_range(0x180000, 0x1807ff), /* ZM */
118 	regmap_reg_range(0x190000, 0x1947ff), /* XM */
119 	regmap_reg_range(0x1a8000, 0x1a97ff), /* YM */
120 };
121 
122 const unsigned int cs_dsp_mock_adsp2_16bit_sysbase = 0x001100;
123 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
124 
125 static const struct regmap_access_table adsp2_16bit_rw = {
126 	.yes_ranges = adsp2_16bit_registers,
127 	.n_yes_ranges = ARRAY_SIZE(adsp2_16bit_registers),
128 };
129 
130 static const struct regmap_config cs_dsp_mock_regmap_adsp2_16bit = {
131 	.reg_bits = 32,
132 	.val_bits = 16,
133 	.reg_stride = 1,
134 	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
135 	.val_format_endian = REGMAP_ENDIAN_BIG,
136 	.wr_table = &adsp2_16bit_rw,
137 	.rd_table = &adsp2_16bit_rw,
138 	.max_register = 0x1a97ff,
139 	.reg_defaults = adsp2_16bit_register_defaults,
140 	.num_reg_defaults = ARRAY_SIZE(adsp2_16bit_register_defaults),
141 	.cache_type = REGCACHE_MAPLE,
142 };
143 
144 static const struct reg_default halo_register_defaults[] = {
145 	/* CORE */
146 	{ 0x2b80010, 0 },	/* HALO_CORE_SOFT_RESET */
147 	{ 0x2b805c0, 0 },	/* HALO_SCRATCH1 */
148 	{ 0x2b805c8, 0 },	/* HALO_SCRATCH2 */
149 	{ 0x2b805d0, 0 },	/* HALO_SCRATCH3 */
150 	{ 0x2b805c8, 0 },	/* HALO_SCRATCH4 */
151 	{ 0x2bc1000, 0 },	/* HALO_CCM_CORE_CONTROL */
152 	{ 0x2bc7000, 0 },	/* HALO_WDT_CONTROL */
153 
154 	/* SYSINFO */
155 	{ 0x25e2040, 0 },	/* HALO_AHBM_WINDOW_DEBUG_0 */
156 	{ 0x25e2044, 0 },	/* HALO_AHBM_WINDOW_DEBUG_1 */
157 };
158 
159 static const struct regmap_range halo_readable_registers[] = {
160 	regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
161 	regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */
162 	regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */
163 	regmap_reg_range(0x2800000, 0x2807fff), /* XM */
164 	regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
165 	regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
166 	regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
167 	regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
168 };
169 
170 static const struct regmap_range halo_writeable_registers[] = {
171 	regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
172 	regmap_reg_range(0x2800000, 0x2807fff), /* XM */
173 	regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
174 	regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
175 	regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
176 	regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
177 };
178 
179 const unsigned int cs_dsp_mock_halo_core_base = 0x2b80000;
180 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_core_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
181 
182 const unsigned int cs_dsp_mock_halo_sysinfo_base = 0x25e0000;
183 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_sysinfo_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
184 
185 static const struct regmap_access_table halo_readable = {
186 	.yes_ranges = halo_readable_registers,
187 	.n_yes_ranges = ARRAY_SIZE(halo_readable_registers),
188 };
189 
190 static const struct regmap_access_table halo_writeable = {
191 	.yes_ranges = halo_writeable_registers,
192 	.n_yes_ranges = ARRAY_SIZE(halo_writeable_registers),
193 };
194 
195 static const struct regmap_config cs_dsp_mock_regmap_halo = {
196 	.reg_bits = 32,
197 	.val_bits = 32,
198 	.reg_stride = 4,
199 	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
200 	.val_format_endian = REGMAP_ENDIAN_BIG,
201 	.wr_table = &halo_writeable,
202 	.rd_table = &halo_readable,
203 	.max_register = 0x3804ffc,
204 	.reg_defaults = halo_register_defaults,
205 	.num_reg_defaults = ARRAY_SIZE(halo_register_defaults),
206 	.cache_type = REGCACHE_MAPLE,
207 };
208 
209 /**
210  * cs_dsp_mock_regmap_drop_range() - drop a range of registers from the cache.
211  *
212  * @priv:	Pointer to struct cs_dsp_test object.
213  * @first_reg:	Address of first register to drop.
214  * @last_reg:	Address of last register to drop.
215  */
cs_dsp_mock_regmap_drop_range(struct cs_dsp_test * priv,unsigned int first_reg,unsigned int last_reg)216 void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
217 				   unsigned int first_reg, unsigned int last_reg)
218 {
219 	regcache_drop_region(priv->dsp->regmap, first_reg, last_reg);
220 }
221 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_range, "FW_CS_DSP_KUNIT_TEST_UTILS");
222 
223 /**
224  * cs_dsp_mock_regmap_drop_regs() - drop a number of registers from the cache.
225  *
226  * @priv:	Pointer to struct cs_dsp_test object.
227  * @first_reg:	Address of first register to drop.
228  * @num_regs:	Number of registers to drop.
229  */
cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test * priv,unsigned int first_reg,size_t num_regs)230 void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
231 				  unsigned int first_reg, size_t num_regs)
232 {
233 	int stride = regmap_get_reg_stride(priv->dsp->regmap);
234 	unsigned int last = first_reg + (stride * (num_regs - 1));
235 
236 	cs_dsp_mock_regmap_drop_range(priv, first_reg, last);
237 }
238 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
239 
240 /**
241  * cs_dsp_mock_regmap_drop_bytes() - drop a number of bytes from the cache.
242  *
243  * @priv:	Pointer to struct cs_dsp_test object.
244  * @first_reg:	Address of first register to drop.
245  * @num_bytes:	Number of bytes to drop from the cache. Will be rounded
246  *		down to a whole number of registers. Trailing bytes that
247  *		are not a multiple of the register size will not be dropped.
248  *		(This is intended to help detect math errors in test code.)
249  */
cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test * priv,unsigned int first_reg,size_t num_bytes)250 void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
251 				   unsigned int first_reg, size_t num_bytes)
252 {
253 	size_t num_regs = num_bytes / regmap_get_val_bytes(priv->dsp->regmap);
254 
255 	cs_dsp_mock_regmap_drop_regs(priv, first_reg, num_regs);
256 }
257 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
258 
259 /**
260  * cs_dsp_mock_regmap_drop_system_regs() - Drop DSP system registers from the cache.
261  *
262  * @priv:	Pointer to struct cs_dsp_test object.
263  *
264  * Drops all DSP system registers from the regmap cache.
265  */
cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test * priv)266 void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv)
267 {
268 	switch (priv->dsp->type) {
269 	case WMFW_ADSP2:
270 		if (priv->dsp->base) {
271 			regcache_drop_region(priv->dsp->regmap,
272 					     priv->dsp->base,
273 					     priv->dsp->base + 0x7c);
274 		}
275 		return;
276 	case WMFW_HALO:
277 		if (priv->dsp->base) {
278 			regcache_drop_region(priv->dsp->regmap,
279 					     priv->dsp->base,
280 					     priv->dsp->base + 0x47000);
281 		}
282 
283 		/* sysinfo registers are read-only so don't drop them */
284 		return;
285 	default:
286 		return;
287 	}
288 }
289 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_system_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
290 
291 /**
292  * cs_dsp_mock_regmap_is_dirty() - Test for dirty registers in the cache.
293  *
294  * @priv:		Pointer to struct cs_dsp_test object.
295  * @drop_system_regs:	If true the DSP system regs will be dropped from
296  *			the cache before checking for dirty.
297  *
298  * All registers that are expected to be written must have been dropped
299  * from the cache (DSP system registers can be dropped by passing
300  * drop_system_regs == true). If any unexpected registers were written
301  * there will still be dirty entries in the cache and a cache sync will
302  * cause a write.
303  *
304  * Returns: true if there were dirty entries, false if not.
305  */
cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test * priv,bool drop_system_regs)306 bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs)
307 {
308 	if (drop_system_regs)
309 		cs_dsp_mock_regmap_drop_system_regs(priv);
310 
311 	priv->saw_bus_write = false;
312 	regcache_cache_only(priv->dsp->regmap, false);
313 	regcache_sync(priv->dsp->regmap);
314 	regcache_cache_only(priv->dsp->regmap, true);
315 
316 	return priv->saw_bus_write;
317 }
318 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_is_dirty, "FW_CS_DSP_KUNIT_TEST_UTILS");
319 
320 /**
321  * cs_dsp_mock_regmap_init() - Initialize a mock regmap.
322  *
323  * @priv:	Pointer to struct cs_dsp_test object. This must have a
324  *		valid pointer to a struct cs_dsp in which the type and
325  *		rev fields are set to the type of DSP to be simulated.
326  *
327  * On success the priv->dsp->regmap will point to the created
328  * regmap instance.
329  *
330  * Return: zero on success, else negative error code.
331  */
cs_dsp_mock_regmap_init(struct cs_dsp_test * priv)332 int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv)
333 {
334 	const struct regmap_config *config;
335 	int ret;
336 
337 	switch (priv->dsp->type) {
338 	case WMFW_HALO:
339 		config = &cs_dsp_mock_regmap_halo;
340 		break;
341 	case WMFW_ADSP2:
342 		if (priv->dsp->rev == 0)
343 			config = &cs_dsp_mock_regmap_adsp2_16bit;
344 		else
345 			config = &cs_dsp_mock_regmap_adsp2_32bit;
346 		break;
347 	default:
348 		config = NULL;
349 		break;
350 	}
351 
352 	priv->dsp->regmap = devm_regmap_init(priv->dsp->dev,
353 					     &cs_dsp_mock_regmap_bus,
354 					     priv,
355 					     config);
356 	if (IS_ERR(priv->dsp->regmap)) {
357 		ret = PTR_ERR(priv->dsp->regmap);
358 		kunit_err(priv->test, "Failed to allocate register map: %d\n", ret);
359 		return ret;
360 	}
361 
362 	/* Put regmap in cache-only so it accumulates the writes done by cs_dsp */
363 	regcache_cache_only(priv->dsp->regmap, true);
364 
365 	return 0;
366 }
367 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
368