On Tue, Apr 29, 2025 at 09:39:56PM +0530, Badal Nilawar wrote: > From: Rodrigo Vivi <rodrigo.v...@intel.com> > > Xe PCODE FWCTL implements the generic FWCTL IOCLTs to allow limited > access from user space (as admin) to some very specific PCODE > Mailboxes only related to hardware configuration. > > PCODE is a Firmware in Intel GPUs which is the main responsible > component for power and thermal aspects of the Intel GPUs. > > Each different Intel GPU came with different PCODE versions with > different mailboxes and different needs. In the lack of an unified > interface, the per platform sysfs entries at the device level is > trending to grow, to allow admins to control different aspects of > the Hardware. > > In this first experiment, xe_pcode_fwctl only adds support for the > Battlemage late-binding firmware information. > > Late-binding is the name given to 2 other new auxiliary firmware > blobs that for now lives in the Flash like PCODE, but that soon > it is coming to linux-firmware.git: Fan-controller and > Voltage-regulator. Then, PCODE provides some mailboxes where the > status of both late-binding firmware can be queried as specified > in the documentation that is added along with the new uAPI here. > > RFC IMPORTANT NOTE: > =================== > Admins will need to query this information. This code here aims > to be used by Level0-Sysman and/or Intel XPU Manager directly > from user space. But following the drm upstream rules, the > userspace code will need to be ready before we can consider > getting this patch merged! > > Signed-off-by: Rodrigo Vivi <rodrigo.v...@intel.com> > Signed-off-by: Badal Nilawar <badal.nila...@intel.com> > --- > Documentation/userspace-api/fwctl/index.rst | 1 + > drivers/gpu/drm/xe/Kconfig | 1 + > drivers/gpu/drm/xe/Makefile | 1 + > drivers/gpu/drm/xe/xe_pci.c | 5 + > drivers/gpu/drm/xe/xe_pcode_fwctl.c | 218 ++++++++++++++++++++ > drivers/gpu/drm/xe/xe_pcode_fwctl.h | 13 ++ > include/uapi/fwctl/fwctl.h | 1 + > include/uapi/fwctl/xe_pcode.h | 80 +++++++ > 8 files changed, 320 insertions(+) > create mode 100644 drivers/gpu/drm/xe/xe_pcode_fwctl.c > create mode 100644 drivers/gpu/drm/xe/xe_pcode_fwctl.h > create mode 100644 include/uapi/fwctl/xe_pcode.h > > diff --git a/Documentation/userspace-api/fwctl/index.rst > b/Documentation/userspace-api/fwctl/index.rst > index 316ac456ad3b..186f8cf17583 100644 > --- a/Documentation/userspace-api/fwctl/index.rst > +++ b/Documentation/userspace-api/fwctl/index.rst > @@ -12,3 +12,4 @@ to securely construct and execute RPCs inside device > firmware. > fwctl > fwctl-cxl > pds_fwctl > + xe_pcode_fwctl
I just noticed that I forgot to actually include this file and add to the MAINTAINERS list under xe. But the important part which is the documentation itself is inside the include/uapi/fwctl/xe_pcode.h For the record, the missing file is: $ cat Documentation/userspace-api/fwctl/xe_pcode_fwctl.rst .. SPDX-License-Identifier: (GPL-2.0+ OR MIT) ================== fwctl drm/xe pcode ================== .. kernel-doc:: drivers/gpu/drm/xe/xe_pcode_fwctl.c :doc: XE PCODE FWCTL uAPI ==== .. kernel-doc:: include/uapi/fwctl/xe_pcode.h :internal: .. kernel-doc:: include/uapi/fwctl/xe_pcode.h :doc: Late Binding Commands Also cc'ing FWCTL and DRM maintainers and LKML to ensure we get the proper feedback on this first attempt of using the fwctl with xe. Thanks, Rodrigo. > diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig > index a8cc1876a24f..ee77039b9256 100644 > --- a/drivers/gpu/drm/xe/Kconfig > +++ b/drivers/gpu/drm/xe/Kconfig > @@ -45,6 +45,7 @@ config DRM_XE > select AUXILIARY_BUS > select HMM_MIRROR > select INTEL_MEI_LATE_BIND > + select FWCTL > help > Experimental driver for Intel Xe series GPUs > > diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile > index 6de291a21965..c1f3b2e2da5f 100644 > --- a/drivers/gpu/drm/xe/Makefile > +++ b/drivers/gpu/drm/xe/Makefile > @@ -86,6 +86,7 @@ xe-y += xe_bb.o \ > xe_pat.o \ > xe_pci.o \ > xe_pcode.o \ > + xe_pcode_fwctl.o \ > xe_pm.o \ > xe_preempt_fence.o \ > xe_pt.o \ > diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c > index 882398e09b7e..222e75c7427e 100644 > --- a/drivers/gpu/drm/xe/xe_pci.c > +++ b/drivers/gpu/drm/xe/xe_pci.c > @@ -27,6 +27,7 @@ > #include "xe_module.h" > #include "xe_pci_sriov.h" > #include "xe_pci_types.h" > +#include "xe_pcode_fwctl.h" > #include "xe_pm.h" > #include "xe_sriov.h" > #include "xe_step.h" > @@ -868,6 +869,10 @@ static int xe_pci_probe(struct pci_dev *pdev, const > struct pci_device_id *ent) > if (err) > goto err_driver_cleanup; > > + err = xe_pcode_fwctl_init(xe); > + if (err) > + goto err_driver_cleanup; > + > drm_dbg(&xe->drm, "d3cold: capable=%s\n", > str_yes_no(xe->d3cold.capable)); > > diff --git a/drivers/gpu/drm/xe/xe_pcode_fwctl.c > b/drivers/gpu/drm/xe/xe_pcode_fwctl.c > new file mode 100644 > index 000000000000..d6443aa4a60a > --- /dev/null > +++ b/drivers/gpu/drm/xe/xe_pcode_fwctl.c > @@ -0,0 +1,218 @@ > +// SPDX-License-Identifier: MIT > +/* > + * Copyright © 2025 Intel Corporation > + */ > + > +#include "xe_pcode_fwctl.h" > + > +#include <linux/fwctl.h> > +#include <uapi/fwctl/xe_pcode.h> > + > +#include "xe_device.h" > +#include "xe_pcode_api.h" > +#include "xe_pcode.h" > +#include "xe_pm.h" > + > +/** > + * DOC: XE PCODE FWCTL > + * > + * Xe PCODE FWCTL implements the generic FWCTL IOCLTs to allow limited access > + * from user space (as admin) to some very specific PCODE Mailboxes. > + * > + * User space first needs to issue the ```FWCTL_INFO``` ioctl and check for > the > + * capability flag, which will indicate which group of Mailboxes commands are > + * supported on that current running firmware. > + * > + * After verifying the availability of the desired Mailbox command, > + * ```FWCTL_RPC``` needs to be issued with in and out parameter both using > + * pointers to a ```struct fwctl_rpc_xe_pcode``` allocated by userspace. > + * In and out length needs to be sizeof(struct fwctl_rpc_xe_pcode). > + * > + * Any command that is not listed in the include/uapi/fwctl/xe_pcode.h or not > + * supported by the running firmware, will return ERR_PTR(-EBADMSG). > + * > + * Example: > + * > + * .. code-block:: C > + * > + * struct fwctl_info_xe_pcode xe_pcode_info; > + * > + * struct fwctl_info info = { > + * .size = sizeof(struct fwctl_info), > + * .flags = 0, > + * .out_device_type = 0, > + * .device_data_len = sizeof(struct fwctl_info_xe_pcode), > + * .out_device_data = (__aligned_u64) &xe_pcode_info, > + * }; > + * > + * fd = open("/dev/fwctl/fwctl0", O_RDWR); > + * if (fd < 0) { > + * perror("Failed to open /dev/fwctl/fwctl0"); > + * return -1; > + * } > + * > + * if (ioctl(fd, FWCTL_INFO, &info)) { > + * perror("ioctl(FWCTL_INFO) failed"); > + * close(fd); > + * return -1; > + * } > + * > + * if (xe_pcode_info.uctx_caps & FWCTL_XE_PCODE_LATEBINDING) { > + * struct fwctl_rpc_xe_pcode rpc_in = { > + * .command = PCODE_CMD_LATE_BINDING, > + * .param1 = PARAM1_GET_CAPABILITY_STATUS, > + * }; > + * > + * struct fwctl_rpc_xe_pcode rpc_out = {0}; > + * > + * struct fwctl_rpc rpc = { > + * .size = sizeof(struct fwctl_rpc), > + * .scope = FWCTL_RPC_CONFIGURATION, > + * .in_len = sizeof(struct fwctl_rpc_xe_pcode), > + * .out_len = sizeof(struct fwctl_rpc_xe_pcode), > + * .in = (__aligned_u64) &rpc_in, > + * .out = (__aligned_u64) &rpc_out, > + * }; > + * > + * if (ioctl(fd, FWCTL_RPC, &rpc)) { > + * perror("ioctl(FWCTL_RPC) failed"); > + * close(fd); > + * return -1; > + * } > + * > + */ > + > +struct xe_pcode_fwctl_dev { > + struct fwctl_device fwctl; > + struct xe_device *xe; > +}; > + > +DEFINE_FREE(xe_pcode_fwctl, struct xe_pcode_fwctl_dev *, if (_T) > fwctl_put(&_T->fwctl)) > + > +static int xe_pcode_fwctl_uctx_open(struct fwctl_uctx *uctx) > +{ > + struct xe_pcode_fwctl_dev *fwctl_dev = > + container_of(uctx->fwctl, struct xe_pcode_fwctl_dev, fwctl); > + struct xe_device *xe = fwctl_dev->xe; > + > + xe_pm_runtime_get(xe); > + > + return 0; > +} > + > +static void xe_pcode_fwctl_uctx_close(struct fwctl_uctx *uctx) > +{ > + struct xe_pcode_fwctl_dev *fwctl_dev = > + container_of(uctx->fwctl, struct xe_pcode_fwctl_dev, fwctl); > + struct xe_device *xe = fwctl_dev->xe; > + > + xe_pm_runtime_put(xe); > +} > + > +static void *xe_pcode_fwctl_info(struct fwctl_uctx *uctx, size_t *length) > +{ > + struct xe_pcode_fwctl_dev *fwctl_dev = > + container_of(uctx->fwctl, struct xe_pcode_fwctl_dev, fwctl); > + struct xe_device *xe = fwctl_dev->xe; > + struct fwctl_info_xe_pcode *info; > + > + info = kzalloc(sizeof(*info), GFP_KERNEL); > + if (!info) > + return ERR_PTR(-ENOMEM); > + > + if (xe->info.platform == XE_BATTLEMAGE) > + info->uctx_caps = FWCTL_XE_PCODE_LATEBINDING; > + > + *length = sizeof(*info); > + > + return info; > +} > + > +static bool xe_pcode_fwctl_rpc_validate(struct fwctl_rpc_xe_pcode *rpc, > + enum fwctl_rpc_scope scope) > +{ > + u32 mbox = PCODE_MBOX(rpc->command, rpc->param1, rpc->param2); > + > + if (mbox == PCODE_MBOX(PCODE_CMD_LATE_BINDING, > + PARAM1_GET_CAPABILITY_STATUS, 0)) > + return scope == FWCTL_RPC_CONFIGURATION; > + > + if (mbox == PCODE_MBOX(PCODE_CMD_LATE_BINDING, > + PARAM1_GET_VERSION_LOW, 0)) > + return (rpc->data0 == DATA0_TYPE_FAN_CONTROLLER || > + rpc->data0 == DATA0_TYPE_VOLTAGE_REGULATOR) && > + scope == FWCTL_RPC_CONFIGURATION; > + > + return false; > +} > + > +static void *xe_pcode_fwctl_rpc(struct fwctl_uctx *uctx, > + enum fwctl_rpc_scope scope, > + void *in, size_t in_len, size_t *out_len) > +{ > + struct xe_pcode_fwctl_dev *fwctl_dev = > + container_of(uctx->fwctl, struct xe_pcode_fwctl_dev, fwctl); > + struct xe_tile *root_tile = xe_device_get_root_tile(fwctl_dev->xe); > + struct fwctl_rpc_xe_pcode *rpc = in; > + int err; > + > + if (in_len != sizeof(struct fwctl_rpc_xe_pcode) || > + *out_len != sizeof(struct fwctl_rpc_xe_pcode)) > + return ERR_PTR(-EMSGSIZE); > + > + if (!xe_pcode_fwctl_rpc_validate(rpc, scope)) > + return ERR_PTR(-EBADMSG); > + > + err = xe_pcode_read(root_tile, PCODE_MBOX(rpc->command, > + rpc->param1, > + rpc->param2), > + &rpc->data0, > + &rpc->data1); > + if (err) > + return ERR_PTR(err); > + > + return rpc; > +} > + > +static const struct fwctl_ops xe_pcode_fwctl_ops = { > + .device_type = FWCTL_DEVICE_TYPE_XE_PCODE, > + .uctx_size = sizeof(struct fwctl_uctx), > + .open_uctx = xe_pcode_fwctl_uctx_open, > + .close_uctx = xe_pcode_fwctl_uctx_close, > + .info = xe_pcode_fwctl_info, > + .fw_rpc = xe_pcode_fwctl_rpc, > +}; > + > +static void xe_pcode_fwctl_fini(void *dev) > +{ > + struct fwctl_device *fwctl = dev; > + > + fwctl_unregister(fwctl); > + fwctl_put(fwctl); > +} > + > +int xe_pcode_fwctl_init(struct xe_device *xe) > +{ > + struct xe_pcode_fwctl_dev *fwctl_dev __free(xe_pcode_fwctl) = > + fwctl_alloc_device(xe->drm.dev, &xe_pcode_fwctl_ops, > + struct xe_pcode_fwctl_dev, fwctl); > + int err; > + > + /* For now xe_pcode_fwctl supports only Late-Binding commands on BMG */ > + if (xe->info.platform != XE_BATTLEMAGE) > + return -ENODEV; > + > + if (!fwctl_dev) > + return -ENOMEM; > + > + fwctl_dev->xe = xe; > + > + err = fwctl_register(&fwctl_dev->fwctl); > + if (err) > + return err; > + > + return devm_add_action_or_reset(xe->drm.dev, xe_pcode_fwctl_fini, > + &fwctl_dev->fwctl); > +} > + > +MODULE_IMPORT_NS("FWCTL"); > diff --git a/drivers/gpu/drm/xe/xe_pcode_fwctl.h > b/drivers/gpu/drm/xe/xe_pcode_fwctl.h > new file mode 100644 > index 000000000000..67386d7bf2ea > --- /dev/null > +++ b/drivers/gpu/drm/xe/xe_pcode_fwctl.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: MIT */ > +/* > + * Copyright © 2025 Intel Corporation > + */ > + > +#ifndef _XE_PCODE_FWCTL_H_ > +#define _XE_PCODE_FWCTL_H_ > + > +struct xe_device; > + > +int xe_pcode_fwctl_init(struct xe_device *xe); > + > +#endif > diff --git a/include/uapi/fwctl/fwctl.h b/include/uapi/fwctl/fwctl.h > index 716ac0eee42d..9e7e84aef791 100644 > --- a/include/uapi/fwctl/fwctl.h > +++ b/include/uapi/fwctl/fwctl.h > @@ -45,6 +45,7 @@ enum fwctl_device_type { > FWCTL_DEVICE_TYPE_MLX5 = 1, > FWCTL_DEVICE_TYPE_CXL = 2, > FWCTL_DEVICE_TYPE_PDS = 4, > + FWCTL_DEVICE_TYPE_XE_PCODE = 5, > }; > > /** > diff --git a/include/uapi/fwctl/xe_pcode.h b/include/uapi/fwctl/xe_pcode.h > new file mode 100644 > index 000000000000..8df6db34e5ce > --- /dev/null > +++ b/include/uapi/fwctl/xe_pcode.h > @@ -0,0 +1,80 @@ > +/* SPDX-License-Identifier: MIT */ > +/* > + * Copyright © 2025 Intel Corporation > + */ > + > +#ifndef _UAPI_FWCTL_XE_PCODE_H_ > +#define _UAPI_FWCTL_XE_PCODE_H_ > + > +#include <linux/types.h> > + > +/** > + * struct fwctl_info_xe_pcode - FWCTL Information struct for Xe PCODE > + * > + * @uctx_caps: bitmap of available capabilities: > + * - %FWCTL_XE_PCODE_LATEBINDING - Command to configure Late Bind FW such as > + * Fan Controller and Voltage Regulator > + * @rsvd: Reserved for future usage or flags > + */ > +struct fwctl_info_xe_pcode { > + __u32 uctx_caps; > + __u32 rsvd[3]; > +}; > + > +#define FWCTL_XE_PCODE_LATEBINDING (1 << 0) > + > +/** > + * struct fwctl_rpc_xe_pcode - FWCTL Remote Procedure Calls for Xe PCODE > + */ > +struct fwctl_rpc_xe_pcode { > + /** @command: The main Mailbox command */ > + __u8 command; > + /** @param1: A subcommand or a parameter of the main command */ > + __u16 param1; > + /** @param2: A parameter of a subcommand or a subsubcommand */ > + __u16 param2; > + /** @data0: The first 32 bits of data. In general data-in as param */ > + __u32 data0; > + /** @data1: The other 32 bits of data. In general data-out */ > + __u32 data1; > + /** @pad: Padding the uAPI struct - Must be 0. Not sent to firmware */ > + __u8 pad[3]; > +}; > + > +/** > + * DOC: Late Binding Commands > + * > + * FWCTL info.uctx_caps: FWCTL_XE_PCODE_LATEBINDING > + * FWCTL rpc.scope: FWCTL_RPC_CONFIGURATION > + * > + * Command 0x5C - LATE_BINDING > + * Param1 0x0 - GET_CAPABILITY_STATUS > + * Param2 0 > + * Data in None > + * Data out: > + * > + * - Bit0: ate binding for V1 Fan Tables is supported. > + * - Bit3: Late binding for VR parameters. > + * - Bit16: Late binding done for V1 Fan tables > + * - Bit17: Late binding done for power co-efficients. > + * - Bit18: Late binding done for V2 Fan tables > + * - Bit19: Late binding done for VR Parameters > + * > + * Command 0x5C - LATE_BINDING > + * Param1 0x1 - GET_VERSION_LOW > + * Param2 0 > + * Data in - conveys the Type of the Late Binding Configuration: > + * > + * - FAN_CONTROLLER = 1 > + * - VOLTAGE_REGULATOR = 2 > + * > + * Data out - Lower 32 bits of Version Number for Late Binding configuration > + * that has been applied successfully. > + */ > +#define PCODE_CMD_LATE_BINDING 0x5C > +#define PARAM1_GET_CAPABILITY_STATUS 0x0 > +#define PARAM1_GET_VERSION_LOW 0x1 > +#define DATA0_TYPE_FAN_CONTROLLER 1 > +#define DATA0_TYPE_VOLTAGE_REGULATOR 2 > + > +#endif /* _UAPI_FWCTL_XE_PCODE_H_ */ > -- > 2.34.1 >