Compare commits

..

11 Commits

Author SHA1 Message Date
Andy Nguyen
cb839ab600 linux: set fan target temp to 80 celsius 2026-07-05 09:35:45 +02:00
Andy Nguyen
1926418e4e linux: move fan autoservo to fan.c 2026-07-05 09:34:17 +02:00
Andy Nguyen
c445e9adf3 linux: add spdx license identifier 2026-07-05 09:26:20 +02:00
Andy Nguyen
e81e281f38 config: enable CONFIG_MODULE_ALLOW_BTF_MISMATCH 2026-07-05 09:11:33 +02:00
Andy Nguyen
050704a2f5 config: update to 7.1.3 2026-07-05 09:10:35 +02:00
rmux
bf16f7e8b7 Add fan driver for PS5 (#25)
* linux: ps5: add fan driver

Thanks to cow for EMC RE and general help.

* ps5: remove redundant autoservo call in spcie
2026-07-04 21:22:16 +02:00
mia26MAjFm
8fbb1b2057 build-kernel: stash .deb + .pkg.tar.zst around the rpm run so all three survive (#26) 2026-07-04 20:12:56 +02:00
mia26MAjFm
935dc3e322 build-kernel: run --distro fedora after --distro all so .rpm ships (#23)
* build-kernel: release .rpm alongside .deb + .pkg.tar.zst

Now that ps5-linux-image's --distro all also produces an .rpm (paired
image PR), pick it up in the artifact upload, checksum list and
release files: block. Also add rpm as an explicit format choice so
someone can dispatch an rpm-only kernel build.

The sha256sum pipeline gets a 2>/dev/null so it doesn't complain
when an older ps5-linux-image checkout is used that only writes
deb + pkg.tar.zst.

* test: run on ubuntu-latest, point at paired ps5-linux-image branch, skip release on fork

* build-kernel: run --distro fedora after --distro all so .rpm ships

---------

Co-authored-by: Your Name <you@example.com>
2026-07-04 18:27:59 +02:00
mia26MAjFm
9ee33721e4 build-kernel: release .rpm alongside .deb + .pkg.tar.zst (#22)
Now that ps5-linux-image's --distro all also produces an .rpm (paired
image PR), pick it up in the artifact upload, checksum list and
release files: block. Also add rpm as an explicit format choice so
someone can dispatch an rpm-only kernel build.

The sha256sum pipeline gets a 2>/dev/null so it doesn't complain
when an older ps5-linux-image checkout is used that only writes
deb + pkg.tar.zst.

Co-authored-by: Your Name <you@example.com>
2026-07-04 08:56:04 +02:00
Andy Nguyen
a57d783d89 config: update to 7.1.2 2026-06-30 11:26:04 +02:00
Andy Nguyen
07f9d7f847 readme: remove todo for xhci 2026-06-30 09:18:59 +02:00
4 changed files with 505 additions and 214 deletions

14
.config
View File

@@ -1,6 +1,6 @@
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 7.1.1 Kernel Configuration
# Linux/x86 7.1.3 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0"
CONFIG_CC_IS_GCC=y
@@ -11,9 +11,9 @@ CONFIG_AS_VERSION=24600
CONFIG_LD_IS_BFD=y
CONFIG_LD_VERSION=24600
CONFIG_LLD_VERSION=0
CONFIG_RUSTC_VERSION=0
CONFIG_RUSTC_LLVM_VERSION=0
CONFIG_RUSTC_LLVM_MAJOR_VERSION=0
CONFIG_RUSTC_VERSION=109600
CONFIG_RUSTC_LLVM_VERSION=220102
CONFIG_RUSTC_LLVM_MAJOR_VERSION=22
CONFIG_CC_CAN_LINK=y
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
@@ -24,6 +24,10 @@ CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
CONFIG_CC_HAS_COUNTED_BY=y
CONFIG_CC_HAS_MULTIDIMENSIONAL_NONSTRING=y
CONFIG_LD_CAN_USE_KEEP_IN_OVERLAY=y
CONFIG_RUSTC_HAS_SPAN_FILE=y
CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES=y
CONFIG_RUSTC_HAS_FILE_WITH_NUL=y
CONFIG_RUSTC_HAS_FILE_AS_C_STR=y
CONFIG_PAHOLE_VERSION=131
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_TABLE_SORT=y
@@ -5637,7 +5641,7 @@ CONFIG_DEBUG_INFO_COMPRESSED_NONE=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_PAHOLE_HAS_LANG_EXCLUDE=y
CONFIG_DEBUG_INFO_BTF_MODULES=y
# CONFIG_MODULE_ALLOW_BTF_MISMATCH is not set
CONFIG_MODULE_ALLOW_BTF_MISMATCH=y
CONFIG_GDB_SCRIPTS=y
CONFIG_FRAME_WARN=2048
# CONFIG_STRIP_ASM_SYMS is not set

View File

@@ -8,7 +8,7 @@ on:
description: 'Package format'
default: 'all'
type: choice
options: [deb, arch, all]
options: [deb, arch, rpm, all]
patches_ref:
description: 'Patches branch/tag/SHA (empty = default)'
default: ''
@@ -43,6 +43,18 @@ jobs:
bash image/build_image.sh --kernel-only \
--distro "$FORMAT" \
--patches-ref "$PATCHES_REF"
# build_image.sh's --distro all skips the rpm packager, and each
# kernel-build call wipes image/linux-bin/. Stash the deb + pkg.tar.zst
# from the "all" run before firing the rpm-producing "fedora" run,
# then restore so the release ships all three formats.
if [ "$FORMAT" = "all" ]; then
mkdir -p /tmp/kernel-stash
cp image/linux-bin/*.deb image/linux-bin/*.pkg.tar.zst /tmp/kernel-stash/
bash image/build_image.sh --kernel-only \
--distro fedora \
--patches-ref "$PATCHES_REF"
cp /tmp/kernel-stash/*.deb /tmp/kernel-stash/*.pkg.tar.zst image/linux-bin/
fi
- name: Read kernel and patches version
id: version
@@ -61,6 +73,7 @@ jobs:
path: |
image/linux-bin/*.deb
image/linux-bin/*.pkg.tar.zst
image/linux-bin/*.rpm
retention-days: 7
- name: Compute checksums
@@ -68,7 +81,7 @@ jobs:
run: |
{
echo 'sums<<SHAEOF'
sha256sum image/linux-bin/*.deb image/linux-bin/*.pkg.tar.zst | sed 's|image/linux-bin/||'
sha256sum image/linux-bin/*.deb image/linux-bin/*.pkg.tar.zst image/linux-bin/*.rpm 2>/dev/null | sed 's|image/linux-bin/||'
echo 'SHAEOF'
} >> "$GITHUB_OUTPUT"
@@ -90,4 +103,5 @@ jobs:
files: |
image/linux-bin/*.deb
image/linux-bin/*.pkg.tar.zst
image/linux-bin/*.rpm
make_latest: true

View File

@@ -24,7 +24,6 @@ sudo make install
## TODO
- amdgpu smu driver to show correct gpu frequency and temperature
- xhci driver adjustment for 0x104d:0x9108 to enable bluetooth
- hdmi converter improvments: hdr, rgb range, 120hz
## Bugs

View File

@@ -336,10 +336,12 @@ index 000000000000..5263181876f8
+obj-$(CONFIG_X86_PS5) += ps5.o
diff --git a/arch/x86/platform/ps5/ps5.c b/arch/x86/platform/ps5/ps5.c
new file mode 100644
index 000000000000..731fed163a83
index 000000000000..acfbaf873315
--- /dev/null
+++ b/arch/x86/platform/ps5/ps5.c
@@ -0,0 +1,51 @@
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/ps5.h>
+
@@ -1091,7 +1093,7 @@ index 000000000000..39cc76f39e06
+
+#endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 830fefb342c6..f13155f049c4 100644
index 830fefb342c6..380bc421583d 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -67,6 +67,10 @@ static struct usb_driver btusb_driver;
@@ -1112,10 +1114,10 @@ index 830fefb342c6..f13155f049c4 100644
+ /* PS5 IW620 Bluetooth device */
+ { USB_DEVICE(0x1286, 0x2059),
+ .driver_info = BTUSB_MARVELL |
+ BTUSB_IFNUM_0 |
+ BTUSB_BROKEN_LE_DEFAULT_PHY |
+ BTUSB_BROKEN_READ_LOCAL_NAME |
+ BTUSB_NO_AUTOSUSPEND },
+ BTUSB_IFNUM_0 |
+ BTUSB_BROKEN_LE_DEFAULT_PHY |
+ BTUSB_BROKEN_READ_LOCAL_NAME |
+ BTUSB_NO_AUTOSUSPEND },
+
{ } /* Terminating entry */
};
@@ -1125,10 +1127,10 @@ index 830fefb342c6..f13155f049c4 100644
{ USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
{ USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL },
+ { USB_DEVICE(0x1286, 0x2059), .driver_info = BTUSB_MARVELL |
+ BTUSB_IFNUM_0 |
+ BTUSB_BROKEN_LE_DEFAULT_PHY |
+ BTUSB_BROKEN_READ_LOCAL_NAME |
+ BTUSB_NO_AUTOSUSPEND },
+ BTUSB_IFNUM_0 |
+ BTUSB_BROKEN_LE_DEFAULT_PHY |
+ BTUSB_BROKEN_READ_LOCAL_NAME |
+ BTUSB_NO_AUTOSUSPEND },
/* Intel Bluetooth devices */
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED },
@@ -3409,18 +3411,20 @@ index b63cd0c310bc..495b2714838b 100644
diff --git a/drivers/ps5/Makefile b/drivers/ps5/Makefile
new file mode 100644
index 000000000000..ccb2740e3375
index 000000000000..b4b42394aed9
--- /dev/null
+++ b/drivers/ps5/Makefile
@@ -0,0 +1,2 @@
+obj-y += spcie.o tpcie.o hdmi.o mp1.o buzzer.o svm.o vmenter.o
+obj-y += spcie.o tpcie.o hdmi.o mp1.o buzzer.o svm.o vmenter.o fan.o
+obj-y += led/
diff --git a/drivers/ps5/autoservo_param.h b/drivers/ps5/autoservo_param.h
new file mode 100644
index 000000000000..d2c25e461bf7
index 000000000000..612aee5b273a
--- /dev/null
+++ b/drivers/ps5/autoservo_param.h
@@ -0,0 +1,1902 @@
@@ -0,0 +1,1904 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _AUTOSERVO_PARAM_H
+#define _AUTOSERVO_PARAM_H
+
@@ -5620,12 +5624,388 @@ index 000000000000..f48ad6f92534
+MODULE_AUTHOR("Armandas Kvietkus");
+MODULE_DESCRIPTION("PlayStation 5 chassis piezo buzzer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ps5/fan.c b/drivers/ps5/fan.c
new file mode 100644
index 000000000000..e68819f9e32a
--- /dev/null
+++ b/drivers/ps5/fan.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/unaligned.h>
+#include <linux/ps5.h>
+#include <linux/io.h>
+#include "ps5-fan.h"
+#include "autoservo_param.h"
+
+#define PS5_FAN_BOOT_TARGET_TEMP 80
+
+#define GDDR6_SAMSUNG 0x01
+#define GDDR6_HYNIX 0x06
+#define GDDR6_MICRON 0x0f
+
+#define BOARD_MASK_A 0xffffffffffffff00
+#define BOARD_MASK_B 0xffffffffff00ff00
+
+#define BOARD_SANCARLO_1 0x2001010104010400
+#define BOARD_SANCARLO_2 0x2001010104010500
+#define BOARD_A_EIGER_1 0x3002010104010400
+#define BOARD_A_EIGER_2 0x3002010104010500
+#define BOARD_A_DENALI_1 0x3002010104030400
+#define BOARD_A_DENALI_2 0x3002010104030500
+#define BOARD_B_EIGER_DENALI_1 0x3002020101000400
+#define BOARD_B_EIGER_DENALI_2 0x3002020101000500
+#define BOARD_D_EIGER_DENALI_1 0x3002040101000400
+#define BOARD_D_EIGER_DENALI_2 0x3002040101000500
+
+static void bootparam_get_config_board_id(u64 *board_id)
+{
+ void __iomem *bootparam = ioremap(0x9b000, 0x2000);
+ *board_id = readq(bootparam + 0x328);
+ iounmap(bootparam);
+}
+
+static void bootparam_get_config_gddr6_id(u16 *gddr6_id)
+{
+ void __iomem *bootparam = ioremap(0x9b000, 0x2000);
+ *gddr6_id = readw(bootparam + 0x1184);
+ iounmap(bootparam);
+}
+
+int icc_fan_update_param(const u8 *param)
+{
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
+ struct icc_msg *msg = (struct icc_msg *)buf;
+
+ msg->service_id = ICC_SERVICE_ID_FAN;
+ msg->msg_type = 0xe;
+ msg->length = sizeof(*msg) + 0x6ae;
+ memcpy(msg->data, param, 0x6ae);
+
+ return icc_query(buf, buf);
+}
+EXPORT_SYMBOL(icc_fan_update_param);
+
+int icc_fan_update_autoservo_param(void)
+{
+ u64 board_id, board_mask_a, board_mask_b;
+ u16 gddr6_id;
+ u8 gddr6_vendor, gddr6_revision;
+ const char *board_name = NULL;
+ const char *gddr6_name = NULL;
+ const u8 *param = NULL;
+ int ret;
+
+ bootparam_get_config_board_id(&board_id);
+ board_id = be64_to_cpu(board_id);
+ board_mask_a = board_id & BOARD_MASK_A;
+ board_mask_b = board_id & BOARD_MASK_B;
+
+ bootparam_get_config_gddr6_id(&gddr6_id);
+ gddr6_vendor = gddr6_id & 0xf;
+ gddr6_revision = gddr6_id >> 4;
+
+ if (board_mask_a == BOARD_SANCARLO_1 || board_mask_a == BOARD_SANCARLO_2) {
+ board_name = "Sancarlo";
+ param = autoservo_param_sancarlo;
+ } else if (board_mask_a == BOARD_A_EIGER_1 || board_mask_a == BOARD_A_EIGER_2) {
+ board_name = "A-Eiger";
+ switch (gddr6_vendor) {
+ case GDDR6_SAMSUNG:
+ gddr6_name = "Samsung";
+ param = autoservo_param_a_eiger_samsung;
+ break;
+ case GDDR6_HYNIX:
+ gddr6_name = "Hynix";
+ param = autoservo_param_a_eiger_hynix;
+ break;
+ case GDDR6_MICRON:
+ gddr6_name = "Micron";
+ param = autoservo_param_a_eiger_micron;
+ break;
+ default:
+ pr_info("FanServo: %s Unknown Vendor 0x%x\n", board_name, gddr6_vendor);
+ goto not_updated;
+ }
+ } else if (board_mask_a == BOARD_A_DENALI_1 || board_mask_a == BOARD_A_DENALI_2) {
+ board_name = "A-Denali";
+ switch (gddr6_vendor) {
+ case GDDR6_SAMSUNG:
+ gddr6_name = "Samsung";
+ param = autoservo_param_a_denali_samsung;
+ break;
+ case GDDR6_HYNIX:
+ gddr6_name = "Hynix";
+ param = autoservo_param_a_denali_hynix;
+ break;
+ case GDDR6_MICRON:
+ gddr6_name = "Micron";
+ param = autoservo_param_a_denali_micron;
+ break;
+ default:
+ pr_info("FanServo: %s Unknown Vendor 0x%x\n", board_name, gddr6_vendor);
+ goto not_updated;
+ }
+ } else if (board_mask_b == BOARD_B_EIGER_DENALI_1 || board_mask_b == BOARD_B_EIGER_DENALI_2) {
+ board_name = "B-Eiger/Denali";
+ if (gddr6_vendor == GDDR6_SAMSUNG && gddr6_revision == 8) {
+ gddr6_name = "Samsung D1x";
+ param = autoservo_param_b_eiger_denali_samsung_d1x;
+ } else if (gddr6_vendor == GDDR6_SAMSUNG && gddr6_revision == 9) {
+ gddr6_name = "Samsung D1z";
+ param = NULL;
+ } else if (gddr6_vendor == GDDR6_HYNIX && gddr6_revision == 0) {
+ gddr6_name = "Hynix 1x";
+ param = autoservo_param_b_eiger_denali_hynix_1x_micron_120s;
+ } else if (gddr6_vendor == GDDR6_HYNIX && gddr6_revision == 1) {
+ gddr6_name = "Hynix 1y";
+ param = autoservo_param_b_eiger_denali_hynix_1y_micron_130s;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 0) {
+ gddr6_name = "Micron 120s";
+ param = autoservo_param_b_eiger_denali_hynix_1x_micron_120s;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 1) {
+ gddr6_name = "Micron 130s";
+ param = autoservo_param_b_eiger_denali_hynix_1y_micron_130s;
+ } else {
+ pr_info("FanServo: %s Unknown Vendor/Revision 0x%x/0x%x\n", board_name, gddr6_vendor, gddr6_revision);
+ goto not_updated;
+ }
+ } else if (board_mask_b == BOARD_D_EIGER_DENALI_1 || board_mask_b == BOARD_D_EIGER_DENALI_2) {
+ board_name = "D-Eiger/Denali";
+ if (gddr6_vendor == GDDR6_SAMSUNG && gddr6_revision == 9) {
+ gddr6_name = "Samsung D1z";
+ param = autoservo_param_d_eiger_denali_samsung_d1z;
+ } else if (gddr6_vendor == GDDR6_HYNIX && gddr6_revision == 1) {
+ gddr6_name = "Hynix 1y";
+ param = autoservo_param_d_eiger_denali_hynix_1y;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 1) {
+ gddr6_name = "Micron 130s";
+ param = autoservo_param_d_eiger_denali_micron_130s_y31j;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 2) {
+ gddr6_name = "Micron Y31J";
+ param = autoservo_param_d_eiger_denali_micron_130s_y31j;
+ } else {
+ pr_info("FanServo: %s Unknown Vendor/Revision 0x%x/0x%x\n", board_name, gddr6_vendor, gddr6_revision);
+ goto not_updated;
+ }
+ } else {
+ pr_info("FanServo: 0x%016llX\n", board_id);
+ goto not_updated;
+ }
+
+ if (gddr6_name)
+ pr_info("FanServo: %s %s\n", board_name, gddr6_name);
+ else
+ pr_info("FanServo: %s\n", board_name);
+
+ if (!param)
+ goto not_updated;
+
+ ret = icc_fan_update_param(param);
+ if (ret)
+ pr_err("Error: %s: %d\n", __func__, ret);
+
+ return ret;
+
+not_updated:
+ pr_info("FanServo: Not update\n");
+ return 0;
+}
+EXPORT_SYMBOL(icc_fan_update_autoservo_param);
+
+static int icc_fan_txn(u8 svc, u16 mt, u16 len,
+ const u8 *data, size_t dlen,
+ u8 *reply_out, size_t rlen)
+{
+ u8 *buf;
+ struct icc_msg *msg;
+ int ret;
+
+ buf = kzalloc(ICC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ msg = (struct icc_msg *)buf;
+
+ msg->service_id = svc;
+ msg->msg_type = mt;
+ msg->length = len;
+ if (data && dlen)
+ memcpy(msg->data, data, dlen);
+
+ ret = icc_query(buf, buf);
+ if (!ret && reply_out && rlen)
+ memcpy(reply_out, msg->data, rlen);
+
+ kfree(buf);
+ return ret;
+}
+
+static int icc_fan_get_curset(u8 zone, u32 out[PS5_CURSET_COUNT])
+{
+ u8 req[ICC_MSG_MAX_SIZE - sizeof(struct icc_msg)] = {};
+ u8 reply[ICC_MSG_MIN_SIZE] = {};
+ int ret, i;
+
+ req[PS5_FAN_ZONE_OFF] = zone;
+
+ ret = icc_fan_txn(ICC_SERVICE_ID_FAN, PS5_FAN_MT_CURSET_GET,
+ PS5_FAN_CURSET_GET_LEN, req, sizeof(req),
+ reply, sizeof(reply));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < PS5_CURSET_COUNT; i++)
+ out[i] = get_unaligned_le32(reply +
+ PS5_FAN_CURSET_GET_OFF + i * 4);
+ return 0;
+}
+
+static int icc_fan_set_curset(u8 zone, u8 config, const u32 in[PS5_CURSET_COUNT])
+{
+ u8 req[ICC_MSG_MAX_SIZE - sizeof(struct icc_msg)] = {};
+ int i;
+
+ req[PS5_FAN_ZONE_OFF] = zone;
+ req[PS5_FAN_CONFIG_OFF] = config;
+ for (i = 0; i < PS5_CURSET_COUNT; i++)
+ put_unaligned_le32(in[i], req + PS5_FAN_CURSET_SET_OFF + i * 4);
+
+ return icc_fan_txn(ICC_SERVICE_ID_FAN, PS5_FAN_MT_CURSET_SET,
+ PS5_FAN_CURSET_SET_LEN, req, sizeof(req), NULL, 0);
+}
+
+static DEFINE_MUTEX(fan_lock);
+
+static int fan_get_target_temp(u8 zone, u16 *celsius)
+{
+ u32 cs[PS5_CURSET_COUNT];
+ int ret;
+
+ mutex_lock(&fan_lock);
+ ret = icc_fan_get_curset(zone, cs);
+ mutex_unlock(&fan_lock);
+ if (ret)
+ return ret;
+
+ *celsius = cs[PS5_CURSET_TARGETTEMP] >> PS5_TARGETTEMP_SHIFT;
+ return 0;
+}
+
+static int fan_set_target_temp(u8 zone, u8 config, u16 celsius)
+{
+ u32 cs[PS5_CURSET_COUNT];
+ int ret;
+
+ mutex_lock(&fan_lock);
+ ret = icc_fan_get_curset(zone, cs);
+ if (!ret) {
+ cs[PS5_CURSET_TARGETTEMP] = (u32)celsius << PS5_TARGETTEMP_SHIFT;
+ ret = icc_fan_set_curset(zone, config, cs);
+ }
+ mutex_unlock(&fan_lock);
+ return ret;
+}
+
+static ssize_t target_temp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u16 c;
+ int ret = fan_get_target_temp(PS5_ZONE_MAINSOC, &c);
+
+ if (ret)
+ return ret;
+ return sysfs_emit(buf, "%u\n", c);
+}
+
+static ssize_t target_temp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u16 c;
+ int ret;
+
+ if (kstrtou16(buf, 10, &c))
+ return -EINVAL;
+ if (c > 100)
+ return -ERANGE;
+
+ ret = fan_set_target_temp(PS5_ZONE_MAINSOC, 0, c);
+ return ret ? ret : count;
+}
+static DEVICE_ATTR_RW(target_temp);
+
+static struct attribute *ps5_fan_attrs[] = {
+ &dev_attr_target_temp.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ps5_fan);
+
+static int ps5_fan_probe(struct platform_device *pdev)
+{
+ int ret = icc_fan_update_autoservo_param();
+
+ if (ret)
+ dev_warn(&pdev->dev, "autoservo param load failed: %d\n", ret);
+
+ ret = fan_set_target_temp(PS5_ZONE_MAINSOC, 0, PS5_FAN_BOOT_TARGET_TEMP);
+ if (ret)
+ dev_warn(&pdev->dev, "boot target temp set failed: %d\n", ret);
+
+ return 0;
+}
+
+static struct platform_driver ps5_fan_driver = {
+ .probe = ps5_fan_probe,
+ .driver = {
+ .name = "ps5-fan",
+ .dev_groups = ps5_fan_groups,
+ },
+};
+
+static struct platform_device *ps5_fan_pdev;
+
+static int __init ps5_fan_init(void)
+{
+ int ret = platform_driver_register(&ps5_fan_driver);
+
+ if (ret)
+ return ret;
+
+ ps5_fan_pdev = platform_device_register_simple("ps5-fan", -1, NULL, 0);
+ if (IS_ERR(ps5_fan_pdev)) {
+ ret = PTR_ERR(ps5_fan_pdev);
+ platform_driver_unregister(&ps5_fan_driver);
+ ps5_fan_pdev = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit ps5_fan_exit(void)
+{
+ if (ps5_fan_pdev)
+ platform_device_unregister(ps5_fan_pdev);
+ platform_driver_unregister(&ps5_fan_driver);
+}
+
+module_init(ps5_fan_init);
+module_exit(ps5_fan_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS5 fan driver");
+MODULE_ALIAS("platform:ps5-fan");
diff --git a/drivers/ps5/hdmi.c b/drivers/ps5/hdmi.c
new file mode 100644
index 000000000000..8f094490d677
index 000000000000..67871f238abe
--- /dev/null
+++ b/drivers/ps5/hdmi.c
@@ -0,0 +1,1237 @@
@@ -0,0 +1,1239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
@@ -7380,10 +7760,12 @@ index 000000000000..a234788e9990
+#endif
diff --git a/drivers/ps5/mp1.c b/drivers/ps5/mp1.c
new file mode 100644
index 000000000000..4af890c842a7
index 000000000000..3bd1375a444c
--- /dev/null
+++ b/drivers/ps5/mp1.c
@@ -0,0 +1,488 @@
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
@@ -7872,12 +8254,73 @@ index 000000000000..4af890c842a7
+MODULE_AUTHOR("Andy Nguyen");
+MODULE_DESCRIPTION("PlayStation 5 MP1 SMU driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ps5/ps5-fan.h b/drivers/ps5/ps5-fan.h
new file mode 100644
index 000000000000..5ea1519823c3
--- /dev/null
+++ b/drivers/ps5/ps5-fan.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _PS5_FAN_H
+#define _PS5_FAN_H
+
+#include <linux/ps5.h>
+
+#define PS5_FAN_MT_MODE_SET 0x02
+#define PS5_FAN_MT_MODE_GET 0x03
+#define PS5_FAN_MT_CURSET_SET 0x06
+#define PS5_FAN_MT_CURSET_GET 0x07
+
+#define PS5_FAN_CURSET_SET_LEN 0x40
+#define PS5_FAN_CURSET_GET_LEN ICC_MSG_MIN_SIZE
+
+enum ps5_fan_zone {
+ PS5_ZONE_MAINSOC = 0,
+ PS5_ZONE_LOCAL1 = 1,
+ PS5_ZONE_LOCAL2 = 2,
+ PS5_ZONE_LOCAL3 = 3,
+ PS5_ZONE_COUNT = 4,
+};
+
+enum ps5_fan_mode {
+ PS5_FAN_MODE_AUTO = 1,
+ PS5_FAN_MODE_MAXIMUM = 2,
+ PS5_FAN_MODE_MINIMUM = 3,
+ PS5_FAN_MODE_MANUAL = 4,
+ PS5_FAN_MODE_SP1 = 5,
+};
+#define PS5_FAN_MODE_MIN PS5_FAN_MODE_AUTO
+#define PS5_FAN_MODE_MAX PS5_FAN_MODE_SP1
+
+enum ps5_curset_id {
+ PS5_CURSET_TARGETTEMP = 0,
+ PS5_CURSET_PGAIN = 1,
+ PS5_CURSET_IGAIN = 2,
+ PS5_CURSET_ILIMIT = 3,
+ PS5_CURSET_ULIMIT = 4,
+ PS5_CURSET_DLIMIT = 5,
+ PS5_CURSET_COUNT = 6,
+};
+
+#define PS5_FAN_ZONE_OFF 0
+#define PS5_FAN_MODE_OFF 1
+#define PS5_FAN_SETTING_OFF 1
+#define PS5_FAN_CONFIG_OFF 1
+#define PS5_FAN_CURSET_OFF 0x04
+#define PS5_FAN_CURSET_GET_OFF PS5_FAN_CURSET_OFF
+#define PS5_FAN_CURSET_SET_OFF PS5_FAN_CURSET_OFF
+
+#define PS5_TARGETTEMP_SHIFT 8
+
+#endif /* _PS5_FAN_H */
diff --git a/drivers/ps5/spcie.c b/drivers/ps5/spcie.c
new file mode 100644
index 000000000000..e02c074ee0b1
index 000000000000..547b1457ac62
--- /dev/null
+++ b/drivers/ps5/spcie.c
@@ -0,0 +1,1149 @@
@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
@@ -7888,7 +8331,6 @@ index 000000000000..e02c074ee0b1
+#include <linux/dmi.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include "autoservo_param.h"
+
+#define PCI_DEVICE_ID_SPCIE 0x9107
+#define SPCIE_SUBFUNC_ICC 12
@@ -7920,24 +8362,6 @@ index 000000000000..e02c074ee0b1
+
+#define ICC_FAN_CHANGE_SERVO_PATTERN _IOW(ICC_IOC_MAGIC, 1, u8)
+
+#define GDDR6_SAMSUNG 0x01
+#define GDDR6_HYNIX 0x06
+#define GDDR6_MICRON 0x0f
+
+#define BOARD_MASK_A 0xffffffffffffff00
+#define BOARD_MASK_B 0xffffffffff00ff00
+
+#define BOARD_SANCARLO_1 0x2001010104010400
+#define BOARD_SANCARLO_2 0x2001010104010500
+#define BOARD_A_EIGER_1 0x3002010104010400
+#define BOARD_A_EIGER_2 0x3002010104010500
+#define BOARD_A_DENALI_1 0x3002010104030400
+#define BOARD_A_DENALI_2 0x3002010104030500
+#define BOARD_B_EIGER_DENALI_1 0x3002020101000400
+#define BOARD_B_EIGER_DENALI_2 0x3002020101000500
+#define BOARD_D_EIGER_DENALI_1 0x3002040101000400
+#define BOARD_D_EIGER_DENALI_2 0x3002040101000500
+
+struct spcie_dev;
+
+struct spcie_icc_dev {
@@ -7997,20 +8421,6 @@ index 000000000000..e02c074ee0b1
+ 0x00
+};
+
+static void bootparam_get_config_board_id(u64 *board_id)
+{
+ void __iomem *bootparam = ioremap(0x9b000, 0x2000);
+ *board_id = readq(bootparam + 0x328);
+ iounmap(bootparam);
+}
+
+static void bootparam_get_config_gddr6_id(u16 *gddr6_id)
+{
+ void __iomem *bootparam = ioremap(0x9b000, 0x2000);
+ *gddr6_id = readw(bootparam + 0x1184);
+ iounmap(bootparam);
+}
+
+static u16 icc_checksum(struct icc_msg *msg)
+{
+ u16 i;
@@ -8376,147 +8786,6 @@ index 000000000000..e02c074ee0b1
+}
+EXPORT_SYMBOL(icc_fan_change_servo_pattern);
+
+int icc_fan_update_param(const u8 *param)
+{
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
+ struct icc_msg *msg = (struct icc_msg *)buf;
+
+ msg->service_id = ICC_SERVICE_ID_FAN;
+ msg->msg_type = 0xe;
+ msg->length = sizeof(*msg) + 0x6ae;
+ memcpy(msg->data, param, 0x6ae);
+
+ return icc_query(buf, buf);
+}
+EXPORT_SYMBOL(icc_fan_update_param);
+
+int icc_fan_update_autoservo_param(void)
+{
+ u64 board_id, board_mask_a, board_mask_b;
+ u16 gddr6_id;
+ u8 gddr6_vendor, gddr6_revision;
+ const char *board_name = NULL;
+ const char *gddr6_name = NULL;
+ const u8 *param = NULL;
+ int ret;
+
+ bootparam_get_config_board_id(&board_id);
+ board_id = be64_to_cpu(board_id);
+ board_mask_a = board_id & BOARD_MASK_A;
+ board_mask_b = board_id & BOARD_MASK_B;
+
+ bootparam_get_config_gddr6_id(&gddr6_id);
+ gddr6_vendor = gddr6_id & 0xf;
+ gddr6_revision = gddr6_id >> 4;
+
+ if (board_mask_a == BOARD_SANCARLO_1 || board_mask_a == BOARD_SANCARLO_2) {
+ board_name = "Sancarlo";
+ param = autoservo_param_sancarlo;
+ } else if (board_mask_a == BOARD_A_EIGER_1 || board_mask_a == BOARD_A_EIGER_2) {
+ board_name = "A-Eiger";
+ switch (gddr6_vendor) {
+ case GDDR6_SAMSUNG:
+ gddr6_name = "Samsung";
+ param = autoservo_param_a_eiger_samsung;
+ break;
+ case GDDR6_HYNIX:
+ gddr6_name = "Hynix";
+ param = autoservo_param_a_eiger_hynix;
+ break;
+ case GDDR6_MICRON:
+ gddr6_name = "Micron";
+ param = autoservo_param_a_eiger_micron;
+ break;
+ default:
+ pr_info("FanServo: %s Unknown Vendor 0x%x\n", board_name, gddr6_vendor);
+ goto not_updated;
+ }
+ } else if (board_mask_a == BOARD_A_DENALI_1 || board_mask_a == BOARD_A_DENALI_2) {
+ board_name = "A-Denali";
+ switch (gddr6_vendor) {
+ case GDDR6_SAMSUNG:
+ gddr6_name = "Samsung";
+ param = autoservo_param_a_denali_samsung;
+ break;
+ case GDDR6_HYNIX:
+ gddr6_name = "Hynix";
+ param = autoservo_param_a_denali_hynix;
+ break;
+ case GDDR6_MICRON:
+ gddr6_name = "Micron";
+ param = autoservo_param_a_denali_micron;
+ break;
+ default:
+ pr_info("FanServo: %s Unknown Vendor 0x%x\n", board_name, gddr6_vendor);
+ goto not_updated;
+ }
+ } else if (board_mask_b == BOARD_B_EIGER_DENALI_1 || board_mask_b == BOARD_B_EIGER_DENALI_2) {
+ board_name = "B-Eiger/Denali";
+ if (gddr6_vendor == GDDR6_SAMSUNG && gddr6_revision == 8) {
+ gddr6_name = "Samsung D1x";
+ param = autoservo_param_b_eiger_denali_samsung_d1x;
+ } else if (gddr6_vendor == GDDR6_SAMSUNG && gddr6_revision == 9) {
+ gddr6_name = "Samsung D1z";
+ param = NULL;
+ } else if (gddr6_vendor == GDDR6_HYNIX && gddr6_revision == 0) {
+ gddr6_name = "Hynix 1x";
+ param = autoservo_param_b_eiger_denali_hynix_1x_micron_120s;
+ } else if (gddr6_vendor == GDDR6_HYNIX && gddr6_revision == 1) {
+ gddr6_name = "Hynix 1y";
+ param = autoservo_param_b_eiger_denali_hynix_1y_micron_130s;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 0) {
+ gddr6_name = "Micron 120s";
+ param = autoservo_param_b_eiger_denali_hynix_1x_micron_120s;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 1) {
+ gddr6_name = "Micron 130s";
+ param = autoservo_param_b_eiger_denali_hynix_1y_micron_130s;
+ } else {
+ pr_info("FanServo: %s Unknown Vendor/Revision 0x%x/0x%x\n", board_name, gddr6_vendor, gddr6_revision);
+ goto not_updated;
+ }
+ } else if (board_mask_b == BOARD_D_EIGER_DENALI_1 || board_mask_b == BOARD_D_EIGER_DENALI_2) {
+ board_name = "D-Eiger/Denali";
+ if (gddr6_vendor == GDDR6_SAMSUNG && gddr6_revision == 9) {
+ gddr6_name = "Samsung D1z";
+ param = autoservo_param_d_eiger_denali_samsung_d1z;
+ } else if (gddr6_vendor == GDDR6_HYNIX && gddr6_revision == 1) {
+ gddr6_name = "Hynix 1y";
+ param = autoservo_param_d_eiger_denali_hynix_1y;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 1) {
+ gddr6_name = "Micron 130s";
+ param = autoservo_param_d_eiger_denali_micron_130s_y31j;
+ } else if (gddr6_vendor == GDDR6_MICRON && gddr6_revision == 2) {
+ gddr6_name = "Micron Y31J";
+ param = autoservo_param_d_eiger_denali_micron_130s_y31j;
+ } else {
+ pr_info("FanServo: %s Unknown Vendor/Revision 0x%x/0x%x\n", board_name, gddr6_vendor, gddr6_revision);
+ goto not_updated;
+ }
+ } else {
+ pr_info("FanServo: 0x%016llX\n", board_id);
+ goto not_updated;
+ }
+
+ if (gddr6_name)
+ pr_info("FanServo: %s %s\n", board_name, gddr6_name);
+ else
+ pr_info("FanServo: %s\n", board_name);
+
+ if (!param)
+ goto not_updated;
+
+ ret = icc_fan_update_param(param);
+ if (ret)
+ pr_err("Error: %s: %d\n", __func__, ret);
+
+ return ret;
+
+not_updated:
+ pr_info("FanServo: Not update\n");
+ return 0;
+}
+EXPORT_SYMBOL(icc_fan_update_autoservo_param);
+
+static void button_notification_handler(struct icc_msg *msg)
+{
+ if (msg->msg_type == 0x8010) { // power button down
@@ -8709,9 +8978,6 @@ index 000000000000..e02c074ee0b1
+ icc_button_enable_all_notifications(1);
+ icc_thermal_enable_notification(1);
+
+ /* Update autoservo param */
+ icc_fan_update_autoservo_param();
+
+ /* Set LED to white dim */
+ icc_indicator_set_led_white(0);
+
@@ -9029,10 +9295,12 @@ index 000000000000..e02c074ee0b1
+MODULE_LICENSE("GPL");
diff --git a/drivers/ps5/svm.c b/drivers/ps5/svm.c
new file mode 100644
index 000000000000..3fd58e9a6609
index 000000000000..b9e7def39ef7
--- /dev/null
+++ b/drivers/ps5/svm.c
@@ -0,0 +1,169 @@
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/ps5.h>
+
@@ -9204,10 +9472,12 @@ index 000000000000..3fd58e9a6609
+EXPORT_SYMBOL(svm_execute_guest);
diff --git a/drivers/ps5/tpcie.c b/drivers/ps5/tpcie.c
new file mode 100644
index 000000000000..23de43ea4b00
index 000000000000..7e933c30fc3e
--- /dev/null
+++ b/drivers/ps5/tpcie.c
@@ -0,0 +1,303 @@
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/console.h>
@@ -9513,10 +9783,12 @@ index 000000000000..23de43ea4b00
+MODULE_LICENSE("GPL");
diff --git a/drivers/ps5/vmenter.S b/drivers/ps5/vmenter.S
new file mode 100644
index 000000000000..f5348c0d7e82
index 000000000000..e3fd6f3e59b6
--- /dev/null
+++ b/drivers/ps5/vmenter.S
@@ -0,0 +1,30 @@
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
@@ -10296,10 +10568,12 @@ index c8700e6a694d..7d0afd378c4e 100644
extern void dmi_setup(void);
diff --git a/include/linux/ps5.h b/include/linux/ps5.h
new file mode 100644
index 000000000000..24adcd953ab3
index 000000000000..75a2d6dace5d
--- /dev/null
+++ b/include/linux/ps5.h
@@ -0,0 +1,95 @@
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _LINUX_PS5_H
+#define _LINUX_PS5_H
+