Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ps4 5.1.0 #1

Merged
merged 23 commits into from
May 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions arch/x86/platform/PS4/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
obj-$(CONFIG_X86_PS4) += ps4.o calibrate.o
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