Skip to content

Commit

Permalink
Merge pull request #1 from Razor246/ps4-5.1.0
Browse files Browse the repository at this point in the history
Ps4 5.1.0
  • Loading branch information
Razor246 authored May 9, 2019
2 parents 9ebe313 + 6b69437 commit 5e3af20
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 1 deletion.
8 changes: 8 additions & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,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
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,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
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,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
Original file line number Diff line number Diff line change
Expand Up @@ -467,5 +467,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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ obj-y += intel/
obj-y += intel-mid/
obj-y += intel-quark/
obj-y += olpc/
obj-y += ps4/
obj-y += scx200/
obj-y += sfi/
obj-y += ts5500/
Expand Down
116 changes: 116 additions & 0 deletions arch/x86/platform/PS4/calibrate.c
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_SPMI) += spmi/
obj-$(CONFIG_HSI) += hsi/
obj-$(CONFIG_SLIMBUS) += slimbus/
obj-$(CONFIG_X86_PS4) += ps4/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
Expand Down
Loading

0 comments on commit 5e3af20

Please sign in to comment.