Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soundwire: cadence: add parity error injection through debugfs

The Cadence IP can inject errors, let's make use of this capability to
test Slave parity error checks.

See e.g. example log where both the master and slave detect the parity
error injected on a dummy read command.

cd /sys/kernel/debug/soundwire/master-1/intel-sdw/
echo 1 > cdns-parity-error-injection

[ 44.756249] intel-master sdw-master-1: Parity error
[ 44.756313] intel-master sdw-master-1: Msg NACK received
[ 44.756366] intel-master sdw-master-1: Msg NACKed for Slave 15
[ 44.756375] intel-master sdw-master-1: trf on Slave 15 failed:-5
[ 44.756382] intel-master sdw-master-1: parity error injection, read: -5
[ 44.756649] rt1308 sdw:1:25d:1308:0: Parity error detected

The code makes sure the Master device is resumed, hence the clock
restarted, before sending a parity error.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Link: https://lore.kernel.org/r/20200908134521.6781-8-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Pierre-Louis Bossart and committed by
Vinod Koul
32d2a893 a350aff4

+86
+86
drivers/soundwire/cadence_master.c
··· 13 13 #include <linux/io.h> 14 14 #include <linux/module.h> 15 15 #include <linux/mod_devicetable.h> 16 + #include <linux/pm_runtime.h> 16 17 #include <linux/soundwire/sdw_registers.h> 17 18 #include <linux/soundwire/sdw.h> 18 19 #include <sound/pcm_params.h> ··· 51 50 #define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0) 52 51 53 52 #define CDNS_MCP_CMDCTRL 0x8 53 + 54 + #define CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR BIT(2) 55 + 54 56 #define CDNS_MCP_SSPSTAT 0xC 55 57 #define CDNS_MCP_FRAME_SHAPE 0x10 56 58 #define CDNS_MCP_FRAME_SHAPE_INIT 0x14 ··· 370 366 371 367 DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); 372 368 369 + static int cdns_parity_error_injection(void *data, u64 value) 370 + { 371 + struct sdw_cdns *cdns = data; 372 + struct sdw_bus *bus; 373 + int ret; 374 + 375 + if (value != 1) 376 + return -EINVAL; 377 + 378 + bus = &cdns->bus; 379 + 380 + /* 381 + * Resume Master device. If this results in a bus reset, the 382 + * Slave devices will re-attach and be re-enumerated. 383 + */ 384 + ret = pm_runtime_get_sync(bus->dev); 385 + if (ret < 0 && ret != -EACCES) { 386 + dev_err_ratelimited(cdns->dev, 387 + "pm_runtime_get_sync failed in %s, ret %d\n", 388 + __func__, ret); 389 + pm_runtime_put_noidle(bus->dev); 390 + return ret; 391 + } 392 + 393 + /* 394 + * wait long enough for Slave(s) to be in steady state. This 395 + * does not need to be super precise. 396 + */ 397 + msleep(200); 398 + 399 + /* 400 + * Take the bus lock here to make sure that any bus transactions 401 + * will be queued while we inject a parity error on a dummy read 402 + */ 403 + mutex_lock(&bus->bus_lock); 404 + 405 + /* program hardware to inject parity error */ 406 + cdns_updatel(cdns, CDNS_MCP_CMDCTRL, 407 + CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR, 408 + CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR); 409 + 410 + /* commit changes */ 411 + cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, 412 + CDNS_MCP_CONFIG_UPDATE_BIT, 413 + CDNS_MCP_CONFIG_UPDATE_BIT); 414 + 415 + /* do a broadcast dummy read to avoid bus clashes */ 416 + ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0); 417 + dev_info(cdns->dev, "parity error injection, read: %d\n", ret); 418 + 419 + /* program hardware to disable parity error */ 420 + cdns_updatel(cdns, CDNS_MCP_CMDCTRL, 421 + CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR, 422 + 0); 423 + 424 + /* commit changes */ 425 + cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE, 426 + CDNS_MCP_CONFIG_UPDATE_BIT, 427 + CDNS_MCP_CONFIG_UPDATE_BIT); 428 + 429 + /* Continue bus operation with parity error injection disabled */ 430 + mutex_unlock(&bus->bus_lock); 431 + 432 + /* Userspace changed the hardware state behind the kernel's back */ 433 + add_taint(TAINT_USER, LOCKDEP_STILL_OK); 434 + 435 + /* 436 + * allow Master device to enter pm_runtime suspend. This may 437 + * also result in Slave devices suspending. 438 + */ 439 + pm_runtime_mark_last_busy(bus->dev); 440 + pm_runtime_put_autosuspend(bus->dev); 441 + 442 + return 0; 443 + } 444 + 445 + DEFINE_DEBUGFS_ATTRIBUTE(cdns_parity_error_fops, NULL, 446 + cdns_parity_error_injection, "%llu\n"); 447 + 373 448 /** 374 449 * sdw_cdns_debugfs_init() - Cadence debugfs init 375 450 * @cdns: Cadence instance ··· 460 377 461 378 debugfs_create_file("cdns-hw-reset", 0200, root, cdns, 462 379 &cdns_hw_reset_fops); 380 + 381 + debugfs_create_file("cdns-parity-error-injection", 0200, root, cdns, 382 + &cdns_parity_error_fops); 463 383 } 464 384 EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); 465 385