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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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