162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  PS3 platform setup routines.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2006 Sony Computer Entertainment Inc.
662306a36Sopenharmony_ci *  Copyright 2006 Sony Corp.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/root_dev.h>
1362306a36Sopenharmony_ci#include <linux/console.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/memblock.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/machdep.h>
1962306a36Sopenharmony_ci#include <asm/firmware.h>
2062306a36Sopenharmony_ci#include <asm/time.h>
2162306a36Sopenharmony_ci#include <asm/iommu.h>
2262306a36Sopenharmony_ci#include <asm/udbg.h>
2362306a36Sopenharmony_ci#include <asm/lv1call.h>
2462306a36Sopenharmony_ci#include <asm/ps3gpu.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "platform.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#if defined(DEBUG)
2962306a36Sopenharmony_ci#define DBG udbg_printf
3062306a36Sopenharmony_ci#else
3162306a36Sopenharmony_ci#define DBG pr_debug
3262306a36Sopenharmony_ci#endif
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* mutex synchronizing GPU accesses and video mode changes */
3562306a36Sopenharmony_ciDEFINE_MUTEX(ps3_gpu_mutex);
3662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_gpu_mutex);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic union ps3_firmware_version ps3_firmware_version;
3962306a36Sopenharmony_cistatic char ps3_firmware_version_str[16];
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_civoid ps3_get_firmware_version(union ps3_firmware_version *v)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	*v = ps3_firmware_version;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_get_firmware_version);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint ps3_compare_firmware_version(u16 major, u16 minor, u16 rev)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	union ps3_firmware_version x;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	x.pad = 0;
5262306a36Sopenharmony_ci	x.major = major;
5362306a36Sopenharmony_ci	x.minor = minor;
5462306a36Sopenharmony_ci	x.rev = rev;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return (ps3_firmware_version.raw > x.raw) -
5762306a36Sopenharmony_ci	       (ps3_firmware_version.raw < x.raw);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3_compare_firmware_version);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void ps3_power_save(void)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	/*
6462306a36Sopenharmony_ci	 * lv1_pause() puts the PPE thread into inactive state until an
6562306a36Sopenharmony_ci	 * irq on an unmasked plug exists. MSR[EE] has no effect.
6662306a36Sopenharmony_ci	 * flags: 0 = wake on DEC interrupt, 1 = ignore DEC interrupt.
6762306a36Sopenharmony_ci	 */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	lv1_pause(0);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void __noreturn ps3_restart(char *cmd)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	DBG("%s:%d cmd '%s'\n", __func__, __LINE__, cmd);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	smp_send_stop();
7762306a36Sopenharmony_ci	ps3_sys_manager_restart(); /* never returns */
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void ps3_power_off(void)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	DBG("%s:%d\n", __func__, __LINE__);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	smp_send_stop();
8562306a36Sopenharmony_ci	ps3_sys_manager_power_off(); /* never returns */
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void __noreturn ps3_halt(void)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	DBG("%s:%d\n", __func__, __LINE__);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	smp_send_stop();
9362306a36Sopenharmony_ci	ps3_sys_manager_halt(); /* never returns */
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void ps3_panic(char *str)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	DBG("%s:%d %s\n", __func__, __LINE__, str);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	smp_send_stop();
10162306a36Sopenharmony_ci	printk("\n");
10262306a36Sopenharmony_ci	printk("   System does not reboot automatically.\n");
10362306a36Sopenharmony_ci	printk("   Please press POWER button.\n");
10462306a36Sopenharmony_ci	printk("\n");
10562306a36Sopenharmony_ci	panic_flush_kmsg_end();
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	while(1)
10862306a36Sopenharmony_ci		lv1_pause(1);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \
11262306a36Sopenharmony_ci    defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
11362306a36Sopenharmony_cistatic void __init prealloc(struct ps3_prealloc *p)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	if (!p->size)
11662306a36Sopenharmony_ci		return;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	p->address = memblock_alloc(p->size, p->align);
11962306a36Sopenharmony_ci	if (!p->address)
12062306a36Sopenharmony_ci		panic("%s: Failed to allocate %lu bytes align=0x%lx\n",
12162306a36Sopenharmony_ci		      __func__, p->size, p->align);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size,
12462306a36Sopenharmony_ci	       p->address);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
12962306a36Sopenharmony_cistruct ps3_prealloc ps3fb_videomemory = {
13062306a36Sopenharmony_ci	.name = "ps3fb videomemory",
13162306a36Sopenharmony_ci	.size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
13262306a36Sopenharmony_ci	.align = 1024*1024		/* the GPU requires 1 MiB alignment */
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3fb_videomemory);
13562306a36Sopenharmony_ci#define prealloc_ps3fb_videomemory()	prealloc(&ps3fb_videomemory)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int __init early_parse_ps3fb(char *p)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	if (!p)
14062306a36Sopenharmony_ci		return 1;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ps3fb_videomemory.size = ALIGN(memparse(p, &p),
14362306a36Sopenharmony_ci					   ps3fb_videomemory.align);
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ciearly_param("ps3fb", early_parse_ps3fb);
14762306a36Sopenharmony_ci#else
14862306a36Sopenharmony_ci#define prealloc_ps3fb_videomemory()	do { } while (0)
14962306a36Sopenharmony_ci#endif
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
15262306a36Sopenharmony_cistruct ps3_prealloc ps3flash_bounce_buffer = {
15362306a36Sopenharmony_ci	.name = "ps3flash bounce buffer",
15462306a36Sopenharmony_ci	.size = 256*1024,
15562306a36Sopenharmony_ci	.align = 256*1024
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3flash_bounce_buffer);
15862306a36Sopenharmony_ci#define prealloc_ps3flash_bounce_buffer()	prealloc(&ps3flash_bounce_buffer)
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int __init early_parse_ps3flash(char *p)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	if (!p)
16362306a36Sopenharmony_ci		return 1;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!strcmp(p, "off"))
16662306a36Sopenharmony_ci		ps3flash_bounce_buffer.size = 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ciearly_param("ps3flash", early_parse_ps3flash);
17162306a36Sopenharmony_ci#else
17262306a36Sopenharmony_ci#define prealloc_ps3flash_bounce_buffer()	do { } while (0)
17362306a36Sopenharmony_ci#endif
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int ps3_set_dabr(unsigned long dabr, unsigned long dabrx)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	/* Have to set at least one bit in the DABRX */
17862306a36Sopenharmony_ci	if (dabrx == 0 && dabr == 0)
17962306a36Sopenharmony_ci		dabrx = DABRX_USER;
18062306a36Sopenharmony_ci	/* hypervisor only allows us to set BTI, Kernel and user */
18162306a36Sopenharmony_ci	dabrx &= DABRX_BTI | DABRX_KERNEL | DABRX_USER;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return lv1_set_dabr(dabr, dabrx) ? -1 : 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic ssize_t ps3_fw_version_show(struct kobject *kobj,
18762306a36Sopenharmony_ci	struct kobj_attribute *attr, char *buf)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	return sprintf(buf, "%s", ps3_firmware_version_str);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int __init ps3_setup_sysfs(void)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	static struct kobj_attribute attr = __ATTR(fw-version, S_IRUGO,
19562306a36Sopenharmony_ci		ps3_fw_version_show, NULL);
19662306a36Sopenharmony_ci	static struct kobject *kobj;
19762306a36Sopenharmony_ci	int result;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	kobj = kobject_create_and_add("ps3", firmware_kobj);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!kobj) {
20262306a36Sopenharmony_ci		pr_warn("%s:%d: kobject_create_and_add failed.\n", __func__,
20362306a36Sopenharmony_ci			__LINE__);
20462306a36Sopenharmony_ci		return -ENOMEM;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	result = sysfs_create_file(kobj, &attr.attr);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (result) {
21062306a36Sopenharmony_ci		pr_warn("%s:%d: sysfs_create_file failed.\n", __func__,
21162306a36Sopenharmony_ci			__LINE__);
21262306a36Sopenharmony_ci		kobject_put(kobj);
21362306a36Sopenharmony_ci		return -ENOMEM;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_cicore_initcall(ps3_setup_sysfs);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void __init ps3_setup_arch(void)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	u64 tmp;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	DBG(" -> %s:%d\n", __func__, __LINE__);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	lv1_get_version_info(&ps3_firmware_version.raw, &tmp);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	snprintf(ps3_firmware_version_str, sizeof(ps3_firmware_version_str),
22962306a36Sopenharmony_ci		"%u.%u.%u", ps3_firmware_version.major,
23062306a36Sopenharmony_ci		ps3_firmware_version.minor, ps3_firmware_version.rev);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	printk(KERN_INFO "PS3 firmware version %s\n", ps3_firmware_version_str);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ps3_spu_set_platform();
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci#ifdef CONFIG_SMP
23762306a36Sopenharmony_ci	smp_init_ps3();
23862306a36Sopenharmony_ci#endif
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	prealloc_ps3fb_videomemory();
24162306a36Sopenharmony_ci	prealloc_ps3flash_bounce_buffer();
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	ppc_md.power_save = ps3_power_save;
24462306a36Sopenharmony_ci	ps3_os_area_init();
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	DBG(" <- %s:%d\n", __func__, __LINE__);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void __init ps3_progress(char *s, unsigned short hex)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	printk("*** %04x : %s\n", hex, s ? s : "");
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_civoid __init ps3_early_mm_init(void)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	unsigned long htab_size;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ps3_mm_init();
25962306a36Sopenharmony_ci	ps3_mm_vas_create(&htab_size);
26062306a36Sopenharmony_ci	ps3_hpte_init(htab_size);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int __init ps3_probe(void)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	DBG(" -> %s:%d\n", __func__, __LINE__);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ps3_os_area_save_params();
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	pm_power_off = ps3_power_off;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	DBG(" <- %s:%d\n", __func__, __LINE__);
27262306a36Sopenharmony_ci	return 1;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci#if defined(CONFIG_KEXEC_CORE)
27662306a36Sopenharmony_cistatic void ps3_kexec_cpu_down(int crash_shutdown, int secondary)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	int cpu = smp_processor_id();
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ps3_smp_cleanup_cpu(cpu);
28362306a36Sopenharmony_ci	ps3_shutdown_IRQ(cpu);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	DBG(" <- %s:%d\n", __func__, __LINE__);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci#endif
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cidefine_machine(ps3) {
29062306a36Sopenharmony_ci	.name				= "PS3",
29162306a36Sopenharmony_ci	.compatible			= "sony,ps3",
29262306a36Sopenharmony_ci	.probe				= ps3_probe,
29362306a36Sopenharmony_ci	.setup_arch			= ps3_setup_arch,
29462306a36Sopenharmony_ci	.init_IRQ			= ps3_init_IRQ,
29562306a36Sopenharmony_ci	.panic				= ps3_panic,
29662306a36Sopenharmony_ci	.get_boot_time			= ps3_get_boot_time,
29762306a36Sopenharmony_ci	.set_dabr			= ps3_set_dabr,
29862306a36Sopenharmony_ci	.calibrate_decr			= ps3_calibrate_decr,
29962306a36Sopenharmony_ci	.progress			= ps3_progress,
30062306a36Sopenharmony_ci	.restart			= ps3_restart,
30162306a36Sopenharmony_ci	.halt				= ps3_halt,
30262306a36Sopenharmony_ci#if defined(CONFIG_KEXEC_CORE)
30362306a36Sopenharmony_ci	.kexec_cpu_down			= ps3_kexec_cpu_down,
30462306a36Sopenharmony_ci#endif
30562306a36Sopenharmony_ci};
306