Commit 609106b9 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc

* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (38 commits)
  ps3flash: Always read chunks of 256 KiB, and cache them
  ps3flash: Cache the last accessed FLASH chunk
  ps3: Replace direct file operations by callback
  ps3: Switch ps3_os_area_[gs]et_rtc_diff to EXPORT_SYMBOL_GPL()
  ps3: Correct debug message in dma_ioc0_map_pages()
  drivers/ps3: Add missing annotations
  ps3fb: Use ps3_system_bus_[gs]et_drvdata() instead of direct access
  ps3flash: Use ps3_system_bus_[gs]et_drvdata() instead of direct access
  ps3: shorten ps3_system_bus_[gs]et_driver_data to ps3_system_bus_[gs]et_drvdata
  ps3: Use dev_[gs]et_drvdata() instead of direct access for system bus devices
  block/ps3: remove driver_data direct access of struct device
  ps3vram: Make ps3vram_priv.reports a void *
  ps3vram: Remove no longer used ps3vram_priv.ddr_base
  ps3vram: Replace mutex by spinlock + bio_list
  block: Add bio_list_peek()
  powerpc: Use generic atomic64_t implementation on 32-bit processors
  lib: Provide generic atomic64_t implementation
  powerpc: Add compiler memory barrier to mtmsr macro
  powerpc/iseries: Mark signal_vsp_instruction() as maybe unused
  powerpc/iseries: Fix unused function warning in iSeries DT code
  ...
parents 69257cae 42e27bfc
......@@ -93,10 +93,6 @@ config GENERIC_HWEIGHT
bool
default y
config GENERIC_CALIBRATE_DELAY
bool
default y
config GENERIC_FIND_NEXT_BIT
bool
default y
......@@ -129,6 +125,7 @@ config PPC
select USE_GENERIC_SMP_HELPERS if SMP
select HAVE_OPROFILE
select HAVE_SYSCALL_WRAPPERS if PPC64
select GENERIC_ATOMIC64 if PPC32
config EARLY_PRINTK
bool
......
......@@ -18,6 +18,9 @@
# $5 and more - kernel boot files; zImage*, uImage, cuImage.*, etc.
#
# Bail with error code if anything goes wrong
set -e
# User may have a custom install script
if [ -x ~/bin/${CROSS_COMPILE}installkernel ]; then exec ~/bin/${CROSS_COMPILE}installkernel "$@"; fi
......
......@@ -470,6 +470,9 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u)
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
#else /* __powerpc64__ */
#include <asm-generic/atomic64.h>
#endif /* __powerpc64__ */
#include <asm-generic/atomic-long.h>
......
......@@ -80,7 +80,7 @@ static inline void local_irq_disable(void)
__asm__ __volatile__("wrteei 0": : :"memory");
#else
unsigned long msr;
__asm__ __volatile__("": : :"memory");
msr = mfmsr();
SET_MSR_EE(msr & ~MSR_EE);
#endif
......@@ -92,7 +92,7 @@ static inline void local_irq_enable(void)
__asm__ __volatile__("wrteei 1": : :"memory");
#else
unsigned long msr;
__asm__ __volatile__("": : :"memory");
msr = mfmsr();
SET_MSR_EE(msr | MSR_EE);
#endif
......@@ -108,7 +108,6 @@ static inline void local_irq_save_ptr(unsigned long *flags)
#else
SET_MSR_EE(msr & ~MSR_EE);
#endif
__asm__ __volatile__("": : :"memory");
}
#define local_save_flags(flags) ((flags) = mfmsr())
......
......@@ -35,6 +35,16 @@
#define IOMMU_PAGE_MASK (~((1 << IOMMU_PAGE_SHIFT) - 1))
#define IOMMU_PAGE_ALIGN(addr) _ALIGN_UP(addr, IOMMU_PAGE_SIZE)
/* Cell page table entries */
#define CBE_IOPTE_PP_W 0x8000000000000000ul /* protection: write */
#define CBE_IOPTE_PP_R 0x4000000000000000ul /* protection: read */
#define CBE_IOPTE_M 0x2000000000000000ul /* coherency required */
#define CBE_IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
#define CBE_IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
#define CBE_IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
#define CBE_IOPTE_H 0x0000000000000800ul /* cache hint */
#define CBE_IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
/* Boot time flags */
extern int iommu_is_off;
extern int iommu_force_on;
......
......@@ -53,6 +53,13 @@ enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void);
extern u64 ps3_os_area_get_rtc_diff(void);
extern void ps3_os_area_set_rtc_diff(u64 rtc_diff);
struct ps3_os_area_flash_ops {
ssize_t (*read)(void *buf, size_t count, loff_t pos);
ssize_t (*write)(const void *buf, size_t count, loff_t pos);
};
extern void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops);
/* dma routines */
enum ps3_dma_page_size {
......@@ -418,15 +425,15 @@ static inline struct ps3_system_bus_driver *
* @data: Data to set
*/
static inline void ps3_system_bus_set_driver_data(
static inline void ps3_system_bus_set_drvdata(
struct ps3_system_bus_device *dev, void *data)
{
dev->core.driver_data = data;
dev_set_drvdata(&dev->core, data);
}
static inline void *ps3_system_bus_get_driver_data(
static inline void *ps3_system_bus_get_drvdata(
struct ps3_system_bus_device *dev)
{
return dev->core.driver_data;
return dev_get_drvdata(&dev->core);
}
/* These two need global scope for get_dma_ops(). */
......@@ -520,7 +527,4 @@ void ps3_sync_irq(int node);
u32 ps3_get_hw_thread_id(int cpu);
u64 ps3_get_spe_id(void *arg);
/* mutex synchronizing GPU accesses and video mode changes */
extern struct mutex ps3_gpu_mutex;
#endif
/*
* PS3 GPU declarations.
*
* Copyright 2009 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ASM_POWERPC_PS3GPU_H
#define _ASM_POWERPC_PS3GPU_H
#include <linux/mutex.h>
#include <asm/lv1call.h>
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101
#define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102
#define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600
#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT_SYNC 0x602
#define L1GPU_CONTEXT_ATTRIBUTE_FB_CLOSE 0x603
#define L1GPU_FB_BLIT_WAIT_FOR_COMPLETION (1ULL << 32)
#define L1GPU_DISPLAY_SYNC_HSYNC 1
#define L1GPU_DISPLAY_SYNC_VSYNC 2
/* mutex synchronizing GPU accesses and video mode changes */
extern struct mutex ps3_gpu_mutex;
static inline int lv1_gpu_display_sync(u64 context_handle, u64 head,
u64 ddr_offset)
{
return lv1_gpu_context_attribute(context_handle,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC,
head, ddr_offset, 0, 0);
}
static inline int lv1_gpu_display_flip(u64 context_handle, u64 head,
u64 ddr_offset)
{
return lv1_gpu_context_attribute(context_handle,
L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
head, ddr_offset, 0, 0);
}
static inline int lv1_gpu_fb_setup(u64 context_handle, u64 xdr_lpar,
u64 xdr_size, u64 ioif_offset)
{
return lv1_gpu_context_attribute(context_handle,
L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP,
xdr_lpar, xdr_size, ioif_offset, 0);
}
static inline int lv1_gpu_fb_blit(u64 context_handle, u64 ddr_offset,
u64 ioif_offset, u64 sync_width, u64 pitch)
{
return lv1_gpu_context_attribute(context_handle,
L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
ddr_offset, ioif_offset, sync_width,
pitch);
}
static inline int lv1_gpu_fb_close(u64 context_handle)
{
return lv1_gpu_context_attribute(context_handle,
L1GPU_CONTEXT_ATTRIBUTE_FB_CLOSE, 0,
0, 0, 0);
}
#endif /* _ASM_POWERPC_PS3GPU_H */
......@@ -745,11 +745,11 @@
asm volatile("mfmsr %0" : "=r" (rval)); rval;})
#ifdef CONFIG_PPC64
#define __mtmsrd(v, l) asm volatile("mtmsrd %0," __stringify(l) \
: : "r" (v))
: : "r" (v) : "memory")
#define mtmsrd(v) __mtmsrd((v), 0)
#define mtmsr(v) mtmsrd(v)
#else
#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v))
#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v) : "memory")
#endif
#define mfspr(rn) ({unsigned long rval; \
......
......@@ -325,3 +325,4 @@ SYSCALL(inotify_init1)
SYSCALL_SPU(perf_counter_open)
COMPAT_SYS_SPU(preadv)
COMPAT_SYS_SPU(pwritev)
COMPAT_SYS(rt_tgsigqueueinfo)
......@@ -344,10 +344,11 @@
#define __NR_perf_counter_open 319
#define __NR_preadv 320
#define __NR_pwritev 321
#define __NR_rt_tgsigqueueinfo 322
#ifdef __KERNEL__
#define __NR_syscalls 322
#define __NR_syscalls 323
#define __NR__exit __NR_exit
#define NR_syscalls __NR_syscalls
......
......@@ -125,6 +125,7 @@ PHONY += systbl_chk
systbl_chk: $(src)/systbl_chk.sh $(obj)/systbl_chk.i
$(call cmd,systbl_chk)
ifeq ($(CONFIG_PPC_OF_BOOT_TRAMPOLINE),y)
$(obj)/built-in.o: prom_init_check
quiet_cmd_prom_init_check = CALL $<
......@@ -133,5 +134,6 @@ quiet_cmd_prom_init_check = CALL $<
PHONY += prom_init_check
prom_init_check: $(src)/prom_init_check.sh $(obj)/prom_init.o
$(call cmd,prom_init_check)
endif
clean-files := vmlinux.lds
......@@ -424,8 +424,8 @@ void __init setup_system(void)
printk("htab_hash_mask = 0x%lx\n", htab_hash_mask);
#endif /* CONFIG_PPC_STD_MMU_64 */
if (PHYSICAL_START > 0)
printk("physical_start = 0x%lx\n",
PHYSICAL_START);
printk("physical_start = 0x%llx\n",
(unsigned long long)PHYSICAL_START);
printk("-----------------------------------------------------\n");
DBG(" <- setup_system()\n");
......
......@@ -52,6 +52,7 @@
#include <linux/jiffies.h>
#include <linux/posix-timers.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/processor.h>
......@@ -1143,6 +1144,15 @@ void div128_by_32(u64 dividend_high, u64 dividend_low,
}
/* We don't need to calibrate delay, we use the CPU timebase for that */
void calibrate_delay(void)
{
/* Some generic code (such as spinlock debug) use loops_per_jiffy
* as the number of __delay(1) in a jiffy, so make it so
*/
loops_per_jiffy = tb_ticks_per_jiffy;
}
static int __init rtc_init(void)
{
struct platform_device *pdev;
......
......@@ -329,7 +329,7 @@ static struct irq_host_ops msic_host_ops = {
static int axon_msi_shutdown(struct of_device *device)
{
struct axon_msic *msic = device->dev.platform_data;
struct axon_msic *msic = dev_get_drvdata(&device->dev);
u32 tmp;
pr_debug("axon_msi: disabling %s\n",
......@@ -416,7 +416,7 @@ static int axon_msi_probe(struct of_device *device,
msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG)
& MSIC_FIFO_SIZE_MASK;
device->dev.platform_data = msic;
dev_set_drvdata(&device->dev, msic);
ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
......
......@@ -100,16 +100,6 @@
#define IOSTE_PS_1M 0x0000000000000005ul /* - 1MB */
#define IOSTE_PS_16M 0x0000000000000007ul /* - 16MB */
/* Page table entries */
#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */
#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */
#define IOPTE_M 0x2000000000000000ul /* coherency required */
#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
#define IOPTE_H 0x0000000000000800ul /* cache hint */
#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
/* IOMMU sizing */
#define IO_SEGMENT_SHIFT 28
......@@ -193,19 +183,21 @@ static int tce_build_cell(struct iommu_table *tbl, long index, long npages,
*/
const unsigned long prot = 0xc48;
base_pte =
((prot << (52 + 4 * direction)) & (IOPTE_PP_W | IOPTE_PP_R))
| IOPTE_M | IOPTE_SO_RW | (window->ioid & IOPTE_IOID_Mask);
((prot << (52 + 4 * direction)) &
(CBE_IOPTE_PP_W | CBE_IOPTE_PP_R)) |
CBE_IOPTE_M | CBE_IOPTE_SO_RW |
(window->ioid & CBE_IOPTE_IOID_Mask);
#else
base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW |
(window->ioid & IOPTE_IOID_Mask);
base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
CBE_IOPTE_SO_RW | (window->ioid & CBE_IOPTE_IOID_Mask);
#endif
if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)))
base_pte &= ~IOPTE_SO_RW;
base_pte &= ~CBE_IOPTE_SO_RW;
io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
for (i = 0; i < npages; i++, uaddr += IOMMU_PAGE_SIZE)
io_pte[i] = base_pte | (__pa(uaddr) & IOPTE_RPN_Mask);
io_pte[i] = base_pte | (__pa(uaddr) & CBE_IOPTE_RPN_Mask);
mb();
......@@ -231,8 +223,9 @@ static void tce_free_cell(struct iommu_table *tbl, long index, long npages)
#else
/* spider bridge does PCI reads after freeing - insert a mapping
* to a scratch page instead of an invalid entry */
pte = IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | __pa(window->iommu->pad_page)
| (window->ioid & IOPTE_IOID_Mask);
pte = CBE_IOPTE_PP_R | CBE_IOPTE_M | CBE_IOPTE_SO_RW |
__pa(window->iommu->pad_page) |
(window->ioid & CBE_IOPTE_IOID_Mask);
#endif
io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
......@@ -1001,7 +994,7 @@ static void insert_16M_pte(unsigned long addr, unsigned long *ptab,
pr_debug("iommu: addr %lx ptab %p segment %lx offset %lx\n",
addr, ptab, segment, offset);
ptab[offset] = base_pte | (__pa(addr) & IOPTE_RPN_Mask);
ptab[offset] = base_pte | (__pa(addr) & CBE_IOPTE_RPN_Mask);
}
static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu,
......@@ -1016,14 +1009,14 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu,
pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase);
base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M
| (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask);
base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
(cell_iommu_get_ioid(np) & CBE_IOPTE_IOID_Mask);
if (iommu_fixed_is_weak)
pr_info("IOMMU: Using weak ordering for fixed mapping\n");
else {
pr_info("IOMMU: Using strong ordering for fixed mapping\n");
base_pte |= IOPTE_SO_RW;
base_pte |= CBE_IOPTE_SO_RW;
}
for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) {
......
......@@ -204,7 +204,8 @@ static void __init dt_prop_u32(struct iseries_flat_dt *dt, const char *name,
dt_prop(dt, name, &data, sizeof(u32));
}
static void __init dt_prop_u64(struct iseries_flat_dt *dt, const char *name,
static void __init __maybe_unused dt_prop_u64(struct iseries_flat_dt *dt,
const char *name,
u64 data)
{
dt_prop(dt, name, &data, sizeof(u64));
......
......@@ -267,7 +267,8 @@ static struct pending_event *new_pending_event(void)
return ev;
}
static int signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd)
static int __maybe_unused
signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd)
{
struct pending_event *ev = new_pending_event();
int rc;
......
......@@ -24,6 +24,7 @@
#include <linux/lmb.h>
#include <asm/firmware.h>
#include <asm/iommu.h>
#include <asm/prom.h>
#include <asm/udbg.h>
#include <asm/lv1call.h>
......@@ -605,9 +606,8 @@ static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
r->ioid,
iopte_flag);
if (result) {
printk(KERN_WARNING "%s:%d: lv1_map_device_dma_region "
"failed: %s\n", __func__, __LINE__,
ps3_result(result));
pr_warning("%s:%d: lv1_put_iopte failed: %s\n",
__func__, __LINE__, ps3_result(result));
goto fail_map;
}
DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__,
......@@ -1001,7 +1001,8 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r)
if (len > r->len)
len = r->len;
result = dma_sb_map_area(r, virt_addr, len, &tmp,
IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW |
CBE_IOPTE_M);
BUG_ON(result);
}
......@@ -1014,7 +1015,8 @@ static int dma_sb_region_create_linear(struct ps3_dma_region *r)
else
len -= map.rm.size - r->offset;
result = dma_sb_map_area(r, virt_addr, len, &tmp,
IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_SO_RW |
CBE_IOPTE_M);
BUG_ON(result);
}
......
......@@ -226,6 +226,44 @@ static struct property property_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.
*
......@@ -352,12 +390,12 @@ 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 -1;
return -EINVAL;
}
if (db->version != 1) {
pr_debug("%s:%d version failed\n", __func__, __LINE__);
return -1;
return -EINVAL;
}
return 0;
......@@ -578,59 +616,48 @@ static void os_area_db_init(struct os_area_db *db)
*
*/
static void __maybe_unused update_flash_db(void)
static int update_flash_db(void)
{
int result;
int file;
off_t offset;
const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
struct os_area_header *header;
ssize_t count;
static const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
const struct os_area_header *header;
int error;
loff_t pos;
struct os_area_db* db;
/* Read in header and db from flash. */
file = sys_open("/dev/ps3flash", O_RDWR, 0);
if (file < 0) {
pr_debug("%s:%d sys_open failed\n", __func__, __LINE__);
goto fail_open;
}
header = kmalloc(buf_len, GFP_KERNEL);
if (!header) {
pr_debug("%s:%d kmalloc failed\n", __func__, __LINE__);
goto fail_malloc;
pr_debug("%s: kmalloc failed\n", __func__);
return -ENOMEM;
}
offset = sys_lseek(file, 0, SEEK_SET);
if (offset != 0) {
pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
goto fail_header_seek;
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;
}
count = sys_read(file, (char __user *)header, buf_len);
result = count < OS_AREA_SEGMENT_SIZE || verify_header(header)
|| count < header->db_area_offset * OS_AREA_SEGMENT_SIZE;
if (result) {
pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
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);
goto fail_header;
error = -EINVAL;
goto fail;
}
/* Now got a good db offset and some maybe good db data. */
db = (void*)header + header->db_area_offset * OS_AREA_SEGMENT_SIZE;
result = db_verify(db);
db = (void *)header + pos;
if (result) {
printk(KERN_NOTICE "%s:%d: Verify of flash database failed, "
"formatting.\n", __func__, __LINE__);
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);
}
......@@ -639,29 +666,16 @@ static void __maybe_unused update_flash_db(void)
db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
offset = sys_lseek(file, header->db_area_offset * OS_AREA_SEGMENT_SIZE,
SEEK_SET);
if (offset != header->db_area_offset * OS_AREA_SEGMENT_SIZE) {
pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
goto fail_db_seek;
}
count = sys_write(file, (const char __user *)db,
sizeof(struct os_area_db));
count = os_area_flash_write(db, sizeof(struct os_area_db), pos);
if (count < sizeof(struct os_area_db)) {
pr_debug("%s:%d sys_write failed\n", __func__, __LINE__);
pr_debug("%s: os_area_flash_write failed %zd\n", __func__,
count);
error = count < 0 ? count : -EIO;
}
fail_db_seek:
fail_header:
fail_header_seek:
fail:
kfree(header);
fail_malloc:
sys_close(file);
fail_open:
return;
return error;
}
/**
......@@ -674,11 +688,11 @@ fail_open:
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);
......@@ -686,12 +700,10 @@ static void os_area_queue_work_handler(struct work_struct *work)
pr_debug("%s:%d of_find_node_by_path failed\n",
__func__, __LINE__);
#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
update_flash_db();
#else
printk(KERN_WARNING "%s:%d: No flash rom driver configured.\n",
__func__, __LINE__);
#endif
error = update_flash_db();
if (error)
pr_warning("%s: Could not update FLASH ROM\n", __func__);
pr_debug(" <- %s:%d\n", __func__, __LINE__);
}
......@@ -808,7 +820,7 @@ u64 ps3_os_area_get_rtc_diff(void)
{
return saved_params.rtc_diff;
}
EXPORT_SYMBOL(ps3_os_area_get_rtc_diff);
EXPORT_SYMBOL_GPL(ps3_os_area_get_rtc_diff);
/**
* ps3_os_area_set_rtc_diff - Set the rtc diff value.
......@@ -824,7 +836,7 @@ void ps3_os_area_set_rtc_diff(u64 rtc_diff)
os_area_queue_work();
}
}
EXPORT_SYMBOL(ps3_os_area_set_rtc_diff);
EXPORT_SYMBOL_GPL(ps3_os_area_set_rtc_diff);
/**
* ps3_os_area_get_av_multi_out - Returns the default video mode.
......
......@@ -232,14 +232,4 @@ int ps3_repository_read_spu_resource_id(unsigned int res_index,
int ps3_repository_read_vuart_av_port(unsigned int *port);
int ps3_repository_read_vuart_sysmgr_port(unsigned int *port);
/* Page table entries */
#define IOPTE_PP_W 0x8000000000000000ul /* protection: write */
#define IOPTE_PP_R 0x4000000000000000ul /* protection: read */
#define IOPTE_M 0x2000000000000000ul /* coherency required */
#define IOPTE_SO_R 0x1000000000000000ul /* ordering: writes */
#define IOPTE_SO_RW 0x1800000000000000ul /* ordering: r & w */
#define IOPTE_RPN_Mask 0x07fffffffffff000ul /* RPN */
#define IOPTE_H 0x0000000000000800ul /* cache hint */
#define IOPTE_IOID_Mask 0x00000000000007fful /* ioid */
#endif
......@@ -32,6 +32,7 @@
#include <asm/udbg.h>
#include <asm/prom.h>
#include <asm/lv1call.h>
#include <asm/ps3gpu.h>
#include "platform.h"
......
......@@ -27,6 +27,7 @@
#include <asm/udbg.h>
#include <asm/lv1call.h>
#include <asm/firmware.h>
#include <asm/iommu.h>
#include "platform.h"
......@@ -531,7 +532,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size,
}
result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle,
IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
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",
......@@ -575,7 +577,8 @@ static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
&bus_addr,
IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M);
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",
......@@ -596,16 +599,16 @@ static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
u64 iopte_flag;
void *ptr = page_address(page) + offset;
iopte_flag = IOPTE_M;
iopte_flag = CBE_IOPTE_M;
switch (direction) {
case DMA_BIDIRECTIONAL:
iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW;
iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;
break;
case DMA_TO_DEVICE:
iopte_flag |= IOPTE_PP_R | IOPTE_SO_R;
iopte_flag |= CBE_IOPTE_PP_R | CBE_IOPTE_SO_R;
break;
case DMA_FROM_DEVICE:
iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW;
iopte_flag |= CBE_IOPTE_PP_W | CBE_IOPTE_SO_RW;
break;
default:
/* not happned */
......
......@@ -120,7 +120,7 @@ static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
struct request *req)
{
struct ps3disk_private *priv = dev->sbd.core.driver_data;
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
int write = rq_data_dir(req), res;
const char *op = write ? "write" : "read";
u64 start_sector, sectors;
......@@ -168,7 +168,7 @@ static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
static int ps3disk_submit_flush_request(struct ps3_storage_device *dev,
struct request *req)
{
struct ps3disk_private *priv = dev->sbd.core.driver_data;
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
u64 res;
dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
......@@ -213,7 +213,7 @@ static void ps3disk_do_request(struct ps3_storage_device *dev,
static void ps3disk_request(struct request_queue *q)
{
struct ps3_storage_device *dev = q->queuedata;
struct ps3disk_private *priv = dev->sbd.core.driver_data;
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
if (priv->req) {
dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
......@@ -245,7 +245,7 @@ static irqreturn_t ps3disk_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
priv = dev->sbd.core.driver_data;
priv = ps3_system_bus_get_drvdata(&dev->sbd);
req = priv->req;
if (!req) {
dev_dbg(&dev->sbd.core,
......@@ -364,7 +364,7 @@ static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs,
static int ps3disk_identify(struct ps3_storage_device *dev)
{
struct ps3disk_private *priv = dev->sbd.core.driver_data;
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
struct lv1_ata_cmnd_block ata_cmnd;
u16 *id = dev->bounce_buf;
u64 res;
......@@ -445,7 +445,7 @@ static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
goto fail;
}
dev->sbd.core.driver_data = priv;
ps3_system_bus_set_drvdata(_dev, priv);
spin_lock_init(&priv->lock);
dev->bounce_size = BOUNCE_SIZE;
......@@ -523,7 +523,7 @@ fail_free_bounce:
kfree(dev->bounce_buf);
fail_free_priv:
kfree(priv);
dev->sbd.core.driver_data = NULL;
ps3_system_bus_set_drvdata(_dev, NULL);
fail:
mutex_lock(&ps3disk_mask_mutex);
__clear_bit(devidx, &ps3disk_mask);
......@@ -534,7 +534,7 @@ fail:
static int ps3disk_remove(struct ps3_system_bus_device *_dev)
{
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
struct ps3disk_private *priv = dev->sbd.core.driver_data;
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
mutex_lock(&ps3disk_mask_mutex);
__clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS,
......@@ -548,7 +548,7 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev)
ps3stor_teardown(dev);
kfree(dev->bounce_buf);
kfree(priv);
dev->sbd.core.driver_data = NULL;
ps3_system_bus_set_drvdata(_dev, NULL);
return 0;
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -1648,7 +1648,7 @@ static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
result = -ENOMEM;
goto fail_alloc_card;
}
ps3_system_bus_set_driver_data(dev, card);
ps3_system_bus_set_drvdata(dev, card);
card->dev = dev;
/* get internal vlan info */
......@@ -1749,7 +1749,7 @@ fail_alloc_irq:
bus_id(card),
0, 0);
fail_status_indicator:
ps3_system_bus_set_driver_data(dev, NULL);
ps3_system_bus_set_drvdata(dev, NULL);
kfree(netdev_card(netdev)->unalign);
free_netdev(netdev);
fail_alloc_card:
......@@ -1766,7 +1766,7 @@ fail_open:
static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
{
struct gelic_card *card = ps3_system_bus_get_driver_data(dev);
struct gelic_card *card = ps3_system_bus_get_drvdata(dev);
struct net_device *netdev0;
pr_debug("%s: called\n", __func__);
......@@ -1803,7 +1803,7 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
kfree(netdev_card(netdev0)->unalign);
free_netdev(netdev0);
ps3_system_bus_set_driver_data(dev, NULL);
ps3_system_bus_set_drvdata(dev, NULL);
ps3_dma_region_free(dev->d_region);
......
......@@ -706,7 +706,7 @@ static void ps3_sys_manager_work(struct ps3_system_bus_device *dev)
ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN);
}
static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
static int __devinit ps3_sys_manager_probe(struct ps3_system_bus_device *dev)
{
int result;
struct ps3_sys_manager_ops ops;
......
......@@ -80,12 +80,12 @@ static const struct avset_video_mode {
{ 0, }, /* auto */
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_W, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_W, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080},
{ RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768},
......@@ -937,7 +937,7 @@ int ps3av_audio_mute(int mute)
EXPORT_SYMBOL_GPL(ps3av_audio_mute);
static int ps3av_probe(struct ps3_system_bus_device *dev)
static int __devinit ps3av_probe(struct ps3_system_bus_device *dev)
{
int res;
int id;
......@@ -1048,7 +1048,7 @@ static struct ps3_vuart_port_driver ps3av_driver = {
.shutdown = ps3av_shutdown,
};
static int ps3av_module_init(void)
static int __init ps3av_module_init(void)
{
int error;
......
......@@ -21,9 +21,10 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <asm/ps3av.h>
#include <asm/ps3fb.h>
#include <asm/ps3.h>
#include <asm/ps3gpu.h>
#include "vuart.h"
......
......@@ -162,7 +162,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__,
(unsigned long)virq);
ps3_system_bus_set_driver_data(dev, hcd);
ps3_system_bus_set_drvdata(dev, hcd);
result = usb_add_hcd(hcd, virq, IRQF_DISABLED);
......@@ -195,8 +195,7 @@ fail_start:
static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
{
unsigned int tmp;
struct usb_hcd *hcd =
(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
BUG_ON(!hcd);
......@@ -208,7 +207,7 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
ehci_shutdown(hcd);
usb_remove_hcd(hcd);
ps3_system_bus_set_driver_data(dev, NULL);
ps3_system_bus_set_drvdata(dev, NULL);
BUG_ON(!hcd->regs);
iounmap(hcd->regs);
......
......@@ -162,7 +162,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__,
(unsigned long)virq);
ps3_system_bus_set_driver_data(dev, hcd);
ps3_system_bus_set_drvdata(dev, hcd);
result = usb_add_hcd(hcd, virq, IRQF_DISABLED);
......@@ -195,8 +195,7 @@ fail_start:
static int ps3_ohci_remove(struct ps3_system_bus_device *dev)
{
unsigned int tmp;
struct usb_hcd *hcd =
(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
BUG_ON(!hcd);
......@@ -208,7 +207,7 @@ static int ps3_ohci_remove(struct ps3_system_bus_device *dev)
ohci_shutdown(hcd);
usb_remove_hcd(hcd);
ps3_system_bus_set_driver_data(dev, NULL);
ps3_system_bus_set_drvdata(dev, NULL);
BUG_ON(!hcd->regs);
iounmap(hcd->regs);
......
This diff is collapsed.
/*
* Generic implementation of 64-bit atomics using spinlocks,
* useful on processors that don't have 64-bit atomic instructions.
*
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.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.
*/
#ifndef _ASM_GENERIC_ATOMIC64_H
#define _ASM_GENERIC_ATOMIC64_H
typedef struct {
long long counter;
} atomic64_t;
#define ATOMIC64_INIT(i) { (i) }
extern long long atomic64_read(const atomic64_t *v);
extern void atomic64_set(atomic64_t *v, long long i);
extern void atomic64_add(long long a, atomic64_t *v);
extern long long atomic64_add_return(long long a, atomic64_t *v);
extern void atomic64_sub(long long a, atomic64_t *v);
extern long long atomic64_sub_return(long long a, atomic64_t *v);
extern long long atomic64_dec_if_positive(atomic64_t *v);
extern long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n);
extern long long atomic64_xchg(atomic64_t *v, long long new);
extern int atomic64_add_unless(atomic64_t *v, long long a, long long u);
#define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0)
#define atomic64_inc(v) atomic64_add(1LL, (v))
#define atomic64_inc_return(v) atomic64_add_return(1LL, (v))
#define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
#define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0)
#define atomic64_dec(v) atomic64_sub(1LL, (v))
#define atomic64_dec_return(v) atomic64_sub_return(1LL, (v))
#define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0)
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL)
#endif /* _ASM_GENERIC_ATOMIC64_H */
......@@ -590,6 +590,11 @@ static inline void bio_list_merge_head(struct bio_list *bl,
bl->head = bl2->head;
}
static inline struct bio *bio_list_peek(struct bio_list *bl)
{
return bl->head;
}
static inline struct bio *bio_list_pop(struct bio_list *bl)
{
struct bio *bio = bl->head;
......
......@@ -194,4 +194,10 @@ config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
config NLATTR
bool
#
# Generic 64-bit atomic support is selected if needed
#
config GENERIC_ATOMIC64
bool
endmenu
......@@ -95,6 +95,8 @@ obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o
obj-$(CONFIG_GENERIC_CSUM) += checksum.o
obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o
hostprogs-y := gen_crc32table
clean-files := crc32table.h
......
/*
* Generic implementation of 64-bit atomics using spinlocks,
* useful on processors that don't have 64-bit atomic instructions.
*
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.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 <linux/types.h>
#include <linux/cache.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <asm/atomic.h>
/*
* We use a hashed array of spinlocks to provide exclusive access
* to each atomic64_t variable. Since this is expected to used on
* systems with small numbers of CPUs (<= 4 or so), we use a
* relatively small array of 16 spinlocks to avoid wasting too much
* memory on the spinlock array.
*/
#define NR_LOCKS 16
/*
* Ensure each lock is in a separate cacheline.
*/
static union {
spinlock_t lock;
char pad[L1_CACHE_BYTES];
} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp;
static inline spinlock_t *lock_addr(const atomic64_t *v)
{
unsigned long addr = (unsigned long) v;
addr >>= L1_CACHE_SHIFT;
addr ^= (addr >> 8) ^ (addr >> 16);
return &atomic64_lock[addr & (NR_LOCKS - 1)].lock;
}
long long atomic64_read(const atomic64_t *v)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
long long val;
spin_lock_irqsave(lock, flags);
val = v->counter;
spin_unlock_irqrestore(lock, flags);
return val;
}
void atomic64_set(atomic64_t *v, long long i)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
spin_lock_irqsave(lock, flags);
v->counter = i;
spin_unlock_irqrestore(lock, flags);
}
void atomic64_add(long long a, atomic64_t *v)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
spin_lock_irqsave(lock, flags);
v->counter += a;
spin_unlock_irqrestore(lock, flags);
}
long long atomic64_add_return(long long a, atomic64_t *v)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
long long val;
spin_lock_irqsave(lock, flags);
val = v->counter += a;
spin_unlock_irqrestore(lock, flags);
return val;
}
void atomic64_sub(long long a, atomic64_t *v)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
spin_lock_irqsave(lock, flags);
v->counter -= a;
spin_unlock_irqrestore(lock, flags);
}
long long atomic64_sub_return(long long a, atomic64_t *v)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
long long val;
spin_lock_irqsave(lock, flags);
val = v->counter -= a;
spin_unlock_irqrestore(lock, flags);
return val;
}
long long atomic64_dec_if_positive(atomic64_t *v)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
long long val;
spin_lock_irqsave(lock, flags);
val = v->counter - 1;
if (val >= 0)
v->counter = val;
spin_unlock_irqrestore(lock, flags);
return val;
}
long long atomic64_cmpxchg(atomic64_t *v, long long o, long long n)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
long long val;
spin_lock_irqsave(lock, flags);
val = v->counter;
if (val == o)
v->counter = n;
spin_unlock_irqrestore(lock, flags);
return val;
}
long long atomic64_xchg(atomic64_t *v, long long new)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
long long val;
spin_lock_irqsave(lock, flags);
val = v->counter;
v->counter = new;
spin_unlock_irqrestore(lock, flags);
return val;
}
int atomic64_add_unless(atomic64_t *v, long long a, long long u)
{
unsigned long flags;
spinlock_t *lock = lock_addr(v);
int ret = 1;
spin_lock_irqsave(lock, flags);
if (v->counter != u) {
v->counter += a;
ret = 0;
}
spin_unlock_irqrestore(lock, flags);
return ret;
}
static int init_atomic64_lock(void)
{
int i;
for (i = 0; i < NR_LOCKS; ++i)
spin_lock_init(&atomic64_lock[i].lock);
return 0;
}
pure_initcall(init_atomic64_lock);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment