Showing with 3,703 additions and 121 deletions.
  1. +8 −0 arch/x86/Kconfig
  2. +1 −0 arch/x86/include/asm/msi.h
  3. +60 −0 arch/x86/include/asm/ps4.h
  4. +6 −0 arch/x86/include/asm/setup.h
  5. +1 −0 arch/x86/include/uapi/asm/bootparam.h
  6. +1 −1 arch/x86/kernel/apic/msi.c
  7. +7 −0 arch/x86/kernel/head64.c
  8. +1 −0 arch/x86/platform/Makefile
  9. +1 −0 arch/x86/platform/ps4/Makefile
  10. +116 −0 arch/x86/platform/ps4/calibrate.c
  11. +76 −0 arch/x86/platform/ps4/ps4.c
  12. +1 −0 drivers/Makefile
  13. +40 −0 drivers/ata/ahci.c
  14. +1 −1 drivers/gpu/drm/drm_irq.c
  15. +1 −1 drivers/gpu/drm/drm_pci.c
  16. +2 −0 drivers/gpu/drm/radeon/Makefile
  17. +1 −1 drivers/gpu/drm/radeon/atombios_dp.c
  18. +2 −1 drivers/gpu/drm/radeon/atombios_encoders.c
  19. +656 −19 drivers/gpu/drm/radeon/cik.c
  20. +36 −10 drivers/gpu/drm/radeon/cik_sdma.c
  21. +584 −0 drivers/gpu/drm/radeon/ps4_bridge.c
  22. +2 −1 drivers/gpu/drm/radeon/radeon.h
  23. +12 −5 drivers/gpu/drm/radeon/radeon_asic.c
  24. +5 −0 drivers/gpu/drm/radeon/radeon_audio.c
  25. +46 −4 drivers/gpu/drm/radeon/radeon_connectors.c
  26. +1 −0 drivers/gpu/drm/radeon/radeon_device.c
  27. +1 −1 drivers/gpu/drm/radeon/radeon_display.c
  28. +14 −0 drivers/gpu/drm/radeon/radeon_drv.c
  29. +19 −0 drivers/gpu/drm/radeon/radeon_encoders.c
  30. +1 −0 drivers/gpu/drm/radeon/radeon_family.h
  31. +1 −1 drivers/gpu/drm/radeon/radeon_ib.c
  32. +1 −0 drivers/gpu/drm/radeon/radeon_pm.c
  33. +3 −0 drivers/gpu/drm/radeon/radeon_ucode.h
  34. +1 −0 drivers/gpu/drm/radeon/radeon_uvd.c
  35. +1 −0 drivers/gpu/drm/radeon/radeon_vce.c
  36. +2 −1 drivers/iommu/amd_iommu_init.c
  37. +69 −4 drivers/mmc/host/sdhci-pci-core.c
  38. +3 −0 drivers/mmc/host/sdhci-pci.h
  39. +112 −51 drivers/net/ethernet/marvell/sky2.c
  40. +7 −0 drivers/net/ethernet/marvell/sky2.h
  41. +17 −0 drivers/pci/probe.c
  42. +9 −0 drivers/ps4/Makefile
  43. +168 −0 drivers/ps4/aeolia.h
  44. +156 −0 drivers/ps4/icc/i2c.c
  45. +469 −0 drivers/ps4/ps4-apcie-icc.c
  46. +69 −0 drivers/ps4/ps4-apcie-pwrbutton.c
  47. +67 −0 drivers/ps4/ps4-apcie-uart.c
  48. +522 −0 drivers/ps4/ps4-apcie.c
  49. +7 −0 drivers/usb/host/Kconfig
  50. +1 −0 drivers/usb/host/Makefile
  51. +284 −0 drivers/usb/host/xhci-aeolia.c
  52. +16 −14 drivers/usb/host/xhci.c
  53. +1 −0 drivers/usb/host/xhci.h
  54. +2 −2 drivers/video/logo/logo.c
  55. +1 −0 include/drm/drm_pciids.h
  56. +8 −0 include/linux/pci_ids.h
  57. +3 −3 scripts/pnmtologo.c
8 changes: 8 additions & 0 deletions arch/x86/Kconfig
Expand Up @@ -462,6 +462,14 @@ config X86_NUMACHIP
enable more than ~168 cores.
If you don't have one of these, you should say N here.

config X86_PS4
bool "Sony PlayStation 4"
depends on X86_64
depends on X86_EXTENDED_PLATFORM
depends on PCI
---help---
Select to include support for the Sony PlayStation 4 game console.

config X86_VSMP
bool "ScaleMP vSMP"
select HYPERVISOR_GUEST
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/asm/msi.h
Expand Up @@ -9,5 +9,6 @@ int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
msi_alloc_info_t *arg);

void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc);
void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg);

#endif /* _ASM_X86_MSI_H */
60 changes: 60 additions & 0 deletions arch/x86/include/asm/ps4.h
@@ -0,0 +1,60 @@
/*
* ps4.h: Sony PS4 platform setup code
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*/
#ifndef _ASM_X86_PS4_H
#define _ASM_X86_PS4_H

#ifdef CONFIG_X86_PS4

#include <linux/irqdomain.h>

#define PS4_DEFAULT_TSC_FREQ 1594000000

#define EMC_TIMER_BASE 0xd0281000
#define EMC_TIMER_VALUE 0x28

extern unsigned long ps4_calibrate_tsc(void);

/*
* The PS4 Aeolia southbridge device is a composite device containing some
* standard-ish, some not-so-standard, and some completely custom functions,
* all using special MSI handling. This function does the equivalent of
* pci_enable_msi_range and friends, for those devices. Only works after the
* Aeolia MSR routing function device (function 4) has been probed.
* Returns 1 or count, depending on IRQ allocation constraints, or negative on
* error. Assigned IRQ(s) start at dev->irq.
*/
extern int apcie_assign_irqs(struct pci_dev *dev, int nvec);
extern void apcie_free_irqs(unsigned int virq, unsigned int nr_irqs);

extern int apcie_status(void);
extern int apcie_icc_cmd(u8 major, u16 minor, const void *data,
u16 length, void *reply, u16 reply_length);


#else

static inline int apcie_assign_irqs(struct pci_dev *dev, int nvec)
{
return -ENODEV;
}
static inline void apcie_free_irqs(unsigned int virq, unsigned int nvec)
{
}
static inline int apcie_status(void)
{
return -ENODEV;
}
static inline int apcie_icc_cmd(u8 major, u16 minor, const void *data,
u16 length, void *reply, u16 reply_length)
{
return -ENODEV;
}

#endif
#endif
6 changes: 6 additions & 0 deletions arch/x86/include/asm/setup.h
Expand Up @@ -58,6 +58,12 @@ extern void x86_ce4100_early_setup(void);
static inline void x86_ce4100_early_setup(void) { }
#endif

#ifdef CONFIG_X86_PS4
extern void x86_ps4_early_setup(void);
#else
static inline void x86_ps4_early_setup(void) { }
#endif

#ifndef _SETUP

#include <asm/espfix.h>
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/uapi/asm/bootparam.h
Expand Up @@ -202,6 +202,7 @@ enum x86_hardware_subarch {
X86_SUBARCH_XEN,
X86_SUBARCH_INTEL_MID,
X86_SUBARCH_CE4100,
X86_SUBARCH_PS4,
X86_NR_SUBARCHS,
};

Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/apic/msi.c
Expand Up @@ -25,7 +25,7 @@

static struct irq_domain *msi_default_domain;

static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
{
struct irq_cfg *cfg = irqd_cfg(data);

Expand Down
7 changes: 7 additions & 0 deletions arch/x86/kernel/head64.c
Expand Up @@ -192,5 +192,12 @@ void __init x86_64_start_reservations(char *real_mode_data)
break;
}

/* Call the subarch specific early setup function */
switch (boot_params.hdr.hardware_subarch) {
case X86_SUBARCH_PS4:
x86_ps4_early_setup();
break;
}

start_kernel();
}
1 change: 1 addition & 0 deletions arch/x86/platform/Makefile
Expand Up @@ -10,6 +10,7 @@ obj-y += intel-mid/
obj-y += intel-quark/
obj-y += mellanox/
obj-y += olpc/
obj-y += ps4/
obj-y += scx200/
obj-y += sfi/
obj-y += ts5500/
Expand Down
1 change: 1 addition & 0 deletions arch/x86/platform/ps4/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_X86_PS4) += ps4.o calibrate.o
116 changes: 116 additions & 0 deletions arch/x86/platform/ps4/calibrate.c
@@ -0,0 +1,116 @@
/*
* calibrate.c: Sony PS4 TSC/LAPIC calibration
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*/

#define pr_fmt(fmt) "ps4: " fmt

#include <linux/jiffies.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/ps4.h>
#include <asm/delay.h>
#include <asm/apic.h>

/* The PS4 southbridge (Aeolia) has an EMC timer that ticks at 32.768kHz,
* which seems to be an appropriate clock reference for calibration. Both TSC
* and the LAPIC timer are based on the core clock frequency and thus can be
* calibrated together. */
static void __iomem *emc_timer = NULL;

static __init inline u32 emctimer_read32(unsigned int reg)
{
return ioread32(emc_timer + reg);
}

static __init inline void emctimer_write32(unsigned int reg, u32 val)
{
iowrite32(val, emc_timer + reg);
}

static __init inline u32 emctimer_read(void)
{
u32 t1, t2;
t1 = emctimer_read32(EMC_TIMER_VALUE);
while (1) {
t2 = emctimer_read32(EMC_TIMER_VALUE);
if (t1 == t2)
return t1;
t1 = t2;
}
}

static __init unsigned long ps4_measure_tsc_freq(void)
{
unsigned long ret = 0;
u32 t1, t2;
u64 tsc1, tsc2;

// This is part of the Aeolia pcie device, but it's too early to
// do this in a driver.
emc_timer = ioremap(EMC_TIMER_BASE, 0x100);
if (!emc_timer)
goto fail;

// reset/start the timer
emctimer_write32(0x84, emctimer_read32(0x84) & (~0x01));
// udelay is not calibrated yet, so this is likely wildly off, but good
// enough to work.
udelay(300);
emctimer_write32(0x00, emctimer_read32(0x00) | 0x01);
emctimer_write32(0x84, emctimer_read32(0x84) | 0x01);

t1 = emctimer_read();
tsc1 = tsc2 = rdtsc();

while (emctimer_read() == t1) {
// 0.1s timeout should be enough
tsc2 = rdtsc();
if ((tsc2 - tsc1) > (PS4_DEFAULT_TSC_FREQ/10)) {
pr_warn("EMC timer is broken.\n");
goto fail;
}
}
pr_info("EMC timer started in %lld TSC ticks\n", tsc2 - tsc1);

// Wait for a tick boundary
t1 = emctimer_read();
while ((t2 = emctimer_read()) == t1);
tsc1 = rdtsc();

// Wait for 1024 ticks to elapse (31.25ms)
// We don't need to wait very long, as we are looking for transitions.
// At this value, a TSC uncertainty of ~50 ticks corresponds to 1ppm of
// clock accuracy.
while ((emctimer_read() - t2) < 1024);
tsc2 = rdtsc();

// TSC rate is 32 times the elapsed time
ret = (tsc2 - tsc1) * 32;

pr_info("Calibrated TSC frequency: %ld kHz\n", ret);
fail:
if (emc_timer) {
iounmap(emc_timer);
emc_timer = NULL;
}
return ret;
}

unsigned long __init ps4_calibrate_tsc(void)
{
unsigned long tsc_freq = ps4_measure_tsc_freq();

if (!tsc_freq) {
pr_warn("Unable to measure TSC frequency, assuming default.\n");
tsc_freq = PS4_DEFAULT_TSC_FREQ;
}

lapic_timer_frequency = (tsc_freq + 8 * HZ) / (16 * HZ);

return (tsc_freq + 500) / 1000;
}
76 changes: 76 additions & 0 deletions arch/x86/platform/ps4/ps4.c
@@ -0,0 +1,76 @@
/*
* ps4.c: Sony PS4 platform setup code
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*/

#define pr_fmt(fmt) "ps4: " fmt

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/scatterlist.h>
#include <linux/sfi.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/notifier.h>

#include <asm/setup.h>
#include <asm/mpspec_def.h>
#include <asm/hw_irq.h>
#include <asm/apic.h>
#include <asm/io_apic.h>
#include <asm/io.h>
#include <asm/i8259.h>
#include <asm/apb_timer.h>
#include <asm/reboot.h>
#include <asm/msr.h>
#include <asm/ps4.h>

static bool is_ps4;
bool apcie_initialized;

/*
* The RTC is part of the Aeolia PCI device and will be implemented there as
* an RTC class device; stub these out.
*/
static void dummy_get_wallclock(struct timespec *now)
{
now->tv_sec = now->tv_nsec = 0;
}
static int dummy_set_wallclock(const struct timespec *now)
{
return -ENODEV;
}

/*
* Provide a way for generic drivers to query for the availability of the
* PS4 apcie driver/device, which is a dependency for them.
*/
int apcie_status(void)
{
if (!is_ps4)
return -ENODEV;
return apcie_initialized;
}
EXPORT_SYMBOL_GPL(apcie_status);

void icc_reboot(void);

/*
* PS4 specific x86_init function overrides and early setup calls.
*/
void __init x86_ps4_early_setup(void)
{
pr_info("x86_ps4_early_setup: PS4 early setup\n");
is_ps4 = true;
x86_platform.calibrate_tsc = ps4_calibrate_tsc;
x86_platform.get_wallclock = dummy_get_wallclock;
x86_platform.set_wallclock = dummy_set_wallclock;

legacy_pic = &null_legacy_pic;
machine_ops.emergency_restart = icc_reboot;
}
1 change: 1 addition & 0 deletions drivers/Makefile
Expand Up @@ -81,6 +81,7 @@ obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_SPMI) += spmi/
obj-$(CONFIG_HSI) += hsi/
obj-$(CONFIG_X86_PS4) += ps4/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
Expand Down