mirror of
https://github.com/ps5-linux/ps5-linux-patches.git
synced 2026-07-17 07:00:50 +00:00
Compare commits
4 Commits
kernel-7.1
...
kernel-7.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06e7cbf7bd | ||
|
|
498b83a4a5 | ||
|
|
a2c666ba29 | ||
|
|
06bb2bb268 |
880
linux.patch
880
linux.patch
@@ -1090,6 +1090,81 @@ index 000000000000..39cc76f39e06
|
||||
+int salina_sata_phy_init(struct salina_sata_phy *p);
|
||||
+
|
||||
+#endif
|
||||
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
|
||||
index 830fefb342c6..f13155f049c4 100644
|
||||
--- a/drivers/bluetooth/btusb.c
|
||||
+++ b/drivers/bluetooth/btusb.c
|
||||
@@ -67,6 +67,10 @@ static struct usb_driver btusb_driver;
|
||||
#define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26)
|
||||
#define BTUSB_ACTIONS_SEMI BIT(27)
|
||||
#define BTUSB_BARROT BIT(28)
|
||||
+#define BTUSB_IFNUM_0 BIT(29)
|
||||
+#define BTUSB_BROKEN_LE_DEFAULT_PHY BIT(30)
|
||||
+#define BTUSB_BROKEN_READ_LOCAL_NAME BIT(31)
|
||||
+#define BTUSB_NO_AUTOSUSPEND BIT(32)
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
@@ -172,6 +176,14 @@ static const struct usb_device_id btusb_table[] = {
|
||||
{ USB_DEVICE(0x8087, 0x0a5a),
|
||||
.driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
|
||||
|
||||
+ /* 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 },
|
||||
+
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
@@ -472,6 +484,11 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
|
||||
{ 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 },
|
||||
|
||||
/* Intel Bluetooth devices */
|
||||
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
@@ -4055,6 +4072,10 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
(intf->cur_altsetting->desc.bInterfaceNumber != 2))
|
||||
return -ENODEV;
|
||||
|
||||
+ if ((id->driver_info & BTUSB_IFNUM_0) &&
|
||||
+ intf->cur_altsetting->desc.bInterfaceNumber != 0)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||
|
||||
if (!id->driver_info) {
|
||||
@@ -4344,6 +4365,12 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
if (id->driver_info & BTUSB_INVALID_LE_STATES)
|
||||
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
|
||||
|
||||
+ if (id->driver_info & BTUSB_BROKEN_LE_DEFAULT_PHY)
|
||||
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_SET_DEFAULT_PHY);
|
||||
+
|
||||
+ if (id->driver_info & BTUSB_BROKEN_READ_LOCAL_NAME)
|
||||
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_READ_LOCAL_NAME);
|
||||
+
|
||||
if (id->driver_info & BTUSB_DIGIANSWER) {
|
||||
data->cmdreq_type = USB_TYPE_VENDOR;
|
||||
hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE);
|
||||
@@ -4400,7 +4427,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
data->diag = NULL;
|
||||
}
|
||||
|
||||
- if (enable_autosuspend)
|
||||
+ if (enable_autosuspend && !(id->driver_info & BTUSB_NO_AUTOSUSPEND))
|
||||
usb_enable_autosuspend(data->udev);
|
||||
|
||||
data->poll_sync = enable_poll_sync;
|
||||
diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c
|
||||
index 477a37e2ef80..e1af70946c89 100644
|
||||
--- a/drivers/firmware/dmi-id.c
|
||||
@@ -1505,7 +1580,7 @@ index 52f4e9e099cb..6e4ff7444536 100644
|
||||
if (r)
|
||||
return r;
|
||||
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||||
index f8c13bad4ac2..e2e6a2605f2a 100644
|
||||
index f8c13bad4ac2..1a9cccc5fa5c 100644
|
||||
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||||
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||||
@@ -80,6 +80,10 @@
|
||||
@@ -1566,7 +1641,7 @@ index f8c13bad4ac2..e2e6a2605f2a 100644
|
||||
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
|
||||
(mode->flags & DRM_MODE_FLAG_DBLSCAN))
|
||||
return result;
|
||||
@@ -10957,12 +10978,58 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
@@ -10957,12 +10978,64 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
drm_atomic_helper_update_legacy_modeset_state(dev, state);
|
||||
drm_dp_mst_atomic_wait_for_dependencies(state);
|
||||
|
||||
@@ -1583,6 +1658,8 @@ index f8c13bad4ac2..e2e6a2605f2a 100644
|
||||
+ (!new_crtc_state->active ||
|
||||
+ drm_atomic_crtc_needs_modeset(new_crtc_state)))) {
|
||||
+ if (dm_new_crtc_state->stream && dc_is_dp_signal(dm_new_crtc_state->stream->signal)) {
|
||||
+ sceHdmiSetAudioMute(1);
|
||||
+ sceHdmiDeviceSetVideoMute(1);
|
||||
+ sceHdmiInitVideoConfig();
|
||||
+ }
|
||||
+ }
|
||||
@@ -1606,13 +1683,17 @@ index f8c13bad4ac2..e2e6a2605f2a 100644
|
||||
+ drm_atomic_crtc_needs_modeset(new_crtc_state))) {
|
||||
+ if (dm_new_crtc_state->stream && dc_is_dp_signal(dm_new_crtc_state->stream->signal)) {
|
||||
+ struct dc_stream_state *stream = dm_new_crtc_state->stream;
|
||||
+ int channels = 0;
|
||||
+ int channels = 0, j;
|
||||
+
|
||||
+ for (i = 0; i < stream->audio_info.mode_count; i++) {
|
||||
+ if (stream->audio_info.modes[i].channel_count > channels)
|
||||
+ channels = stream->audio_info.modes[i].channel_count;
|
||||
+ for (j = 0; j < stream->audio_info.mode_count; j++) {
|
||||
+ if (stream->audio_info.modes[j].channel_count > channels)
|
||||
+ channels = stream->audio_info.modes[j].channel_count;
|
||||
+ }
|
||||
+
|
||||
+ /* Fallback to stereo if EDID has no audio modes */
|
||||
+ if (channels == 0)
|
||||
+ channels = 2;
|
||||
+
|
||||
+ sceHdmiSetVideoConfig(&new_crtc_state->mode);
|
||||
+ sceHdmiDeviceSetVideoMute(0);
|
||||
+ sceHdmiSetAudioConfig(channels);
|
||||
@@ -8950,6 +9031,739 @@ index 000000000000..f5348c0d7e82
|
||||
+ pop %_ASM_BP
|
||||
+ RET
|
||||
+SYM_FUNC_END(svm_run_guest)
|
||||
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
|
||||
index a07e7ba9cd53..4c020957b7a7 100644
|
||||
--- a/drivers/usb/host/Makefile
|
||||
+++ b/drivers/usb/host/Makefile
|
||||
@@ -71,6 +71,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
|
||||
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
|
||||
obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
|
||||
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
|
||||
+obj-$(CONFIG_USB_XHCI_PCI) += xhci-ps5.o
|
||||
obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o
|
||||
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
|
||||
obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o
|
||||
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
|
||||
index 585b2f3117b0..16d5e2178ae0 100644
|
||||
--- a/drivers/usb/host/xhci-pci.c
|
||||
+++ b/drivers/usb/host/xhci-pci.c
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "xhci.h"
|
||||
#include "xhci-trace.h"
|
||||
#include "xhci-pci.h"
|
||||
+#include "xhci-ps5.h"
|
||||
|
||||
#define SSIC_PORT_NUM 2
|
||||
#define SSIC_PORT_CFG2 0x880c
|
||||
@@ -94,6 +95,9 @@
|
||||
#define PCI_DEVICE_ID_ASMEDIA_3042_XHCI 0x3042
|
||||
#define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242
|
||||
|
||||
+#define PCI_VENDOR_ID_SONY 0x104d
|
||||
+#define PCI_DEVICE_ID_PS5_SALINA_XHCI 0x9108
|
||||
+
|
||||
static const char hcd_name[] = "xhci_hcd";
|
||||
|
||||
static struct hc_driver __read_mostly xhci_pci_hc_driver;
|
||||
@@ -511,6 +515,13 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
if (xhci->hci_version >= 0x120)
|
||||
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
|
||||
|
||||
+ if (pdev->vendor == PCI_VENDOR_ID_SONY &&
|
||||
+ pdev->device == PCI_DEVICE_ID_PS5_SALINA_XHCI) {
|
||||
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "%s detected Salina USB PHY setting quirk", __func__);
|
||||
+ xhci->quirks |= XHCI_PS5_SALINA_PHY;
|
||||
+ xhci->quirks |= XHCI_WRITE_64_HI_LO;
|
||||
+ }
|
||||
+
|
||||
if (xhci->quirks & XHCI_RESET_ON_RESUME)
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
|
||||
"QUIRK: Resetting on resume");
|
||||
@@ -575,6 +586,13 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
/* imod_interval is the interrupt moderation value in nanoseconds. */
|
||||
xhci->imod_interval = 40000;
|
||||
|
||||
+ if (pdev->vendor == PCI_VENDOR_ID_SONY &&
|
||||
+ pdev->device == PCI_DEVICE_ID_PS5_SALINA_XHCI) {
|
||||
+ retval = xhci_salina_init(pdev);
|
||||
+ if (retval)
|
||||
+ return retval;
|
||||
+ }
|
||||
+
|
||||
retval = xhci_gen_setup(hcd, xhci_pci_quirks);
|
||||
if (retval)
|
||||
return retval;
|
||||
@@ -713,6 +731,11 @@ void xhci_pci_remove(struct pci_dev *dev)
|
||||
xhci = hcd_to_xhci(pci_get_drvdata(dev));
|
||||
set_power_d3 = xhci->quirks & XHCI_SPURIOUS_WAKEUP;
|
||||
|
||||
+ if(xhci->quirks & XHCI_PS5_SALINA_PHY) {
|
||||
+ salina_stop_xhci_poller();
|
||||
+ xhci_salina_exit();
|
||||
+ }
|
||||
+
|
||||
xhci->xhc_state |= XHCI_STATE_REMOVING;
|
||||
|
||||
if (pci_choose_state(dev, PMSG_SUSPEND) == PCI_D0)
|
||||
@@ -945,6 +968,7 @@ static const struct pci_device_id pci_ids[] = {
|
||||
/* handle any USB 3.0 xHCI controller */
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
|
||||
},
|
||||
+ { PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_PS5_SALINA_XHCI) },
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
diff --git a/drivers/usb/host/xhci-ps5.c b/drivers/usb/host/xhci-ps5.c
|
||||
new file mode 100644
|
||||
index 000000000000..b1c2ff265c7e
|
||||
--- /dev/null
|
||||
+++ b/drivers/usb/host/xhci-ps5.c
|
||||
@@ -0,0 +1,516 @@
|
||||
+/*
|
||||
+* Playstation 5 Salina XHCI driver helper
|
||||
+* Author: https://github.com/c0w-ar
|
||||
+*/
|
||||
+
|
||||
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
+
|
||||
+#include <linux/pci.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/usb.h>
|
||||
+#include <linux/usb/hcd.h>
|
||||
+#include <linux/workqueue.h>
|
||||
+#include <asm/delay.h>
|
||||
+#include "xhci-ps5.h"
|
||||
+#include "xhci.h"
|
||||
+#include "xhci-trace.h"
|
||||
+#include "xhci-ring.h"
|
||||
+
|
||||
+/* External SPCIE driver functions */
|
||||
+extern bool spcie_is_initialized(void);
|
||||
+extern u32 spcie_get_chip_id(void);
|
||||
+extern u32 spcie_bar2_180000_read(u32 reg);
|
||||
+extern void spcie_bar2_180000_write(u32 reg, u32 val);
|
||||
+extern u32 spcie_pervasive0_4000_read(u32 reg);
|
||||
+
|
||||
+#define SALINA_XHCI_BAR_SIZE 0x140000
|
||||
+#define SALINA_POLL_INTERVAL_MS 20
|
||||
+
|
||||
+struct salina_xhci_dev {
|
||||
+ void __iomem *spcie_bar2;
|
||||
+ void __iomem *spcie_pervasive0;
|
||||
+ void __iomem *xhci_bar;
|
||||
+ u32 revision;
|
||||
+};
|
||||
+static struct salina_xhci_dev salina_xhci;
|
||||
+
|
||||
+struct salina_xhci_poller {
|
||||
+ struct xhci_hcd *xhci;
|
||||
+ struct delayed_work work;
|
||||
+ bool running;
|
||||
+};
|
||||
+static struct salina_xhci_poller salina_poll;
|
||||
+
|
||||
+static int dev_ioremap_bar(struct pci_dev *dev, int bar_nr, resource_size_t min_size, void __iomem **bar) {
|
||||
+ resource_size_t start = pci_resource_start(dev, bar_nr);
|
||||
+ resource_size_t len = pci_resource_len(dev, bar_nr);
|
||||
+
|
||||
+ if (start == 0) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (len < min_size) {
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ *bar = ioremap(start, min_size);
|
||||
+ if (!*bar) {
|
||||
+ pr_err("Could not ioremap memory\n");
|
||||
+ release_mem_region(start, min_size);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void unmap_bar(void) {
|
||||
+ if(salina_xhci.xhci_bar) {
|
||||
+ iounmap(salina_xhci.xhci_bar);
|
||||
+ salina_xhci.xhci_bar = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int init_bar(struct pci_dev *own_pdev) {
|
||||
+ int ret = dev_ioremap_bar(own_pdev, 0, SALINA_XHCI_BAR_SIZE, &salina_xhci.xhci_bar);
|
||||
+ if (ret)
|
||||
+ goto unmap;
|
||||
+ return 0;
|
||||
+
|
||||
+unmap:
|
||||
+ unmap_bar();
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static u32 phys_readl(u64 addr) {
|
||||
+ void __iomem *ptr = ioremap(addr, 4);
|
||||
+ if (!ptr) {
|
||||
+ pr_err("Could not ioremap memory\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+ u32 val = readl(ptr);
|
||||
+ iounmap(ptr);
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
+static u8 usb_get_length(u8 usb_n) {
|
||||
+ return (u8)((phys_readl(0x9bf44) >> (usb_n*8))&0x1f);
|
||||
+}
|
||||
+
|
||||
+inline void spcie_phy_bit_set(u32 reg, u32 bit) {
|
||||
+ u32 val_old = spcie_bar2_180000_read(reg);
|
||||
+ spcie_bar2_180000_write(reg, val_old | BIT(bit));
|
||||
+}
|
||||
+
|
||||
+inline void spcie_phy_bit_unset(u32 reg, u32 bit) {
|
||||
+ u32 val_old = spcie_bar2_180000_read(reg);
|
||||
+ spcie_bar2_180000_write(reg, val_old & ~BIT(bit));
|
||||
+}
|
||||
+
|
||||
+inline u32 spcie_config_read(u32 reg) {
|
||||
+ return spcie_pervasive0_4000_read(reg);
|
||||
+}
|
||||
+
|
||||
+inline u32 xhci_read(u64 offset, u32 reg) {
|
||||
+ if (salina_xhci.xhci_bar)
|
||||
+ return readl(salina_xhci.xhci_bar + offset + reg);
|
||||
+ else
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+inline void xhci_write(u64 offset, u32 reg, u32 val) {
|
||||
+ if (salina_xhci.xhci_bar)
|
||||
+ writel(val, salina_xhci.xhci_bar + offset + reg);
|
||||
+}
|
||||
+
|
||||
+inline void xhci_or(u64 offset, u32 reg, u32 val) {
|
||||
+ if (salina_xhci.xhci_bar) {
|
||||
+ u32 val_old = readl(salina_xhci.xhci_bar + offset + reg);
|
||||
+ writel(val_old | val, salina_xhci.xhci_bar + offset + reg);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+inline void xhci_and(u64 offset, u32 reg, u32 val) {
|
||||
+ if (salina_xhci.xhci_bar) {
|
||||
+ u32 val_old = readl(salina_xhci.xhci_bar + offset + reg);
|
||||
+ writel(val_old & val, salina_xhci.xhci_bar + offset + reg);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+inline void xhci_bit_set(u64 offset, u32 reg, u32 bit) {
|
||||
+ if (salina_xhci.xhci_bar) {
|
||||
+ u32 val_old = readl(salina_xhci.xhci_bar + offset + reg);
|
||||
+ writel(val_old | BIT(bit), salina_xhci.xhci_bar + offset + reg);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+inline void xhci_bit_unset(u64 offset, u32 reg, u32 bit) {
|
||||
+ if (salina_xhci.xhci_bar) {
|
||||
+ u32 val_old = readl(salina_xhci.xhci_bar + offset + reg);
|
||||
+ writel(val_old & ~BIT(bit), salina_xhci.xhci_bar + offset + reg);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+inline void xhci_bit_toggle(u64 offset, u32 reg, u32 bit) {
|
||||
+ xhci_bit_set(offset, reg, bit);
|
||||
+ xhci_bit_unset(offset, reg, bit);
|
||||
+}
|
||||
+
|
||||
+static inline u64 usb_phy_offset (u8 usb_n) {
|
||||
+ switch (usb_n) {
|
||||
+ case 0: return 0x120000;
|
||||
+ case 1: return 0x124000;
|
||||
+ case 2: return 0x128000;
|
||||
+ default: return 0x120000;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static inline u32 usb_trace_trim(u8 usb_n, u32 usb_length) {
|
||||
+ if (usb_length < 3)
|
||||
+ return 8;
|
||||
+ else if (usb_length < 6)
|
||||
+ return 7;
|
||||
+ else if (usb_length < 8)
|
||||
+ return 6;
|
||||
+ else if (usb_length < 10)
|
||||
+ return 5;
|
||||
+ else if (usb_length < 13)
|
||||
+ return 4;
|
||||
+ else {
|
||||
+ switch (usb_n) {
|
||||
+ case 0: return 4;
|
||||
+ case 1: return 6;
|
||||
+ case 2: return 7;
|
||||
+ default: return 4;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void salina_usb_phy_config_step_1(u8 usb_n) {
|
||||
+
|
||||
+ u64 offset = usb_phy_offset(usb_n);
|
||||
+ u32 val32;
|
||||
+ u32 usb_efuse;
|
||||
+ u32 usb_length;
|
||||
+
|
||||
+ val32 = spcie_config_read(0x6c);
|
||||
+ if ((val32 >> 0x19 & val32 >> 0x11 & 1) == 0) {
|
||||
+ printk("[USB%d] eFuse not blown for usb\n", usb_n);
|
||||
+ usb_efuse = 0x98;
|
||||
+ }
|
||||
+ else {
|
||||
+ usb_efuse = (spcie_config_read(0x44) >> (usb_n*5+0x10)) & 0x1f;
|
||||
+ }
|
||||
+
|
||||
+ xhci_write(offset, 0x800, 0x480a);
|
||||
+
|
||||
+ xhci_write(offset, 0x814, 0x10a01000);
|
||||
+
|
||||
+ xhci_bit_unset(offset, 0x818, 23);
|
||||
+
|
||||
+ xhci_bit_set(offset, 0x800, 5);
|
||||
+
|
||||
+ xhci_bit_unset(offset, 0x868, 18);
|
||||
+
|
||||
+ xhci_and(offset, 0x804, 0xff07ffff);
|
||||
+ xhci_or(offset, 0x804, usb_efuse << 0x13);
|
||||
+
|
||||
+ usb_length = usb_trace_trim(usb_n, usb_get_length(usb_n));
|
||||
+
|
||||
+ xhci_and(offset, 0x818, 0xffffff0f);
|
||||
+ xhci_or(offset, 0x818, 0xa0);
|
||||
+
|
||||
+ xhci_and(offset, 0x818, 0xfffffff0);
|
||||
+ xhci_or(offset, 0x818, usb_length);
|
||||
+}
|
||||
+
|
||||
+static int salina_usb_phy_config_step_2(u8 usb_n) {
|
||||
+
|
||||
+ u64 offset = usb_phy_offset(usb_n);
|
||||
+
|
||||
+ xhci_bit_set(offset, 0x814, 15);
|
||||
+
|
||||
+ udelay(2);
|
||||
+
|
||||
+ xhci_bit_set(offset, 0x110, 8);
|
||||
+
|
||||
+ xhci_and(offset, 0x100, 0xff000000);
|
||||
+ xhci_or(offset, 0x100, 0x400);
|
||||
+
|
||||
+ xhci_bit_set(offset, 0x100, 24);
|
||||
+
|
||||
+ int attemps = 100;
|
||||
+ u32 measure;
|
||||
+ do {
|
||||
+ measure = xhci_read(offset, 0x10c);
|
||||
+ udelay(2);
|
||||
+ if (measure!=0) break;
|
||||
+ attemps--;
|
||||
+ } while(attemps!=0);
|
||||
+
|
||||
+ xhci_bit_unset(offset, 0x100, 24);
|
||||
+
|
||||
+ xhci_bit_unset(offset, 0x110, 8);
|
||||
+
|
||||
+ if (!measure) {
|
||||
+ printk("Something went wrong while waiting for USB init\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ u32 calibration = (u32)((0xb9000 / (u64)measure) / 1000);
|
||||
+
|
||||
+ xhci_and(offset, 0x814, 0xffff8fff);
|
||||
+ xhci_or(offset, 0x814, calibration<<12);
|
||||
+
|
||||
+ xhci_bit_unset(offset, 0x814, 15);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void salina_usb_phy_config_step_3(u8 usb_n) {
|
||||
+ u32 offset = usb_phy_offset(usb_n);
|
||||
+ xhci_bit_set(offset, 0x800, 0);
|
||||
+ xhci_bit_unset(offset, 0x808, 17);
|
||||
+ xhci_bit_set(offset, 0x880, 0);
|
||||
+}
|
||||
+
|
||||
+static void salina_usb_phy_config_step_4(void) {
|
||||
+ xhci_and(0, 0xc100, 0xfffffff0);
|
||||
+ xhci_or(0, 0xc100, 0xe);
|
||||
+ xhci_write(0, 0xc12c, 0x7fc0c010);
|
||||
+ xhci_write(0, 0xc200, 0x1548);
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ xhci_write(0, 0xc204, 0x1548);
|
||||
+ xhci_write(0, 0xc208, 0x1548);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int xhci_salina_phy_init(void) {
|
||||
+
|
||||
+ spcie_phy_bit_set(0x24, 1);
|
||||
+ spcie_phy_bit_set(0x24, 0);
|
||||
+ spcie_phy_bit_unset(0x24, 1);
|
||||
+
|
||||
+ salina_usb_phy_config_step_1(0);
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ salina_usb_phy_config_step_1(1);
|
||||
+ salina_usb_phy_config_step_1(2);
|
||||
+ }
|
||||
+
|
||||
+ spcie_phy_bit_unset(0x24, 0);
|
||||
+
|
||||
+ udelay(2000);
|
||||
+
|
||||
+ if(salina_usb_phy_config_step_2(0)) return -1;
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ if(salina_usb_phy_config_step_2(1)) return -1;
|
||||
+ if(salina_usb_phy_config_step_2(2)) return -1;
|
||||
+ }
|
||||
+
|
||||
+ salina_usb_phy_config_step_3(0);
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ salina_usb_phy_config_step_3(1);
|
||||
+ salina_usb_phy_config_step_3(2);
|
||||
+ }
|
||||
+
|
||||
+ xhci_bit_unset(0x100000, 0x4, 0);
|
||||
+
|
||||
+ salina_usb_phy_config_step_4();
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int xhci_salina_init(struct pci_dev *pdev) {
|
||||
+
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!spcie_is_initialized()) {
|
||||
+ dev_warn(&pdev->dev, "spcie not initialized, deferring\n");
|
||||
+ return -EPROBE_DEFER;
|
||||
+ }
|
||||
+
|
||||
+ salina_xhci.revision = spcie_get_chip_id();
|
||||
+ if (salina_xhci.revision!=0x110000 && salina_xhci.revision!=0x120000) {
|
||||
+ dev_warn(&pdev->dev, "PS5 Salina revision unknown %08x\n", salina_xhci.revision);
|
||||
+ return ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ ret = init_bar(pdev);
|
||||
+ if (ret) {
|
||||
+ pr_err("PS5 Salina could not map BAR\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ xhci_salina_phy_init();
|
||||
+
|
||||
+ dev_info(&pdev->dev, "PS5 Salina xHCI PHY initialized\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int xhci_salina_reset(struct xhci_hcd *xhci, u64 timeout_us) {
|
||||
+
|
||||
+ u32 state;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ xhci_bit_unset(0x100000, 0x0, 2);
|
||||
+ }
|
||||
+
|
||||
+ state = readl(&xhci->op_regs->status);
|
||||
+
|
||||
+ if (state == ~(u32)0) {
|
||||
+ if (!(xhci->xhc_state & XHCI_STATE_DYING))
|
||||
+ xhci_warn(xhci, "Host not accessible, reset failed.\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if ((state & STS_HALT) == 0) {
|
||||
+ xhci_warn(xhci, "Host controller not halted, aborting reset.\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ xhci_info(xhci, "// Reset the HC on custom Salina Reset");
|
||||
+ writel(CMD_RESET, &xhci->op_regs->command);
|
||||
+
|
||||
+ udelay(1000);
|
||||
+
|
||||
+ ret = xhci_handshake(&xhci->op_regs->command, CMD_RESET, 0, timeout_us);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = xhci_handshake(&xhci->op_regs->status, STS_CNR, 0, timeout_us);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ xhci_bit_toggle(0x100000, 0x0, 1);
|
||||
+ xhci_bit_toggle(0x100000, 0x0, 17);
|
||||
+ xhci_bit_toggle(0x100000, 0x0, 25);
|
||||
+ udelay(10000);
|
||||
+ udelay(10000);
|
||||
+ udelay(10000);
|
||||
+ }
|
||||
+
|
||||
+ xhci_bit_unset(0x100000, 0x0, 1);
|
||||
+ udelay(10000);
|
||||
+ udelay(10000);
|
||||
+
|
||||
+ if (salina_xhci.revision == 0x110000) {
|
||||
+ xhci_bit_set(0x100000, 0x0, 2);
|
||||
+ udelay(10000);
|
||||
+ udelay(10000);
|
||||
+ }
|
||||
+
|
||||
+ salina_usb_phy_config_step_4();
|
||||
+
|
||||
+ // Reset software state
|
||||
+ xhci->usb2_rhub.bus_state.port_c_suspend = 0;
|
||||
+ xhci->usb2_rhub.bus_state.suspended_ports = 0;
|
||||
+ xhci->usb2_rhub.bus_state.resuming_ports = 0;
|
||||
+ xhci->usb3_rhub.bus_state.port_c_suspend = 0;
|
||||
+ xhci->usb3_rhub.bus_state.suspended_ports = 0;
|
||||
+ xhci->usb3_rhub.bus_state.resuming_ports = 0;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void xhci_salina_exit(void)
|
||||
+{
|
||||
+ unmap_bar();
|
||||
+}
|
||||
+
|
||||
+static bool ring_has_event_pending(struct xhci_ring *ring)
|
||||
+{
|
||||
+ struct xhci_segment *seg;
|
||||
+ u32 idx;
|
||||
+ u32 ccs;
|
||||
+
|
||||
+ if (!ring || !ring->deq_seg || !ring->dequeue)
|
||||
+ return false;
|
||||
+
|
||||
+ seg = ring->deq_seg;
|
||||
+ idx = ring->dequeue - ring->deq_seg->trbs;
|
||||
+ ccs = ring->cycle_state;
|
||||
+
|
||||
+ if (idx >= TRBS_PER_SEGMENT)
|
||||
+ return false;
|
||||
+
|
||||
+ u32 trb_cycle = le32_to_cpu(seg->trbs[idx].generic.field[3]) & TRB_CYCLE;
|
||||
+ return trb_cycle == ccs;
|
||||
+}
|
||||
+
|
||||
+static void salina_xhci_poll_fn(struct work_struct *w)
|
||||
+{
|
||||
+ struct salina_xhci_poller *p =
|
||||
+ container_of(w, struct salina_xhci_poller, work.work);
|
||||
+ struct xhci_hcd *xhci = p->xhci;
|
||||
+ struct xhci_ring *er;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ if (!p->running || !xhci)
|
||||
+ return;
|
||||
+
|
||||
+ er = xhci->interrupters[0]->event_ring;
|
||||
+ if (!er)
|
||||
+ goto resched;
|
||||
+
|
||||
+ if (!spin_trylock_irqsave(&xhci->lock, flags)) {
|
||||
+ printk(KERN_INFO "Salina poller: lock busy, rescheduling\n");
|
||||
+ goto resched;
|
||||
+ }
|
||||
+
|
||||
+ u32 status = readl(&xhci->op_regs->status);
|
||||
+ if (status == ~(u32)0) {
|
||||
+ xhci_hc_died(xhci);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ if (status & STS_HCE) {
|
||||
+ xhci_warn(xhci, "WARNING: Host Controller Error\n");
|
||||
+ xhci_halt(xhci);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ if (status & STS_FATAL) {
|
||||
+ xhci_warn(xhci, "WARNING: Host System Error\n");
|
||||
+ xhci_halt(xhci);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ if (ring_has_event_pending(er)) {
|
||||
+ xhci_handle_events(xhci, xhci->interrupters[0], false);
|
||||
+ }
|
||||
+
|
||||
+unlock:
|
||||
+ spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
+
|
||||
+resched:
|
||||
+ schedule_delayed_work(&p->work,
|
||||
+ msecs_to_jiffies(SALINA_POLL_INTERVAL_MS));
|
||||
+}
|
||||
+
|
||||
+void salina_start_xhci_poller(struct xhci_hcd *xhci)
|
||||
+{
|
||||
+ if (salina_poll.running)
|
||||
+ return;
|
||||
+
|
||||
+ memset(&salina_poll, 0, sizeof(salina_poll));
|
||||
+ salina_poll.xhci = xhci;
|
||||
+ salina_poll.running = true;
|
||||
+
|
||||
+ INIT_DELAYED_WORK(&salina_poll.work, salina_xhci_poll_fn);
|
||||
+
|
||||
+ schedule_delayed_work(&salina_poll.work,
|
||||
+ msecs_to_jiffies(SALINA_POLL_INTERVAL_MS));
|
||||
+
|
||||
+ xhci_info(xhci, "Salina: software IRQ poller enabled\n");
|
||||
+}
|
||||
+
|
||||
+void salina_stop_xhci_poller(void)
|
||||
+{
|
||||
+ if (!salina_poll.running)
|
||||
+ return;
|
||||
+ cancel_delayed_work_sync(&salina_poll.work);
|
||||
+ salina_poll.running = false;
|
||||
+ salina_poll.xhci = NULL;
|
||||
+}
|
||||
diff --git a/drivers/usb/host/xhci-ps5.h b/drivers/usb/host/xhci-ps5.h
|
||||
new file mode 100755
|
||||
index 000000000000..90d41d3dc524
|
||||
--- /dev/null
|
||||
+++ b/drivers/usb/host/xhci-ps5.h
|
||||
@@ -0,0 +1,9 @@
|
||||
+#include <linux/pci.h>
|
||||
+#include "xhci.h"
|
||||
+
|
||||
+int xhci_salina_reset(struct xhci_hcd *xhci, u64 timeout_us);
|
||||
+int xhci_salina_init(struct pci_dev *pdev);
|
||||
+void xhci_salina_exit(void);
|
||||
+
|
||||
+void salina_start_xhci_poller(struct xhci_hcd *xhci);
|
||||
+void salina_stop_xhci_poller(void);
|
||||
\ No newline at end of file
|
||||
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
|
||||
index e47e644b296e..774c4955b2bb 100644
|
||||
--- a/drivers/usb/host/xhci-ring.c
|
||||
+++ b/drivers/usb/host/xhci-ring.c
|
||||
@@ -59,6 +59,8 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include "xhci.h"
|
||||
#include "xhci-trace.h"
|
||||
+#include "xhci-ring.h"
|
||||
+#include "xhci-ps5.h"
|
||||
|
||||
static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
u32 field1, u32 field2,
|
||||
@@ -3083,12 +3085,14 @@ static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
|
||||
* Handle all OS-owned events on an interrupter event ring. It may drop
|
||||
* and reaquire xhci->lock between event processing.
|
||||
*/
|
||||
-static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
+int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
|
||||
bool skip_events)
|
||||
{
|
||||
int event_loop = 0;
|
||||
int err = 0;
|
||||
u64 temp;
|
||||
+ int iters = 0;
|
||||
+ const int max_iters = 32;
|
||||
|
||||
xhci_clear_interrupt_pending(ir);
|
||||
|
||||
@@ -3131,11 +3135,27 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
|
||||
|
||||
if (err)
|
||||
break;
|
||||
+
|
||||
+ if (xhci->quirks & XHCI_PS5_SALINA_PHY) {
|
||||
+ if (++iters > max_iters) {
|
||||
+ xhci_warn(xhci,
|
||||
+ "Salina: event ring appears stuck after %d TRBs\n",
|
||||
+ iters);
|
||||
+ goto salina_stuck;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
xhci_update_erst_dequeue(xhci, ir, true);
|
||||
|
||||
return 0;
|
||||
+
|
||||
+salina_stuck:
|
||||
+ /* mark host dying so we don't deadlock future URBs */
|
||||
+ xhci->xhc_state |= XHCI_STATE_DYING;
|
||||
+
|
||||
+ xhci_update_erst_dequeue(xhci, ir, true);
|
||||
+ return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/drivers/usb/host/xhci-ring.h b/drivers/usb/host/xhci-ring.h
|
||||
new file mode 100755
|
||||
index 000000000000..193ac03a3074
|
||||
--- /dev/null
|
||||
+++ b/drivers/usb/host/xhci-ring.h
|
||||
@@ -0,0 +1,4 @@
|
||||
+#include "xhci.h"
|
||||
+
|
||||
+dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
|
||||
+int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir, bool skip_events);
|
||||
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
|
||||
index a54f5b57f205..0d99186ec967 100644
|
||||
--- a/drivers/usb/host/xhci.c
|
||||
+++ b/drivers/usb/host/xhci.c
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "xhci-trace.h"
|
||||
#include "xhci-debugfs.h"
|
||||
#include "xhci-dbgcap.h"
|
||||
+#include "xhci-ps5.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Sarah Sharp"
|
||||
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
|
||||
@@ -191,6 +192,10 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us)
|
||||
u32 state;
|
||||
int ret;
|
||||
|
||||
+ if (xhci->quirks & XHCI_PS5_SALINA_PHY) {
|
||||
+ return xhci_salina_reset(xhci, 5000);
|
||||
+ }
|
||||
+
|
||||
state = readl(&xhci->op_regs->status);
|
||||
|
||||
if (state == ~(u32)0) {
|
||||
@@ -615,6 +620,10 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
+ if(xhci->quirks & XHCI_PS5_SALINA_PHY) {
|
||||
+ salina_start_xhci_poller(xhci);
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
|
||||
index aeecd301f207..e202fbdd2f41 100644
|
||||
--- a/drivers/usb/host/xhci.h
|
||||
+++ b/drivers/usb/host/xhci.h
|
||||
@@ -1645,6 +1645,7 @@ struct xhci_hcd {
|
||||
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
|
||||
#define XHCI_ETRON_HOST BIT_ULL(49)
|
||||
#define XHCI_LIMIT_ENDPOINT_INTERVAL_9 BIT_ULL(50)
|
||||
+#define XHCI_PS5_SALINA_PHY BIT_ULL(51)
|
||||
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
|
||||
index c8700e6a694d..7d0afd378c4e 100644
|
||||
--- a/include/linux/dmi.h
|
||||
@@ -9065,6 +9879,60 @@ index 000000000000..24adcd953ab3
|
||||
+u64 svm_execute_guest(void *guest_func, u64 a0, u64 a1, u64 a2, u64 a3);
|
||||
+
|
||||
+#endif /* _LINUX_PS5_H */
|
||||
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
|
||||
index 572b1c620c5d..b776491a1241 100644
|
||||
--- a/include/net/bluetooth/hci.h
|
||||
+++ b/include/net/bluetooth/hci.h
|
||||
@@ -309,6 +309,22 @@ enum {
|
||||
*/
|
||||
HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT,
|
||||
|
||||
+ /* When this quirk is set, the HCI_OP_LE_SET_DEFAULT_PHY command is
|
||||
+ * skipped during initialization. This is required for controllers
|
||||
+ * which advertise the command but reject the default 1M PHY request.
|
||||
+ *
|
||||
+ * This quirk can be set before hci_register_dev is called.
|
||||
+ */
|
||||
+ HCI_QUIRK_BROKEN_SET_DEFAULT_PHY,
|
||||
+
|
||||
+ /* When this quirk is set, the HCI_OP_READ_LOCAL_NAME command is
|
||||
+ * skipped during initialization. This is required for controllers
|
||||
+ * which become unstable when queried for the local name.
|
||||
+ *
|
||||
+ * This quirk can be set before hci_register_dev is called.
|
||||
+ */
|
||||
+ HCI_QUIRK_BROKEN_READ_LOCAL_NAME,
|
||||
+
|
||||
/*
|
||||
* When this quirk is set, the HCI_OP_LE_EXT_CREATE_CONN command is
|
||||
* disabled. This is required for the Actions Semiconductor ATS2851
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index df23245d6ccd..1248c3735900 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -3787,6 +3787,9 @@ static int hci_read_dev_class_sync(struct hci_dev *hdev)
|
||||
/* Read Local Name */
|
||||
static int hci_read_local_name_sync(struct hci_dev *hdev)
|
||||
{
|
||||
+ if (hci_test_quirk(hdev, HCI_QUIRK_BROKEN_READ_LOCAL_NAME))
|
||||
+ return 0;
|
||||
+
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_READ_LOCAL_NAME,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
@@ -4874,6 +4877,12 @@ static int hci_le_set_default_phy_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_le_set_default_phy cp;
|
||||
|
||||
+ if (hci_test_quirk(hdev, HCI_QUIRK_BROKEN_SET_DEFAULT_PHY)) {
|
||||
+ hdev->le_tx_def_phys = HCI_LE_SET_PHY_1M;
|
||||
+ hdev->le_rx_def_phys = HCI_LE_SET_PHY_1M;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
if (!(hdev->commands[35] & 0x20)) {
|
||||
/* If the command is not supported it means only 1M PHY is
|
||||
* supported.
|
||||
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
|
||||
index 28169d7e143b..44f516daa21d 100755
|
||||
--- a/scripts/setlocalversion
|
||||
|
||||
Reference in New Issue
Block a user