Commit e3cfd49c authored by Abhijit Gangurde's avatar Abhijit Gangurde Committed by Greg Kroah-Hartman
Browse files

cdx: add support for bus enable and disable



CDX bus needs to be disabled before updating/writing devices
in the FPGA. Once the devices are written, the bus shall be
rescanned. This change provides sysfs entry to enable/disable the
CDX bus.

Co-developed-by: default avatarNipun Gupta <nipun.gupta@amd.com>
Signed-off-by: default avatarNipun Gupta <nipun.gupta@amd.com>
Signed-off-by: default avatarAbhijit Gangurde <abhijit.gangurde@amd.com>
Link: https://lore.kernel.org/r/20231017160505.10640-6-abhijit.gangurde@amd.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ce558a39
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -28,6 +28,19 @@ Description:
		of a device manufacturer.
		Combination of Vendor ID and Device ID identifies a device.

What:		/sys/bus/cdx/devices/.../enable
Date:		October 2023
Contact:	abhijit.gangurde@amd.com
Description:
		CDX bus should be disabled before updating the devices in FPGA.
		Writing n/0/off will attempt to disable the CDX bus and.
		writing y/1/on will attempt to enable the CDX bus. Reading this file
		gives the current state of the bus, 1 for enabled and 0 for disabled.

		For example::

		  # echo 1 > /sys/bus/cdx/.../enable

What:		/sys/bus/cdx/devices/.../reset
Date:		March 2023
Contact:	nipun.gupta@amd.com
+72 −0
Original line number Diff line number Diff line
@@ -124,9 +124,12 @@ static int cdx_unregister_device(struct device *dev,
				 void *data)
{
	struct cdx_device *cdx_dev = to_cdx_device(dev);
	struct cdx_controller *cdx = cdx_dev->cdx;

	if (cdx_dev->is_bus) {
		device_for_each_child(dev, NULL, cdx_unregister_device);
		if (cdx_dev->enabled && cdx->ops->bus_disable)
			cdx->ops->bus_disable(cdx, cdx_dev->bus_num);
	} else {
		kfree(cdx_dev->driver_override);
		cdx_dev->driver_override = NULL;
@@ -383,6 +386,41 @@ static ssize_t driver_override_show(struct device *dev,
}
static DEVICE_ATTR_RW(driver_override);

static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t count)
{
	struct cdx_device *cdx_dev = to_cdx_device(dev);
	struct cdx_controller *cdx = cdx_dev->cdx;
	bool enable;
	int ret;

	if (kstrtobool(buf, &enable) < 0)
		return -EINVAL;

	if (enable == cdx_dev->enabled)
		return count;

	if (enable && cdx->ops->bus_enable)
		ret = cdx->ops->bus_enable(cdx, cdx_dev->bus_num);
	else if (!enable && cdx->ops->bus_disable)
		ret = cdx->ops->bus_disable(cdx, cdx_dev->bus_num);
	else
		ret = -EOPNOTSUPP;

	if (!ret)
		cdx_dev->enabled = enable;

	return ret < 0 ? ret : count;
}

static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct cdx_device *cdx_dev = to_cdx_device(dev);

	return sysfs_emit(buf, "%u\n", cdx_dev->enabled);
}
static DEVICE_ATTR_RW(enable);

static umode_t cdx_dev_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n)
{
	struct device *dev = kobj_to_dev(kobj);
@@ -395,6 +433,18 @@ static umode_t cdx_dev_attrs_are_visible(struct kobject *kobj, struct attribute
	return 0;
}

static umode_t cdx_bus_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n)
{
	struct device *dev = kobj_to_dev(kobj);
	struct cdx_device *cdx_dev;

	cdx_dev = to_cdx_device(dev);
	if (cdx_dev->is_bus)
		return a->mode;

	return 0;
}

static struct attribute *cdx_dev_attrs[] = {
	&dev_attr_remove.attr,
	&dev_attr_reset.attr,
@@ -409,8 +459,19 @@ static const struct attribute_group cdx_dev_group = {
	.is_visible = cdx_dev_attrs_are_visible,
};

static struct attribute *cdx_bus_dev_attrs[] = {
	&dev_attr_enable.attr,
	NULL,
};

static const struct attribute_group cdx_bus_dev_group = {
	.attrs = cdx_bus_dev_attrs,
	.is_visible = cdx_bus_attrs_are_visible,
};

static const struct attribute_group *cdx_dev_groups[] = {
	&cdx_dev_group,
	&cdx_bus_dev_group,
	NULL,
};

@@ -588,8 +649,19 @@ struct device *cdx_bus_add(struct cdx_controller *cdx, u8 bus_num)
		goto device_add_fail;
	}

	if (cdx->ops->bus_enable) {
		ret = cdx->ops->bus_enable(cdx, bus_num);
		if (ret && ret != -EALREADY) {
			dev_err(cdx->dev, "cdx bus enable failed: %d\n", ret);
			goto bus_enable_fail;
		}
	}

	cdx_dev->enabled = true;
	return &cdx_dev->dev;

bus_enable_fail:
	device_del(&cdx_dev->dev);
device_add_fail:
	put_device(&cdx_dev->dev);

+12 −0
Original line number Diff line number Diff line
@@ -33,6 +33,16 @@ static const struct cdx_mcdi_ops mcdi_ops = {
	.mcdi_request = cdx_mcdi_request,
};

static int cdx_bus_enable(struct cdx_controller *cdx, u8 bus_num)
{
	return cdx_mcdi_bus_enable(cdx->priv, bus_num);
}

static int cdx_bus_disable(struct cdx_controller *cdx, u8 bus_num)
{
	return cdx_mcdi_bus_disable(cdx->priv, bus_num);
}

void cdx_rpmsg_post_probe(struct cdx_controller *cdx)
{
	/* Register CDX controller with CDX bus driver */
@@ -128,6 +138,8 @@ static int cdx_scan_devices(struct cdx_controller *cdx)
}

static struct cdx_ops cdx_ops = {
	.bus_enable		= cdx_bus_enable,
	.bus_disable	= cdx_bus_disable,
	.scan		= cdx_scan_devices,
	.dev_configure	= cdx_configure_device,
};
+54 −0
Original line number Diff line number Diff line
@@ -455,6 +455,60 @@
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST			84
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN			4

/***********************************/
/*
 * MC_CMD_CDX_BUS_DOWN
 * Asserting reset on the CDX bus causes all devices on the bus to be quiesced.
 * DMA bus mastering is disabled and any pending DMA request are flushed. Once
 * the response is returned, the devices are guaranteed to no longer issue DMA
 * requests or raise MSI interrupts. Further device MMIO accesses may have
 * undefined results. While the bus reset is asserted, any of the enumeration
 * or device configuration MCDIs will fail with EAGAIN. It is only legal to
 * reload the relevant PL region containing CDX devices if the corresponding CDX
 * bus is in reset. Depending on the implementation, the firmware may or may
 * not enforce this restriction and it is up to the caller to make sure this
 * requirement is satisfied.
 */
#define MC_CMD_CDX_BUS_DOWN					0x4
#define MC_CMD_CDX_BUS_DOWN_MSGSET			0x4

/* MC_CMD_CDX_BUS_DOWN_IN msgrequest */
#define MC_CMD_CDX_BUS_DOWN_IN_LEN			4
/* Bus number to put in reset, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_BUS_DOWN_IN_BUS_OFST		0
#define MC_CMD_CDX_BUS_DOWN_IN_BUS_LEN		4

/*
 * MC_CMD_CDX_BUS_DOWN_OUT msgresponse: The bus is quiesced, no further
 * upstream traffic for devices on this bus.
 */
#define MC_CMD_CDX_BUS_DOWN_OUT_LEN			0

/***********************************/
/*
 * MC_CMD_CDX_BUS_UP
 * After bus reset is de-asserted, devices are in a state which is functionally
 * equivalent to each device having been reset with MC_CMD_CDX_DEVICE_RESET. In
 * other words, device logic is reset in a hardware-specific way, MMIO accesses
 * are forwarded to the device, DMA bus mastering is disabled and needs to be
 * re-enabled with MC_CMD_CDX_DEVICE_DMA_ENABLE once the driver is ready to
 * start servicing DMA. If the underlying number of devices or device resources
 * changed (e.g. if PL was reloaded) while the bus was in reset, the bus driver
 * is expected to re-enumerate the bus. Returns EALREADY if the bus was already
 * up before the call.
 */
#define MC_CMD_CDX_BUS_UP					0x5
#define MC_CMD_CDX_BUS_UP_MSGSET			0x5

/* MC_CMD_CDX_BUS_UP_IN msgrequest */
#define MC_CMD_CDX_BUS_UP_IN_LEN			4
/* Bus number to take out of reset, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_BUS_UP_IN_BUS_OFST		0
#define MC_CMD_CDX_BUS_UP_IN_BUS_LEN		4

/* MC_CMD_CDX_BUS_UP_OUT msgresponse: The bus can now be enumerated. */
#define MC_CMD_CDX_BUS_UP_OUT_LEN			0

/***********************************/
/*
 * MC_CMD_CDX_DEVICE_RESET
+24 −0
Original line number Diff line number Diff line
@@ -124,6 +124,30 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
	return 0;
}

int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_UP_IN_LEN);
	int ret;

	MCDI_SET_DWORD(inbuf, CDX_BUS_UP_IN_BUS, bus_num);
	ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_UP, inbuf, sizeof(inbuf),
			   NULL, 0, NULL);

	return ret;
}

int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_DOWN_IN_LEN);
	int ret;

	MCDI_SET_DWORD(inbuf, CDX_BUS_DOWN_IN_BUS, bus_num);
	ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_DOWN, inbuf, sizeof(inbuf),
			   NULL, 0, NULL);

	return ret;
}

int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num)
{
	MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN);
Loading