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: Nipun Gupta <nipun.gupta@amd.com> Signed-off-by: Nipun Gupta <nipun.gupta@amd.com> Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com> Link: https://lore.kernel.org/r/20231017160505.10640-6-abhijit.gangurde@amd.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
ce558a391d
commit
e3cfd49cb9
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -47,6 +47,24 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
|
||||
u8 bus_num, u8 dev_num,
|
||||
struct cdx_dev_params *dev_params);
|
||||
|
||||
/**
|
||||
* cdx_mcdi_bus_enable - Enable CDX bus represented by bus_num
|
||||
* @cdx: pointer to MCDI interface.
|
||||
* @bus_num: Bus number.
|
||||
*
|
||||
* Return: 0 on success, <0 on failure
|
||||
*/
|
||||
int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num);
|
||||
|
||||
/**
|
||||
* cdx_mcdi_bus_disable - Disable CDX bus represented by bus_num
|
||||
* @cdx: pointer to MCDI interface.
|
||||
* @bus_num: Bus number.
|
||||
*
|
||||
* Return: 0 on success, <0 on failure
|
||||
*/
|
||||
int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num);
|
||||
|
||||
/**
|
||||
* cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num
|
||||
* @cdx: pointer to MCDI interface.
|
||||
|
@ -28,6 +28,10 @@ struct cdx_device_config {
|
||||
u8 type;
|
||||
};
|
||||
|
||||
typedef int (*cdx_bus_enable_cb)(struct cdx_controller *cdx, u8 bus_num);
|
||||
|
||||
typedef int (*cdx_bus_disable_cb)(struct cdx_controller *cdx, u8 bus_num);
|
||||
|
||||
typedef int (*cdx_scan_cb)(struct cdx_controller *cdx);
|
||||
|
||||
typedef int (*cdx_dev_configure_cb)(struct cdx_controller *cdx,
|
||||
@ -49,11 +53,15 @@ typedef int (*cdx_dev_configure_cb)(struct cdx_controller *cdx,
|
||||
|
||||
/**
|
||||
* struct cdx_ops - Callbacks supported by CDX controller.
|
||||
* @bus_enable: enable bus on the controller
|
||||
* @bus_disable: disable bus on the controller
|
||||
* @scan: scan the devices on the controller
|
||||
* @dev_configure: configuration like reset, master_enable,
|
||||
* msi_config etc for a CDX device
|
||||
*/
|
||||
struct cdx_ops {
|
||||
cdx_bus_enable_cb bus_enable;
|
||||
cdx_bus_disable_cb bus_disable;
|
||||
cdx_scan_cb scan;
|
||||
cdx_dev_configure_cb dev_configure;
|
||||
};
|
||||
@ -89,6 +97,7 @@ struct cdx_controller {
|
||||
* @flags: CDX device flags
|
||||
* @req_id: Requestor ID associated with CDX device
|
||||
* @is_bus: Is this bus device
|
||||
* @enabled: is this bus enabled
|
||||
* @driver_override: driver name to force a match; do not set directly,
|
||||
* because core frees it; use driver_set_override() to
|
||||
* set or clear it.
|
||||
@ -106,6 +115,7 @@ struct cdx_device {
|
||||
u16 flags;
|
||||
u32 req_id;
|
||||
bool is_bus;
|
||||
bool enabled;
|
||||
const char *driver_override;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user