Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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

View 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

View 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);

View 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>

View 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);

View 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);
}

View 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>

View 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));
}

File diff suppressed because it is too large Load diff

View 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);

View 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

File diff suppressed because it is too large Load diff

View 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
};

View 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__);
}

View 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;
}

View 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);

View 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);