static int cdns_parity_error_injection()

in cadence_master.c [374:448]


static int cdns_parity_error_injection(void *data, u64 value)
{
	struct sdw_cdns *cdns = data;
	struct sdw_bus *bus;
	int ret;

	if (value != 1)
		return -EINVAL;

	bus = &cdns->bus;

	/*
	 * Resume Master device. If this results in a bus reset, the
	 * Slave devices will re-attach and be re-enumerated.
	 */
	ret = pm_runtime_get_sync(bus->dev);
	if (ret < 0 && ret != -EACCES) {
		dev_err_ratelimited(cdns->dev,
				    "pm_runtime_get_sync failed in %s, ret %d\n",
				    __func__, ret);
		pm_runtime_put_noidle(bus->dev);
		return ret;
	}

	/*
	 * wait long enough for Slave(s) to be in steady state. This
	 * does not need to be super precise.
	 */
	msleep(200);

	/*
	 * Take the bus lock here to make sure that any bus transactions
	 * will be queued while we inject a parity error on a dummy read
	 */
	mutex_lock(&bus->bus_lock);

	/* program hardware to inject parity error */
	cdns_updatel(cdns, CDNS_MCP_CMDCTRL,
		     CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR,
		     CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR);

	/* commit changes */
	cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
		     CDNS_MCP_CONFIG_UPDATE_BIT,
		     CDNS_MCP_CONFIG_UPDATE_BIT);

	/* do a broadcast dummy read to avoid bus clashes */
	ret = sdw_bread_no_pm_unlocked(&cdns->bus, 0xf, SDW_SCP_DEVID_0);
	dev_info(cdns->dev, "parity error injection, read: %d\n", ret);

	/* program hardware to disable parity error */
	cdns_updatel(cdns, CDNS_MCP_CMDCTRL,
		     CDNS_MCP_CMDCTRL_INSERT_PARITY_ERR,
		     0);

	/* commit changes */
	cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
		     CDNS_MCP_CONFIG_UPDATE_BIT,
		     CDNS_MCP_CONFIG_UPDATE_BIT);

	/* Continue bus operation with parity error injection disabled */
	mutex_unlock(&bus->bus_lock);

	/* Userspace changed the hardware state behind the kernel's back */
	add_taint(TAINT_USER, LOCKDEP_STILL_OK);

	/*
	 * allow Master device to enter pm_runtime suspend. This may
	 * also result in Slave devices suspending.
	 */
	pm_runtime_mark_last_busy(bus->dev);
	pm_runtime_put_autosuspend(bus->dev);

	return 0;
}