meta-gsj: phosphor-pid-control: Add fan control algorithm

This change adds fan control function with following features.

1) Fans will be at full speed while rebooting or a sensor is not ready.
2) Detect different skus automatically.

(From meta-quanta rev: bf5dffe65b849f9548f75b3a18d05ef7e899c4a3)

Signed-off-by: Hank Liou <Hank.Liou@quantatw.com>
Change-Id: I9667e832109d6a06b6e4f6410de0df97481fd3fb
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/config-2ssd.json b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/config-2ssd.json
new file mode 100644
index 0000000..a4552bc
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/config-2ssd.json
@@ -0,0 +1,412 @@
+{
+    "sensors" : [
+        {
+            "name": "Fan0_0_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan0_0_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm1",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan0_1_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan0_1_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm1",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan1_0_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan1_0_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm2",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan1_1_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan1_1_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm2",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan2_0_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan2_0_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm3",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan2_1_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan2_1_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm3",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "KL0",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/KL0",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme0",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme0",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme1",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme1",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        }
+    ],
+    "zones" : [
+        {
+            "id": 0,
+            "minThermalOutput": 0.0,
+            "failsafePercent": 100.0,
+            "pids": [
+                {
+                    "name": "Fan0_0_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan0_0_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan0_1_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan0_1_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan1_0_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan1_0_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan1_1_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan1_1_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan2_0_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan2_0_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan2_1_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan2_1_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "nvme0",
+                    "type": "temp",
+                    "inputs": ["nvme0"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme1",
+                    "type": "temp",
+                    "inputs": ["nvme1"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "KL0",
+                    "type": "stepwise",
+                    "inputs": ["KL0"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 0.0,
+                        "negativeHysteresis": 0.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 25,
+                            "1": 26,
+                            "2": 27,
+                            "3": 28,
+                            "4": 29,
+                            "5": 30,
+                            "6": 31,
+                            "7": 32,
+                            "8": 33,
+                            "9": 34,
+                            "10": 35,
+                            "11": 36,
+                            "12": 37,
+                            "13": 38,
+                            "14": 39,
+                            "15": 40,
+                            "16": 42,
+                            "17": 44,
+                            "18": 45,
+                            "19": 50
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 10,
+                            "3": 10,
+                            "4": 10,
+                            "5": 10,
+                            "6": 20,
+                            "7": 30,
+                            "8": 40,
+                            "9": 50,
+                            "10": 60,
+                            "11": 73,
+                            "12": 76,
+                            "13": 79,
+                            "14": 82,
+                            "15": 86,
+                            "16": 90,
+                            "17": 90,
+                            "18": 100,
+                            "19": 100
+                        }
+                    }
+                },
+                {
+                    "name": "nvme00",
+                    "type": "stepwise",
+                    "inputs": ["nvme0"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 5,
+                            "1": 5,
+                            "2": 5,
+                            "3": 6,
+                            "4": 6,
+                            "5": 7,
+                            "6": 7,
+                            "7": 8,
+                            "8": 10,
+                            "9": 12,
+                            "10": 14,
+                            "11": 16,
+                            "12": 18,
+                            "13": 20,
+                            "14": 21,
+                            "15": 21,
+                            "16": 22,
+                            "17": 22,
+                            "18": 25,
+                            "19": 25
+                        }
+                    }
+                },
+                {
+                    "name": "nvme10",
+                    "type": "stepwise",
+                    "inputs": ["nvme1"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 5,
+                            "1": 5,
+                            "2": 5,
+                            "3": 6,
+                            "4": 6,
+                            "5": 7,
+                            "6": 7,
+                            "7": 8,
+                            "8": 10,
+                            "9": 12,
+                            "10": 14,
+                            "11": 16,
+                            "12": 18,
+                            "13": 20,
+                            "14": 21,
+                            "15": 21,
+                            "16": 22,
+                            "17": 22,
+                            "18": 25,
+                            "19": 25
+                        }
+                    }
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/config-8ssd.json b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/config-8ssd.json
new file mode 100644
index 0000000..7cfa861
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/config-8ssd.json
@@ -0,0 +1,928 @@
+{
+    "sensors" : [
+        {
+            "name": "Fan0_0_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan0_0_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm1",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan0_1_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan0_1_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm1",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan1_0_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan1_0_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm2",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan1_1_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan1_1_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm2",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan2_0_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan2_0_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm3",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "Fan2_1_RPM",
+            "type": "fan",
+            "readPath": "/xyz/openbmc_project/sensors/fan_tach/Fan2_1_RPM",
+            "writePath": "/sys/devices/platform/ahb/ahb:apb/f0103000.pwm-fan-controller/hwmon/**/pwm3",
+            "min": 0,
+            "max": 255
+        },
+        {
+            "name": "KL0",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/KL0",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme0",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme0",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme1",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme1",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme2",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme2",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme3",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme3",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme4",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme4",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme5",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme5",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme6",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme6",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        },
+        {
+            "name": "nvme7",
+            "type": "temp",
+            "readPath": "/xyz/openbmc_project/sensors/temperature/nvme7",
+            "writePath": "",
+            "min": 0,
+            "max": 0,
+            "timeout": 0
+        }
+    ],
+    "zones" : [
+        {
+            "id": 0,
+            "minThermalOutput": 0.0,
+            "failsafePercent": 100.0,
+            "pids": [
+                {
+                    "name": "Fan0_0_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan0_0_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan0_1_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan0_1_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan1_0_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan1_0_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan1_1_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan1_1_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan2_0_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan2_0_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "Fan2_1_RPM",
+                    "type": "fan",
+                    "inputs": ["Fan2_1_RPM"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": 0.0,
+                        "integralCoeff": 0.0,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 1.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 3.0,
+                        "outLim_max": 100.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0
+                    }
+                },
+                {
+                    "name": "nvme0",
+                    "type": "temp",
+                    "inputs": ["nvme0"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme1",
+                    "type": "temp",
+                    "inputs": ["nvme1"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme2",
+                    "type": "temp",
+                    "inputs": ["nvme2"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme3",
+                    "type": "temp",
+                    "inputs": ["nvme3"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme4",
+                    "type": "temp",
+                    "inputs": ["nvme4"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme5",
+                    "type": "temp",
+                    "inputs": ["nvme5"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme6",
+                    "type": "temp",
+                    "inputs": ["nvme6"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "nvme7",
+                    "type": "temp",
+                    "inputs": ["nvme7"],
+                    "setpoint": 65.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "proportionalCoeff": -20.0,
+                        "integralCoeff": -0.1,
+                        "feedFwdOffsetCoeff": 0.0,
+                        "feedFwdGainCoeff": 0.0,
+                        "integralLimit_min": 0.0,
+                        "integralLimit_max": 0.0,
+                        "outLim_min": 0.0,
+                        "outLim_max": 16000.0,
+                        "slewNeg": 0.0,
+                        "slewPos": 0.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0
+                    }
+                },
+                {
+                    "name": "KL0",
+                    "type": "stepwise",
+                    "inputs": ["KL0"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 0.0,
+                        "negativeHysteresis": 0.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 25,
+                            "1": 26,
+                            "2": 27,
+                            "3": 28,
+                            "4": 29,
+                            "5": 30,
+                            "6": 31,
+                            "7": 32,
+                            "8": 33,
+                            "9": 34,
+                            "10": 35,
+                            "11": 36,
+                            "12": 37,
+                            "13": 38,
+                            "14": 39,
+                            "15": 40,
+                            "16": 42,
+                            "17": 44,
+                            "18": 45,
+                            "19": 50
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 10,
+                            "3": 10,
+                            "4": 10,
+                            "5": 10,
+                            "6": 20,
+                            "7": 30,
+                            "8": 40,
+                            "9": 50,
+                            "10": 60,
+                            "11": 73,
+                            "12": 76,
+                            "13": 79,
+                            "14": 82,
+                            "15": 86,
+                            "16": 90,
+                            "17": 90,
+                            "18": 100,
+                            "19": 100
+                        }
+                    }
+                },
+                {
+                    "name": "nvme00",
+                    "type": "stepwise",
+                    "inputs": ["nvme0"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme10",
+                    "type": "stepwise",
+                    "inputs": ["nvme1"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme20",
+                    "type": "stepwise",
+                    "inputs": ["nvme2"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme30",
+                    "type": "stepwise",
+                    "inputs": ["nvme3"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme40",
+                    "type": "stepwise",
+                    "inputs": ["nvme4"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme50",
+                    "type": "stepwise",
+                    "inputs": ["nvme5"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme60",
+                    "type": "stepwise",
+                    "inputs": ["nvme6"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                },
+                {
+                    "name": "nvme70",
+                    "type": "stepwise",
+                    "inputs": ["nvme7"],
+                    "setpoint": 30.0,
+                    "pid": {
+                        "samplePeriod": 1.0,
+                        "positiveHysteresis": 1.0,
+                        "negativeHysteresis": 1.0,
+                        "isCeiling": false,
+                        "reading": {
+                            "0": 45,
+                            "1": 46,
+                            "2": 47,
+                            "3": 48,
+                            "4": 49,
+                            "5": 50,
+                            "6": 51,
+                            "7": 52,
+                            "8": 53,
+                            "9": 54,
+                            "10": 55,
+                            "11": 56,
+                            "12": 57,
+                            "13": 58,
+                            "14": 59,
+                            "15": 60,
+                            "16": 61,
+                            "17": 62,
+                            "18": 63,
+                            "19": 64
+                        },
+                        "output": {
+                            "0": 10,
+                            "1": 10,
+                            "2": 13,
+                            "3": 15,
+                            "4": 18,
+                            "5": 21,
+                            "6": 24,
+                            "7": 26,
+                            "8": 28,
+                            "9": 30,
+                            "10": 32,
+                            "11": 34,
+                            "12": 36,
+                            "13": 38,
+                            "14": 40,
+                            "15": 41,
+                            "16": 42,
+                            "17": 43,
+                            "18": 44,
+                            "19": 45
+                        }
+                    }
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-control.sh b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-control.sh
new file mode 100644
index 0000000..3c84ce4
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-control.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+NVME_GPIO_NUM=( 148 149 150 151 152 153 154 155 )
+NVME=( 1 1 1 1 1 1 1 1 )
+FAN_TABLE_PATH="/usr/share/swampd/config.json"
+FAN_TABLE=( "/usr/share/swampd/config-8ssd.json" "/usr/share/swampd/config-2ssd.json" )
+TYPE=-1
+
+# get nvme presence
+for i in {0..7}
+do
+  gpioNum=${NVME_GPIO_NUM[$i]}
+  NVME[$i]=$(cat /sys/class/gpio/gpio$gpioNum/value)
+done
+
+# distinguish between 8-ssd and 2-ssd sku
+for i in {2..7}
+do
+  if [ ${NVME[$i]} -eq 0 ]; then
+    TYPE=0
+  fi
+done
+if [ $TYPE -eq -1 ]; then
+  for i in {0..1}
+  do
+    if [ ${NVME[$i]} -eq 0 ]; then
+      TYPE=1
+    fi
+  done
+fi
+
+if [ $TYPE -eq 1 ]; then
+  cp ${FAN_TABLE[1]} $FAN_TABLE_PATH
+else
+  cp ${FAN_TABLE[0]} $FAN_TABLE_PATH
+fi
+
+# start pid control
+/usr/bin/swampd
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-full-speed.sh b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-full-speed.sh
new file mode 100644
index 0000000..dd411d2
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-full-speed.sh
@@ -0,0 +1,5 @@
+#!bin/bash
+
+echo 255 > /sys/class/hwmon/*/pwm1
+echo 255 > /sys/class/hwmon/*/pwm2
+echo 255 > /sys/class/hwmon/*/pwm3
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-reboot-control.service b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-reboot-control.service
new file mode 100644
index 0000000..4396d18
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/fan-reboot-control.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Set Fan to Full Speed as Rebooting
+DefaultDependencies=no
+After=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=true
+ExecStart=/usr/bin/fan-full-speed.sh
+
+[Install]
+WantedBy=shutdown.target
\ No newline at end of file
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service
new file mode 100644
index 0000000..135e673
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control/phosphor-pid-control.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=OpenBMC Fan Control Daemon
+After=xyz.openbmc_project.nvme.manager.service
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/fan-control.sh
+Restart=always
+RestartSec=5
+StartLimitInterval=0
+ExecStopPost=/usr/bin/fan-full-speed.sh
+
+[Install]
+WantedBy=basic.target
\ No newline at end of file
diff --git a/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control_%.bbappend b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control_%.bbappend
new file mode 100644
index 0000000..68d6dd4
--- /dev/null
+++ b/meta-quanta/meta-gsj/recipes-phosphor/fans/phosphor-pid-control_%.bbappend
@@ -0,0 +1,36 @@
+FILESEXTRAPATHS_prepend_gsj := "${THISDIR}/${PN}:"
+SRC_URI_append_gsj = " file://config-8ssd.json"
+SRC_URI_append_gsj = " file://config-2ssd.json"
+SRC_URI_append_gsj = " file://fan-control.sh"
+SRC_URI_append_gsj = " file://fan-full-speed.sh"
+SRC_URI_append_gsj = " file://phosphor-pid-control.service"
+SRC_URI_append_gsj = " file://fan-reboot-control.service"
+
+FILES_${PN}_append_gsj = " ${datadir}/swampd/config-8ssd.json"
+FILES_${PN}_append_gsj = " ${datadir}/swampd/config-2ssd.json"
+FILES_${PN}_append_gsj = " ${bindir}/fan-control.sh"
+FILES_${PN}_append_gsj = " ${bindir}/fan-full-speed.sh"
+
+inherit systemd
+RDEPENDS_${PN} += "bash"
+
+SYSTEMD_SERVICE_${PN}_append_gsj = " phosphor-pid-control.service"
+SYSTEMD_SERVICE_${PN}_append_gsj = " fan-reboot-control.service"
+
+do_install_append_gsj() {
+    install -d ${D}/${bindir}
+    install -m 0755 ${WORKDIR}/fan-control.sh ${D}/${bindir}
+    install -m 0755 ${WORKDIR}/fan-full-speed.sh ${D}/${bindir}
+
+    install -d ${D}${datadir}/swampd
+    install -m 0644 -D ${WORKDIR}/config-8ssd.json \
+        ${D}${datadir}/swampd/config-8ssd.json
+    install -m 0644 -D ${WORKDIR}/config-2ssd.json \
+        ${D}${datadir}/swampd/config-2ssd.json
+
+    install -d ${D}${systemd_unitdir}/system/
+    install -m 0644 ${WORKDIR}/phosphor-pid-control.service \
+        ${D}${systemd_unitdir}/system
+    install -m 0644 ${WORKDIR}/fan-reboot-control.service \
+        ${D}${systemd_unitdir}/system
+}
\ No newline at end of file