* Author: Gary R Hook <gary.hook@amd.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include "ccp-dev.h"
 #include "psp-dev.h"
 
+/* used for version string AA.BB.CC.DD */
+#define AA                             GENMASK(31, 24)
+#define BB                             GENMASK(23, 16)
+#define CC                             GENMASK(15, 8)
+#define DD                             GENMASK(7, 0)
+
 #define MSIX_VECTORS                   2
 
 struct sp_pci {
        .is_visible = psp_security_is_visible,
 };
 
+#define version_attribute_show(name, _offset)                                  \
+static ssize_t name##_show(struct device *d, struct device_attribute *attr,    \
+                          char *buf)                                           \
+{                                                                              \
+       struct sp_device *sp = dev_get_drvdata(d);                              \
+       struct psp_device *psp = sp->psp_data;                                  \
+       unsigned int val = ioread32(psp->io_regs + _offset);                    \
+       return sysfs_emit(buf, "%02lx.%02lx.%02lx.%02lx\n",                     \
+                         FIELD_GET(AA, val),                   \
+                         FIELD_GET(BB, val),                   \
+                         FIELD_GET(CC, val),                   \
+                         FIELD_GET(DD, val));                  \
+}
+
+version_attribute_show(bootloader_version, psp->vdata->bootloader_info_reg)
+static DEVICE_ATTR_RO(bootloader_version);
+version_attribute_show(tee_version, psp->vdata->tee->info_reg)
+static DEVICE_ATTR_RO(tee_version);
+
+static struct attribute *psp_firmware_attrs[] = {
+       &dev_attr_bootloader_version.attr,
+       &dev_attr_tee_version.attr,
+       NULL,
+};
+
+static umode_t psp_firmware_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct sp_device *sp = dev_get_drvdata(dev);
+       struct psp_device *psp = sp->psp_data;
+       unsigned int val = 0xffffffff;
+
+       if (!psp)
+               return 0;
+
+       if (attr == &dev_attr_bootloader_version.attr &&
+           psp->vdata->bootloader_info_reg)
+               val = ioread32(psp->io_regs + psp->vdata->bootloader_info_reg);
+
+       if (attr == &dev_attr_tee_version.attr &&
+           psp->capability & PSP_CAPABILITY_TEE &&
+           psp->vdata->tee->info_reg)
+               val = ioread32(psp->io_regs + psp->vdata->tee->info_reg);
+
+       /* If platform disallows accessing this register it will be all f's */
+       if (val != 0xffffffff)
+               return 0444;
+
+       return 0;
+}
+
+static struct attribute_group psp_firmware_attr_group = {
+       .attrs = psp_firmware_attrs,
+       .is_visible = psp_firmware_is_visible,
+};
+
 static const struct attribute_group *psp_groups[] = {
        &psp_security_attr_group,
+       &psp_firmware_attr_group,
        NULL,
 };