# echo 0x1234ABCD > /sys/devices/platform/firmware\:zynqmp-firmware/pggs0
 
 Users:         Xilinx
+
+What:          /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
+Date:          March 2020
+KernelVersion: 5.6
+Contact:       "Jolly Shah" <jollys@xilinx.com>
+Description:
+               This sysfs interface allows to set the shutdown scope for the
+               next shutdown request. When the next shutdown is performed, the
+               platform specific portion of PSCI-system_off can use the chosen
+               shutdown scope.
+
+               Following are available shutdown scopes(subtypes):
+
+               subsystem:      Only the APU along with all of its peripherals
+                               not used by other processing units will be
+                               shut down. This may result in the FPD power
+                               domain being shut down provided that no other
+                               processing unit uses FPD peripherals or DRAM.
+               ps_only:        The complete PS will be shut down, including the
+                               RPU, PMU, etc.  Only the PL domain (FPGA)
+                               remains untouched.
+               system:         The complete system/device is shut down.
+
+               Usage:
+               # cat /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
+               # echo <scope> > /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
+
+               Example:
+               # cat /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
+               # echo "subsystem" > /sys/devices/platform/firmware\:zynqmp-firmware/shutdown_scope
+
+Users:         Xilinx
 
                                   0, 0, NULL);
 }
 
+/**
+ * struct zynqmp_pm_shutdown_scope - Struct for shutdown scope
+ * @subtype:   Shutdown subtype
+ * @name:      Matching string for scope argument
+ *
+ * This struct encapsulates mapping between shutdown scope ID and string.
+ */
+struct zynqmp_pm_shutdown_scope {
+       const enum zynqmp_pm_shutdown_subtype subtype;
+       const char *name;
+};
+
+static struct zynqmp_pm_shutdown_scope shutdown_scopes[] = {
+       [ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM] = {
+               .subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM,
+               .name = "subsystem",
+       },
+       [ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY] = {
+               .subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY,
+               .name = "ps_only",
+       },
+       [ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM] = {
+               .subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM,
+               .name = "system",
+       },
+};
+
+static struct zynqmp_pm_shutdown_scope *selected_scope =
+               &shutdown_scopes[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM];
+
+/**
+ * zynqmp_pm_is_shutdown_scope_valid - Check if shutdown scope string is valid
+ * @scope_string:      Shutdown scope string
+ *
+ * Return:             Return pointer to matching shutdown scope struct from
+ *                     array of available options in system if string is valid,
+ *                     otherwise returns NULL.
+ */
+static struct zynqmp_pm_shutdown_scope*
+               zynqmp_pm_is_shutdown_scope_valid(const char *scope_string)
+{
+       int count;
+
+       for (count = 0; count < ARRAY_SIZE(shutdown_scopes); count++)
+               if (sysfs_streq(scope_string, shutdown_scopes[count].name))
+                       return &shutdown_scopes[count];
+
+       return NULL;
+}
+
+static ssize_t shutdown_scope_show(struct device *device,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(shutdown_scopes); i++) {
+               if (&shutdown_scopes[i] == selected_scope) {
+                       strcat(buf, "[");
+                       strcat(buf, shutdown_scopes[i].name);
+                       strcat(buf, "]");
+               } else {
+                       strcat(buf, shutdown_scopes[i].name);
+               }
+               strcat(buf, " ");
+       }
+       strcat(buf, "\n");
+
+       return strlen(buf);
+}
+
+static ssize_t shutdown_scope_store(struct device *device,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       int ret;
+       struct zynqmp_pm_shutdown_scope *scope;
+
+       scope = zynqmp_pm_is_shutdown_scope_valid(buf);
+       if (!scope)
+               return -EINVAL;
+
+       ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,
+                                       scope->subtype);
+       if (ret) {
+               pr_err("unable to set shutdown scope %s\n", buf);
+               return ret;
+       }
+
+       selected_scope = scope;
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(shutdown_scope);
+
 static ssize_t ggs_show(struct device *device,
                        struct device_attribute *attr,
                        char *buf,
        ret = zynqmp_pm_write_ggs(reg, value);
        if (ret)
                count = -EFAULT;
-
 err:
        return count;
 }
        &dev_attr_pggs1.attr,
        &dev_attr_pggs2.attr,
        &dev_attr_pggs3.attr,
+       &dev_attr_shutdown_scope.attr,
        NULL,
 };
 
 
        PM_DLL_RESET_PULSE,
 };
 
+enum zynqmp_pm_shutdown_type {
+       ZYNQMP_PM_SHUTDOWN_TYPE_SHUTDOWN,
+       ZYNQMP_PM_SHUTDOWN_TYPE_RESET,
+       ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,
+};
+
+enum zynqmp_pm_shutdown_subtype {
+       ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM,
+       ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY,
+       ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM,
+};
+
 /**
  * struct zynqmp_pm_query_data - PM query data
  * @qid:       query ID