mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 15:28:50 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
173
arch/powerpc/platforms/ps3/Kconfig
Normal file
173
arch/powerpc/platforms/ps3/Kconfig
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
config PPC_PS3
|
||||
bool "Sony PS3"
|
||||
depends on PPC64 && PPC_BOOK3S
|
||||
select PPC_CELL
|
||||
select USB_OHCI_LITTLE_ENDIAN
|
||||
select USB_OHCI_BIG_ENDIAN_MMIO
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select PPC_PCI_CHOICE
|
||||
help
|
||||
This option enables support for the Sony PS3 game console
|
||||
and other platforms using the PS3 hypervisor. Enabling this
|
||||
option will allow building otheros.bld, a kernel image suitable
|
||||
for programming into flash memory, and vmlinux, a kernel image
|
||||
suitable for loading via kexec.
|
||||
|
||||
menu "PS3 Platform Options"
|
||||
depends on PPC_PS3
|
||||
|
||||
config PS3_ADVANCED
|
||||
depends on PPC_PS3
|
||||
bool "PS3 Advanced configuration options"
|
||||
help
|
||||
This gives you access to some advanced options for the PS3. The
|
||||
defaults should be fine for most users, but these options may make
|
||||
it possible to better control the kernel configuration if you know
|
||||
what you are doing.
|
||||
|
||||
Note that the answer to this question won't directly affect the
|
||||
kernel: saying N will just cause the configurator to skip all
|
||||
the questions about these options.
|
||||
|
||||
Most users should say N to this question.
|
||||
|
||||
config PS3_HTAB_SIZE
|
||||
depends on PPC_PS3
|
||||
int "PS3 Platform pagetable size" if PS3_ADVANCED
|
||||
range 18 20
|
||||
default 20
|
||||
help
|
||||
This option is only for experts who may have the desire to fine
|
||||
tune the pagetable size on their system. The value here is
|
||||
expressed as the log2 of the page table size. Valid values are
|
||||
18, 19, and 20, corresponding to 256KB, 512KB and 1MB respectively.
|
||||
|
||||
If unsure, choose the default (20) with the confidence that your
|
||||
system will have optimal runtime performance.
|
||||
|
||||
config PS3_DYNAMIC_DMA
|
||||
depends on PPC_PS3
|
||||
bool "PS3 Platform dynamic DMA page table management"
|
||||
default n
|
||||
help
|
||||
This option will enable kernel support to take advantage of the
|
||||
per device dynamic DMA page table management provided by the Cell
|
||||
processor's IO Controller. This support incurs some runtime
|
||||
overhead and also slightly increases kernel memory usage. The
|
||||
current implementation should be considered experimental.
|
||||
|
||||
This support is mainly for Linux kernel development. If unsure,
|
||||
say N.
|
||||
|
||||
config PS3_VUART
|
||||
depends on PPC_PS3
|
||||
tristate
|
||||
|
||||
config PS3_PS3AV
|
||||
depends on PPC_PS3
|
||||
tristate "PS3 AV settings driver" if PS3_ADVANCED
|
||||
select PS3_VUART
|
||||
default y
|
||||
help
|
||||
Include support for the PS3 AV Settings driver.
|
||||
|
||||
This support is required for PS3 graphics and sound. In
|
||||
general, all users will say Y or M.
|
||||
|
||||
config PS3_SYS_MANAGER
|
||||
depends on PPC_PS3
|
||||
tristate "PS3 System Manager driver" if PS3_ADVANCED
|
||||
select PS3_VUART
|
||||
default y
|
||||
help
|
||||
Include support for the PS3 System Manager.
|
||||
|
||||
This support is required for PS3 system control. In
|
||||
general, all users will say Y or M.
|
||||
|
||||
config PS3_REPOSITORY_WRITE
|
||||
bool "PS3 Repository write support" if PS3_ADVANCED
|
||||
depends on PPC_PS3
|
||||
default n
|
||||
help
|
||||
Enables support for writing to the PS3 System Repository.
|
||||
|
||||
This support is intended for bootloaders that need to store data
|
||||
in the repository for later boot stages.
|
||||
|
||||
If in doubt, say N here and reduce the size of the kernel by a
|
||||
small amount.
|
||||
|
||||
config PS3_STORAGE
|
||||
depends on PPC_PS3
|
||||
tristate
|
||||
|
||||
config PS3_DISK
|
||||
tristate "PS3 Disk Storage Driver"
|
||||
depends on PPC_PS3 && BLOCK
|
||||
select PS3_STORAGE
|
||||
help
|
||||
Include support for the PS3 Disk Storage.
|
||||
|
||||
This support is required to access the PS3 hard disk.
|
||||
In general, all users will say Y or M.
|
||||
|
||||
config PS3_ROM
|
||||
tristate "PS3 BD/DVD/CD-ROM Storage Driver"
|
||||
depends on PPC_PS3 && SCSI
|
||||
select PS3_STORAGE
|
||||
help
|
||||
Include support for the PS3 ROM Storage.
|
||||
|
||||
This support is required to access the PS3 BD/DVD/CD-ROM drive.
|
||||
In general, all users will say Y or M.
|
||||
Also make sure to say Y or M to "SCSI CDROM support" later.
|
||||
|
||||
config PS3_FLASH
|
||||
tristate "PS3 FLASH ROM Storage Driver"
|
||||
depends on PPC_PS3
|
||||
select PS3_STORAGE
|
||||
help
|
||||
Include support for the PS3 FLASH ROM Storage.
|
||||
|
||||
This support is required to access the PS3 FLASH ROM, which
|
||||
contains the boot loader and some boot options.
|
||||
In general, PS3 OtherOS users will say Y or M.
|
||||
|
||||
As this driver needs a fixed buffer of 256 KiB of memory, it can
|
||||
be disabled on the kernel command line using "ps3flash=off", to
|
||||
not allocate this fixed buffer.
|
||||
|
||||
config PS3_VRAM
|
||||
tristate "PS3 Video RAM Storage Driver"
|
||||
depends on FB_PS3=y && BLOCK && m
|
||||
help
|
||||
This driver allows you to use excess PS3 video RAM as volatile
|
||||
storage or system swap.
|
||||
|
||||
config PS3_LPM
|
||||
tristate "PS3 Logical Performance Monitor support"
|
||||
depends on PPC_PS3
|
||||
help
|
||||
Include support for the PS3 Logical Performance Monitor.
|
||||
|
||||
This support is required to use the logical performance monitor
|
||||
of the PS3's LV1 hypervisor.
|
||||
|
||||
If you intend to use the advanced performance monitoring and
|
||||
profiling support of the Cell processor with programs like
|
||||
oprofile and perfmon2, then say Y or M, otherwise say N.
|
||||
|
||||
config PS3GELIC_UDBG
|
||||
bool "PS3 udbg output via UDP broadcasts on Ethernet"
|
||||
depends on PPC_PS3
|
||||
help
|
||||
Enables udbg early debugging output by sending broadcast UDP
|
||||
via the Ethernet port (UDP port number 18194).
|
||||
|
||||
This driver uses a trivial implementation and is independent
|
||||
from the main PS3 gelic network driver.
|
||||
|
||||
If in doubt, say N here.
|
||||
|
||||
endmenu
|
||||
8
arch/powerpc/platforms/ps3/Makefile
Normal file
8
arch/powerpc/platforms/ps3/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o
|
||||
obj-y += interrupt.o exports.o os-area.o
|
||||
obj-y += system-bus.o
|
||||
|
||||
obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_SPU_BASE) += spu.o
|
||||
obj-y += device-init.o
|
||||
991
arch/powerpc/platforms/ps3/device-init.c
Normal file
991
arch/powerpc/platforms/ps3/device-init.c
Normal file
|
|
@ -0,0 +1,991 @@
|
|||
/*
|
||||
* PS3 device registration routines.
|
||||
*
|
||||
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||
* Copyright 2007 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/ps3stor.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
static int __init ps3_register_lpm_devices(void)
|
||||
{
|
||||
int result;
|
||||
u64 tmp1;
|
||||
u64 tmp2;
|
||||
struct ps3_system_bus_device *dev;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->match_id = PS3_MATCH_ID_LPM;
|
||||
dev->dev_type = PS3_DEVICE_TYPE_LPM;
|
||||
|
||||
/* The current lpm driver only supports a single BE processor. */
|
||||
|
||||
result = ps3_repository_read_be_node_id(0, &dev->lpm.node_id);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_repository_read_be_node_id failed \n",
|
||||
__func__, __LINE__);
|
||||
goto fail_read_repo;
|
||||
}
|
||||
|
||||
result = ps3_repository_read_lpm_privileges(dev->lpm.node_id, &tmp1,
|
||||
&dev->lpm.rights);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_repository_read_lpm_privleges failed \n",
|
||||
__func__, __LINE__);
|
||||
goto fail_read_repo;
|
||||
}
|
||||
|
||||
lv1_get_logical_partition_id(&tmp2);
|
||||
|
||||
if (tmp1 != tmp2) {
|
||||
pr_debug("%s:%d: wrong lpar\n",
|
||||
__func__, __LINE__);
|
||||
result = -ENODEV;
|
||||
goto fail_rights;
|
||||
}
|
||||
|
||||
if (!(dev->lpm.rights & PS3_LPM_RIGHTS_USE_LPM)) {
|
||||
pr_debug("%s:%d: don't have rights to use lpm\n",
|
||||
__func__, __LINE__);
|
||||
result = -EPERM;
|
||||
goto fail_rights;
|
||||
}
|
||||
|
||||
pr_debug("%s:%d: pu_id %llu, rights %llu(%llxh)\n",
|
||||
__func__, __LINE__, dev->lpm.pu_id, dev->lpm.rights,
|
||||
dev->lpm.rights);
|
||||
|
||||
result = ps3_repository_read_pu_id(0, &dev->lpm.pu_id);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_repository_read_pu_id failed \n",
|
||||
__func__, __LINE__);
|
||||
goto fail_read_repo;
|
||||
}
|
||||
|
||||
result = ps3_system_bus_device_register(dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
|
||||
|
||||
fail_register:
|
||||
fail_rights:
|
||||
fail_read_repo:
|
||||
kfree(dev);
|
||||
pr_debug(" <- %s:%d: failed\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_setup_gelic_device - Setup and register a gelic device instance.
|
||||
*
|
||||
* Allocates memory for a struct ps3_system_bus_device instance, initialises the
|
||||
* structure members, and registers the device instance with the system bus.
|
||||
*/
|
||||
|
||||
static int __init ps3_setup_gelic_device(
|
||||
const struct ps3_repository_device *repo)
|
||||
{
|
||||
int result;
|
||||
struct layout {
|
||||
struct ps3_system_bus_device dev;
|
||||
struct ps3_dma_region d_region;
|
||||
} *p;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB);
|
||||
BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_GELIC);
|
||||
|
||||
p = kzalloc(sizeof(struct layout), GFP_KERNEL);
|
||||
|
||||
if (!p) {
|
||||
result = -ENOMEM;
|
||||
goto fail_malloc;
|
||||
}
|
||||
|
||||
p->dev.match_id = PS3_MATCH_ID_GELIC;
|
||||
p->dev.dev_type = PS3_DEVICE_TYPE_SB;
|
||||
p->dev.bus_id = repo->bus_id;
|
||||
p->dev.dev_id = repo->dev_id;
|
||||
p->dev.d_region = &p->d_region;
|
||||
|
||||
result = ps3_repository_find_interrupt(repo,
|
||||
PS3_INTERRUPT_TYPE_EVENT_PORT, &p->dev.interrupt_id);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_repository_find_interrupt failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_find_interrupt;
|
||||
}
|
||||
|
||||
BUG_ON(p->dev.interrupt_id != 0);
|
||||
|
||||
result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K,
|
||||
PS3_DMA_OTHER, NULL, 0);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_dma_region_init failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_dma_init;
|
||||
}
|
||||
|
||||
result = ps3_system_bus_device_register(&p->dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return result;
|
||||
|
||||
fail_device_register:
|
||||
fail_dma_init:
|
||||
fail_find_interrupt:
|
||||
kfree(p);
|
||||
fail_malloc:
|
||||
pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init_refok ps3_setup_uhc_device(
|
||||
const struct ps3_repository_device *repo, enum ps3_match_id match_id,
|
||||
enum ps3_interrupt_type interrupt_type, enum ps3_reg_type reg_type)
|
||||
{
|
||||
int result;
|
||||
struct layout {
|
||||
struct ps3_system_bus_device dev;
|
||||
struct ps3_dma_region d_region;
|
||||
struct ps3_mmio_region m_region;
|
||||
} *p;
|
||||
u64 bus_addr;
|
||||
u64 len;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
BUG_ON(repo->bus_type != PS3_BUS_TYPE_SB);
|
||||
BUG_ON(repo->dev_type != PS3_DEV_TYPE_SB_USB);
|
||||
|
||||
p = kzalloc(sizeof(struct layout), GFP_KERNEL);
|
||||
|
||||
if (!p) {
|
||||
result = -ENOMEM;
|
||||
goto fail_malloc;
|
||||
}
|
||||
|
||||
p->dev.match_id = match_id;
|
||||
p->dev.dev_type = PS3_DEVICE_TYPE_SB;
|
||||
p->dev.bus_id = repo->bus_id;
|
||||
p->dev.dev_id = repo->dev_id;
|
||||
p->dev.d_region = &p->d_region;
|
||||
p->dev.m_region = &p->m_region;
|
||||
|
||||
result = ps3_repository_find_interrupt(repo,
|
||||
interrupt_type, &p->dev.interrupt_id);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_repository_find_interrupt failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_find_interrupt;
|
||||
}
|
||||
|
||||
result = ps3_repository_find_reg(repo, reg_type,
|
||||
&bus_addr, &len);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_repository_find_reg failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_find_reg;
|
||||
}
|
||||
|
||||
result = ps3_dma_region_init(&p->dev, p->dev.d_region, PS3_DMA_64K,
|
||||
PS3_DMA_INTERNAL, NULL, 0);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_dma_region_init failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_dma_init;
|
||||
}
|
||||
|
||||
result = ps3_mmio_region_init(&p->dev, p->dev.m_region, bus_addr, len,
|
||||
PS3_MMIO_4K);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_mmio_region_init failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_mmio_init;
|
||||
}
|
||||
|
||||
result = ps3_system_bus_device_register(&p->dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return result;
|
||||
|
||||
fail_device_register:
|
||||
fail_mmio_init:
|
||||
fail_dma_init:
|
||||
fail_find_reg:
|
||||
fail_find_interrupt:
|
||||
kfree(p);
|
||||
fail_malloc:
|
||||
pr_debug(" <- %s:%d: fail.\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init ps3_setup_ehci_device(
|
||||
const struct ps3_repository_device *repo)
|
||||
{
|
||||
return ps3_setup_uhc_device(repo, PS3_MATCH_ID_EHCI,
|
||||
PS3_INTERRUPT_TYPE_SB_EHCI, PS3_REG_TYPE_SB_EHCI);
|
||||
}
|
||||
|
||||
static int __init ps3_setup_ohci_device(
|
||||
const struct ps3_repository_device *repo)
|
||||
{
|
||||
return ps3_setup_uhc_device(repo, PS3_MATCH_ID_OHCI,
|
||||
PS3_INTERRUPT_TYPE_SB_OHCI, PS3_REG_TYPE_SB_OHCI);
|
||||
}
|
||||
|
||||
static int __init ps3_setup_vuart_device(enum ps3_match_id match_id,
|
||||
unsigned int port_number)
|
||||
{
|
||||
int result;
|
||||
struct layout {
|
||||
struct ps3_system_bus_device dev;
|
||||
} *p;
|
||||
|
||||
pr_debug(" -> %s:%d: match_id %u, port %u\n", __func__, __LINE__,
|
||||
match_id, port_number);
|
||||
|
||||
p = kzalloc(sizeof(struct layout), GFP_KERNEL);
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->dev.match_id = match_id;
|
||||
p->dev.dev_type = PS3_DEVICE_TYPE_VUART;
|
||||
p->dev.port_number = port_number;
|
||||
|
||||
result = ps3_system_bus_device_register(&p->dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
|
||||
fail_device_register:
|
||||
kfree(p);
|
||||
pr_debug(" <- %s:%d fail\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
|
||||
enum ps3_match_id match_id)
|
||||
{
|
||||
int result;
|
||||
struct ps3_storage_device *p;
|
||||
u64 port, blk_size, num_blocks;
|
||||
unsigned int num_regions, i;
|
||||
|
||||
pr_debug(" -> %s:%u: match_id %u\n", __func__, __LINE__, match_id);
|
||||
|
||||
result = ps3_repository_read_stor_dev_info(repo->bus_index,
|
||||
repo->dev_index, &port,
|
||||
&blk_size, &num_blocks,
|
||||
&num_regions);
|
||||
if (result) {
|
||||
printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n",
|
||||
__func__, __LINE__, result);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_debug("%s:%u: (%u:%u:%u): port %llu blk_size %llu num_blocks %llu "
|
||||
"num_regions %u\n", __func__, __LINE__, repo->bus_index,
|
||||
repo->dev_index, repo->dev_type, port, blk_size, num_blocks,
|
||||
num_regions);
|
||||
|
||||
p = kzalloc(sizeof(struct ps3_storage_device) +
|
||||
num_regions * sizeof(struct ps3_storage_region),
|
||||
GFP_KERNEL);
|
||||
if (!p) {
|
||||
result = -ENOMEM;
|
||||
goto fail_malloc;
|
||||
}
|
||||
|
||||
p->sbd.match_id = match_id;
|
||||
p->sbd.dev_type = PS3_DEVICE_TYPE_SB;
|
||||
p->sbd.bus_id = repo->bus_id;
|
||||
p->sbd.dev_id = repo->dev_id;
|
||||
p->sbd.d_region = &p->dma_region;
|
||||
p->blk_size = blk_size;
|
||||
p->num_regions = num_regions;
|
||||
|
||||
result = ps3_repository_find_interrupt(repo,
|
||||
PS3_INTERRUPT_TYPE_EVENT_PORT,
|
||||
&p->sbd.interrupt_id);
|
||||
if (result) {
|
||||
printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__,
|
||||
__LINE__, result);
|
||||
result = -ENODEV;
|
||||
goto fail_find_interrupt;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_regions; i++) {
|
||||
unsigned int id;
|
||||
u64 start, size;
|
||||
|
||||
result = ps3_repository_read_stor_dev_region(repo->bus_index,
|
||||
repo->dev_index,
|
||||
i, &id, &start,
|
||||
&size);
|
||||
if (result) {
|
||||
printk(KERN_ERR
|
||||
"%s:%u: read_stor_dev_region failed %d\n",
|
||||
__func__, __LINE__, result);
|
||||
result = -ENODEV;
|
||||
goto fail_read_region;
|
||||
}
|
||||
pr_debug("%s:%u: region %u: id %u start %llu size %llu\n",
|
||||
__func__, __LINE__, i, id, start, size);
|
||||
|
||||
p->regions[i].id = id;
|
||||
p->regions[i].start = start;
|
||||
p->regions[i].size = size;
|
||||
}
|
||||
|
||||
result = ps3_system_bus_device_register(&p->sbd);
|
||||
if (result) {
|
||||
pr_debug("%s:%u ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
|
||||
pr_debug(" <- %s:%u\n", __func__, __LINE__);
|
||||
return 0;
|
||||
|
||||
fail_device_register:
|
||||
fail_read_region:
|
||||
fail_find_interrupt:
|
||||
kfree(p);
|
||||
fail_malloc:
|
||||
pr_debug(" <- %s:%u: fail.\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init ps3_register_vuart_devices(void)
|
||||
{
|
||||
int result;
|
||||
unsigned int port_number;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
result = ps3_repository_read_vuart_av_port(&port_number);
|
||||
if (result)
|
||||
port_number = 0; /* av default */
|
||||
|
||||
result = ps3_setup_vuart_device(PS3_MATCH_ID_AV_SETTINGS, port_number);
|
||||
WARN_ON(result);
|
||||
|
||||
result = ps3_repository_read_vuart_sysmgr_port(&port_number);
|
||||
if (result)
|
||||
port_number = 2; /* sysmgr default */
|
||||
|
||||
result = ps3_setup_vuart_device(PS3_MATCH_ID_SYSTEM_MANAGER,
|
||||
port_number);
|
||||
WARN_ON(result);
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init ps3_register_sound_devices(void)
|
||||
{
|
||||
int result;
|
||||
struct layout {
|
||||
struct ps3_system_bus_device dev;
|
||||
struct ps3_dma_region d_region;
|
||||
struct ps3_mmio_region m_region;
|
||||
} *p;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->dev.match_id = PS3_MATCH_ID_SOUND;
|
||||
p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
|
||||
p->dev.d_region = &p->d_region;
|
||||
p->dev.m_region = &p->m_region;
|
||||
|
||||
result = ps3_system_bus_device_register(&p->dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
|
||||
fail_device_register:
|
||||
kfree(p);
|
||||
pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init ps3_register_graphics_devices(void)
|
||||
{
|
||||
int result;
|
||||
struct layout {
|
||||
struct ps3_system_bus_device dev;
|
||||
} *p;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
p = kzalloc(sizeof(struct layout), GFP_KERNEL);
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->dev.match_id = PS3_MATCH_ID_GPU;
|
||||
p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_FB;
|
||||
p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
|
||||
|
||||
result = ps3_system_bus_device_register(&p->dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
|
||||
fail_device_register:
|
||||
kfree(p);
|
||||
pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init ps3_register_ramdisk_device(void)
|
||||
{
|
||||
int result;
|
||||
struct layout {
|
||||
struct ps3_system_bus_device dev;
|
||||
} *p;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
p = kzalloc(sizeof(struct layout), GFP_KERNEL);
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->dev.match_id = PS3_MATCH_ID_GPU;
|
||||
p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK;
|
||||
p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
|
||||
|
||||
result = ps3_system_bus_device_register(&p->dev);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_device_register;
|
||||
}
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
|
||||
fail_device_register:
|
||||
kfree(p);
|
||||
pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_setup_dynamic_device - Setup a dynamic device from the repository
|
||||
*/
|
||||
|
||||
static int ps3_setup_dynamic_device(const struct ps3_repository_device *repo)
|
||||
{
|
||||
int result;
|
||||
|
||||
switch (repo->dev_type) {
|
||||
case PS3_DEV_TYPE_STOR_DISK:
|
||||
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_DISK);
|
||||
|
||||
/* Some devices are not accessible from the Other OS lpar. */
|
||||
if (result == -ENODEV) {
|
||||
result = 0;
|
||||
pr_debug("%s:%u: not accessible\n", __func__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
if (result)
|
||||
pr_debug("%s:%u ps3_setup_storage_dev failed\n",
|
||||
__func__, __LINE__);
|
||||
break;
|
||||
|
||||
case PS3_DEV_TYPE_STOR_ROM:
|
||||
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ROM);
|
||||
if (result)
|
||||
pr_debug("%s:%u ps3_setup_storage_dev failed\n",
|
||||
__func__, __LINE__);
|
||||
break;
|
||||
|
||||
case PS3_DEV_TYPE_STOR_FLASH:
|
||||
result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_FLASH);
|
||||
if (result)
|
||||
pr_debug("%s:%u ps3_setup_storage_dev failed\n",
|
||||
__func__, __LINE__);
|
||||
break;
|
||||
|
||||
default:
|
||||
result = 0;
|
||||
pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__,
|
||||
repo->dev_type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_setup_static_device - Setup a static device from the repository
|
||||
*/
|
||||
|
||||
static int __init ps3_setup_static_device(const struct ps3_repository_device *repo)
|
||||
{
|
||||
int result;
|
||||
|
||||
switch (repo->dev_type) {
|
||||
case PS3_DEV_TYPE_SB_GELIC:
|
||||
result = ps3_setup_gelic_device(repo);
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_setup_gelic_device failed\n",
|
||||
__func__, __LINE__);
|
||||
}
|
||||
break;
|
||||
case PS3_DEV_TYPE_SB_USB:
|
||||
|
||||
/* Each USB device has both an EHCI and an OHCI HC */
|
||||
|
||||
result = ps3_setup_ehci_device(repo);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_setup_ehci_device failed\n",
|
||||
__func__, __LINE__);
|
||||
}
|
||||
|
||||
result = ps3_setup_ohci_device(repo);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_setup_ohci_device failed\n",
|
||||
__func__, __LINE__);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return ps3_setup_dynamic_device(repo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ps3_find_and_add_device(u64 bus_id, u64 dev_id)
|
||||
{
|
||||
struct ps3_repository_device repo;
|
||||
int res;
|
||||
unsigned int retries;
|
||||
unsigned long rem;
|
||||
|
||||
/*
|
||||
* On some firmware versions (e.g. 1.90), the device may not show up
|
||||
* in the repository immediately
|
||||
*/
|
||||
for (retries = 0; retries < 10; retries++) {
|
||||
res = ps3_repository_find_device_by_id(&repo, bus_id, dev_id);
|
||||
if (!res)
|
||||
goto found;
|
||||
|
||||
rem = msleep_interruptible(100);
|
||||
if (rem)
|
||||
break;
|
||||
}
|
||||
pr_warning("%s:%u: device %llu:%llu not found\n", __func__, __LINE__,
|
||||
bus_id, dev_id);
|
||||
return;
|
||||
|
||||
found:
|
||||
if (retries)
|
||||
pr_debug("%s:%u: device %llu:%llu found after %u retries\n",
|
||||
__func__, __LINE__, bus_id, dev_id, retries);
|
||||
|
||||
ps3_setup_dynamic_device(&repo);
|
||||
return;
|
||||
}
|
||||
|
||||
#define PS3_NOTIFICATION_DEV_ID ULONG_MAX
|
||||
#define PS3_NOTIFICATION_INTERRUPT_ID 0
|
||||
|
||||
struct ps3_notification_device {
|
||||
struct ps3_system_bus_device sbd;
|
||||
spinlock_t lock;
|
||||
u64 tag;
|
||||
u64 lv1_status;
|
||||
struct completion done;
|
||||
};
|
||||
|
||||
enum ps3_notify_type {
|
||||
notify_device_ready = 0,
|
||||
notify_region_probe = 1,
|
||||
notify_region_update = 2,
|
||||
};
|
||||
|
||||
struct ps3_notify_cmd {
|
||||
u64 operation_code; /* must be zero */
|
||||
u64 event_mask; /* OR of 1UL << enum ps3_notify_type */
|
||||
};
|
||||
|
||||
struct ps3_notify_event {
|
||||
u64 event_type; /* enum ps3_notify_type */
|
||||
u64 bus_id;
|
||||
u64 dev_id;
|
||||
u64 dev_type;
|
||||
u64 dev_port;
|
||||
};
|
||||
|
||||
static irqreturn_t ps3_notification_interrupt(int irq, void *data)
|
||||
{
|
||||
struct ps3_notification_device *dev = data;
|
||||
int res;
|
||||
u64 tag, status;
|
||||
|
||||
spin_lock(&dev->lock);
|
||||
res = lv1_storage_get_async_status(PS3_NOTIFICATION_DEV_ID, &tag,
|
||||
&status);
|
||||
if (tag != dev->tag)
|
||||
pr_err("%s:%u: tag mismatch, got %llx, expected %llx\n",
|
||||
__func__, __LINE__, tag, dev->tag);
|
||||
|
||||
if (res) {
|
||||
pr_err("%s:%u: res %d status 0x%llx\n", __func__, __LINE__, res,
|
||||
status);
|
||||
} else {
|
||||
pr_debug("%s:%u: completed, status 0x%llx\n", __func__,
|
||||
__LINE__, status);
|
||||
dev->lv1_status = status;
|
||||
complete(&dev->done);
|
||||
}
|
||||
spin_unlock(&dev->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ps3_notification_read_write(struct ps3_notification_device *dev,
|
||||
u64 lpar, int write)
|
||||
{
|
||||
const char *op = write ? "write" : "read";
|
||||
unsigned long flags;
|
||||
int res;
|
||||
|
||||
init_completion(&dev->done);
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
res = write ? lv1_storage_write(dev->sbd.dev_id, 0, 0, 1, 0, lpar,
|
||||
&dev->tag)
|
||||
: lv1_storage_read(dev->sbd.dev_id, 0, 0, 1, 0, lpar,
|
||||
&dev->tag);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
if (res) {
|
||||
pr_err("%s:%u: %s failed %d\n", __func__, __LINE__, op, res);
|
||||
return -EPERM;
|
||||
}
|
||||
pr_debug("%s:%u: notification %s issued\n", __func__, __LINE__, op);
|
||||
|
||||
res = wait_event_interruptible(dev->done.wait,
|
||||
dev->done.done || kthread_should_stop());
|
||||
if (kthread_should_stop())
|
||||
res = -EINTR;
|
||||
if (res) {
|
||||
pr_debug("%s:%u: interrupted %s\n", __func__, __LINE__, op);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (dev->lv1_status) {
|
||||
pr_err("%s:%u: %s not completed, status 0x%llx\n", __func__,
|
||||
__LINE__, op, dev->lv1_status);
|
||||
return -EIO;
|
||||
}
|
||||
pr_debug("%s:%u: notification %s completed\n", __func__, __LINE__, op);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct task_struct *probe_task;
|
||||
|
||||
/**
|
||||
* ps3_probe_thread - Background repository probing at system startup.
|
||||
*
|
||||
* This implementation only supports background probing on a single bus.
|
||||
* It uses the hypervisor's storage device notification mechanism to wait until
|
||||
* a storage device is ready. The device notification mechanism uses a
|
||||
* pseudo device to asynchronously notify the guest when storage devices become
|
||||
* ready. The notification device has a block size of 512 bytes.
|
||||
*/
|
||||
|
||||
static int ps3_probe_thread(void *data)
|
||||
{
|
||||
struct ps3_notification_device dev;
|
||||
int res;
|
||||
unsigned int irq;
|
||||
u64 lpar;
|
||||
void *buf;
|
||||
struct ps3_notify_cmd *notify_cmd;
|
||||
struct ps3_notify_event *notify_event;
|
||||
|
||||
pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__);
|
||||
|
||||
buf = kzalloc(512, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
lpar = ps3_mm_phys_to_lpar(__pa(buf));
|
||||
notify_cmd = buf;
|
||||
notify_event = buf;
|
||||
|
||||
/* dummy system bus device */
|
||||
dev.sbd.bus_id = (u64)data;
|
||||
dev.sbd.dev_id = PS3_NOTIFICATION_DEV_ID;
|
||||
dev.sbd.interrupt_id = PS3_NOTIFICATION_INTERRUPT_ID;
|
||||
|
||||
res = lv1_open_device(dev.sbd.bus_id, dev.sbd.dev_id, 0);
|
||||
if (res) {
|
||||
pr_err("%s:%u: lv1_open_device failed %s\n", __func__,
|
||||
__LINE__, ps3_result(res));
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
res = ps3_sb_event_receive_port_setup(&dev.sbd, PS3_BINDING_CPU_ANY,
|
||||
&irq);
|
||||
if (res) {
|
||||
pr_err("%s:%u: ps3_sb_event_receive_port_setup failed %d\n",
|
||||
__func__, __LINE__, res);
|
||||
goto fail_close_device;
|
||||
}
|
||||
|
||||
spin_lock_init(&dev.lock);
|
||||
|
||||
res = request_irq(irq, ps3_notification_interrupt, 0,
|
||||
"ps3_notification", &dev);
|
||||
if (res) {
|
||||
pr_err("%s:%u: request_irq failed %d\n", __func__, __LINE__,
|
||||
res);
|
||||
goto fail_sb_event_receive_port_destroy;
|
||||
}
|
||||
|
||||
/* Setup and write the request for device notification. */
|
||||
notify_cmd->operation_code = 0; /* must be zero */
|
||||
notify_cmd->event_mask = 1UL << notify_region_probe;
|
||||
|
||||
res = ps3_notification_read_write(&dev, lpar, 1);
|
||||
if (res)
|
||||
goto fail_free_irq;
|
||||
|
||||
/* Loop here processing the requested notification events. */
|
||||
do {
|
||||
try_to_freeze();
|
||||
|
||||
memset(notify_event, 0, sizeof(*notify_event));
|
||||
|
||||
res = ps3_notification_read_write(&dev, lpar, 0);
|
||||
if (res)
|
||||
break;
|
||||
|
||||
pr_debug("%s:%u: notify event type 0x%llx bus id %llu dev id %llu"
|
||||
" type %llu port %llu\n", __func__, __LINE__,
|
||||
notify_event->event_type, notify_event->bus_id,
|
||||
notify_event->dev_id, notify_event->dev_type,
|
||||
notify_event->dev_port);
|
||||
|
||||
if (notify_event->event_type != notify_region_probe ||
|
||||
notify_event->bus_id != dev.sbd.bus_id) {
|
||||
pr_warning("%s:%u: bad notify_event: event %llu, "
|
||||
"dev_id %llu, dev_type %llu\n",
|
||||
__func__, __LINE__, notify_event->event_type,
|
||||
notify_event->dev_id,
|
||||
notify_event->dev_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
ps3_find_and_add_device(dev.sbd.bus_id, notify_event->dev_id);
|
||||
|
||||
} while (!kthread_should_stop());
|
||||
|
||||
fail_free_irq:
|
||||
free_irq(irq, &dev);
|
||||
fail_sb_event_receive_port_destroy:
|
||||
ps3_sb_event_receive_port_destroy(&dev.sbd, irq);
|
||||
fail_close_device:
|
||||
lv1_close_device(dev.sbd.bus_id, dev.sbd.dev_id);
|
||||
fail_free:
|
||||
kfree(buf);
|
||||
|
||||
probe_task = NULL;
|
||||
|
||||
pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_stop_probe_thread - Stops the background probe thread.
|
||||
*
|
||||
*/
|
||||
|
||||
static int ps3_stop_probe_thread(struct notifier_block *nb, unsigned long code,
|
||||
void *data)
|
||||
{
|
||||
if (probe_task)
|
||||
kthread_stop(probe_task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block nb = {
|
||||
.notifier_call = ps3_stop_probe_thread
|
||||
};
|
||||
|
||||
/**
|
||||
* ps3_start_probe_thread - Starts the background probe thread.
|
||||
*
|
||||
*/
|
||||
|
||||
static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type)
|
||||
{
|
||||
int result;
|
||||
struct task_struct *task;
|
||||
struct ps3_repository_device repo;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
memset(&repo, 0, sizeof(repo));
|
||||
|
||||
repo.bus_type = bus_type;
|
||||
|
||||
result = ps3_repository_find_bus(repo.bus_type, 0, &repo.bus_index);
|
||||
|
||||
if (result) {
|
||||
printk(KERN_ERR "%s: Cannot find bus (%d)\n", __func__, result);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
result = ps3_repository_read_bus_id(repo.bus_index, &repo.bus_id);
|
||||
|
||||
if (result) {
|
||||
printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__,
|
||||
result);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
task = kthread_run(ps3_probe_thread, (void *)repo.bus_id,
|
||||
"ps3-probe-%u", bus_type);
|
||||
|
||||
if (IS_ERR(task)) {
|
||||
result = PTR_ERR(task);
|
||||
printk(KERN_ERR "%s: kthread_run failed %d\n", __func__,
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
probe_task = task;
|
||||
register_reboot_notifier(&nb);
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_register_devices - Probe the system and register devices found.
|
||||
*
|
||||
* A device_initcall() routine.
|
||||
*/
|
||||
|
||||
static int __init ps3_register_devices(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
return -ENODEV;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
/* ps3_repository_dump_bus_info(); */
|
||||
|
||||
result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE);
|
||||
|
||||
ps3_register_vuart_devices();
|
||||
|
||||
ps3_register_graphics_devices();
|
||||
|
||||
ps3_repository_find_devices(PS3_BUS_TYPE_SB, ps3_setup_static_device);
|
||||
|
||||
ps3_register_sound_devices();
|
||||
|
||||
ps3_register_lpm_devices();
|
||||
|
||||
ps3_register_ramdisk_device();
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(ps3_register_devices);
|
||||
25
arch/powerpc/platforms/ps3/exports.c
Normal file
25
arch/powerpc/platforms/ps3/exports.c
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* PS3 hvcall exports for modules.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define LV1_CALL(name, in, out, num) \
|
||||
extern s64 _lv1_##name(LV1_##in##_IN_##out##_OUT_ARG_DECL); \
|
||||
EXPORT_SYMBOL(_lv1_##name);
|
||||
|
||||
#include <asm/lv1call.h>
|
||||
273
arch/powerpc/platforms/ps3/gelic_udbg.c
Normal file
273
arch/powerpc/platforms/ps3/gelic_udbg.c
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* udbg debug output routine via GELIC UDP broadcasts
|
||||
*
|
||||
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006, 2007 Sony Corporation
|
||||
* Copyright (C) 2010 Hector Martin <hector@marcansoft.com>
|
||||
* Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/lv1call.h>
|
||||
|
||||
#define GELIC_BUS_ID 1
|
||||
#define GELIC_DEVICE_ID 0
|
||||
#define GELIC_DEBUG_PORT 18194
|
||||
#define GELIC_MAX_MESSAGE_SIZE 1000
|
||||
|
||||
#define GELIC_LV1_GET_MAC_ADDRESS 1
|
||||
#define GELIC_LV1_GET_VLAN_ID 4
|
||||
#define GELIC_LV1_VLAN_TX_ETHERNET_0 2
|
||||
|
||||
#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000
|
||||
#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000
|
||||
|
||||
#define GELIC_DESCR_TX_DMA_IKE 0x00080000
|
||||
#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000
|
||||
#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000
|
||||
|
||||
#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \
|
||||
GELIC_DESCR_TX_DMA_IKE | \
|
||||
GELIC_DESCR_TX_DMA_NO_CHKSUM)
|
||||
|
||||
static u64 bus_addr;
|
||||
|
||||
struct gelic_descr {
|
||||
/* as defined by the hardware */
|
||||
__be32 buf_addr;
|
||||
__be32 buf_size;
|
||||
__be32 next_descr_addr;
|
||||
__be32 dmac_cmd_status;
|
||||
__be32 result_size;
|
||||
__be32 valid_size; /* all zeroes for tx */
|
||||
__be32 data_status;
|
||||
__be32 data_error; /* all zeroes for tx */
|
||||
} __attribute__((aligned(32)));
|
||||
|
||||
struct debug_block {
|
||||
struct gelic_descr descr;
|
||||
u8 pkt[1520];
|
||||
} __packed;
|
||||
|
||||
struct ethhdr {
|
||||
u8 dest[6];
|
||||
u8 src[6];
|
||||
u16 type;
|
||||
} __packed;
|
||||
|
||||
struct vlantag {
|
||||
u16 vlan;
|
||||
u16 subtype;
|
||||
} __packed;
|
||||
|
||||
struct iphdr {
|
||||
u8 ver_len;
|
||||
u8 dscp_ecn;
|
||||
u16 total_length;
|
||||
u16 ident;
|
||||
u16 frag_off_flags;
|
||||
u8 ttl;
|
||||
u8 proto;
|
||||
u16 checksum;
|
||||
u32 src;
|
||||
u32 dest;
|
||||
} __packed;
|
||||
|
||||
struct udphdr {
|
||||
u16 src;
|
||||
u16 dest;
|
||||
u16 len;
|
||||
u16 checksum;
|
||||
} __packed;
|
||||
|
||||
static __iomem struct ethhdr *h_eth;
|
||||
static __iomem struct vlantag *h_vlan;
|
||||
static __iomem struct iphdr *h_ip;
|
||||
static __iomem struct udphdr *h_udp;
|
||||
|
||||
static __iomem char *pmsg;
|
||||
static __iomem char *pmsgc;
|
||||
|
||||
static __iomem struct debug_block dbg __attribute__((aligned(32)));
|
||||
|
||||
static int header_size;
|
||||
|
||||
static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len,
|
||||
u64 *real_bus_addr)
|
||||
{
|
||||
s64 result;
|
||||
u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL;
|
||||
u64 real_end = real_addr + len;
|
||||
u64 map_start = real_addr & ~0xfff;
|
||||
u64 map_end = (real_end + 0xfff) & ~0xfff;
|
||||
u64 bus_addr = 0;
|
||||
|
||||
u64 flags = 0xf800000000000000UL;
|
||||
|
||||
result = lv1_allocate_device_dma_region(bus_id, dev_id,
|
||||
map_end - map_start, 12, 0,
|
||||
&bus_addr);
|
||||
if (result)
|
||||
lv1_panic(0);
|
||||
|
||||
result = lv1_map_device_dma_region(bus_id, dev_id, map_start,
|
||||
bus_addr, map_end - map_start,
|
||||
flags);
|
||||
if (result)
|
||||
lv1_panic(0);
|
||||
|
||||
*real_bus_addr = bus_addr + real_addr - map_start;
|
||||
}
|
||||
|
||||
static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len)
|
||||
{
|
||||
s64 result;
|
||||
u64 real_bus_addr;
|
||||
|
||||
real_bus_addr = bus_addr & ~0xfff;
|
||||
len += bus_addr - real_bus_addr;
|
||||
len = (len + 0xfff) & ~0xfff;
|
||||
|
||||
result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr,
|
||||
len);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr);
|
||||
}
|
||||
|
||||
static void gelic_debug_init(void)
|
||||
{
|
||||
s64 result;
|
||||
u64 v2;
|
||||
u64 mac;
|
||||
u64 vlan_id;
|
||||
|
||||
result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0);
|
||||
if (result)
|
||||
lv1_panic(0);
|
||||
|
||||
map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg),
|
||||
&bus_addr);
|
||||
|
||||
memset(&dbg, 0, sizeof(dbg));
|
||||
|
||||
dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt);
|
||||
|
||||
wmb();
|
||||
|
||||
result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
|
||||
GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0,
|
||||
&mac, &v2);
|
||||
if (result)
|
||||
lv1_panic(0);
|
||||
|
||||
mac <<= 16;
|
||||
|
||||
h_eth = (struct ethhdr *)dbg.pkt;
|
||||
|
||||
memset(&h_eth->dest, 0xff, 6);
|
||||
memcpy(&h_eth->src, &mac, 6);
|
||||
|
||||
header_size = sizeof(struct ethhdr);
|
||||
|
||||
result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
|
||||
GELIC_LV1_GET_VLAN_ID,
|
||||
GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0,
|
||||
&vlan_id, &v2);
|
||||
if (!result) {
|
||||
h_eth->type = 0x8100;
|
||||
|
||||
header_size += sizeof(struct vlantag);
|
||||
h_vlan = (struct vlantag *)(h_eth + 1);
|
||||
h_vlan->vlan = vlan_id;
|
||||
h_vlan->subtype = 0x0800;
|
||||
h_ip = (struct iphdr *)(h_vlan + 1);
|
||||
} else {
|
||||
h_eth->type = 0x0800;
|
||||
h_ip = (struct iphdr *)(h_eth + 1);
|
||||
}
|
||||
|
||||
header_size += sizeof(struct iphdr);
|
||||
h_ip->ver_len = 0x45;
|
||||
h_ip->ttl = 10;
|
||||
h_ip->proto = 0x11;
|
||||
h_ip->src = 0x00000000;
|
||||
h_ip->dest = 0xffffffff;
|
||||
|
||||
header_size += sizeof(struct udphdr);
|
||||
h_udp = (struct udphdr *)(h_ip + 1);
|
||||
h_udp->src = GELIC_DEBUG_PORT;
|
||||
h_udp->dest = GELIC_DEBUG_PORT;
|
||||
|
||||
pmsgc = pmsg = (char *)(h_udp + 1);
|
||||
}
|
||||
|
||||
static void gelic_debug_shutdown(void)
|
||||
{
|
||||
if (bus_addr)
|
||||
unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID,
|
||||
bus_addr, sizeof(dbg));
|
||||
lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID);
|
||||
}
|
||||
|
||||
static void gelic_sendbuf(int msgsize)
|
||||
{
|
||||
u16 *p;
|
||||
u32 sum;
|
||||
int i;
|
||||
|
||||
dbg.descr.buf_size = header_size + msgsize;
|
||||
h_ip->total_length = msgsize + sizeof(struct udphdr) +
|
||||
sizeof(struct iphdr);
|
||||
h_udp->len = msgsize + sizeof(struct udphdr);
|
||||
|
||||
h_ip->checksum = 0;
|
||||
sum = 0;
|
||||
p = (u16 *)h_ip;
|
||||
for (i = 0; i < 5; i++)
|
||||
sum += *p++;
|
||||
h_ip->checksum = ~(sum + (sum >> 16));
|
||||
|
||||
dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM |
|
||||
GELIC_DESCR_TX_DMA_FRAME_TAIL;
|
||||
dbg.descr.result_size = 0;
|
||||
dbg.descr.data_status = 0;
|
||||
|
||||
wmb();
|
||||
|
||||
lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0);
|
||||
|
||||
while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) ==
|
||||
GELIC_DESCR_DMA_CARDOWNED)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static void ps3gelic_udbg_putc(char ch)
|
||||
{
|
||||
*pmsgc++ = ch;
|
||||
if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) {
|
||||
gelic_sendbuf(pmsgc-pmsg);
|
||||
pmsgc = pmsg;
|
||||
}
|
||||
}
|
||||
|
||||
void __init udbg_init_ps3gelic(void)
|
||||
{
|
||||
gelic_debug_init();
|
||||
udbg_putc = ps3gelic_udbg_putc;
|
||||
}
|
||||
|
||||
void udbg_shutdown_ps3gelic(void)
|
||||
{
|
||||
udbg_putc = NULL;
|
||||
gelic_debug_shutdown();
|
||||
}
|
||||
EXPORT_SYMBOL(udbg_shutdown_ps3gelic);
|
||||
207
arch/powerpc/platforms/ps3/htab.c
Normal file
207
arch/powerpc/platforms/ps3/htab.c
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* PS3 pagetable management routines.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006, 2007 Sony Corporation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/memblock.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/ps3fb.h>
|
||||
|
||||
#define PS3_VERBOSE_RESULT
|
||||
#include "platform.h"
|
||||
|
||||
/**
|
||||
* enum lpar_vas_id - id of LPAR virtual address space.
|
||||
* @lpar_vas_id_current: Current selected virtual address space
|
||||
*
|
||||
* Identify the target LPAR address space.
|
||||
*/
|
||||
|
||||
enum ps3_lpar_vas_id {
|
||||
PS3_LPAR_VAS_ID_CURRENT = 0,
|
||||
};
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(ps3_htab_lock);
|
||||
|
||||
static long ps3_hpte_insert(unsigned long hpte_group, unsigned long vpn,
|
||||
unsigned long pa, unsigned long rflags, unsigned long vflags,
|
||||
int psize, int apsize, int ssize)
|
||||
{
|
||||
int result;
|
||||
u64 hpte_v, hpte_r;
|
||||
u64 inserted_index;
|
||||
u64 evicted_v, evicted_r;
|
||||
u64 hpte_v_array[4], hpte_rs;
|
||||
unsigned long flags;
|
||||
long ret = -1;
|
||||
|
||||
/*
|
||||
* lv1_insert_htab_entry() will search for victim
|
||||
* entry in both primary and secondary pte group
|
||||
*/
|
||||
vflags &= ~HPTE_V_SECONDARY;
|
||||
|
||||
hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
|
||||
hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize) | rflags;
|
||||
|
||||
spin_lock_irqsave(&ps3_htab_lock, flags);
|
||||
|
||||
/* talk hvc to replace entries BOLTED == 0 */
|
||||
result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group,
|
||||
hpte_v, hpte_r,
|
||||
HPTE_V_BOLTED, 0,
|
||||
&inserted_index,
|
||||
&evicted_v, &evicted_r);
|
||||
|
||||
if (result) {
|
||||
/* all entries bolted !*/
|
||||
pr_info("%s:result=%s vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n",
|
||||
__func__, ps3_result(result), vpn, pa, hpte_group,
|
||||
hpte_v, hpte_r);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* see if the entry is inserted into secondary pteg
|
||||
*/
|
||||
result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT,
|
||||
inserted_index & ~0x3UL,
|
||||
&hpte_v_array[0], &hpte_v_array[1],
|
||||
&hpte_v_array[2], &hpte_v_array[3],
|
||||
&hpte_rs);
|
||||
BUG_ON(result);
|
||||
|
||||
if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY)
|
||||
ret = (inserted_index & 7) | (1 << 3);
|
||||
else
|
||||
ret = inserted_index & 7;
|
||||
|
||||
spin_unlock_irqrestore(&ps3_htab_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long ps3_hpte_remove(unsigned long hpte_group)
|
||||
{
|
||||
panic("ps3_hpte_remove() not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp,
|
||||
unsigned long vpn, int psize, int apsize,
|
||||
int ssize, int local)
|
||||
{
|
||||
int result;
|
||||
u64 hpte_v, want_v, hpte_rs;
|
||||
u64 hpte_v_array[4];
|
||||
unsigned long flags;
|
||||
long ret;
|
||||
|
||||
want_v = hpte_encode_avpn(vpn, psize, ssize);
|
||||
|
||||
spin_lock_irqsave(&ps3_htab_lock, flags);
|
||||
|
||||
result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL,
|
||||
&hpte_v_array[0], &hpte_v_array[1],
|
||||
&hpte_v_array[2], &hpte_v_array[3],
|
||||
&hpte_rs);
|
||||
|
||||
if (result) {
|
||||
pr_info("%s: result=%s read vpn=%lx slot=%lx psize=%d\n",
|
||||
__func__, ps3_result(result), vpn, slot, psize);
|
||||
BUG();
|
||||
}
|
||||
|
||||
hpte_v = hpte_v_array[slot % 4];
|
||||
|
||||
/*
|
||||
* As lv1_read_htab_entries() does not give us the RPN, we can
|
||||
* not synthesize the new hpte_r value here, and therefore can
|
||||
* not update the hpte with lv1_insert_htab_entry(), so we
|
||||
* instead invalidate it and ask the caller to update it via
|
||||
* ps3_hpte_insert() by returning a -1 value.
|
||||
*/
|
||||
if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
|
||||
/* not found */
|
||||
ret = -1;
|
||||
} else {
|
||||
/* entry found, just invalidate it */
|
||||
result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT,
|
||||
slot, 0, 0);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ps3_htab_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
|
||||
int psize, int ssize)
|
||||
{
|
||||
panic("ps3_hpte_updateboltedpp() not implemented");
|
||||
}
|
||||
|
||||
static void ps3_hpte_invalidate(unsigned long slot, unsigned long vpn,
|
||||
int psize, int apsize, int ssize, int local)
|
||||
{
|
||||
unsigned long flags;
|
||||
int result;
|
||||
|
||||
spin_lock_irqsave(&ps3_htab_lock, flags);
|
||||
|
||||
result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0);
|
||||
|
||||
if (result) {
|
||||
pr_info("%s: result=%s vpn=%lx slot=%lx psize=%d\n",
|
||||
__func__, ps3_result(result), vpn, slot, psize);
|
||||
BUG();
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ps3_htab_lock, flags);
|
||||
}
|
||||
|
||||
static void ps3_hpte_clear(void)
|
||||
{
|
||||
unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4;
|
||||
u64 i;
|
||||
|
||||
for (i = 0; i < hpte_count; i++)
|
||||
lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0);
|
||||
|
||||
ps3_mm_shutdown();
|
||||
ps3_mm_vas_destroy();
|
||||
}
|
||||
|
||||
void __init ps3_hpte_init(unsigned long htab_size)
|
||||
{
|
||||
ppc_md.hpte_invalidate = ps3_hpte_invalidate;
|
||||
ppc_md.hpte_updatepp = ps3_hpte_updatepp;
|
||||
ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp;
|
||||
ppc_md.hpte_insert = ps3_hpte_insert;
|
||||
ppc_md.hpte_remove = ps3_hpte_remove;
|
||||
ppc_md.hpte_clear_all = ps3_hpte_clear;
|
||||
|
||||
ppc64_pft_size = __ilog2(htab_size);
|
||||
}
|
||||
|
||||
804
arch/powerpc/platforms/ps3/hvcall.S
Normal file
804
arch/powerpc/platforms/ps3/hvcall.S
Normal file
|
|
@ -0,0 +1,804 @@
|
|||
/*
|
||||
* PS3 hvcall interface.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
* Copyright 2003, 2004 (c) MontaVista Software, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ppc_asm.h>
|
||||
|
||||
#define lv1call .long 0x44000022; extsw r3, r3
|
||||
|
||||
#define LV1_N_IN_0_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_0_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_1_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_2_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_3_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_4_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_5_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_6_IN_0_OUT LV1_N_IN_0_OUT
|
||||
#define LV1_7_IN_0_OUT LV1_N_IN_0_OUT
|
||||
|
||||
#define LV1_0_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r3, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_0_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r3, -8(r1); \
|
||||
stdu r4, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_0_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r3, -8(r1); \
|
||||
std r4, -16(r1); \
|
||||
stdu r5, -24(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 24; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_0_IN_7_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r3, -8(r1); \
|
||||
std r4, -16(r1); \
|
||||
std r5, -24(r1); \
|
||||
std r6, -32(r1); \
|
||||
std r7, -40(r1); \
|
||||
std r8, -48(r1); \
|
||||
stdu r9, -56(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 56; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
ld r11, -40(r1); \
|
||||
std r8, 0(r11); \
|
||||
ld r11, -48(r1); \
|
||||
std r9, 0(r11); \
|
||||
ld r11, -56(r1); \
|
||||
std r10, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r4, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r4, -8(r1); \
|
||||
stdu r5, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r4, -8(r1); \
|
||||
std r5, -16(r1); \
|
||||
stdu r6, -24(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 24; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_4_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r4, -8(r1); \
|
||||
std r5, -16(r1); \
|
||||
std r6, -24(r1); \
|
||||
stdu r7, -32(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 32; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_5_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r4, -8(r1); \
|
||||
std r5, -16(r1); \
|
||||
std r6, -24(r1); \
|
||||
std r7, -32(r1); \
|
||||
stdu r8, -40(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 40; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
ld r11, -40(r1); \
|
||||
std r8, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_6_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r4, -8(r1); \
|
||||
std r5, -16(r1); \
|
||||
std r6, -24(r1); \
|
||||
std r7, -32(r1); \
|
||||
std r8, -40(r1); \
|
||||
stdu r9, -48(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 48; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
ld r11, -40(r1); \
|
||||
std r8, 0(r11); \
|
||||
ld r11, -48(r1); \
|
||||
std r9, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_1_IN_7_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r4, -8(r1); \
|
||||
std r5, -16(r1); \
|
||||
std r6, -24(r1); \
|
||||
std r7, -32(r1); \
|
||||
std r8, -40(r1); \
|
||||
std r9, -48(r1); \
|
||||
stdu r10, -56(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 56; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
ld r11, -40(r1); \
|
||||
std r8, 0(r11); \
|
||||
ld r11, -48(r1); \
|
||||
std r9, 0(r11); \
|
||||
ld r11, -56(r1); \
|
||||
std r10, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_2_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r5, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_2_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r5, -8(r1); \
|
||||
stdu r6, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_2_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r5, -8(r1); \
|
||||
std r6, -16(r1); \
|
||||
stdu r7, -24(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 24; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_2_IN_4_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r5, -8(r1); \
|
||||
std r6, -16(r1); \
|
||||
std r7, -24(r1); \
|
||||
stdu r8, -32(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 32; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_2_IN_5_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r5, -8(r1); \
|
||||
std r6, -16(r1); \
|
||||
std r7, -24(r1); \
|
||||
std r8, -32(r1); \
|
||||
stdu r9, -40(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 40; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, -32(r1); \
|
||||
std r7, 0(r11); \
|
||||
ld r11, -40(r1); \
|
||||
std r8, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_3_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r6, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_3_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r6, -8(r1); \
|
||||
stdu r7, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_3_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r6, -8(r1); \
|
||||
std r7, -16(r1); \
|
||||
stdu r8, -24(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 24; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_4_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r7, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_4_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r7, -8(r1); \
|
||||
stdu r8, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_4_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r7, -8(r1); \
|
||||
std r8, -16(r1); \
|
||||
stdu r9, -24(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 24; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_5_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r8, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_5_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r8, -8(r1); \
|
||||
stdu r9, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_5_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r8, -8(r1); \
|
||||
std r9, -16(r1); \
|
||||
stdu r10, -24(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 24; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, -24(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_6_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r9, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_6_IN_2_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r9, -8(r1); \
|
||||
stdu r10, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_6_IN_3_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r9, -8(r1); \
|
||||
stdu r10, -16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 16; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, -16(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, 48+8*8(r1); \
|
||||
std r6, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_7_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
stdu r10, -8(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
addi r1, r1, 8; \
|
||||
ld r11, -8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_7_IN_6_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
std r10, 48+8*7(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
ld r11, 48+8*7(r1); \
|
||||
std r4, 0(r11); \
|
||||
ld r11, 48+8*8(r1); \
|
||||
std r5, 0(r11); \
|
||||
ld r11, 48+8*9(r1); \
|
||||
std r6, 0(r11); \
|
||||
ld r11, 48+8*10(r1); \
|
||||
std r7, 0(r11); \
|
||||
ld r11, 48+8*11(r1); \
|
||||
std r8, 0(r11); \
|
||||
ld r11, 48+8*12(r1); \
|
||||
std r9, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
#define LV1_8_IN_1_OUT(API_NAME, API_NUMBER) \
|
||||
_GLOBAL(_##API_NAME) \
|
||||
\
|
||||
mflr r0; \
|
||||
std r0, 16(r1); \
|
||||
\
|
||||
li r11, API_NUMBER; \
|
||||
lv1call; \
|
||||
\
|
||||
ld r11, 48+8*8(r1); \
|
||||
std r4, 0(r11); \
|
||||
\
|
||||
ld r0, 16(r1); \
|
||||
mtlr r0; \
|
||||
blr
|
||||
|
||||
.text
|
||||
|
||||
/* the lv1 underscored call definitions expand here */
|
||||
|
||||
#define LV1_CALL(name, in, out, num) LV1_##in##_IN_##out##_OUT(lv1_##name, num)
|
||||
#include <asm/lv1call.h>
|
||||
793
arch/powerpc/platforms/ps3/interrupt.c
Normal file
793
arch/powerpc/platforms/ps3/interrupt.c
Normal file
|
|
@ -0,0 +1,793 @@
|
|||
/*
|
||||
* PS3 interrupt routines.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define DBG udbg_printf
|
||||
#define FAIL udbg_printf
|
||||
#else
|
||||
#define DBG pr_devel
|
||||
#define FAIL pr_debug
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct ps3_bmp - a per cpu irq status and mask bitmap structure
|
||||
* @status: 256 bit status bitmap indexed by plug
|
||||
* @unused_1: Alignment
|
||||
* @mask: 256 bit mask bitmap indexed by plug
|
||||
* @unused_2: Alignment
|
||||
*
|
||||
* The HV maintains per SMT thread mappings of HV outlet to HV plug on
|
||||
* behalf of the guest. These mappings are implemented as 256 bit guest
|
||||
* supplied bitmaps indexed by plug number. The addresses of the bitmaps
|
||||
* are registered with the HV through lv1_configure_irq_state_bitmap().
|
||||
* The HV requires that the 512 bits of status + mask not cross a page
|
||||
* boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte
|
||||
* alignment.
|
||||
*
|
||||
* The HV supports 256 plugs per thread, assigned as {0..255}, for a total
|
||||
* of 512 plugs supported on a processor. To simplify the logic this
|
||||
* implementation equates HV plug value to Linux virq value, constrains each
|
||||
* interrupt to have a system wide unique plug number, and limits the range
|
||||
* of the plug values to map into the first dword of the bitmaps. This
|
||||
* gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note
|
||||
* that there is no constraint on how many in this set an individual thread
|
||||
* can acquire.
|
||||
*
|
||||
* The mask is declared as unsigned long so we can use set/clear_bit on it.
|
||||
*/
|
||||
|
||||
#define PS3_BMP_MINALIGN 64
|
||||
|
||||
struct ps3_bmp {
|
||||
struct {
|
||||
u64 status;
|
||||
u64 unused_1[3];
|
||||
unsigned long mask;
|
||||
u64 unused_2[3];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ps3_private - a per cpu data structure
|
||||
* @bmp: ps3_bmp structure
|
||||
* @bmp_lock: Syncronize access to bmp.
|
||||
* @ipi_debug_brk_mask: Mask for debug break IPIs
|
||||
* @ppe_id: HV logical_ppe_id
|
||||
* @thread_id: HV thread_id
|
||||
* @ipi_mask: Mask of IPI virqs
|
||||
*/
|
||||
|
||||
struct ps3_private {
|
||||
struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
|
||||
spinlock_t bmp_lock;
|
||||
u64 ppe_id;
|
||||
u64 thread_id;
|
||||
unsigned long ipi_debug_brk_mask;
|
||||
unsigned long ipi_mask;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct ps3_private, ps3_private);
|
||||
|
||||
/**
|
||||
* ps3_chip_mask - Set an interrupt mask bit in ps3_bmp.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
|
||||
*/
|
||||
|
||||
static void ps3_chip_mask(struct irq_data *d)
|
||||
{
|
||||
struct ps3_private *pd = irq_data_get_irq_chip_data(d);
|
||||
unsigned long flags;
|
||||
|
||||
DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__,
|
||||
pd->thread_id, d->irq);
|
||||
|
||||
local_irq_save(flags);
|
||||
clear_bit(63 - d->irq, &pd->bmp.mask);
|
||||
lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
|
||||
*/
|
||||
|
||||
static void ps3_chip_unmask(struct irq_data *d)
|
||||
{
|
||||
struct ps3_private *pd = irq_data_get_irq_chip_data(d);
|
||||
unsigned long flags;
|
||||
|
||||
DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__,
|
||||
pd->thread_id, d->irq);
|
||||
|
||||
local_irq_save(flags);
|
||||
set_bit(63 - d->irq, &pd->bmp.mask);
|
||||
lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_chip_eoi - HV end-of-interrupt.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Calls lv1_end_of_interrupt_ext().
|
||||
*/
|
||||
|
||||
static void ps3_chip_eoi(struct irq_data *d)
|
||||
{
|
||||
const struct ps3_private *pd = irq_data_get_irq_chip_data(d);
|
||||
|
||||
/* non-IPIs are EOIed here. */
|
||||
|
||||
if (!test_bit(63 - d->irq, &pd->ipi_mask))
|
||||
lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip.
|
||||
*/
|
||||
|
||||
static struct irq_chip ps3_irq_chip = {
|
||||
.name = "ps3",
|
||||
.irq_mask = ps3_chip_mask,
|
||||
.irq_unmask = ps3_chip_unmask,
|
||||
.irq_eoi = ps3_chip_eoi,
|
||||
};
|
||||
|
||||
/**
|
||||
* ps3_virq_setup - virq related setup.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @outlet: The HV outlet from the various create outlet routines.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Calls irq_create_mapping() to get a virq and sets the chip data to
|
||||
* ps3_private data.
|
||||
*/
|
||||
|
||||
static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
|
||||
unsigned int *virq)
|
||||
{
|
||||
int result;
|
||||
struct ps3_private *pd;
|
||||
|
||||
/* This defines the default interrupt distribution policy. */
|
||||
|
||||
if (cpu == PS3_BINDING_CPU_ANY)
|
||||
cpu = 0;
|
||||
|
||||
pd = &per_cpu(ps3_private, cpu);
|
||||
|
||||
*virq = irq_create_mapping(NULL, outlet);
|
||||
|
||||
if (*virq == NO_IRQ) {
|
||||
FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n",
|
||||
__func__, __LINE__, outlet);
|
||||
result = -ENOMEM;
|
||||
goto fail_create;
|
||||
}
|
||||
|
||||
DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
|
||||
outlet, cpu, *virq);
|
||||
|
||||
result = irq_set_chip_data(*virq, pd);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: irq_set_chip_data failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_set;
|
||||
}
|
||||
|
||||
ps3_chip_mask(irq_get_irq_data(*virq));
|
||||
|
||||
return result;
|
||||
|
||||
fail_set:
|
||||
irq_dispose_mapping(*virq);
|
||||
fail_create:
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_virq_destroy - virq related teardown.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Clears chip data and calls irq_dispose_mapping() for the virq.
|
||||
*/
|
||||
|
||||
static int ps3_virq_destroy(unsigned int virq)
|
||||
{
|
||||
const struct ps3_private *pd = irq_get_chip_data(virq);
|
||||
|
||||
DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__,
|
||||
__LINE__, pd->ppe_id, pd->thread_id, virq);
|
||||
|
||||
irq_set_chip_data(virq, NULL);
|
||||
irq_dispose_mapping(virq);
|
||||
|
||||
DBG("%s:%d <-\n", __func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_irq_plug_setup - Generic outlet and virq related setup.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @outlet: The HV outlet from the various create outlet routines.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Sets up virq and connects the irq plug.
|
||||
*/
|
||||
|
||||
int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
|
||||
unsigned int *virq)
|
||||
{
|
||||
int result;
|
||||
struct ps3_private *pd;
|
||||
|
||||
result = ps3_virq_setup(cpu, outlet, virq);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__);
|
||||
goto fail_setup;
|
||||
}
|
||||
|
||||
pd = irq_get_chip_data(*virq);
|
||||
|
||||
/* Binds outlet to cpu + virq. */
|
||||
|
||||
result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq,
|
||||
outlet, 0);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
result = -EPERM;
|
||||
goto fail_connect;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
fail_connect:
|
||||
ps3_virq_destroy(*virq);
|
||||
fail_setup:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);
|
||||
|
||||
/**
|
||||
* ps3_irq_plug_destroy - Generic outlet and virq related teardown.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Disconnects the irq plug and tears down virq.
|
||||
* Do not call for system bus event interrupts setup with
|
||||
* ps3_sb_event_receive_port_setup().
|
||||
*/
|
||||
|
||||
int ps3_irq_plug_destroy(unsigned int virq)
|
||||
{
|
||||
int result;
|
||||
const struct ps3_private *pd = irq_get_chip_data(virq);
|
||||
|
||||
DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__,
|
||||
__LINE__, pd->ppe_id, pd->thread_id, virq);
|
||||
|
||||
ps3_chip_mask(irq_get_irq_data(virq));
|
||||
|
||||
result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq);
|
||||
|
||||
if (result)
|
||||
FAIL("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
|
||||
ps3_virq_destroy(virq);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);
|
||||
|
||||
/**
|
||||
* ps3_event_receive_port_setup - Setup an event receive port.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* The virq can be used with lv1_connect_interrupt_event_receive_port() to
|
||||
* arrange to receive interrupts from system-bus devices, or with
|
||||
* ps3_send_event_locally() to signal events.
|
||||
*/
|
||||
|
||||
int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq)
|
||||
{
|
||||
int result;
|
||||
u64 outlet;
|
||||
|
||||
result = lv1_construct_event_receive_port(&outlet);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_construct_event_receive_port failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
*virq = NO_IRQ;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ps3_irq_plug_setup(cpu, outlet, virq);
|
||||
BUG_ON(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup);
|
||||
|
||||
/**
|
||||
* ps3_event_receive_port_destroy - Destroy an event receive port.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* Since ps3_event_receive_port_destroy destroys the receive port outlet,
|
||||
* SB devices need to call disconnect_interrupt_event_receive_port() before
|
||||
* this.
|
||||
*/
|
||||
|
||||
int ps3_event_receive_port_destroy(unsigned int virq)
|
||||
{
|
||||
int result;
|
||||
|
||||
DBG(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
|
||||
|
||||
ps3_chip_mask(irq_get_irq_data(virq));
|
||||
|
||||
result = lv1_destruct_event_receive_port(virq_to_hw(virq));
|
||||
|
||||
if (result)
|
||||
FAIL("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
|
||||
/*
|
||||
* Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
|
||||
* calls from interrupt context (smp_call_function) when kexecing.
|
||||
*/
|
||||
|
||||
DBG(" <- %s:%d\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ps3_send_event_locally(unsigned int virq)
|
||||
{
|
||||
return lv1_send_event_locally(virq_to_hw(virq));
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @dev: The system bus device instance.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* An event irq represents a virtual device interrupt. The interrupt_id
|
||||
* coresponds to the software interrupt number.
|
||||
*/
|
||||
|
||||
int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev,
|
||||
enum ps3_cpu_binding cpu, unsigned int *virq)
|
||||
{
|
||||
/* this should go in system-bus.c */
|
||||
|
||||
int result;
|
||||
|
||||
result = ps3_event_receive_port_setup(cpu, virq);
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = lv1_connect_interrupt_event_receive_port(dev->bus_id,
|
||||
dev->dev_id, virq_to_hw(*virq), dev->interrupt_id);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_connect_interrupt_event_receive_port"
|
||||
" failed: %s\n", __func__, __LINE__,
|
||||
ps3_result(result));
|
||||
ps3_event_receive_port_destroy(*virq);
|
||||
*virq = NO_IRQ;
|
||||
return result;
|
||||
}
|
||||
|
||||
DBG("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
|
||||
dev->interrupt_id, *virq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);
|
||||
|
||||
int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev,
|
||||
unsigned int virq)
|
||||
{
|
||||
/* this should go in system-bus.c */
|
||||
|
||||
int result;
|
||||
|
||||
DBG(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
|
||||
dev->interrupt_id, virq);
|
||||
|
||||
result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id,
|
||||
dev->dev_id, virq_to_hw(virq), dev->interrupt_id);
|
||||
|
||||
if (result)
|
||||
FAIL("%s:%d: lv1_disconnect_interrupt_event_receive_port"
|
||||
" failed: %s\n", __func__, __LINE__,
|
||||
ps3_result(result));
|
||||
|
||||
result = ps3_event_receive_port_destroy(virq);
|
||||
BUG_ON(result);
|
||||
|
||||
/*
|
||||
* ps3_event_receive_port_destroy() destroys the IRQ plug,
|
||||
* so don't call ps3_irq_plug_destroy() here.
|
||||
*/
|
||||
|
||||
result = ps3_virq_destroy(virq);
|
||||
BUG_ON(result);
|
||||
|
||||
DBG(" <- %s:%d\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy);
|
||||
|
||||
/**
|
||||
* ps3_io_irq_setup - Setup a system bus io irq.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @interrupt_id: The device interrupt id read from the system repository.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* An io irq represents a non-virtualized device interrupt. interrupt_id
|
||||
* coresponds to the interrupt number of the interrupt controller.
|
||||
*/
|
||||
|
||||
int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
|
||||
unsigned int *virq)
|
||||
{
|
||||
int result;
|
||||
u64 outlet;
|
||||
|
||||
result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ps3_irq_plug_setup(cpu, outlet, virq);
|
||||
BUG_ON(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
|
||||
|
||||
int ps3_io_irq_destroy(unsigned int virq)
|
||||
{
|
||||
int result;
|
||||
unsigned long outlet = virq_to_hw(virq);
|
||||
|
||||
ps3_chip_mask(irq_get_irq_data(virq));
|
||||
|
||||
/*
|
||||
* lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
|
||||
* so call ps3_irq_plug_destroy() first.
|
||||
*/
|
||||
|
||||
result = ps3_irq_plug_destroy(virq);
|
||||
BUG_ON(result);
|
||||
|
||||
result = lv1_destruct_io_irq_outlet(outlet);
|
||||
|
||||
if (result)
|
||||
FAIL("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
|
||||
|
||||
/**
|
||||
* ps3_vuart_irq_setup - Setup the system virtual uart virq.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
* The system supports only a single virtual uart, so multiple calls without
|
||||
* freeing the interrupt will return a wrong state error.
|
||||
*/
|
||||
|
||||
int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
|
||||
unsigned int *virq)
|
||||
{
|
||||
int result;
|
||||
u64 outlet;
|
||||
u64 lpar_addr;
|
||||
|
||||
BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
|
||||
|
||||
lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
|
||||
|
||||
result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ps3_irq_plug_setup(cpu, outlet, virq);
|
||||
BUG_ON(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup);
|
||||
|
||||
int ps3_vuart_irq_destroy(unsigned int virq)
|
||||
{
|
||||
int result;
|
||||
|
||||
ps3_chip_mask(irq_get_irq_data(virq));
|
||||
result = lv1_deconfigure_virtual_uart_irq();
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ps3_irq_plug_destroy(virq);
|
||||
BUG_ON(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy);
|
||||
|
||||
/**
|
||||
* ps3_spe_irq_setup - Setup an spe virq.
|
||||
* @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
|
||||
* serviced on.
|
||||
* @spe_id: The spe_id returned from lv1_construct_logical_spe().
|
||||
* @class: The spe interrupt class {0,1,2}.
|
||||
* @virq: The assigned Linux virq.
|
||||
*
|
||||
*/
|
||||
|
||||
int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
|
||||
unsigned int class, unsigned int *virq)
|
||||
{
|
||||
int result;
|
||||
u64 outlet;
|
||||
|
||||
BUG_ON(class > 2);
|
||||
|
||||
result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
|
||||
|
||||
if (result) {
|
||||
FAIL("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ps3_irq_plug_setup(cpu, outlet, virq);
|
||||
BUG_ON(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ps3_spe_irq_destroy(unsigned int virq)
|
||||
{
|
||||
int result;
|
||||
|
||||
ps3_chip_mask(irq_get_irq_data(virq));
|
||||
|
||||
result = ps3_irq_plug_destroy(virq);
|
||||
BUG_ON(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
|
||||
#define PS3_PLUG_MAX 63
|
||||
|
||||
#if defined(DEBUG)
|
||||
static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
|
||||
const char* func, int line)
|
||||
{
|
||||
pr_debug("%s:%d: %s %u {%04llx_%04llx_%04llx_%04llx}\n",
|
||||
func, line, header, cpu,
|
||||
*p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
|
||||
*p & 0xffff);
|
||||
}
|
||||
|
||||
static void __maybe_unused _dump_256_bmp(const char *header,
|
||||
const u64 *p, unsigned cpu, const char* func, int line)
|
||||
{
|
||||
pr_debug("%s:%d: %s %u {%016llx:%016llx:%016llx:%016llx}\n",
|
||||
func, line, header, cpu, p[0], p[1], p[2], p[3]);
|
||||
}
|
||||
|
||||
#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
|
||||
static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pd->bmp_lock, flags);
|
||||
_dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line);
|
||||
_dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line);
|
||||
spin_unlock_irqrestore(&pd->bmp_lock, flags);
|
||||
}
|
||||
|
||||
#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
|
||||
static void __maybe_unused _dump_mask(struct ps3_private *pd,
|
||||
const char* func, int line)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pd->bmp_lock, flags);
|
||||
_dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line);
|
||||
spin_unlock_irqrestore(&pd->bmp_lock, flags);
|
||||
}
|
||||
#else
|
||||
static void dump_bmp(struct ps3_private* pd) {};
|
||||
#endif /* defined(DEBUG) */
|
||||
|
||||
static int ps3_host_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
|
||||
virq);
|
||||
|
||||
irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ps3_host_match(struct irq_domain *h, struct device_node *np)
|
||||
{
|
||||
/* Match all */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops ps3_host_ops = {
|
||||
.map = ps3_host_map,
|
||||
.match = ps3_host_match,
|
||||
};
|
||||
|
||||
void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
|
||||
{
|
||||
struct ps3_private *pd = &per_cpu(ps3_private, cpu);
|
||||
|
||||
set_bit(63 - virq, &pd->ipi_debug_brk_mask);
|
||||
|
||||
DBG("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__,
|
||||
cpu, virq, pd->ipi_debug_brk_mask);
|
||||
}
|
||||
|
||||
void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq)
|
||||
{
|
||||
struct ps3_private *pd = &per_cpu(ps3_private, cpu);
|
||||
|
||||
set_bit(63 - virq, &pd->ipi_mask);
|
||||
|
||||
DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__,
|
||||
cpu, virq, pd->ipi_mask);
|
||||
}
|
||||
|
||||
static unsigned int ps3_get_irq(void)
|
||||
{
|
||||
struct ps3_private *pd = &__get_cpu_var(ps3_private);
|
||||
u64 x = (pd->bmp.status & pd->bmp.mask);
|
||||
unsigned int plug;
|
||||
|
||||
/* check for ipi break first to stop this cpu ASAP */
|
||||
|
||||
if (x & pd->ipi_debug_brk_mask)
|
||||
x &= pd->ipi_debug_brk_mask;
|
||||
|
||||
asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x));
|
||||
plug &= 0x3f;
|
||||
|
||||
if (unlikely(plug == NO_IRQ)) {
|
||||
DBG("%s:%d: no plug found: thread_id %llu\n", __func__,
|
||||
__LINE__, pd->thread_id);
|
||||
dump_bmp(&per_cpu(ps3_private, 0));
|
||||
dump_bmp(&per_cpu(ps3_private, 1));
|
||||
return NO_IRQ;
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) {
|
||||
dump_bmp(&per_cpu(ps3_private, 0));
|
||||
dump_bmp(&per_cpu(ps3_private, 1));
|
||||
BUG();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* IPIs are EOIed here. */
|
||||
|
||||
if (test_bit(63 - plug, &pd->ipi_mask))
|
||||
lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug);
|
||||
|
||||
return plug;
|
||||
}
|
||||
|
||||
void __init ps3_init_IRQ(void)
|
||||
{
|
||||
int result;
|
||||
unsigned cpu;
|
||||
struct irq_domain *host;
|
||||
|
||||
host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL);
|
||||
irq_set_default_host(host);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct ps3_private *pd = &per_cpu(ps3_private, cpu);
|
||||
|
||||
lv1_get_logical_ppe_id(&pd->ppe_id);
|
||||
pd->thread_id = get_hard_smp_processor_id(cpu);
|
||||
spin_lock_init(&pd->bmp_lock);
|
||||
|
||||
DBG("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n",
|
||||
__func__, __LINE__, pd->ppe_id, pd->thread_id,
|
||||
ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
|
||||
|
||||
result = lv1_configure_irq_state_bitmap(pd->ppe_id,
|
||||
pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
|
||||
|
||||
if (result)
|
||||
FAIL("%s:%d: lv1_configure_irq_state_bitmap failed:"
|
||||
" %s\n", __func__, __LINE__,
|
||||
ps3_result(result));
|
||||
}
|
||||
|
||||
ppc_md.get_irq = ps3_get_irq;
|
||||
}
|
||||
|
||||
void ps3_shutdown_IRQ(int cpu)
|
||||
{
|
||||
int result;
|
||||
u64 ppe_id;
|
||||
u64 thread_id = get_hard_smp_processor_id(cpu);
|
||||
|
||||
lv1_get_logical_ppe_id(&ppe_id);
|
||||
result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0);
|
||||
|
||||
DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__,
|
||||
__LINE__, ppe_id, thread_id, cpu, ps3_result(result));
|
||||
}
|
||||
1238
arch/powerpc/platforms/ps3/mm.c
Normal file
1238
arch/powerpc/platforms/ps3/mm.c
Normal file
File diff suppressed because it is too large
Load diff
851
arch/powerpc/platforms/ps3/os-area.c
Normal file
851
arch/powerpc/platforms/ps3/os-area.c
Normal file
|
|
@ -0,0 +1,851 @@
|
|||
/*
|
||||
* PS3 flash memory os area.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
enum {
|
||||
OS_AREA_SEGMENT_SIZE = 0X200,
|
||||
};
|
||||
|
||||
enum os_area_ldr_format {
|
||||
HEADER_LDR_FORMAT_RAW = 0,
|
||||
HEADER_LDR_FORMAT_GZIP = 1,
|
||||
};
|
||||
|
||||
#define OS_AREA_HEADER_MAGIC_NUM "cell_ext_os_area"
|
||||
|
||||
/**
|
||||
* struct os_area_header - os area header segment.
|
||||
* @magic_num: Always 'cell_ext_os_area'.
|
||||
* @hdr_version: Header format version number.
|
||||
* @db_area_offset: Starting segment number of other os database area.
|
||||
* @ldr_area_offset: Starting segment number of bootloader image area.
|
||||
* @ldr_format: HEADER_LDR_FORMAT flag.
|
||||
* @ldr_size: Size of bootloader image in bytes.
|
||||
*
|
||||
* Note that the docs refer to area offsets. These are offsets in units of
|
||||
* segments from the start of the os area (top of the header). These are
|
||||
* better thought of as segment numbers. The os area of the os area is
|
||||
* reserved for the os image.
|
||||
*/
|
||||
|
||||
struct os_area_header {
|
||||
u8 magic_num[16];
|
||||
u32 hdr_version;
|
||||
u32 db_area_offset;
|
||||
u32 ldr_area_offset;
|
||||
u32 _reserved_1;
|
||||
u32 ldr_format;
|
||||
u32 ldr_size;
|
||||
u32 _reserved_2[6];
|
||||
};
|
||||
|
||||
enum os_area_boot_flag {
|
||||
PARAM_BOOT_FLAG_GAME_OS = 0,
|
||||
PARAM_BOOT_FLAG_OTHER_OS = 1,
|
||||
};
|
||||
|
||||
enum os_area_ctrl_button {
|
||||
PARAM_CTRL_BUTTON_O_IS_YES = 0,
|
||||
PARAM_CTRL_BUTTON_X_IS_YES = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct os_area_params - os area params segment.
|
||||
* @boot_flag: User preference of operating system, PARAM_BOOT_FLAG flag.
|
||||
* @num_params: Number of params in this (params) segment.
|
||||
* @rtc_diff: Difference in seconds between 1970 and the ps3 rtc value.
|
||||
* @av_multi_out: User preference of AV output, PARAM_AV_MULTI_OUT flag.
|
||||
* @ctrl_button: User preference of controller button config, PARAM_CTRL_BUTTON
|
||||
* flag.
|
||||
* @static_ip_addr: User preference of static IP address.
|
||||
* @network_mask: User preference of static network mask.
|
||||
* @default_gateway: User preference of static default gateway.
|
||||
* @dns_primary: User preference of static primary dns server.
|
||||
* @dns_secondary: User preference of static secondary dns server.
|
||||
*
|
||||
* The ps3 rtc maintains a read-only value that approximates seconds since
|
||||
* 2000-01-01 00:00:00 UTC.
|
||||
*
|
||||
* User preference of zero for static_ip_addr means use dhcp.
|
||||
*/
|
||||
|
||||
struct os_area_params {
|
||||
u32 boot_flag;
|
||||
u32 _reserved_1[3];
|
||||
u32 num_params;
|
||||
u32 _reserved_2[3];
|
||||
/* param 0 */
|
||||
s64 rtc_diff;
|
||||
u8 av_multi_out;
|
||||
u8 ctrl_button;
|
||||
u8 _reserved_3[6];
|
||||
/* param 1 */
|
||||
u8 static_ip_addr[4];
|
||||
u8 network_mask[4];
|
||||
u8 default_gateway[4];
|
||||
u8 _reserved_4[4];
|
||||
/* param 2 */
|
||||
u8 dns_primary[4];
|
||||
u8 dns_secondary[4];
|
||||
u8 _reserved_5[8];
|
||||
};
|
||||
|
||||
#define OS_AREA_DB_MAGIC_NUM "-db-"
|
||||
|
||||
/**
|
||||
* struct os_area_db - Shared flash memory database.
|
||||
* @magic_num: Always '-db-'.
|
||||
* @version: os_area_db format version number.
|
||||
* @index_64: byte offset of the database id index for 64 bit variables.
|
||||
* @count_64: number of usable 64 bit index entries
|
||||
* @index_32: byte offset of the database id index for 32 bit variables.
|
||||
* @count_32: number of usable 32 bit index entries
|
||||
* @index_16: byte offset of the database id index for 16 bit variables.
|
||||
* @count_16: number of usable 16 bit index entries
|
||||
*
|
||||
* Flash rom storage for exclusive use by guests running in the other os lpar.
|
||||
* The current system configuration allocates 1K (two segments) for other os
|
||||
* use.
|
||||
*/
|
||||
|
||||
struct os_area_db {
|
||||
u8 magic_num[4];
|
||||
u16 version;
|
||||
u16 _reserved_1;
|
||||
u16 index_64;
|
||||
u16 count_64;
|
||||
u16 index_32;
|
||||
u16 count_32;
|
||||
u16 index_16;
|
||||
u16 count_16;
|
||||
u32 _reserved_2;
|
||||
u8 _db_data[1000];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum os_area_db_owner - Data owners.
|
||||
*/
|
||||
|
||||
enum os_area_db_owner {
|
||||
OS_AREA_DB_OWNER_ANY = -1,
|
||||
OS_AREA_DB_OWNER_NONE = 0,
|
||||
OS_AREA_DB_OWNER_PROTOTYPE = 1,
|
||||
OS_AREA_DB_OWNER_LINUX = 2,
|
||||
OS_AREA_DB_OWNER_PETITBOOT = 3,
|
||||
OS_AREA_DB_OWNER_MAX = 32,
|
||||
};
|
||||
|
||||
enum os_area_db_key {
|
||||
OS_AREA_DB_KEY_ANY = -1,
|
||||
OS_AREA_DB_KEY_NONE = 0,
|
||||
OS_AREA_DB_KEY_RTC_DIFF = 1,
|
||||
OS_AREA_DB_KEY_VIDEO_MODE = 2,
|
||||
OS_AREA_DB_KEY_MAX = 8,
|
||||
};
|
||||
|
||||
struct os_area_db_id {
|
||||
int owner;
|
||||
int key;
|
||||
};
|
||||
|
||||
static const struct os_area_db_id os_area_db_id_empty = {
|
||||
.owner = OS_AREA_DB_OWNER_NONE,
|
||||
.key = OS_AREA_DB_KEY_NONE
|
||||
};
|
||||
|
||||
static const struct os_area_db_id os_area_db_id_any = {
|
||||
.owner = OS_AREA_DB_OWNER_ANY,
|
||||
.key = OS_AREA_DB_KEY_ANY
|
||||
};
|
||||
|
||||
static const struct os_area_db_id os_area_db_id_rtc_diff = {
|
||||
.owner = OS_AREA_DB_OWNER_LINUX,
|
||||
.key = OS_AREA_DB_KEY_RTC_DIFF
|
||||
};
|
||||
|
||||
static const struct os_area_db_id os_area_db_id_video_mode = {
|
||||
.owner = OS_AREA_DB_OWNER_LINUX,
|
||||
.key = OS_AREA_DB_KEY_VIDEO_MODE
|
||||
};
|
||||
|
||||
#define SECONDS_FROM_1970_TO_2000 946684800LL
|
||||
|
||||
/**
|
||||
* struct saved_params - Static working copies of data from the PS3 'os area'.
|
||||
*
|
||||
* The order of preference we use for the rtc_diff source:
|
||||
* 1) The database value.
|
||||
* 2) The game os value.
|
||||
* 3) The number of seconds from 1970 to 2000.
|
||||
*/
|
||||
|
||||
struct saved_params {
|
||||
unsigned int valid;
|
||||
s64 rtc_diff;
|
||||
unsigned int av_multi_out;
|
||||
} static saved_params;
|
||||
|
||||
static struct property property_rtc_diff = {
|
||||
.name = "linux,rtc_diff",
|
||||
.length = sizeof(saved_params.rtc_diff),
|
||||
.value = &saved_params.rtc_diff,
|
||||
};
|
||||
|
||||
static struct property property_av_multi_out = {
|
||||
.name = "linux,av_multi_out",
|
||||
.length = sizeof(saved_params.av_multi_out),
|
||||
.value = &saved_params.av_multi_out,
|
||||
};
|
||||
|
||||
|
||||
static DEFINE_MUTEX(os_area_flash_mutex);
|
||||
|
||||
static const struct ps3_os_area_flash_ops *os_area_flash_ops;
|
||||
|
||||
void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops)
|
||||
{
|
||||
mutex_lock(&os_area_flash_mutex);
|
||||
os_area_flash_ops = ops;
|
||||
mutex_unlock(&os_area_flash_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_os_area_flash_register);
|
||||
|
||||
static ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos)
|
||||
{
|
||||
ssize_t res = -ENODEV;
|
||||
|
||||
mutex_lock(&os_area_flash_mutex);
|
||||
if (os_area_flash_ops)
|
||||
res = os_area_flash_ops->read(buf, count, pos);
|
||||
mutex_unlock(&os_area_flash_mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos)
|
||||
{
|
||||
ssize_t res = -ENODEV;
|
||||
|
||||
mutex_lock(&os_area_flash_mutex);
|
||||
if (os_area_flash_ops)
|
||||
res = os_area_flash_ops->write(buf, count, pos);
|
||||
mutex_unlock(&os_area_flash_mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* os_area_set_property - Add or overwrite a saved_params value to the device tree.
|
||||
*
|
||||
* Overwrites an existing property.
|
||||
*/
|
||||
|
||||
static void os_area_set_property(struct device_node *node,
|
||||
struct property *prop)
|
||||
{
|
||||
int result;
|
||||
struct property *tmp = of_find_property(node, prop->name, NULL);
|
||||
|
||||
if (tmp) {
|
||||
pr_debug("%s:%d found %s\n", __func__, __LINE__, prop->name);
|
||||
of_remove_property(node, tmp);
|
||||
}
|
||||
|
||||
result = of_add_property(node, prop);
|
||||
|
||||
if (result)
|
||||
pr_debug("%s:%d of_set_property failed\n", __func__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* os_area_get_property - Get a saved_params value from the device tree.
|
||||
*
|
||||
*/
|
||||
|
||||
static void __init os_area_get_property(struct device_node *node,
|
||||
struct property *prop)
|
||||
{
|
||||
const struct property *tmp = of_find_property(node, prop->name, NULL);
|
||||
|
||||
if (tmp) {
|
||||
BUG_ON(prop->length != tmp->length);
|
||||
memcpy(prop->value, tmp->value, prop->length);
|
||||
} else
|
||||
pr_debug("%s:%d not found %s\n", __func__, __LINE__,
|
||||
prop->name);
|
||||
}
|
||||
|
||||
static void dump_field(char *s, const u8 *field, int size_of_field)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size_of_field; i++)
|
||||
s[i] = isprint(field[i]) ? field[i] : '.';
|
||||
s[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define dump_header(_a) _dump_header(_a, __func__, __LINE__)
|
||||
static void _dump_header(const struct os_area_header *h, const char *func,
|
||||
int line)
|
||||
{
|
||||
char str[sizeof(h->magic_num) + 1];
|
||||
|
||||
dump_field(str, h->magic_num, sizeof(h->magic_num));
|
||||
pr_debug("%s:%d: h.magic_num: '%s'\n", func, line,
|
||||
str);
|
||||
pr_debug("%s:%d: h.hdr_version: %u\n", func, line,
|
||||
h->hdr_version);
|
||||
pr_debug("%s:%d: h.db_area_offset: %u\n", func, line,
|
||||
h->db_area_offset);
|
||||
pr_debug("%s:%d: h.ldr_area_offset: %u\n", func, line,
|
||||
h->ldr_area_offset);
|
||||
pr_debug("%s:%d: h.ldr_format: %u\n", func, line,
|
||||
h->ldr_format);
|
||||
pr_debug("%s:%d: h.ldr_size: %xh\n", func, line,
|
||||
h->ldr_size);
|
||||
}
|
||||
|
||||
#define dump_params(_a) _dump_params(_a, __func__, __LINE__)
|
||||
static void _dump_params(const struct os_area_params *p, const char *func,
|
||||
int line)
|
||||
{
|
||||
pr_debug("%s:%d: p.boot_flag: %u\n", func, line, p->boot_flag);
|
||||
pr_debug("%s:%d: p.num_params: %u\n", func, line, p->num_params);
|
||||
pr_debug("%s:%d: p.rtc_diff %lld\n", func, line, p->rtc_diff);
|
||||
pr_debug("%s:%d: p.av_multi_out %u\n", func, line, p->av_multi_out);
|
||||
pr_debug("%s:%d: p.ctrl_button: %u\n", func, line, p->ctrl_button);
|
||||
pr_debug("%s:%d: p.static_ip_addr: %u.%u.%u.%u\n", func, line,
|
||||
p->static_ip_addr[0], p->static_ip_addr[1],
|
||||
p->static_ip_addr[2], p->static_ip_addr[3]);
|
||||
pr_debug("%s:%d: p.network_mask: %u.%u.%u.%u\n", func, line,
|
||||
p->network_mask[0], p->network_mask[1],
|
||||
p->network_mask[2], p->network_mask[3]);
|
||||
pr_debug("%s:%d: p.default_gateway: %u.%u.%u.%u\n", func, line,
|
||||
p->default_gateway[0], p->default_gateway[1],
|
||||
p->default_gateway[2], p->default_gateway[3]);
|
||||
pr_debug("%s:%d: p.dns_primary: %u.%u.%u.%u\n", func, line,
|
||||
p->dns_primary[0], p->dns_primary[1],
|
||||
p->dns_primary[2], p->dns_primary[3]);
|
||||
pr_debug("%s:%d: p.dns_secondary: %u.%u.%u.%u\n", func, line,
|
||||
p->dns_secondary[0], p->dns_secondary[1],
|
||||
p->dns_secondary[2], p->dns_secondary[3]);
|
||||
}
|
||||
|
||||
static int verify_header(const struct os_area_header *header)
|
||||
{
|
||||
if (memcmp(header->magic_num, OS_AREA_HEADER_MAGIC_NUM,
|
||||
sizeof(header->magic_num))) {
|
||||
pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->hdr_version < 1) {
|
||||
pr_debug("%s:%d hdr_version failed\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->db_area_offset > header->ldr_area_offset) {
|
||||
pr_debug("%s:%d offsets failed\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db_verify(const struct os_area_db *db)
|
||||
{
|
||||
if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM,
|
||||
sizeof(db->magic_num))) {
|
||||
pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (db->version != 1) {
|
||||
pr_debug("%s:%d version failed\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct db_index {
|
||||
uint8_t owner:5;
|
||||
uint8_t key:3;
|
||||
};
|
||||
|
||||
struct db_iterator {
|
||||
const struct os_area_db *db;
|
||||
struct os_area_db_id match_id;
|
||||
struct db_index *idx;
|
||||
struct db_index *last_idx;
|
||||
union {
|
||||
uint64_t *value_64;
|
||||
uint32_t *value_32;
|
||||
uint16_t *value_16;
|
||||
};
|
||||
};
|
||||
|
||||
static unsigned int db_align_up(unsigned int val, unsigned int size)
|
||||
{
|
||||
return (val + (size - 1)) & (~(size - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* db_for_each_64 - Iterator for 64 bit entries.
|
||||
*
|
||||
* A NULL value for id can be used to match all entries.
|
||||
* OS_AREA_DB_OWNER_ANY and OS_AREA_DB_KEY_ANY can be used to match all.
|
||||
*/
|
||||
|
||||
static int db_for_each_64(const struct os_area_db *db,
|
||||
const struct os_area_db_id *match_id, struct db_iterator *i)
|
||||
{
|
||||
next:
|
||||
if (!i->db) {
|
||||
i->db = db;
|
||||
i->match_id = match_id ? *match_id : os_area_db_id_any;
|
||||
i->idx = (void *)db + db->index_64;
|
||||
i->last_idx = i->idx + db->count_64;
|
||||
i->value_64 = (void *)db + db->index_64
|
||||
+ db_align_up(db->count_64, 8);
|
||||
} else {
|
||||
i->idx++;
|
||||
i->value_64++;
|
||||
}
|
||||
|
||||
if (i->idx >= i->last_idx) {
|
||||
pr_debug("%s:%d: reached end\n", __func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i->match_id.owner != OS_AREA_DB_OWNER_ANY
|
||||
&& i->match_id.owner != (int)i->idx->owner)
|
||||
goto next;
|
||||
if (i->match_id.key != OS_AREA_DB_KEY_ANY
|
||||
&& i->match_id.key != (int)i->idx->key)
|
||||
goto next;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int db_delete_64(struct os_area_db *db, const struct os_area_db_id *id)
|
||||
{
|
||||
struct db_iterator i;
|
||||
|
||||
for (i.db = NULL; db_for_each_64(db, id, &i); ) {
|
||||
|
||||
pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__,
|
||||
i.idx->owner, i.idx->key,
|
||||
(unsigned long long)*i.value_64);
|
||||
|
||||
i.idx->owner = 0;
|
||||
i.idx->key = 0;
|
||||
*i.value_64 = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db_set_64(struct os_area_db *db, const struct os_area_db_id *id,
|
||||
uint64_t value)
|
||||
{
|
||||
struct db_iterator i;
|
||||
|
||||
pr_debug("%s:%d: (%d:%d) <= %llxh\n", __func__, __LINE__,
|
||||
id->owner, id->key, (unsigned long long)value);
|
||||
|
||||
if (!id->owner || id->owner == OS_AREA_DB_OWNER_ANY
|
||||
|| id->key == OS_AREA_DB_KEY_ANY) {
|
||||
pr_debug("%s:%d: bad id: (%d:%d)\n", __func__,
|
||||
__LINE__, id->owner, id->key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
db_delete_64(db, id);
|
||||
|
||||
i.db = NULL;
|
||||
if (db_for_each_64(db, &os_area_db_id_empty, &i)) {
|
||||
|
||||
pr_debug("%s:%d: got (%d:%d) %llxh\n", __func__, __LINE__,
|
||||
i.idx->owner, i.idx->key,
|
||||
(unsigned long long)*i.value_64);
|
||||
|
||||
i.idx->owner = id->owner;
|
||||
i.idx->key = id->key;
|
||||
*i.value_64 = value;
|
||||
|
||||
pr_debug("%s:%d: set (%d:%d) <= %llxh\n", __func__, __LINE__,
|
||||
i.idx->owner, i.idx->key,
|
||||
(unsigned long long)*i.value_64);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("%s:%d: database full.\n",
|
||||
__func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int db_get_64(const struct os_area_db *db,
|
||||
const struct os_area_db_id *id, uint64_t *value)
|
||||
{
|
||||
struct db_iterator i;
|
||||
|
||||
i.db = NULL;
|
||||
if (db_for_each_64(db, id, &i)) {
|
||||
*value = *i.value_64;
|
||||
pr_debug("%s:%d: found %lld\n", __func__, __LINE__,
|
||||
(long long int)*i.value_64);
|
||||
return 0;
|
||||
}
|
||||
pr_debug("%s:%d: not found\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int db_get_rtc_diff(const struct os_area_db *db, int64_t *rtc_diff)
|
||||
{
|
||||
return db_get_64(db, &os_area_db_id_rtc_diff, (uint64_t*)rtc_diff);
|
||||
}
|
||||
|
||||
#define dump_db(a) _dump_db(a, __func__, __LINE__)
|
||||
static void _dump_db(const struct os_area_db *db, const char *func,
|
||||
int line)
|
||||
{
|
||||
char str[sizeof(db->magic_num) + 1];
|
||||
|
||||
dump_field(str, db->magic_num, sizeof(db->magic_num));
|
||||
pr_debug("%s:%d: db.magic_num: '%s'\n", func, line,
|
||||
str);
|
||||
pr_debug("%s:%d: db.version: %u\n", func, line,
|
||||
db->version);
|
||||
pr_debug("%s:%d: db.index_64: %u\n", func, line,
|
||||
db->index_64);
|
||||
pr_debug("%s:%d: db.count_64: %u\n", func, line,
|
||||
db->count_64);
|
||||
pr_debug("%s:%d: db.index_32: %u\n", func, line,
|
||||
db->index_32);
|
||||
pr_debug("%s:%d: db.count_32: %u\n", func, line,
|
||||
db->count_32);
|
||||
pr_debug("%s:%d: db.index_16: %u\n", func, line,
|
||||
db->index_16);
|
||||
pr_debug("%s:%d: db.count_16: %u\n", func, line,
|
||||
db->count_16);
|
||||
}
|
||||
|
||||
static void os_area_db_init(struct os_area_db *db)
|
||||
{
|
||||
enum {
|
||||
HEADER_SIZE = offsetof(struct os_area_db, _db_data),
|
||||
INDEX_64_COUNT = 64,
|
||||
VALUES_64_COUNT = 57,
|
||||
INDEX_32_COUNT = 64,
|
||||
VALUES_32_COUNT = 57,
|
||||
INDEX_16_COUNT = 64,
|
||||
VALUES_16_COUNT = 57,
|
||||
};
|
||||
|
||||
memset(db, 0, sizeof(struct os_area_db));
|
||||
|
||||
memcpy(db->magic_num, OS_AREA_DB_MAGIC_NUM, sizeof(db->magic_num));
|
||||
db->version = 1;
|
||||
db->index_64 = HEADER_SIZE;
|
||||
db->count_64 = VALUES_64_COUNT;
|
||||
db->index_32 = HEADER_SIZE
|
||||
+ INDEX_64_COUNT * sizeof(struct db_index)
|
||||
+ VALUES_64_COUNT * sizeof(u64);
|
||||
db->count_32 = VALUES_32_COUNT;
|
||||
db->index_16 = HEADER_SIZE
|
||||
+ INDEX_64_COUNT * sizeof(struct db_index)
|
||||
+ VALUES_64_COUNT * sizeof(u64)
|
||||
+ INDEX_32_COUNT * sizeof(struct db_index)
|
||||
+ VALUES_32_COUNT * sizeof(u32);
|
||||
db->count_16 = VALUES_16_COUNT;
|
||||
|
||||
/* Rules to check db layout. */
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct db_index) != 1);
|
||||
BUILD_BUG_ON(sizeof(struct os_area_db) != 2 * OS_AREA_SEGMENT_SIZE);
|
||||
BUILD_BUG_ON(INDEX_64_COUNT & 0x7);
|
||||
BUILD_BUG_ON(VALUES_64_COUNT > INDEX_64_COUNT);
|
||||
BUILD_BUG_ON(INDEX_32_COUNT & 0x7);
|
||||
BUILD_BUG_ON(VALUES_32_COUNT > INDEX_32_COUNT);
|
||||
BUILD_BUG_ON(INDEX_16_COUNT & 0x7);
|
||||
BUILD_BUG_ON(VALUES_16_COUNT > INDEX_16_COUNT);
|
||||
BUILD_BUG_ON(HEADER_SIZE
|
||||
+ INDEX_64_COUNT * sizeof(struct db_index)
|
||||
+ VALUES_64_COUNT * sizeof(u64)
|
||||
+ INDEX_32_COUNT * sizeof(struct db_index)
|
||||
+ VALUES_32_COUNT * sizeof(u32)
|
||||
+ INDEX_16_COUNT * sizeof(struct db_index)
|
||||
+ VALUES_16_COUNT * sizeof(u16)
|
||||
> sizeof(struct os_area_db));
|
||||
}
|
||||
|
||||
/**
|
||||
* update_flash_db - Helper for os_area_queue_work_handler.
|
||||
*
|
||||
*/
|
||||
|
||||
static int update_flash_db(void)
|
||||
{
|
||||
const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
|
||||
struct os_area_header *header;
|
||||
ssize_t count;
|
||||
int error;
|
||||
loff_t pos;
|
||||
struct os_area_db* db;
|
||||
|
||||
/* Read in header and db from flash. */
|
||||
|
||||
header = kmalloc(buf_len, GFP_KERNEL);
|
||||
if (!header) {
|
||||
pr_debug("%s: kmalloc failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
count = os_area_flash_read(header, buf_len, 0);
|
||||
if (count < 0) {
|
||||
pr_debug("%s: os_area_flash_read failed %zd\n", __func__,
|
||||
count);
|
||||
error = count;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE;
|
||||
if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) ||
|
||||
count < pos) {
|
||||
pr_debug("%s: verify_header failed\n", __func__);
|
||||
dump_header(header);
|
||||
error = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Now got a good db offset and some maybe good db data. */
|
||||
|
||||
db = (void *)header + pos;
|
||||
|
||||
error = db_verify(db);
|
||||
if (error) {
|
||||
pr_notice("%s: Verify of flash database failed, formatting.\n",
|
||||
__func__);
|
||||
dump_db(db);
|
||||
os_area_db_init(db);
|
||||
}
|
||||
|
||||
/* Now got good db data. */
|
||||
|
||||
db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
|
||||
|
||||
count = os_area_flash_write(db, sizeof(struct os_area_db), pos);
|
||||
if (count < sizeof(struct os_area_db)) {
|
||||
pr_debug("%s: os_area_flash_write failed %zd\n", __func__,
|
||||
count);
|
||||
error = count < 0 ? count : -EIO;
|
||||
}
|
||||
|
||||
fail:
|
||||
kfree(header);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* os_area_queue_work_handler - Asynchronous write handler.
|
||||
*
|
||||
* An asynchronous write for flash memory and the device tree. Do not
|
||||
* call directly, use os_area_queue_work().
|
||||
*/
|
||||
|
||||
static void os_area_queue_work_handler(struct work_struct *work)
|
||||
{
|
||||
struct device_node *node;
|
||||
int error;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
node = of_find_node_by_path("/");
|
||||
if (node) {
|
||||
os_area_set_property(node, &property_rtc_diff);
|
||||
of_node_put(node);
|
||||
} else
|
||||
pr_debug("%s:%d of_find_node_by_path failed\n",
|
||||
__func__, __LINE__);
|
||||
|
||||
error = update_flash_db();
|
||||
if (error)
|
||||
pr_warning("%s: Could not update FLASH ROM\n", __func__);
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
static void os_area_queue_work(void)
|
||||
{
|
||||
static DECLARE_WORK(q, os_area_queue_work_handler);
|
||||
|
||||
wmb();
|
||||
schedule_work(&q);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_os_area_save_params - Copy data from os area mirror to @saved_params.
|
||||
*
|
||||
* For the convenience of the guest the HV makes a copy of the os area in
|
||||
* flash to a high address in the boot memory region and then puts that RAM
|
||||
* address and the byte count into the repository for retrieval by the guest.
|
||||
* We copy the data we want into a static variable and allow the memory setup
|
||||
* by the HV to be claimed by the memblock manager.
|
||||
*
|
||||
* The os area mirror will not be available to a second stage kernel, and
|
||||
* the header verify will fail. In this case, the saved_params values will
|
||||
* be set from flash memory or the passed in device tree in ps3_os_area_init().
|
||||
*/
|
||||
|
||||
void __init ps3_os_area_save_params(void)
|
||||
{
|
||||
int result;
|
||||
u64 lpar_addr;
|
||||
unsigned int size;
|
||||
struct os_area_header *header;
|
||||
struct os_area_params *params;
|
||||
struct os_area_db *db;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
result = ps3_repository_read_boot_dat_info(&lpar_addr, &size);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d ps3_repository_read_boot_dat_info failed\n",
|
||||
__func__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
header = (struct os_area_header *)__va(lpar_addr);
|
||||
params = (struct os_area_params *)__va(lpar_addr
|
||||
+ OS_AREA_SEGMENT_SIZE);
|
||||
|
||||
result = verify_header(header);
|
||||
|
||||
if (result) {
|
||||
/* Second stage kernels exit here. */
|
||||
pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
|
||||
dump_header(header);
|
||||
return;
|
||||
}
|
||||
|
||||
db = (struct os_area_db *)__va(lpar_addr
|
||||
+ header->db_area_offset * OS_AREA_SEGMENT_SIZE);
|
||||
|
||||
dump_header(header);
|
||||
dump_params(params);
|
||||
dump_db(db);
|
||||
|
||||
result = db_verify(db) || db_get_rtc_diff(db, &saved_params.rtc_diff);
|
||||
if (result)
|
||||
saved_params.rtc_diff = params->rtc_diff ? params->rtc_diff
|
||||
: SECONDS_FROM_1970_TO_2000;
|
||||
saved_params.av_multi_out = params->av_multi_out;
|
||||
saved_params.valid = 1;
|
||||
|
||||
memset(header, 0, sizeof(*header));
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_os_area_init - Setup os area device tree properties as needed.
|
||||
*/
|
||||
|
||||
void __init ps3_os_area_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
node = of_find_node_by_path("/");
|
||||
|
||||
if (!saved_params.valid && node) {
|
||||
/* Second stage kernels should have a dt entry. */
|
||||
os_area_get_property(node, &property_rtc_diff);
|
||||
os_area_get_property(node, &property_av_multi_out);
|
||||
}
|
||||
|
||||
if(!saved_params.rtc_diff)
|
||||
saved_params.rtc_diff = SECONDS_FROM_1970_TO_2000;
|
||||
|
||||
if (node) {
|
||||
os_area_set_property(node, &property_rtc_diff);
|
||||
os_area_set_property(node, &property_av_multi_out);
|
||||
of_node_put(node);
|
||||
} else
|
||||
pr_debug("%s:%d of_find_node_by_path failed\n",
|
||||
__func__, __LINE__);
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_os_area_get_rtc_diff - Returns the rtc diff value.
|
||||
*/
|
||||
|
||||
u64 ps3_os_area_get_rtc_diff(void)
|
||||
{
|
||||
return saved_params.rtc_diff;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_os_area_get_rtc_diff);
|
||||
|
||||
/**
|
||||
* ps3_os_area_set_rtc_diff - Set the rtc diff value.
|
||||
*
|
||||
* An asynchronous write is needed to support writing updates from
|
||||
* the timer interrupt context.
|
||||
*/
|
||||
|
||||
void ps3_os_area_set_rtc_diff(u64 rtc_diff)
|
||||
{
|
||||
if (saved_params.rtc_diff != rtc_diff) {
|
||||
saved_params.rtc_diff = rtc_diff;
|
||||
os_area_queue_work();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_os_area_set_rtc_diff);
|
||||
|
||||
/**
|
||||
* ps3_os_area_get_av_multi_out - Returns the default video mode.
|
||||
*/
|
||||
|
||||
enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void)
|
||||
{
|
||||
return saved_params.av_multi_out;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_os_area_get_av_multi_out);
|
||||
252
arch/powerpc/platforms/ps3/platform.h
Normal file
252
arch/powerpc/platforms/ps3/platform.h
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* PS3 platform declarations.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#if !defined(_PS3_PLATFORM_H)
|
||||
#define _PS3_PLATFORM_H
|
||||
|
||||
#include <linux/rtc.h>
|
||||
#include <scsi/scsi.h>
|
||||
|
||||
#include <asm/ps3.h>
|
||||
|
||||
/* htab */
|
||||
|
||||
void __init ps3_hpte_init(unsigned long htab_size);
|
||||
void __init ps3_map_htab(void);
|
||||
|
||||
/* mm */
|
||||
|
||||
void __init ps3_mm_init(void);
|
||||
void __init ps3_mm_vas_create(unsigned long* htab_size);
|
||||
void ps3_mm_vas_destroy(void);
|
||||
void ps3_mm_shutdown(void);
|
||||
|
||||
/* irq */
|
||||
|
||||
void ps3_init_IRQ(void);
|
||||
void ps3_shutdown_IRQ(int cpu);
|
||||
void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq);
|
||||
void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq);
|
||||
|
||||
/* smp */
|
||||
|
||||
void smp_init_ps3(void);
|
||||
#ifdef CONFIG_SMP
|
||||
void ps3_smp_cleanup_cpu(int cpu);
|
||||
#else
|
||||
static inline void ps3_smp_cleanup_cpu(int cpu) { }
|
||||
#endif
|
||||
|
||||
/* time */
|
||||
|
||||
void __init ps3_calibrate_decr(void);
|
||||
unsigned long __init ps3_get_boot_time(void);
|
||||
void ps3_get_rtc_time(struct rtc_time *time);
|
||||
int ps3_set_rtc_time(struct rtc_time *time);
|
||||
|
||||
/* os area */
|
||||
|
||||
void __init ps3_os_area_save_params(void);
|
||||
void __init ps3_os_area_init(void);
|
||||
|
||||
/* spu */
|
||||
|
||||
#if defined(CONFIG_SPU_BASE)
|
||||
void ps3_spu_set_platform (void);
|
||||
#else
|
||||
static inline void ps3_spu_set_platform (void) {}
|
||||
#endif
|
||||
|
||||
/* repository bus info */
|
||||
|
||||
enum ps3_bus_type {
|
||||
PS3_BUS_TYPE_SB = 4,
|
||||
PS3_BUS_TYPE_STORAGE = 5,
|
||||
};
|
||||
|
||||
enum ps3_dev_type {
|
||||
PS3_DEV_TYPE_STOR_DISK = TYPE_DISK, /* 0 */
|
||||
PS3_DEV_TYPE_SB_GELIC = 3,
|
||||
PS3_DEV_TYPE_SB_USB = 4,
|
||||
PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */
|
||||
PS3_DEV_TYPE_SB_GPIO = 6,
|
||||
PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */
|
||||
};
|
||||
|
||||
int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
|
||||
u64 *value);
|
||||
int ps3_repository_read_bus_id(unsigned int bus_index, u64 *bus_id);
|
||||
int ps3_repository_read_bus_type(unsigned int bus_index,
|
||||
enum ps3_bus_type *bus_type);
|
||||
int ps3_repository_read_bus_num_dev(unsigned int bus_index,
|
||||
unsigned int *num_dev);
|
||||
|
||||
/* repository bus device info */
|
||||
|
||||
enum ps3_interrupt_type {
|
||||
PS3_INTERRUPT_TYPE_EVENT_PORT = 2,
|
||||
PS3_INTERRUPT_TYPE_SB_OHCI = 3,
|
||||
PS3_INTERRUPT_TYPE_SB_EHCI = 4,
|
||||
PS3_INTERRUPT_TYPE_OTHER = 5,
|
||||
};
|
||||
|
||||
enum ps3_reg_type {
|
||||
PS3_REG_TYPE_SB_OHCI = 3,
|
||||
PS3_REG_TYPE_SB_EHCI = 4,
|
||||
PS3_REG_TYPE_SB_GPIO = 5,
|
||||
};
|
||||
|
||||
int ps3_repository_read_dev_str(unsigned int bus_index,
|
||||
unsigned int dev_index, const char *dev_str, u64 *value);
|
||||
int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index,
|
||||
u64 *dev_id);
|
||||
int ps3_repository_read_dev_type(unsigned int bus_index,
|
||||
unsigned int dev_index, enum ps3_dev_type *dev_type);
|
||||
int ps3_repository_read_dev_intr(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int intr_index,
|
||||
enum ps3_interrupt_type *intr_type, unsigned int *interrupt_id);
|
||||
int ps3_repository_read_dev_reg_type(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int reg_index,
|
||||
enum ps3_reg_type *reg_type);
|
||||
int ps3_repository_read_dev_reg_addr(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int reg_index, u64 *bus_addr,
|
||||
u64 *len);
|
||||
int ps3_repository_read_dev_reg(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int reg_index,
|
||||
enum ps3_reg_type *reg_type, u64 *bus_addr, u64 *len);
|
||||
|
||||
/* repository bus enumerators */
|
||||
|
||||
struct ps3_repository_device {
|
||||
unsigned int bus_index;
|
||||
unsigned int dev_index;
|
||||
enum ps3_bus_type bus_type;
|
||||
enum ps3_dev_type dev_type;
|
||||
u64 bus_id;
|
||||
u64 dev_id;
|
||||
};
|
||||
|
||||
int ps3_repository_find_device(struct ps3_repository_device *repo);
|
||||
int ps3_repository_find_device_by_id(struct ps3_repository_device *repo,
|
||||
u64 bus_id, u64 dev_id);
|
||||
int ps3_repository_find_devices(enum ps3_bus_type bus_type,
|
||||
int (*callback)(const struct ps3_repository_device *repo));
|
||||
int ps3_repository_find_bus(enum ps3_bus_type bus_type, unsigned int from,
|
||||
unsigned int *bus_index);
|
||||
int ps3_repository_find_interrupt(const struct ps3_repository_device *repo,
|
||||
enum ps3_interrupt_type intr_type, unsigned int *interrupt_id);
|
||||
int ps3_repository_find_reg(const struct ps3_repository_device *repo,
|
||||
enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len);
|
||||
|
||||
/* repository block device info */
|
||||
|
||||
int ps3_repository_read_stor_dev_port(unsigned int bus_index,
|
||||
unsigned int dev_index, u64 *port);
|
||||
int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index,
|
||||
unsigned int dev_index, u64 *blk_size);
|
||||
int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index,
|
||||
unsigned int dev_index, u64 *num_blocks);
|
||||
int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int *num_regions);
|
||||
int ps3_repository_read_stor_dev_region_id(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int region_index,
|
||||
unsigned int *region_id);
|
||||
int ps3_repository_read_stor_dev_region_size(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int region_index, u64 *region_size);
|
||||
int ps3_repository_read_stor_dev_region_start(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int region_index, u64 *region_start);
|
||||
int ps3_repository_read_stor_dev_info(unsigned int bus_index,
|
||||
unsigned int dev_index, u64 *port, u64 *blk_size,
|
||||
u64 *num_blocks, unsigned int *num_regions);
|
||||
int ps3_repository_read_stor_dev_region(unsigned int bus_index,
|
||||
unsigned int dev_index, unsigned int region_index,
|
||||
unsigned int *region_id, u64 *region_start, u64 *region_size);
|
||||
|
||||
/* repository logical pu and memory info */
|
||||
|
||||
int ps3_repository_read_num_pu(u64 *num_pu);
|
||||
int ps3_repository_read_pu_id(unsigned int pu_index, u64 *pu_id);
|
||||
int ps3_repository_read_rm_base(unsigned int ppe_id, u64 *rm_base);
|
||||
int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size);
|
||||
int ps3_repository_read_region_total(u64 *region_total);
|
||||
int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size,
|
||||
u64 *region_total);
|
||||
int ps3_repository_read_highmem_region_count(unsigned int *region_count);
|
||||
int ps3_repository_read_highmem_base(unsigned int region_index,
|
||||
u64 *highmem_base);
|
||||
int ps3_repository_read_highmem_size(unsigned int region_index,
|
||||
u64 *highmem_size);
|
||||
int ps3_repository_read_highmem_info(unsigned int region_index,
|
||||
u64 *highmem_base, u64 *highmem_size);
|
||||
|
||||
int ps3_repository_write_highmem_region_count(unsigned int region_count);
|
||||
int ps3_repository_write_highmem_base(unsigned int region_index,
|
||||
u64 highmem_base);
|
||||
int ps3_repository_write_highmem_size(unsigned int region_index,
|
||||
u64 highmem_size);
|
||||
int ps3_repository_write_highmem_info(unsigned int region_index,
|
||||
u64 highmem_base, u64 highmem_size);
|
||||
int ps3_repository_delete_highmem_info(unsigned int region_index);
|
||||
|
||||
/* repository pme info */
|
||||
|
||||
int ps3_repository_read_num_be(unsigned int *num_be);
|
||||
int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id);
|
||||
int ps3_repository_read_be_id(u64 node_id, u64 *be_id);
|
||||
int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq);
|
||||
int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq);
|
||||
|
||||
/* repository performance monitor info */
|
||||
|
||||
int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar,
|
||||
u64 *rights);
|
||||
|
||||
/* repository 'Other OS' area */
|
||||
|
||||
int ps3_repository_read_boot_dat_addr(u64 *lpar_addr);
|
||||
int ps3_repository_read_boot_dat_size(unsigned int *size);
|
||||
int ps3_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size);
|
||||
|
||||
/* repository spu info */
|
||||
|
||||
/**
|
||||
* enum spu_resource_type - Type of spu resource.
|
||||
* @spu_resource_type_shared: Logical spu is shared with other partions.
|
||||
* @spu_resource_type_exclusive: Logical spu is not shared with other partions.
|
||||
*
|
||||
* Returned by ps3_repository_read_spu_resource_id().
|
||||
*/
|
||||
|
||||
enum ps3_spu_resource_type {
|
||||
PS3_SPU_RESOURCE_TYPE_SHARED = 0,
|
||||
PS3_SPU_RESOURCE_TYPE_EXCLUSIVE = 0x8000000000000000UL,
|
||||
};
|
||||
|
||||
int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved);
|
||||
int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id);
|
||||
int ps3_repository_read_spu_resource_id(unsigned int res_index,
|
||||
enum ps3_spu_resource_type* resource_type, unsigned int *resource_id);
|
||||
|
||||
/* repository vuart info */
|
||||
|
||||
int ps3_repository_read_vuart_av_port(unsigned int *port);
|
||||
int ps3_repository_read_vuart_sysmgr_port(unsigned int *port);
|
||||
|
||||
#endif
|
||||
1404
arch/powerpc/platforms/ps3/repository.c
Normal file
1404
arch/powerpc/platforms/ps3/repository.c
Normal file
File diff suppressed because it is too large
Load diff
286
arch/powerpc/platforms/ps3/setup.c
Normal file
286
arch/powerpc/platforms/ps3/setup.c
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* PS3 platform setup routines.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/ps3gpu.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define DBG udbg_printf
|
||||
#else
|
||||
#define DBG pr_debug
|
||||
#endif
|
||||
|
||||
/* mutex synchronizing GPU accesses and video mode changes */
|
||||
DEFINE_MUTEX(ps3_gpu_mutex);
|
||||
EXPORT_SYMBOL_GPL(ps3_gpu_mutex);
|
||||
|
||||
static union ps3_firmware_version ps3_firmware_version;
|
||||
|
||||
void ps3_get_firmware_version(union ps3_firmware_version *v)
|
||||
{
|
||||
*v = ps3_firmware_version;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_get_firmware_version);
|
||||
|
||||
int ps3_compare_firmware_version(u16 major, u16 minor, u16 rev)
|
||||
{
|
||||
union ps3_firmware_version x;
|
||||
|
||||
x.pad = 0;
|
||||
x.major = major;
|
||||
x.minor = minor;
|
||||
x.rev = rev;
|
||||
|
||||
return (ps3_firmware_version.raw > x.raw) -
|
||||
(ps3_firmware_version.raw < x.raw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_compare_firmware_version);
|
||||
|
||||
static void ps3_power_save(void)
|
||||
{
|
||||
/*
|
||||
* lv1_pause() puts the PPE thread into inactive state until an
|
||||
* irq on an unmasked plug exists. MSR[EE] has no effect.
|
||||
* flags: 0 = wake on DEC interrupt, 1 = ignore DEC interrupt.
|
||||
*/
|
||||
|
||||
lv1_pause(0);
|
||||
}
|
||||
|
||||
static void ps3_restart(char *cmd)
|
||||
{
|
||||
DBG("%s:%d cmd '%s'\n", __func__, __LINE__, cmd);
|
||||
|
||||
smp_send_stop();
|
||||
ps3_sys_manager_restart(); /* never returns */
|
||||
}
|
||||
|
||||
static void ps3_power_off(void)
|
||||
{
|
||||
DBG("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
smp_send_stop();
|
||||
ps3_sys_manager_power_off(); /* never returns */
|
||||
}
|
||||
|
||||
static void ps3_halt(void)
|
||||
{
|
||||
DBG("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
smp_send_stop();
|
||||
ps3_sys_manager_halt(); /* never returns */
|
||||
}
|
||||
|
||||
static void ps3_panic(char *str)
|
||||
{
|
||||
DBG("%s:%d %s\n", __func__, __LINE__, str);
|
||||
|
||||
smp_send_stop();
|
||||
printk("\n");
|
||||
printk(" System does not reboot automatically.\n");
|
||||
printk(" Please press POWER button.\n");
|
||||
printk("\n");
|
||||
|
||||
while(1)
|
||||
lv1_pause(1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) || \
|
||||
defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
|
||||
static void __init prealloc(struct ps3_prealloc *p)
|
||||
{
|
||||
if (!p->size)
|
||||
return;
|
||||
|
||||
p->address = __alloc_bootmem(p->size, p->align, __pa(MAX_DMA_ADDRESS));
|
||||
if (!p->address) {
|
||||
printk(KERN_ERR "%s: Cannot allocate %s\n", __func__,
|
||||
p->name);
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size,
|
||||
p->address);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
|
||||
struct ps3_prealloc ps3fb_videomemory = {
|
||||
.name = "ps3fb videomemory",
|
||||
.size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
|
||||
.align = 1024*1024 /* the GPU requires 1 MiB alignment */
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(ps3fb_videomemory);
|
||||
#define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory)
|
||||
|
||||
static int __init early_parse_ps3fb(char *p)
|
||||
{
|
||||
if (!p)
|
||||
return 1;
|
||||
|
||||
ps3fb_videomemory.size = _ALIGN_UP(memparse(p, &p),
|
||||
ps3fb_videomemory.align);
|
||||
return 0;
|
||||
}
|
||||
early_param("ps3fb", early_parse_ps3fb);
|
||||
#else
|
||||
#define prealloc_ps3fb_videomemory() do { } while (0)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
|
||||
struct ps3_prealloc ps3flash_bounce_buffer = {
|
||||
.name = "ps3flash bounce buffer",
|
||||
.size = 256*1024,
|
||||
.align = 256*1024
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(ps3flash_bounce_buffer);
|
||||
#define prealloc_ps3flash_bounce_buffer() prealloc(&ps3flash_bounce_buffer)
|
||||
|
||||
static int __init early_parse_ps3flash(char *p)
|
||||
{
|
||||
if (!p)
|
||||
return 1;
|
||||
|
||||
if (!strcmp(p, "off"))
|
||||
ps3flash_bounce_buffer.size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("ps3flash", early_parse_ps3flash);
|
||||
#else
|
||||
#define prealloc_ps3flash_bounce_buffer() do { } while (0)
|
||||
#endif
|
||||
|
||||
static int ps3_set_dabr(unsigned long dabr, unsigned long dabrx)
|
||||
{
|
||||
/* Have to set at least one bit in the DABRX */
|
||||
if (dabrx == 0 && dabr == 0)
|
||||
dabrx = DABRX_USER;
|
||||
/* hypervisor only allows us to set BTI, Kernel and user */
|
||||
dabrx &= DABRX_BTI | DABRX_KERNEL | DABRX_USER;
|
||||
|
||||
return lv1_set_dabr(dabr, dabrx) ? -1 : 0;
|
||||
}
|
||||
|
||||
static void __init ps3_setup_arch(void)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
DBG(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
lv1_get_version_info(&ps3_firmware_version.raw, &tmp);
|
||||
|
||||
printk(KERN_INFO "PS3 firmware version %u.%u.%u\n",
|
||||
ps3_firmware_version.major, ps3_firmware_version.minor,
|
||||
ps3_firmware_version.rev);
|
||||
|
||||
ps3_spu_set_platform();
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
smp_init_ps3();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DUMMY_CONSOLE
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
|
||||
prealloc_ps3fb_videomemory();
|
||||
prealloc_ps3flash_bounce_buffer();
|
||||
|
||||
ppc_md.power_save = ps3_power_save;
|
||||
ps3_os_area_init();
|
||||
|
||||
DBG(" <- %s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
static void __init ps3_progress(char *s, unsigned short hex)
|
||||
{
|
||||
printk("*** %04x : %s\n", hex, s ? s : "");
|
||||
}
|
||||
|
||||
static int __init ps3_probe(void)
|
||||
{
|
||||
unsigned long htab_size;
|
||||
unsigned long dt_root;
|
||||
|
||||
DBG(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
dt_root = of_get_flat_dt_root();
|
||||
if (!of_flat_dt_is_compatible(dt_root, "sony,ps3"))
|
||||
return 0;
|
||||
|
||||
powerpc_firmware_features |= FW_FEATURE_PS3_POSSIBLE;
|
||||
|
||||
ps3_os_area_save_params();
|
||||
ps3_mm_init();
|
||||
ps3_mm_vas_create(&htab_size);
|
||||
ps3_hpte_init(htab_size);
|
||||
|
||||
DBG(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_KEXEC)
|
||||
static void ps3_kexec_cpu_down(int crash_shutdown, int secondary)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
|
||||
|
||||
ps3_smp_cleanup_cpu(cpu);
|
||||
ps3_shutdown_IRQ(cpu);
|
||||
|
||||
DBG(" <- %s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
|
||||
define_machine(ps3) {
|
||||
.name = "PS3",
|
||||
.probe = ps3_probe,
|
||||
.setup_arch = ps3_setup_arch,
|
||||
.init_IRQ = ps3_init_IRQ,
|
||||
.panic = ps3_panic,
|
||||
.get_boot_time = ps3_get_boot_time,
|
||||
.set_dabr = ps3_set_dabr,
|
||||
.calibrate_decr = ps3_calibrate_decr,
|
||||
.progress = ps3_progress,
|
||||
.restart = ps3_restart,
|
||||
.power_off = ps3_power_off,
|
||||
.halt = ps3_halt,
|
||||
#if defined(CONFIG_KEXEC)
|
||||
.kexec_cpu_down = ps3_kexec_cpu_down,
|
||||
#endif
|
||||
};
|
||||
134
arch/powerpc/platforms/ps3/smp.c
Normal file
134
arch/powerpc/platforms/ps3/smp.c
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* PS3 SMP routines.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define DBG udbg_printf
|
||||
#else
|
||||
#define DBG pr_debug
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ps3_ipi_virqs - a per cpu array of virqs for ipi use
|
||||
*/
|
||||
|
||||
#define MSG_COUNT 4
|
||||
static DEFINE_PER_CPU(unsigned int [MSG_COUNT], ps3_ipi_virqs);
|
||||
|
||||
static void ps3_smp_message_pass(int cpu, int msg)
|
||||
{
|
||||
int result;
|
||||
unsigned int virq;
|
||||
|
||||
if (msg >= MSG_COUNT) {
|
||||
DBG("%s:%d: bad msg: %d\n", __func__, __LINE__, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
virq = per_cpu(ps3_ipi_virqs, cpu)[msg];
|
||||
result = ps3_send_event_locally(virq);
|
||||
|
||||
if (result)
|
||||
DBG("%s:%d: ps3_send_event_locally(%d, %d) failed"
|
||||
" (%d)\n", __func__, __LINE__, cpu, msg, result);
|
||||
}
|
||||
|
||||
static int __init ps3_smp_probe(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for (cpu = 0; cpu < 2; cpu++) {
|
||||
int result;
|
||||
unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu);
|
||||
int i;
|
||||
|
||||
DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
|
||||
|
||||
/*
|
||||
* Check assumptions on ps3_ipi_virqs[] indexing. If this
|
||||
* check fails, then a different mapping of PPC_MSG_
|
||||
* to index needs to be setup.
|
||||
*/
|
||||
|
||||
BUILD_BUG_ON(PPC_MSG_CALL_FUNCTION != 0);
|
||||
BUILD_BUG_ON(PPC_MSG_RESCHEDULE != 1);
|
||||
BUILD_BUG_ON(PPC_MSG_TICK_BROADCAST != 2);
|
||||
BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3);
|
||||
|
||||
for (i = 0; i < MSG_COUNT; i++) {
|
||||
result = ps3_event_receive_port_setup(cpu, &virqs[i]);
|
||||
|
||||
if (result)
|
||||
continue;
|
||||
|
||||
DBG("%s:%d: (%d, %d) => virq %u\n",
|
||||
__func__, __LINE__, cpu, i, virqs[i]);
|
||||
|
||||
result = smp_request_message_ipi(virqs[i], i);
|
||||
|
||||
if (result)
|
||||
virqs[i] = NO_IRQ;
|
||||
else
|
||||
ps3_register_ipi_irq(cpu, virqs[i]);
|
||||
}
|
||||
|
||||
ps3_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]);
|
||||
|
||||
DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
void ps3_smp_cleanup_cpu(int cpu)
|
||||
{
|
||||
unsigned int *virqs = per_cpu(ps3_ipi_virqs, cpu);
|
||||
int i;
|
||||
|
||||
DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);
|
||||
|
||||
for (i = 0; i < MSG_COUNT; i++) {
|
||||
/* Can't call free_irq from interrupt context. */
|
||||
ps3_event_receive_port_destroy(virqs[i]);
|
||||
virqs[i] = NO_IRQ;
|
||||
}
|
||||
|
||||
DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
|
||||
}
|
||||
|
||||
static struct smp_ops_t ps3_smp_ops = {
|
||||
.probe = ps3_smp_probe,
|
||||
.message_pass = ps3_smp_message_pass,
|
||||
.kick_cpu = smp_generic_kick_cpu,
|
||||
};
|
||||
|
||||
void smp_init_ps3(void)
|
||||
{
|
||||
DBG(" -> %s\n", __func__);
|
||||
smp_ops = &ps3_smp_ops;
|
||||
DBG(" <- %s\n", __func__);
|
||||
}
|
||||
636
arch/powerpc/platforms/ps3/spu.c
Normal file
636
arch/powerpc/platforms/ps3/spu.c
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
/*
|
||||
* PS3 Platform spu routines.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/spu.h>
|
||||
#include <asm/spu_priv1.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/ps3.h>
|
||||
|
||||
#include "../cell/spufs/spufs.h"
|
||||
#include "platform.h"
|
||||
|
||||
/* spu_management_ops */
|
||||
|
||||
/**
|
||||
* enum spe_type - Type of spe to create.
|
||||
* @spe_type_logical: Standard logical spe.
|
||||
*
|
||||
* For use with lv1_construct_logical_spe(). The current HV does not support
|
||||
* any types other than those listed.
|
||||
*/
|
||||
|
||||
enum spe_type {
|
||||
SPE_TYPE_LOGICAL = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spe_shadow - logical spe shadow register area.
|
||||
*
|
||||
* Read-only shadow of spe registers.
|
||||
*/
|
||||
|
||||
struct spe_shadow {
|
||||
u8 padding_0140[0x0140];
|
||||
u64 int_status_class0_RW; /* 0x0140 */
|
||||
u64 int_status_class1_RW; /* 0x0148 */
|
||||
u64 int_status_class2_RW; /* 0x0150 */
|
||||
u8 padding_0158[0x0610-0x0158];
|
||||
u64 mfc_dsisr_RW; /* 0x0610 */
|
||||
u8 padding_0618[0x0620-0x0618];
|
||||
u64 mfc_dar_RW; /* 0x0620 */
|
||||
u8 padding_0628[0x0800-0x0628];
|
||||
u64 mfc_dsipr_R; /* 0x0800 */
|
||||
u8 padding_0808[0x0810-0x0808];
|
||||
u64 mfc_lscrr_R; /* 0x0810 */
|
||||
u8 padding_0818[0x0c00-0x0818];
|
||||
u64 mfc_cer_R; /* 0x0c00 */
|
||||
u8 padding_0c08[0x0f00-0x0c08];
|
||||
u64 spe_execution_status; /* 0x0f00 */
|
||||
u8 padding_0f08[0x1000-0x0f08];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum spe_ex_state - Logical spe execution state.
|
||||
* @spe_ex_state_unexecutable: Uninitialized.
|
||||
* @spe_ex_state_executable: Enabled, not ready.
|
||||
* @spe_ex_state_executed: Ready for use.
|
||||
*
|
||||
* The execution state (status) of the logical spe as reported in
|
||||
* struct spe_shadow:spe_execution_status.
|
||||
*/
|
||||
|
||||
enum spe_ex_state {
|
||||
SPE_EX_STATE_UNEXECUTABLE = 0,
|
||||
SPE_EX_STATE_EXECUTABLE = 2,
|
||||
SPE_EX_STATE_EXECUTED = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct priv1_cache - Cached values of priv1 registers.
|
||||
* @masks[]: Array of cached spe interrupt masks, indexed by class.
|
||||
* @sr1: Cached mfc_sr1 register.
|
||||
* @tclass_id: Cached mfc_tclass_id register.
|
||||
*/
|
||||
|
||||
struct priv1_cache {
|
||||
u64 masks[3];
|
||||
u64 sr1;
|
||||
u64 tclass_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spu_pdata - Platform state variables.
|
||||
* @spe_id: HV spe id returned by lv1_construct_logical_spe().
|
||||
* @resource_id: HV spe resource id returned by
|
||||
* ps3_repository_read_spe_resource_id().
|
||||
* @priv2_addr: lpar address of spe priv2 area returned by
|
||||
* lv1_construct_logical_spe().
|
||||
* @shadow_addr: lpar address of spe register shadow area returned by
|
||||
* lv1_construct_logical_spe().
|
||||
* @shadow: Virtual (ioremap) address of spe register shadow area.
|
||||
* @cache: Cached values of priv1 registers.
|
||||
*/
|
||||
|
||||
struct spu_pdata {
|
||||
u64 spe_id;
|
||||
u64 resource_id;
|
||||
u64 priv2_addr;
|
||||
u64 shadow_addr;
|
||||
struct spe_shadow __iomem *shadow;
|
||||
struct priv1_cache cache;
|
||||
};
|
||||
|
||||
static struct spu_pdata *spu_pdata(struct spu *spu)
|
||||
{
|
||||
return spu->pdata;
|
||||
}
|
||||
|
||||
#define dump_areas(_a, _b, _c, _d, _e) \
|
||||
_dump_areas(_a, _b, _c, _d, _e, __func__, __LINE__)
|
||||
static void _dump_areas(unsigned int spe_id, unsigned long priv2,
|
||||
unsigned long problem, unsigned long ls, unsigned long shadow,
|
||||
const char* func, int line)
|
||||
{
|
||||
pr_debug("%s:%d: spe_id: %xh (%u)\n", func, line, spe_id, spe_id);
|
||||
pr_debug("%s:%d: priv2: %lxh\n", func, line, priv2);
|
||||
pr_debug("%s:%d: problem: %lxh\n", func, line, problem);
|
||||
pr_debug("%s:%d: ls: %lxh\n", func, line, ls);
|
||||
pr_debug("%s:%d: shadow: %lxh\n", func, line, shadow);
|
||||
}
|
||||
|
||||
u64 ps3_get_spe_id(void *arg)
|
||||
{
|
||||
return spu_pdata(arg)->spe_id;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_get_spe_id);
|
||||
|
||||
static unsigned long get_vas_id(void)
|
||||
{
|
||||
u64 id;
|
||||
|
||||
lv1_get_logical_ppe_id(&id);
|
||||
lv1_get_virtual_address_space_id_of_ppe(&id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static int __init construct_spu(struct spu *spu)
|
||||
{
|
||||
int result;
|
||||
u64 unused;
|
||||
u64 problem_phys;
|
||||
u64 local_store_phys;
|
||||
|
||||
result = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT,
|
||||
PAGE_SHIFT, PAGE_SHIFT, get_vas_id(), SPE_TYPE_LOGICAL,
|
||||
&spu_pdata(spu)->priv2_addr, &problem_phys,
|
||||
&local_store_phys, &unused,
|
||||
&spu_pdata(spu)->shadow_addr,
|
||||
&spu_pdata(spu)->spe_id);
|
||||
spu->problem_phys = problem_phys;
|
||||
spu->local_store_phys = local_store_phys;
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: lv1_construct_logical_spe failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void spu_unmap(struct spu *spu)
|
||||
{
|
||||
iounmap(spu->priv2);
|
||||
iounmap(spu->problem);
|
||||
iounmap((__force u8 __iomem *)spu->local_store);
|
||||
iounmap(spu_pdata(spu)->shadow);
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_areas - Map the spu regions into the address space.
|
||||
*
|
||||
* The current HV requires the spu shadow regs to be mapped with the
|
||||
* PTE page protection bits set as read-only (PP=3). This implementation
|
||||
* uses the low level __ioremap() to bypass the page protection settings
|
||||
* inforced by ioremap_prot() to get the needed PTE bits set for the
|
||||
* shadow regs.
|
||||
*/
|
||||
|
||||
static int __init setup_areas(struct spu *spu)
|
||||
{
|
||||
struct table {char* name; unsigned long addr; unsigned long size;};
|
||||
static const unsigned long shadow_flags = _PAGE_NO_CACHE | 3;
|
||||
|
||||
spu_pdata(spu)->shadow = __ioremap(spu_pdata(spu)->shadow_addr,
|
||||
sizeof(struct spe_shadow),
|
||||
shadow_flags);
|
||||
if (!spu_pdata(spu)->shadow) {
|
||||
pr_debug("%s:%d: ioremap shadow failed\n", __func__, __LINE__);
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
spu->local_store = (__force void *)ioremap_prot(spu->local_store_phys,
|
||||
LS_SIZE, _PAGE_NO_CACHE);
|
||||
|
||||
if (!spu->local_store) {
|
||||
pr_debug("%s:%d: ioremap local_store failed\n",
|
||||
__func__, __LINE__);
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
spu->problem = ioremap(spu->problem_phys,
|
||||
sizeof(struct spu_problem));
|
||||
|
||||
if (!spu->problem) {
|
||||
pr_debug("%s:%d: ioremap problem failed\n", __func__, __LINE__);
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
spu->priv2 = ioremap(spu_pdata(spu)->priv2_addr,
|
||||
sizeof(struct spu_priv2));
|
||||
|
||||
if (!spu->priv2) {
|
||||
pr_debug("%s:%d: ioremap priv2 failed\n", __func__, __LINE__);
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
dump_areas(spu_pdata(spu)->spe_id, spu_pdata(spu)->priv2_addr,
|
||||
spu->problem_phys, spu->local_store_phys,
|
||||
spu_pdata(spu)->shadow_addr);
|
||||
dump_areas(spu_pdata(spu)->spe_id, (unsigned long)spu->priv2,
|
||||
(unsigned long)spu->problem, (unsigned long)spu->local_store,
|
||||
(unsigned long)spu_pdata(spu)->shadow);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_ioremap:
|
||||
spu_unmap(spu);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __init setup_interrupts(struct spu *spu)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
|
||||
0, &spu->irqs[0]);
|
||||
|
||||
if (result)
|
||||
goto fail_alloc_0;
|
||||
|
||||
result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
|
||||
1, &spu->irqs[1]);
|
||||
|
||||
if (result)
|
||||
goto fail_alloc_1;
|
||||
|
||||
result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
|
||||
2, &spu->irqs[2]);
|
||||
|
||||
if (result)
|
||||
goto fail_alloc_2;
|
||||
|
||||
return result;
|
||||
|
||||
fail_alloc_2:
|
||||
ps3_spe_irq_destroy(spu->irqs[1]);
|
||||
fail_alloc_1:
|
||||
ps3_spe_irq_destroy(spu->irqs[0]);
|
||||
fail_alloc_0:
|
||||
spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init enable_spu(struct spu *spu)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = lv1_enable_logical_spe(spu_pdata(spu)->spe_id,
|
||||
spu_pdata(spu)->resource_id);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: lv1_enable_logical_spe failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
goto fail_enable;
|
||||
}
|
||||
|
||||
result = setup_areas(spu);
|
||||
|
||||
if (result)
|
||||
goto fail_areas;
|
||||
|
||||
result = setup_interrupts(spu);
|
||||
|
||||
if (result)
|
||||
goto fail_interrupts;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_interrupts:
|
||||
spu_unmap(spu);
|
||||
fail_areas:
|
||||
lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0);
|
||||
fail_enable:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_destroy_spu(struct spu *spu)
|
||||
{
|
||||
int result;
|
||||
|
||||
pr_debug("%s:%d spu_%d\n", __func__, __LINE__, spu->number);
|
||||
|
||||
result = lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0);
|
||||
BUG_ON(result);
|
||||
|
||||
ps3_spe_irq_destroy(spu->irqs[2]);
|
||||
ps3_spe_irq_destroy(spu->irqs[1]);
|
||||
ps3_spe_irq_destroy(spu->irqs[0]);
|
||||
|
||||
spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ;
|
||||
|
||||
spu_unmap(spu);
|
||||
|
||||
result = lv1_destruct_logical_spe(spu_pdata(spu)->spe_id);
|
||||
BUG_ON(result);
|
||||
|
||||
kfree(spu->pdata);
|
||||
spu->pdata = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ps3_create_spu(struct spu *spu, void *data)
|
||||
{
|
||||
int result;
|
||||
|
||||
pr_debug("%s:%d spu_%d\n", __func__, __LINE__, spu->number);
|
||||
|
||||
spu->pdata = kzalloc(sizeof(struct spu_pdata),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!spu->pdata) {
|
||||
result = -ENOMEM;
|
||||
goto fail_malloc;
|
||||
}
|
||||
|
||||
spu_pdata(spu)->resource_id = (unsigned long)data;
|
||||
|
||||
/* Init cached reg values to HV defaults. */
|
||||
|
||||
spu_pdata(spu)->cache.sr1 = 0x33;
|
||||
|
||||
result = construct_spu(spu);
|
||||
|
||||
if (result)
|
||||
goto fail_construct;
|
||||
|
||||
/* For now, just go ahead and enable it. */
|
||||
|
||||
result = enable_spu(spu);
|
||||
|
||||
if (result)
|
||||
goto fail_enable;
|
||||
|
||||
/* Make sure the spu is in SPE_EX_STATE_EXECUTED. */
|
||||
|
||||
/* need something better here!!! */
|
||||
while (in_be64(&spu_pdata(spu)->shadow->spe_execution_status)
|
||||
!= SPE_EX_STATE_EXECUTED)
|
||||
(void)0;
|
||||
|
||||
return result;
|
||||
|
||||
fail_enable:
|
||||
fail_construct:
|
||||
ps3_destroy_spu(spu);
|
||||
fail_malloc:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __init ps3_enumerate_spus(int (*fn)(void *data))
|
||||
{
|
||||
int result;
|
||||
unsigned int num_resource_id;
|
||||
unsigned int i;
|
||||
|
||||
result = ps3_repository_read_num_spu_resource_id(&num_resource_id);
|
||||
|
||||
pr_debug("%s:%d: num_resource_id %u\n", __func__, __LINE__,
|
||||
num_resource_id);
|
||||
|
||||
/*
|
||||
* For now, just create logical spus equal to the number
|
||||
* of physical spus reserved for the partition.
|
||||
*/
|
||||
|
||||
for (i = 0; i < num_resource_id; i++) {
|
||||
enum ps3_spu_resource_type resource_type;
|
||||
unsigned int resource_id;
|
||||
|
||||
result = ps3_repository_read_spu_resource_id(i,
|
||||
&resource_type, &resource_id);
|
||||
|
||||
if (result)
|
||||
break;
|
||||
|
||||
if (resource_type == PS3_SPU_RESOURCE_TYPE_EXCLUSIVE) {
|
||||
result = fn((void*)(unsigned long)resource_id);
|
||||
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
printk(KERN_WARNING "%s:%d: Error initializing spus\n",
|
||||
__func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
return num_resource_id;
|
||||
}
|
||||
|
||||
static int ps3_init_affinity(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_enable_spu - Enable SPU run control.
|
||||
*
|
||||
* An outstanding enhancement for the PS3 would be to add a guard to check
|
||||
* for incorrect access to the spu problem state when the spu context is
|
||||
* disabled. This check could be implemented with a flag added to the spu
|
||||
* context that would inhibit mapping problem state pages, and a routine
|
||||
* to unmap spu problem state pages. When the spu is enabled with
|
||||
* ps3_enable_spu() the flag would be set allowing pages to be mapped,
|
||||
* and when the spu is disabled with ps3_disable_spu() the flag would be
|
||||
* cleared and the mapped problem state pages would be unmapped.
|
||||
*/
|
||||
|
||||
static void ps3_enable_spu(struct spu_context *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static void ps3_disable_spu(struct spu_context *ctx)
|
||||
{
|
||||
ctx->ops->runcntl_stop(ctx);
|
||||
}
|
||||
|
||||
const struct spu_management_ops spu_management_ps3_ops = {
|
||||
.enumerate_spus = ps3_enumerate_spus,
|
||||
.create_spu = ps3_create_spu,
|
||||
.destroy_spu = ps3_destroy_spu,
|
||||
.enable_spu = ps3_enable_spu,
|
||||
.disable_spu = ps3_disable_spu,
|
||||
.init_affinity = ps3_init_affinity,
|
||||
};
|
||||
|
||||
/* spu_priv1_ops */
|
||||
|
||||
static void int_mask_and(struct spu *spu, int class, u64 mask)
|
||||
{
|
||||
u64 old_mask;
|
||||
|
||||
/* are these serialized by caller??? */
|
||||
old_mask = spu_int_mask_get(spu, class);
|
||||
spu_int_mask_set(spu, class, old_mask & mask);
|
||||
}
|
||||
|
||||
static void int_mask_or(struct spu *spu, int class, u64 mask)
|
||||
{
|
||||
u64 old_mask;
|
||||
|
||||
old_mask = spu_int_mask_get(spu, class);
|
||||
spu_int_mask_set(spu, class, old_mask | mask);
|
||||
}
|
||||
|
||||
static void int_mask_set(struct spu *spu, int class, u64 mask)
|
||||
{
|
||||
spu_pdata(spu)->cache.masks[class] = mask;
|
||||
lv1_set_spe_interrupt_mask(spu_pdata(spu)->spe_id, class,
|
||||
spu_pdata(spu)->cache.masks[class]);
|
||||
}
|
||||
|
||||
static u64 int_mask_get(struct spu *spu, int class)
|
||||
{
|
||||
return spu_pdata(spu)->cache.masks[class];
|
||||
}
|
||||
|
||||
static void int_stat_clear(struct spu *spu, int class, u64 stat)
|
||||
{
|
||||
/* Note that MFC_DSISR will be cleared when class1[MF] is set. */
|
||||
|
||||
lv1_clear_spe_interrupt_status(spu_pdata(spu)->spe_id, class,
|
||||
stat, 0);
|
||||
}
|
||||
|
||||
static u64 int_stat_get(struct spu *spu, int class)
|
||||
{
|
||||
u64 stat;
|
||||
|
||||
lv1_get_spe_interrupt_status(spu_pdata(spu)->spe_id, class, &stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
static void cpu_affinity_set(struct spu *spu, int cpu)
|
||||
{
|
||||
/* No support. */
|
||||
}
|
||||
|
||||
static u64 mfc_dar_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu_pdata(spu)->shadow->mfc_dar_RW);
|
||||
}
|
||||
|
||||
static void mfc_dsisr_set(struct spu *spu, u64 dsisr)
|
||||
{
|
||||
/* Nothing to do, cleared in int_stat_clear(). */
|
||||
}
|
||||
|
||||
static u64 mfc_dsisr_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu_pdata(spu)->shadow->mfc_dsisr_RW);
|
||||
}
|
||||
|
||||
static void mfc_sdr_setup(struct spu *spu)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
static void mfc_sr1_set(struct spu *spu, u64 sr1)
|
||||
{
|
||||
/* Check bits allowed by HV. */
|
||||
|
||||
static const u64 allowed = ~(MFC_STATE1_LOCAL_STORAGE_DECODE_MASK
|
||||
| MFC_STATE1_PROBLEM_STATE_MASK);
|
||||
|
||||
BUG_ON((sr1 & allowed) != (spu_pdata(spu)->cache.sr1 & allowed));
|
||||
|
||||
spu_pdata(spu)->cache.sr1 = sr1;
|
||||
lv1_set_spe_privilege_state_area_1_register(
|
||||
spu_pdata(spu)->spe_id,
|
||||
offsetof(struct spu_priv1, mfc_sr1_RW),
|
||||
spu_pdata(spu)->cache.sr1);
|
||||
}
|
||||
|
||||
static u64 mfc_sr1_get(struct spu *spu)
|
||||
{
|
||||
return spu_pdata(spu)->cache.sr1;
|
||||
}
|
||||
|
||||
static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id)
|
||||
{
|
||||
spu_pdata(spu)->cache.tclass_id = tclass_id;
|
||||
lv1_set_spe_privilege_state_area_1_register(
|
||||
spu_pdata(spu)->spe_id,
|
||||
offsetof(struct spu_priv1, mfc_tclass_id_RW),
|
||||
spu_pdata(spu)->cache.tclass_id);
|
||||
}
|
||||
|
||||
static u64 mfc_tclass_id_get(struct spu *spu)
|
||||
{
|
||||
return spu_pdata(spu)->cache.tclass_id;
|
||||
}
|
||||
|
||||
static void tlb_invalidate(struct spu *spu)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
static void resource_allocation_groupID_set(struct spu *spu, u64 id)
|
||||
{
|
||||
/* No support. */
|
||||
}
|
||||
|
||||
static u64 resource_allocation_groupID_get(struct spu *spu)
|
||||
{
|
||||
return 0; /* No support. */
|
||||
}
|
||||
|
||||
static void resource_allocation_enable_set(struct spu *spu, u64 enable)
|
||||
{
|
||||
/* No support. */
|
||||
}
|
||||
|
||||
static u64 resource_allocation_enable_get(struct spu *spu)
|
||||
{
|
||||
return 0; /* No support. */
|
||||
}
|
||||
|
||||
const struct spu_priv1_ops spu_priv1_ps3_ops = {
|
||||
.int_mask_and = int_mask_and,
|
||||
.int_mask_or = int_mask_or,
|
||||
.int_mask_set = int_mask_set,
|
||||
.int_mask_get = int_mask_get,
|
||||
.int_stat_clear = int_stat_clear,
|
||||
.int_stat_get = int_stat_get,
|
||||
.cpu_affinity_set = cpu_affinity_set,
|
||||
.mfc_dar_get = mfc_dar_get,
|
||||
.mfc_dsisr_set = mfc_dsisr_set,
|
||||
.mfc_dsisr_get = mfc_dsisr_get,
|
||||
.mfc_sdr_setup = mfc_sdr_setup,
|
||||
.mfc_sr1_set = mfc_sr1_set,
|
||||
.mfc_sr1_get = mfc_sr1_get,
|
||||
.mfc_tclass_id_set = mfc_tclass_id_set,
|
||||
.mfc_tclass_id_get = mfc_tclass_id_get,
|
||||
.tlb_invalidate = tlb_invalidate,
|
||||
.resource_allocation_groupID_set = resource_allocation_groupID_set,
|
||||
.resource_allocation_groupID_get = resource_allocation_groupID_get,
|
||||
.resource_allocation_enable_set = resource_allocation_enable_set,
|
||||
.resource_allocation_enable_get = resource_allocation_enable_get,
|
||||
};
|
||||
|
||||
void ps3_spu_set_platform(void)
|
||||
{
|
||||
spu_priv1_ops = &spu_priv1_ps3_ops;
|
||||
spu_management_ops = &spu_management_ps3_ops;
|
||||
}
|
||||
813
arch/powerpc/platforms/ps3/system-bus.c
Normal file
813
arch/powerpc/platforms/ps3/system-bus.c
Normal file
|
|
@ -0,0 +1,813 @@
|
|||
/*
|
||||
* PS3 system bus driver.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/cell-regs.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
static struct device ps3_system_bus = {
|
||||
.init_name = "ps3_system",
|
||||
};
|
||||
|
||||
/* FIXME: need device usage counters! */
|
||||
struct {
|
||||
struct mutex mutex;
|
||||
int sb_11; /* usb 0 */
|
||||
int sb_12; /* usb 0 */
|
||||
int gpu;
|
||||
} static usage_hack;
|
||||
|
||||
static int ps3_is_device(struct ps3_system_bus_device *dev, u64 bus_id,
|
||||
u64 dev_id)
|
||||
{
|
||||
return dev->bus_id == bus_id && dev->dev_id == dev_id;
|
||||
}
|
||||
|
||||
static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
BUG_ON(!dev->bus_id);
|
||||
mutex_lock(&usage_hack.mutex);
|
||||
|
||||
if (ps3_is_device(dev, 1, 1)) {
|
||||
usage_hack.sb_11++;
|
||||
if (usage_hack.sb_11 > 1) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps3_is_device(dev, 1, 2)) {
|
||||
usage_hack.sb_12++;
|
||||
if (usage_hack.sb_12 > 1) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
result = lv1_open_device(dev->bus_id, dev->dev_id, 0);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__,
|
||||
__LINE__, ps3_result(result));
|
||||
result = -EPERM;
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock(&usage_hack.mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
BUG_ON(!dev->bus_id);
|
||||
mutex_lock(&usage_hack.mutex);
|
||||
|
||||
if (ps3_is_device(dev, 1, 1)) {
|
||||
usage_hack.sb_11--;
|
||||
if (usage_hack.sb_11) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps3_is_device(dev, 1, 2)) {
|
||||
usage_hack.sb_12--;
|
||||
if (usage_hack.sb_12) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
result = lv1_close_device(dev->bus_id, dev->dev_id);
|
||||
BUG_ON(result);
|
||||
|
||||
done:
|
||||
mutex_unlock(&usage_hack.mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&usage_hack.mutex);
|
||||
|
||||
usage_hack.gpu++;
|
||||
if (usage_hack.gpu > 1) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = lv1_gpu_open(0);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__,
|
||||
__LINE__, ps3_result(result));
|
||||
result = -EPERM;
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock(&usage_hack.mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&usage_hack.mutex);
|
||||
|
||||
usage_hack.gpu--;
|
||||
if (usage_hack.gpu) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = lv1_gpu_close();
|
||||
BUG_ON(result);
|
||||
|
||||
done:
|
||||
mutex_unlock(&usage_hack.mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ps3_open_hv_device(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
BUG_ON(!dev);
|
||||
pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);
|
||||
|
||||
switch (dev->match_id) {
|
||||
case PS3_MATCH_ID_EHCI:
|
||||
case PS3_MATCH_ID_OHCI:
|
||||
case PS3_MATCH_ID_GELIC:
|
||||
case PS3_MATCH_ID_STOR_DISK:
|
||||
case PS3_MATCH_ID_STOR_ROM:
|
||||
case PS3_MATCH_ID_STOR_FLASH:
|
||||
return ps3_open_hv_device_sb(dev);
|
||||
|
||||
case PS3_MATCH_ID_SOUND:
|
||||
case PS3_MATCH_ID_GPU:
|
||||
return ps3_open_hv_device_gpu(dev);
|
||||
|
||||
case PS3_MATCH_ID_AV_SETTINGS:
|
||||
case PS3_MATCH_ID_SYSTEM_MANAGER:
|
||||
pr_debug("%s:%d: unsupported match_id: %u\n", __func__,
|
||||
__LINE__, dev->match_id);
|
||||
pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__,
|
||||
dev->bus_id);
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,
|
||||
dev->match_id);
|
||||
BUG();
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_open_hv_device);
|
||||
|
||||
int ps3_close_hv_device(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
BUG_ON(!dev);
|
||||
pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);
|
||||
|
||||
switch (dev->match_id) {
|
||||
case PS3_MATCH_ID_EHCI:
|
||||
case PS3_MATCH_ID_OHCI:
|
||||
case PS3_MATCH_ID_GELIC:
|
||||
case PS3_MATCH_ID_STOR_DISK:
|
||||
case PS3_MATCH_ID_STOR_ROM:
|
||||
case PS3_MATCH_ID_STOR_FLASH:
|
||||
return ps3_close_hv_device_sb(dev);
|
||||
|
||||
case PS3_MATCH_ID_SOUND:
|
||||
case PS3_MATCH_ID_GPU:
|
||||
return ps3_close_hv_device_gpu(dev);
|
||||
|
||||
case PS3_MATCH_ID_AV_SETTINGS:
|
||||
case PS3_MATCH_ID_SYSTEM_MANAGER:
|
||||
pr_debug("%s:%d: unsupported match_id: %u\n", __func__,
|
||||
__LINE__, dev->match_id);
|
||||
pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__,
|
||||
dev->bus_id);
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,
|
||||
dev->match_id);
|
||||
BUG();
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_close_hv_device);
|
||||
|
||||
#define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__)
|
||||
static void _dump_mmio_region(const struct ps3_mmio_region* r,
|
||||
const char* func, int line)
|
||||
{
|
||||
pr_debug("%s:%d: dev %llu:%llu\n", func, line, r->dev->bus_id,
|
||||
r->dev->dev_id);
|
||||
pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr);
|
||||
pr_debug("%s:%d: len %lxh\n", func, line, r->len);
|
||||
pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr);
|
||||
}
|
||||
|
||||
static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r)
|
||||
{
|
||||
int result;
|
||||
u64 lpar_addr;
|
||||
|
||||
result = lv1_map_device_mmio_region(r->dev->bus_id, r->dev->dev_id,
|
||||
r->bus_addr, r->len, r->page_size, &lpar_addr);
|
||||
r->lpar_addr = lpar_addr;
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
r->lpar_addr = 0;
|
||||
}
|
||||
|
||||
dump_mmio_region(r);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r)
|
||||
{
|
||||
/* device specific; do nothing currently */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ps3_mmio_region_create(struct ps3_mmio_region *r)
|
||||
{
|
||||
return r->mmio_ops->create(r);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_mmio_region_create);
|
||||
|
||||
static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r)
|
||||
{
|
||||
int result;
|
||||
|
||||
dump_mmio_region(r);
|
||||
result = lv1_unmap_device_mmio_region(r->dev->bus_id, r->dev->dev_id,
|
||||
r->lpar_addr);
|
||||
|
||||
if (result)
|
||||
pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n",
|
||||
__func__, __LINE__, ps3_result(result));
|
||||
|
||||
r->lpar_addr = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r)
|
||||
{
|
||||
/* device specific; do nothing currently */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ps3_free_mmio_region(struct ps3_mmio_region *r)
|
||||
{
|
||||
return r->mmio_ops->free(r);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ps3_free_mmio_region);
|
||||
|
||||
static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = {
|
||||
.create = ps3_sb_mmio_region_create,
|
||||
.free = ps3_sb_free_mmio_region
|
||||
};
|
||||
|
||||
static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = {
|
||||
.create = ps3_ioc0_mmio_region_create,
|
||||
.free = ps3_ioc0_free_mmio_region
|
||||
};
|
||||
|
||||
int ps3_mmio_region_init(struct ps3_system_bus_device *dev,
|
||||
struct ps3_mmio_region *r, unsigned long bus_addr, unsigned long len,
|
||||
enum ps3_mmio_page_size page_size)
|
||||
{
|
||||
r->dev = dev;
|
||||
r->bus_addr = bus_addr;
|
||||
r->len = len;
|
||||
r->page_size = page_size;
|
||||
switch (dev->dev_type) {
|
||||
case PS3_DEVICE_TYPE_SB:
|
||||
r->mmio_ops = &ps3_mmio_sb_region_ops;
|
||||
break;
|
||||
case PS3_DEVICE_TYPE_IOC0:
|
||||
r->mmio_ops = &ps3_mmio_ioc0_region_ops;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ps3_mmio_region_init);
|
||||
|
||||
static int ps3_system_bus_match(struct device *_dev,
|
||||
struct device_driver *_drv)
|
||||
{
|
||||
int result;
|
||||
struct ps3_system_bus_driver *drv = ps3_drv_to_system_bus_drv(_drv);
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
|
||||
if (!dev->match_sub_id)
|
||||
result = dev->match_id == drv->match_id;
|
||||
else
|
||||
result = dev->match_sub_id == drv->match_sub_id &&
|
||||
dev->match_id == drv->match_id;
|
||||
|
||||
if (result)
|
||||
pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n",
|
||||
__func__, __LINE__,
|
||||
dev->match_id, dev->match_sub_id, dev_name(&dev->core),
|
||||
drv->match_id, drv->match_sub_id, drv->core.name);
|
||||
else
|
||||
pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n",
|
||||
__func__, __LINE__,
|
||||
dev->match_id, dev->match_sub_id, dev_name(&dev->core),
|
||||
drv->match_id, drv->match_sub_id, drv->core.name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_system_bus_probe(struct device *_dev)
|
||||
{
|
||||
int result = 0;
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
struct ps3_system_bus_driver *drv;
|
||||
|
||||
BUG_ON(!dev);
|
||||
dev_dbg(_dev, "%s:%d\n", __func__, __LINE__);
|
||||
|
||||
drv = ps3_system_bus_dev_to_system_bus_drv(dev);
|
||||
BUG_ON(!drv);
|
||||
|
||||
if (drv->probe)
|
||||
result = drv->probe(dev);
|
||||
else
|
||||
pr_debug("%s:%d: %s no probe method\n", __func__, __LINE__,
|
||||
dev_name(&dev->core));
|
||||
|
||||
pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core));
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ps3_system_bus_remove(struct device *_dev)
|
||||
{
|
||||
int result = 0;
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
struct ps3_system_bus_driver *drv;
|
||||
|
||||
BUG_ON(!dev);
|
||||
dev_dbg(_dev, "%s:%d\n", __func__, __LINE__);
|
||||
|
||||
drv = ps3_system_bus_dev_to_system_bus_drv(dev);
|
||||
BUG_ON(!drv);
|
||||
|
||||
if (drv->remove)
|
||||
result = drv->remove(dev);
|
||||
else
|
||||
dev_dbg(&dev->core, "%s:%d %s: no remove method\n",
|
||||
__func__, __LINE__, drv->core.name);
|
||||
|
||||
pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ps3_system_bus_shutdown(struct device *_dev)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
struct ps3_system_bus_driver *drv;
|
||||
|
||||
BUG_ON(!dev);
|
||||
|
||||
dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
|
||||
dev->match_id);
|
||||
|
||||
if (!dev->core.driver) {
|
||||
dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
drv = ps3_system_bus_dev_to_system_bus_drv(dev);
|
||||
|
||||
BUG_ON(!drv);
|
||||
|
||||
dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__,
|
||||
dev_name(&dev->core), drv->core.name);
|
||||
|
||||
if (drv->shutdown)
|
||||
drv->shutdown(dev);
|
||||
else if (drv->remove) {
|
||||
dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n",
|
||||
__func__, __LINE__, drv->core.name);
|
||||
drv->remove(dev);
|
||||
} else {
|
||||
dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n",
|
||||
__func__, __LINE__, drv->core.name);
|
||||
BUG();
|
||||
}
|
||||
|
||||
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
|
||||
if (add_uevent_var(env, "MODALIAS=ps3:%d:%d", dev->match_id,
|
||||
dev->match_sub_id))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
int len = snprintf(buf, PAGE_SIZE, "ps3:%d:%d\n", dev->match_id,
|
||||
dev->match_sub_id);
|
||||
|
||||
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
|
||||
}
|
||||
|
||||
static struct device_attribute ps3_system_bus_dev_attrs[] = {
|
||||
__ATTR_RO(modalias),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
struct bus_type ps3_system_bus_type = {
|
||||
.name = "ps3_system_bus",
|
||||
.match = ps3_system_bus_match,
|
||||
.uevent = ps3_system_bus_uevent,
|
||||
.probe = ps3_system_bus_probe,
|
||||
.remove = ps3_system_bus_remove,
|
||||
.shutdown = ps3_system_bus_shutdown,
|
||||
.dev_attrs = ps3_system_bus_dev_attrs,
|
||||
};
|
||||
|
||||
static int __init ps3_system_bus_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
return -ENODEV;
|
||||
|
||||
pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
mutex_init(&usage_hack.mutex);
|
||||
|
||||
result = device_register(&ps3_system_bus);
|
||||
BUG_ON(result);
|
||||
|
||||
result = bus_register(&ps3_system_bus_type);
|
||||
BUG_ON(result);
|
||||
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
core_initcall(ps3_system_bus_init);
|
||||
|
||||
/* Allocates a contiguous real buffer and creates mappings over it.
|
||||
* Returns the virtual address of the buffer and sets dma_handle
|
||||
* to the dma address (mapping) of the first page.
|
||||
*/
|
||||
static void * ps3_alloc_coherent(struct device *_dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flag,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
int result;
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
unsigned long virt_addr;
|
||||
|
||||
flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
|
||||
flag |= __GFP_ZERO;
|
||||
|
||||
virt_addr = __get_free_pages(flag, get_order(size));
|
||||
|
||||
if (!virt_addr) {
|
||||
pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__);
|
||||
goto clean_none;
|
||||
}
|
||||
|
||||
result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle,
|
||||
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
|
||||
CBE_IOPTE_SO_RW | CBE_IOPTE_M);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
|
||||
__func__, __LINE__, result);
|
||||
BUG_ON("check region type");
|
||||
goto clean_alloc;
|
||||
}
|
||||
|
||||
return (void*)virt_addr;
|
||||
|
||||
clean_alloc:
|
||||
free_pages(virt_addr, get_order(size));
|
||||
clean_none:
|
||||
dma_handle = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle, struct dma_attrs *attrs)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
|
||||
ps3_dma_unmap(dev->d_region, dma_handle, size);
|
||||
free_pages((unsigned long)vaddr, get_order(size));
|
||||
}
|
||||
|
||||
/* Creates TCEs for a user provided buffer. The user buffer must be
|
||||
* contiguous real kernel storage (not vmalloc). The address passed here
|
||||
* comprises a page address and offset into that page. The dma_addr_t
|
||||
* returned will point to the same byte within the page as was passed in.
|
||||
*/
|
||||
|
||||
static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
|
||||
unsigned long offset, size_t size, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
int result;
|
||||
dma_addr_t bus_addr;
|
||||
void *ptr = page_address(page) + offset;
|
||||
|
||||
result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
|
||||
&bus_addr,
|
||||
CBE_IOPTE_PP_R | CBE_IOPTE_PP_W |
|
||||
CBE_IOPTE_SO_RW | CBE_IOPTE_M);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
|
||||
__func__, __LINE__, result);
|
||||
}
|
||||
|
||||
return bus_addr;
|
||||
}
|
||||
|
||||
static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
|
||||
unsigned long offset, size_t size,
|
||||
enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
int result;
|
||||
dma_addr_t bus_addr;
|
||||
u64 iopte_flag;
|
||||
void *ptr = page_address(page) + offset;
|
||||
|
||||
iopte_flag = CBE_IOPTE_M;
|
||||
switch (direction) {
|
||||
case DMA_BIDIRECTIONAL:
|
||||
iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;
|
||||
break;
|
||||
case DMA_TO_DEVICE:
|
||||
iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R;
|
||||
break;
|
||||
case DMA_FROM_DEVICE:
|
||||
iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;
|
||||
break;
|
||||
default:
|
||||
/* not happned */
|
||||
BUG();
|
||||
};
|
||||
result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
|
||||
&bus_addr, iopte_flag);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
|
||||
__func__, __LINE__, result);
|
||||
}
|
||||
return bus_addr;
|
||||
}
|
||||
|
||||
static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr,
|
||||
size_t size, enum dma_data_direction direction, struct dma_attrs *attrs)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
int result;
|
||||
|
||||
result = ps3_dma_unmap(dev->d_region, dma_addr, size);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n",
|
||||
__func__, __LINE__, result);
|
||||
}
|
||||
}
|
||||
|
||||
static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl,
|
||||
int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
|
||||
{
|
||||
#if defined(CONFIG_PS3_DYNAMIC_DMA)
|
||||
BUG_ON("do");
|
||||
return -EPERM;
|
||||
#else
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgl, sg, nents, i) {
|
||||
int result = ps3_dma_map(dev->d_region, sg_phys(sg),
|
||||
sg->length, &sg->dma_address, 0);
|
||||
|
||||
if (result) {
|
||||
pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
|
||||
__func__, __LINE__, result);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sg->dma_length = sg->length;
|
||||
}
|
||||
|
||||
return nents;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg,
|
||||
int nents,
|
||||
enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
|
||||
{
|
||||
#if defined(CONFIG_PS3_DYNAMIC_DMA)
|
||||
BUG_ON("do");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static int ps3_dma_supported(struct device *_dev, u64 mask)
|
||||
{
|
||||
return mask >= DMA_BIT_MASK(32);
|
||||
}
|
||||
|
||||
static u64 ps3_dma_get_required_mask(struct device *_dev)
|
||||
{
|
||||
return DMA_BIT_MASK(32);
|
||||
}
|
||||
|
||||
static struct dma_map_ops ps3_sb_dma_ops = {
|
||||
.alloc = ps3_alloc_coherent,
|
||||
.free = ps3_free_coherent,
|
||||
.map_sg = ps3_sb_map_sg,
|
||||
.unmap_sg = ps3_sb_unmap_sg,
|
||||
.dma_supported = ps3_dma_supported,
|
||||
.get_required_mask = ps3_dma_get_required_mask,
|
||||
.map_page = ps3_sb_map_page,
|
||||
.unmap_page = ps3_unmap_page,
|
||||
};
|
||||
|
||||
static struct dma_map_ops ps3_ioc0_dma_ops = {
|
||||
.alloc = ps3_alloc_coherent,
|
||||
.free = ps3_free_coherent,
|
||||
.map_sg = ps3_ioc0_map_sg,
|
||||
.unmap_sg = ps3_ioc0_unmap_sg,
|
||||
.dma_supported = ps3_dma_supported,
|
||||
.get_required_mask = ps3_dma_get_required_mask,
|
||||
.map_page = ps3_ioc0_map_page,
|
||||
.unmap_page = ps3_unmap_page,
|
||||
};
|
||||
|
||||
/**
|
||||
* ps3_system_bus_release_device - remove a device from the system bus
|
||||
*/
|
||||
|
||||
static void ps3_system_bus_release_device(struct device *_dev)
|
||||
{
|
||||
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ps3_system_bus_device_register - add a device to the system bus
|
||||
*
|
||||
* ps3_system_bus_device_register() expects the dev object to be allocated
|
||||
* dynamically by the caller. The system bus takes ownership of the dev
|
||||
* object and frees the object in ps3_system_bus_release_device().
|
||||
*/
|
||||
|
||||
int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int result;
|
||||
static unsigned int dev_ioc0_count;
|
||||
static unsigned int dev_sb_count;
|
||||
static unsigned int dev_vuart_count;
|
||||
static unsigned int dev_lpm_count;
|
||||
|
||||
if (!dev->core.parent)
|
||||
dev->core.parent = &ps3_system_bus;
|
||||
dev->core.bus = &ps3_system_bus_type;
|
||||
dev->core.release = ps3_system_bus_release_device;
|
||||
|
||||
switch (dev->dev_type) {
|
||||
case PS3_DEVICE_TYPE_IOC0:
|
||||
dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops;
|
||||
dev_set_name(&dev->core, "ioc0_%02x", ++dev_ioc0_count);
|
||||
break;
|
||||
case PS3_DEVICE_TYPE_SB:
|
||||
dev->core.archdata.dma_ops = &ps3_sb_dma_ops;
|
||||
dev_set_name(&dev->core, "sb_%02x", ++dev_sb_count);
|
||||
|
||||
break;
|
||||
case PS3_DEVICE_TYPE_VUART:
|
||||
dev_set_name(&dev->core, "vuart_%02x", ++dev_vuart_count);
|
||||
break;
|
||||
case PS3_DEVICE_TYPE_LPM:
|
||||
dev_set_name(&dev->core, "lpm_%02x", ++dev_lpm_count);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
};
|
||||
|
||||
dev->core.of_node = NULL;
|
||||
set_dev_node(&dev->core, 0);
|
||||
|
||||
pr_debug("%s:%d add %s\n", __func__, __LINE__, dev_name(&dev->core));
|
||||
|
||||
result = device_register(&dev->core);
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ps3_system_bus_device_register);
|
||||
|
||||
int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv)
|
||||
{
|
||||
int result;
|
||||
|
||||
pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
return -ENODEV;
|
||||
|
||||
drv->core.bus = &ps3_system_bus_type;
|
||||
|
||||
result = driver_register(&drv->core);
|
||||
pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register);
|
||||
|
||||
void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv)
|
||||
{
|
||||
pr_debug(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);
|
||||
driver_unregister(&drv->core);
|
||||
pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister);
|
||||
96
arch/powerpc/platforms/ps3/time.c
Normal file
96
arch/powerpc/platforms/ps3/time.c
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* PS3 time and rtc routines.
|
||||
*
|
||||
* Copyright (C) 2006 Sony Computer Entertainment Inc.
|
||||
* Copyright 2006 Sony Corp.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/lv1call.h>
|
||||
#include <asm/ps3.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#define dump_tm(_a) _dump_tm(_a, __func__, __LINE__)
|
||||
static void _dump_tm(const struct rtc_time *tm, const char* func, int line)
|
||||
{
|
||||
pr_debug("%s:%d tm_sec %d\n", func, line, tm->tm_sec);
|
||||
pr_debug("%s:%d tm_min %d\n", func, line, tm->tm_min);
|
||||
pr_debug("%s:%d tm_hour %d\n", func, line, tm->tm_hour);
|
||||
pr_debug("%s:%d tm_mday %d\n", func, line, tm->tm_mday);
|
||||
pr_debug("%s:%d tm_mon %d\n", func, line, tm->tm_mon);
|
||||
pr_debug("%s:%d tm_year %d\n", func, line, tm->tm_year);
|
||||
pr_debug("%s:%d tm_wday %d\n", func, line, tm->tm_wday);
|
||||
}
|
||||
|
||||
#define dump_time(_a) _dump_time(_a, __func__, __LINE__)
|
||||
static void __maybe_unused _dump_time(int time, const char *func,
|
||||
int line)
|
||||
{
|
||||
struct rtc_time tm;
|
||||
|
||||
to_tm(time, &tm);
|
||||
|
||||
pr_debug("%s:%d time %d\n", func, line, time);
|
||||
_dump_tm(&tm, func, line);
|
||||
}
|
||||
|
||||
void __init ps3_calibrate_decr(void)
|
||||
{
|
||||
int result;
|
||||
u64 tmp;
|
||||
|
||||
result = ps3_repository_read_be_tb_freq(0, &tmp);
|
||||
BUG_ON(result);
|
||||
|
||||
ppc_tb_freq = tmp;
|
||||
ppc_proc_freq = ppc_tb_freq * 40;
|
||||
}
|
||||
|
||||
static u64 read_rtc(void)
|
||||
{
|
||||
int result;
|
||||
u64 rtc_val;
|
||||
u64 tb_val;
|
||||
|
||||
result = lv1_get_rtc(&rtc_val, &tb_val);
|
||||
BUG_ON(result);
|
||||
|
||||
return rtc_val;
|
||||
}
|
||||
|
||||
unsigned long __init ps3_get_boot_time(void)
|
||||
{
|
||||
return read_rtc() + ps3_os_area_get_rtc_diff();
|
||||
}
|
||||
|
||||
static int __init ps3_rtc_init(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
|
||||
return -ENODEV;
|
||||
|
||||
pdev = platform_device_register_simple("rtc-ps3", -1, NULL, 0);
|
||||
|
||||
return PTR_ERR_OR_ZERO(pdev);
|
||||
}
|
||||
|
||||
module_init(ps3_rtc_init);
|
||||
Loading…
Add table
Add a link
Reference in a new issue