diff options
Diffstat (limited to 'drivers/ps3')
| -rw-r--r-- | drivers/ps3/Makefile | 7 | ||||
| -rw-r--r-- | drivers/ps3/ps3-lpm.c | 1253 | ||||
| -rw-r--r-- | drivers/ps3/ps3-sys-manager.c (renamed from drivers/ps3/sys-manager.c) | 137 | ||||
| -rw-r--r-- | drivers/ps3/ps3-vuart.c (renamed from drivers/ps3/vuart.c) | 78 | ||||
| -rw-r--r-- | drivers/ps3/ps3av.c | 515 | ||||
| -rw-r--r-- | drivers/ps3/ps3av_cmd.c | 109 | ||||
| -rw-r--r-- | drivers/ps3/ps3stor_lib.c | 84 | ||||
| -rw-r--r-- | drivers/ps3/sys-manager-core.c | 17 |
8 files changed, 1785 insertions, 415 deletions
diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index 746031de219..50cb1e1b4a1 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -1,6 +1,7 @@ -obj-$(CONFIG_PS3_VUART) += vuart.o +obj-$(CONFIG_PS3_VUART) += ps3-vuart.o obj-$(CONFIG_PS3_PS3AV) += ps3av_mod.o -ps3av_mod-objs += ps3av.o ps3av_cmd.o +ps3av_mod-y := ps3av.o ps3av_cmd.o obj-$(CONFIG_PPC_PS3) += sys-manager-core.o -obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o +obj-$(CONFIG_PS3_SYS_MANAGER) += ps3-sys-manager.o obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o +obj-$(CONFIG_PS3_LPM) += ps3-lpm.o diff --git a/drivers/ps3/ps3-lpm.c b/drivers/ps3/ps3-lpm.c new file mode 100644 index 00000000000..b139b7792e9 --- /dev/null +++ b/drivers/ps3/ps3-lpm.c @@ -0,0 +1,1253 @@ +/* + * PS3 Logical Performance Monitor. + * + * 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/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/uaccess.h> +#include <asm/smp.h> +#include <asm/time.h> +#include <asm/ps3.h> +#include <asm/lv1call.h> +#include <asm/cell-pmu.h> + + +/* BOOKMARK tag macros */ +#define PS3_PM_BOOKMARK_START 0x8000000000000000ULL +#define PS3_PM_BOOKMARK_STOP 0x4000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_KERNEL 0x1000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_USER 0x3000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_MASK_HI 0xF000000000000000ULL +#define PS3_PM_BOOKMARK_TAG_MASK_LO 0x0F00000000000000ULL + +/* CBE PM CONTROL register macros */ +#define PS3_PM_CONTROL_PPU_TH0_BOOKMARK 0x00001000 +#define PS3_PM_CONTROL_PPU_TH1_BOOKMARK 0x00000800 +#define PS3_PM_CONTROL_PPU_COUNT_MODE_MASK 0x000C0000 +#define PS3_PM_CONTROL_PPU_COUNT_MODE_PROBLEM 0x00080000 +#define PS3_WRITE_PM_MASK 0xFFFFFFFFFFFFFFFFULL + +/* CBE PM START STOP register macros */ +#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START 0x02000000 +#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START 0x01000000 +#define PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP 0x00020000 +#define PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP 0x00010000 +#define PS3_PM_START_STOP_START_MASK 0xFF000000 +#define PS3_PM_START_STOP_STOP_MASK 0x00FF0000 + +/* CBE PM COUNTER register macres */ +#define PS3_PM_COUNTER_MASK_HI 0xFFFFFFFF00000000ULL +#define PS3_PM_COUNTER_MASK_LO 0x00000000FFFFFFFFULL + +/* BASE SIGNAL GROUP NUMBER macros */ +#define PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER 0 +#define PM_ISLAND2_SIGNAL_GROUP_NUMBER1 6 +#define PM_ISLAND2_SIGNAL_GROUP_NUMBER2 7 +#define PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER 7 +#define PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER 15 +#define PM_SPU_TRIGGER_SIGNAL_GROUP_NUMBER 17 +#define PM_SPU_EVENT_SIGNAL_GROUP_NUMBER 18 +#define PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER 18 +#define PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER 24 +#define PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER 49 +#define PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER 52 +#define PM_SIG_GROUP_SPU 41 +#define PM_SIG_GROUP_SPU_TRIGGER 42 +#define PM_SIG_GROUP_SPU_EVENT 43 +#define PM_SIG_GROUP_MFC_MAX 60 + +/** + * struct ps3_lpm_shadow_regs - Performance monitor shadow registers. + * + * @pm_control: Shadow of the processor's pm_control register. + * @pm_start_stop: Shadow of the processor's pm_start_stop register. + * @group_control: Shadow of the processor's group_control register. + * @debug_bus_control: Shadow of the processor's debug_bus_control register. + * + * The logical performance monitor provides a write-only interface to + * these processor registers. These shadow variables cache the processor + * register values for reading. + * + * The initial value of the shadow registers at lpm creation is + * PS3_LPM_SHADOW_REG_INIT. + */ + +struct ps3_lpm_shadow_regs { + u64 pm_control; + u64 pm_start_stop; + u64 group_control; + u64 debug_bus_control; +}; + +#define PS3_LPM_SHADOW_REG_INIT 0xFFFFFFFF00000000ULL + +/** + * struct ps3_lpm_priv - Private lpm device data. + * + * @open: An atomic variable indicating the lpm driver has been opened. + * @rights: The lpm rigths granted by the system policy module. A logical + * OR of enum ps3_lpm_rights. + * @node_id: The node id of a BE prosessor whose performance monitor this + * lpar has the right to use. + * @pu_id: The lv1 id of the logical PU. + * @lpm_id: The lv1 id of this lpm instance. + * @outlet_id: The outlet created by lv1 for this lpm instance. + * @tb_count: The number of bytes of data held in the lv1 trace buffer. + * @tb_cache: Kernel buffer to receive the data from the lv1 trace buffer. + * Must be 128 byte aligned. + * @tb_cache_size: Size of the kernel @tb_cache buffer. Must be 128 byte + * aligned. + * @tb_cache_internal: An unaligned buffer allocated by this driver to be + * used for the trace buffer cache when ps3_lpm_open() is called with a + * NULL tb_cache argument. Otherwise unused. + * @shadow: Processor register shadow of type struct ps3_lpm_shadow_regs. + * @sbd: The struct ps3_system_bus_device attached to this driver. + * + * The trace buffer is a buffer allocated and used internally to the lv1 + * hypervisor to collect trace data. The trace buffer cache is a guest + * buffer that accepts the trace data from the trace buffer. + */ + +struct ps3_lpm_priv { + atomic_t open; + u64 rights; + u64 node_id; + u64 pu_id; + u64 lpm_id; + u64 outlet_id; + u64 tb_count; + void *tb_cache; + u64 tb_cache_size; + void *tb_cache_internal; + struct ps3_lpm_shadow_regs shadow; + struct ps3_system_bus_device *sbd; +}; + +enum { + PS3_LPM_DEFAULT_TB_CACHE_SIZE = 0x4000, +}; + +/** + * lpm_priv - Static instance of the lpm data. + * + * Since the exported routines don't support the notion of a device + * instance we need to hold the instance in this static variable + * and then only allow at most one instance at a time to be created. + */ + +static struct ps3_lpm_priv *lpm_priv; + +static struct device *sbd_core(void) +{ + BUG_ON(!lpm_priv || !lpm_priv->sbd); + return &lpm_priv->sbd->core; +} + +/** + * use_start_stop_bookmark - Enable the PPU bookmark trace. + * + * And it enables PPU bookmark triggers ONLY if the other triggers are not set. + * The start/stop bookmarks are inserted at ps3_enable_pm() and ps3_disable_pm() + * to start/stop LPM. + * + * Used to get good quality of the performance counter. + */ + +enum {use_start_stop_bookmark = 1,}; + +void ps3_set_bookmark(u64 bookmark) +{ + /* + * As per the PPE book IV, to avoid bookmark loss there must + * not be a traced branch within 10 cycles of setting the + * SPRN_BKMK register. The actual text is unclear if 'within' + * includes cycles before the call. + */ + + asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;"); + mtspr(SPRN_BKMK, bookmark); + asm volatile("nop;nop;nop;nop;nop;nop;nop;nop;nop;"); +} +EXPORT_SYMBOL_GPL(ps3_set_bookmark); + +void ps3_set_pm_bookmark(u64 tag, u64 incident, u64 th_id) +{ + u64 bookmark; + + bookmark = (get_tb() & 0x00000000FFFFFFFFULL) | + PS3_PM_BOOKMARK_TAG_KERNEL; + bookmark = ((tag << 56) & PS3_PM_BOOKMARK_TAG_MASK_LO) | + (incident << 48) | (th_id << 32) | bookmark; + ps3_set_bookmark(bookmark); +} +EXPORT_SYMBOL_GPL(ps3_set_pm_bookmark); + +/** + * ps3_read_phys_ctr - Read physical counter registers. + * + * Each physical counter can act as one 32 bit counter or as two 16 bit + * counters. + */ + +u32 ps3_read_phys_ctr(u32 cpu, u32 phys_ctr) +{ + int result; + u64 counter0415; + u64 counter2637; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return 0; + } + + result = lv1_set_lpm_counter(lpm_priv->lpm_id, 0, 0, 0, 0, &counter0415, + &counter2637); + if (result) { + dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: " + "phys_ctr %u, %s\n", __func__, __LINE__, phys_ctr, + ps3_result(result)); + return 0; + } + + switch (phys_ctr) { + case 0: + return counter0415 >> 32; + case 1: + return counter0415 & PS3_PM_COUNTER_MASK_LO; + case 2: + return counter2637 >> 32; + case 3: + return counter2637 & PS3_PM_COUNTER_MASK_LO; + default: + BUG(); + } + return 0; +} +EXPORT_SYMBOL_GPL(ps3_read_phys_ctr); + +/** + * ps3_write_phys_ctr - Write physical counter registers. + * + * Each physical counter can act as one 32 bit counter or as two 16 bit + * counters. + */ + +void ps3_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) +{ + u64 counter0415; + u64 counter0415_mask; + u64 counter2637; + u64 counter2637_mask; + int result; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return; + } + + switch (phys_ctr) { + case 0: + counter0415 = (u64)val << 32; + counter0415_mask = PS3_PM_COUNTER_MASK_HI; + counter2637 = 0x0; + counter2637_mask = 0x0; + break; + case 1: + counter0415 = (u64)val; + counter0415_mask = PS3_PM_COUNTER_MASK_LO; + counter2637 = 0x0; + counter2637_mask = 0x0; + break; + case 2: + counter0415 = 0x0; + counter0415_mask = 0x0; + counter2637 = (u64)val << 32; + counter2637_mask = PS3_PM_COUNTER_MASK_HI; + break; + case 3: + counter0415 = 0x0; + counter0415_mask = 0x0; + counter2637 = (u64)val; + counter2637_mask = PS3_PM_COUNTER_MASK_LO; + break; + default: + BUG(); + } + + result = lv1_set_lpm_counter(lpm_priv->lpm_id, + counter0415, counter0415_mask, + counter2637, counter2637_mask, + &counter0415, &counter2637); + if (result) + dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter failed: " + "phys_ctr %u, val %u, %s\n", __func__, __LINE__, + phys_ctr, val, ps3_result(result)); +} +EXPORT_SYMBOL_GPL(ps3_write_phys_ctr); + +/** + * ps3_read_ctr - Read counter. + * + * Read 16 or 32 bits depending on the current size of the counter. + * Counters 4, 5, 6 & 7 are always 16 bit. + */ + +u32 ps3_read_ctr(u32 cpu, u32 ctr) +{ + u32 val; + u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + val = ps3_read_phys_ctr(cpu, phys_ctr); + + if (ps3_get_ctr_size(cpu, phys_ctr) == 16) + val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); + + return val; +} +EXPORT_SYMBOL_GPL(ps3_read_ctr); + +/** + * ps3_write_ctr - Write counter. + * + * Write 16 or 32 bits depending on the current size of the counter. + * Counters 4, 5, 6 & 7 are always 16 bit. + */ + +void ps3_write_ctr(u32 cpu, u32 ctr, u32 val) +{ + u32 phys_ctr; + u32 phys_val; + + phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + if (ps3_get_ctr_size(cpu, phys_ctr) == 16) { + phys_val = ps3_read_phys_ctr(cpu, phys_ctr); + + if (ctr < NR_PHYS_CTRS) + val = (val << 16) | (phys_val & 0xffff); + else + val = (val & 0xffff) | (phys_val & 0xffff0000); + } + + ps3_write_phys_ctr(cpu, phys_ctr, val); +} +EXPORT_SYMBOL_GPL(ps3_write_ctr); + +/** + * ps3_read_pm07_control - Read counter control registers. + * + * Each logical counter has a corresponding control register. + */ + +u32 ps3_read_pm07_control(u32 cpu, u32 ctr) +{ + return 0; +} +EXPORT_SYMBOL_GPL(ps3_read_pm07_control); + +/** + * ps3_write_pm07_control - Write counter control registers. + * + * Each logical counter has a corresponding control register. + */ + +void ps3_write_pm07_control(u32 cpu, u32 ctr, u32 val) +{ + int result; + static const u64 mask = 0xFFFFFFFFFFFFFFFFULL; + u64 old_value; + + if (ctr >= NR_CTRS) { + dev_dbg(sbd_core(), "%s:%u: ctr too big: %u\n", __func__, + __LINE__, ctr); + return; + } + + result = lv1_set_lpm_counter_control(lpm_priv->lpm_id, ctr, val, mask, + &old_value); + if (result) + dev_err(sbd_core(), "%s:%u: lv1_set_lpm_counter_control " + "failed: ctr %u, %s\n", __func__, __LINE__, ctr, + ps3_result(result)); +} +EXPORT_SYMBOL_GPL(ps3_write_pm07_control); + +/** + * ps3_read_pm - Read Other LPM control registers. + */ + +u32 ps3_read_pm(u32 cpu, enum pm_reg_name reg) +{ + int result = 0; + u64 val = 0; + + switch (reg) { + case pm_control: + return lpm_priv->shadow.pm_control; + case trace_address: + return CBE_PM_TRACE_BUF_EMPTY; + case pm_start_stop: + return lpm_priv->shadow.pm_start_stop; + case pm_interval: + result = lv1_set_lpm_interval(lpm_priv->lpm_id, 0, 0, &val); + if (result) { + val = 0; + dev_dbg(sbd_core(), "%s:%u: lv1 set_inteval failed: " + "reg %u, %s\n", __func__, __LINE__, reg, + ps3_result(result)); + } + return (u32)val; + case group_control: + return lpm_priv->shadow.group_control; + case debug_bus_control: + return lpm_priv->shadow.debug_bus_control; + case pm_status: + result = lv1_get_lpm_interrupt_status(lpm_priv->lpm_id, + &val); + if (result) { + val = 0; + dev_dbg(sbd_core(), "%s:%u: lv1 get_lpm_status failed: " + "reg %u, %s\n", __func__, __LINE__, reg, + ps3_result(result)); + } + return (u32)val; + case ext_tr_timer: + return 0; + default: + dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__, + __LINE__, reg); + BUG(); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ps3_read_pm); + +/** + * ps3_write_pm - Write Other LPM control registers. + */ + +void ps3_write_pm(u32 cpu, enum pm_reg_name reg, u32 val) +{ + int result = 0; + u64 dummy; + + switch (reg) { + case group_control: + if (val != lpm_priv->shadow.group_control) + result = lv1_set_lpm_group_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + &dummy); + lpm_priv->shadow.group_control = val; + break; + case debug_bus_control: + if (val != lpm_priv->shadow.debug_bus_control) + result = lv1_set_lpm_debug_bus_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + &dummy); + lpm_priv->shadow.debug_bus_control = val; + break; + case pm_control: + if (use_start_stop_bookmark) + val |= (PS3_PM_CONTROL_PPU_TH0_BOOKMARK | + PS3_PM_CONTROL_PPU_TH1_BOOKMARK); + if (val != lpm_priv->shadow.pm_control) + result = lv1_set_lpm_general_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + 0, 0, &dummy, + &dummy); + lpm_priv->shadow.pm_control = val; + break; + case pm_interval: + result = lv1_set_lpm_interval(lpm_priv->lpm_id, val, + PS3_WRITE_PM_MASK, &dummy); + break; + case pm_start_stop: + if (val != lpm_priv->shadow.pm_start_stop) + result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id, + val, + PS3_WRITE_PM_MASK, + &dummy); + lpm_priv->shadow.pm_start_stop = val; + break; + case trace_address: + case ext_tr_timer: + case pm_status: + break; + default: + dev_dbg(sbd_core(), "%s:%u: unknown reg: %d\n", __func__, + __LINE__, reg); + BUG(); + break; + } + + if (result) + dev_err(sbd_core(), "%s:%u: lv1 set_control failed: " + "reg %u, %s\n", __func__, __LINE__, reg, + ps3_result(result)); +} +EXPORT_SYMBOL_GPL(ps3_write_pm); + +/** + * ps3_get_ctr_size - Get the size of a physical counter. + * + * Returns either 16 or 32. + */ + +u32 ps3_get_ctr_size(u32 cpu, u32 phys_ctr) +{ + u32 pm_ctrl; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return 0; + } + + pm_ctrl = ps3_read_pm(cpu, pm_control); + return (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32; +} +EXPORT_SYMBOL_GPL(ps3_get_ctr_size); + +/** + * ps3_set_ctr_size - Set the size of a physical counter to 16 or 32 bits. + */ + +void ps3_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) +{ + u32 pm_ctrl; + + if (phys_ctr >= NR_PHYS_CTRS) { + dev_dbg(sbd_core(), "%s:%u: phys_ctr too big: %u\n", __func__, + __LINE__, phys_ctr); + return; + } + + pm_ctrl = ps3_read_pm(cpu, pm_control); + + switch (ctr_size) { + case 16: + pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr); + ps3_write_pm(cpu, pm_control, pm_ctrl); + break; + + case 32: + pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr); + ps3_write_pm(cpu, pm_control, pm_ctrl); + break; + default: + BUG(); + } +} +EXPORT_SYMBOL_GPL(ps3_set_ctr_size); + +static u64 pm_translate_signal_group_number_on_island2(u64 subgroup) +{ + + if (subgroup == 2) + subgroup = 3; + + if (subgroup <= 6) + return PM_ISLAND2_BASE_SIGNAL_GROUP_NUMBER + subgroup; + else if (subgroup == 7) + return PM_ISLAND2_SIGNAL_GROUP_NUMBER1; + else + return PM_ISLAND2_SIGNAL_GROUP_NUMBER2; +} + +static u64 pm_translate_signal_group_number_on_island3(u64 subgroup) +{ + + switch (subgroup) { + case 2: + case 3: + case 4: + subgroup += 2; + break; + case 5: + subgroup = 8; + break; + default: + break; + } + return PM_ISLAND3_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island4(u64 subgroup) +{ + return PM_ISLAND4_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island5(u64 subgroup) +{ + + switch (subgroup) { + case 3: + subgroup = 4; + break; + case 4: + subgroup = 6; + break; + default: + break; + } + return PM_ISLAND5_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island6(u64 subgroup, + u64 subsubgroup) +{ + switch (subgroup) { + case 3: + case 4: + case 5: + subgroup += 1; + break; + default: + break; + } + + switch (subsubgroup) { + case 4: + case 5: + case 6: + subsubgroup += 2; + break; + case 7: + case 8: + case 9: + case 10: + subsubgroup += 4; + break; + case 11: + case 12: + case 13: + subsubgroup += 5; + break; + default: + break; + } + + if (subgroup <= 5) + return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup); + else + return (PM_ISLAND6_BASE_SIGNAL_GROUP_NUMBER + subgroup + + subsubgroup - 1); +} + +static u64 pm_translate_signal_group_number_on_island7(u64 subgroup) +{ + return PM_ISLAND7_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_translate_signal_group_number_on_island8(u64 subgroup) +{ + return PM_ISLAND8_BASE_SIGNAL_GROUP_NUMBER + subgroup; +} + +static u64 pm_signal_group_to_ps3_lv1_signal_group(u64 group) +{ + u64 island; + u64 subgroup; + u64 subsubgroup; + + subgroup = 0; + subsubgroup = 0; + island = 0; + if (group < 1000) { + if (group < 100) { + if (20 <= group && group < 30) { + island = 2; + subgroup = group - 20; + } else if (30 <= group && group < 40) { + island = 3; + subgroup = group - 30; + } else if (40 <= group && group < 50) { + island = 4; + subgroup = group - 40; + } else if (50 <= group && group < 60) { + island = 5; + subgroup = group - 50; + } else if (60 <= group && group < 70) { + island = 6; + subgroup = group - 60; + } else if (70 <= group && group < 80) { + island = 7; + subgroup = group - 70; + } else if (80 <= group && group < 90) { + island = 8; + subgroup = group - 80; + } + } else if (200 <= group && group < 300) { + island = 2; + subgroup = group - 200; + } else if (600 <= group && group < 700) { + island = 6; + subgroup = 5; + subsubgroup = group - 650; + } + } else if (6000 <= group && group < 7000) { + island = 6; + subgroup = 5; + subsubgroup = group - 6500; + } + + switch (island) { + case 2: + return pm_translate_signal_group_number_on_island2(subgroup); + case 3: + return pm_translate_signal_group_number_on_island3(subgroup); + case 4: + return pm_translate_signal_group_number_on_island4(subgroup); + case 5: + return pm_translate_signal_group_number_on_island5(subgroup); + case 6: + return pm_translate_signal_group_number_on_island6(subgroup, + subsubgroup); + case 7: + return pm_translate_signal_group_number_on_island7(subgroup); + case 8: + return pm_translate_signal_group_number_on_island8(subgroup); + default: + dev_dbg(sbd_core(), "%s:%u: island not found: %llu\n", __func__, + __LINE__, group); + BUG(); + break; + } + return 0; +} + +static u64 pm_bus_word_to_ps3_lv1_bus_word(u8 word) +{ + + switch (word) { + case 1: + return 0xF000; + case 2: + return 0x0F00; + case 4: + return 0x00F0; + case 8: + default: + return 0x000F; + } +} + +static int __ps3_set_signal(u64 lv1_signal_group, u64 bus_select, + u64 signal_select, u64 attr1, u64 attr2, u64 attr3) +{ + int ret; + + ret = lv1_set_lpm_signal(lpm_priv->lpm_id, lv1_signal_group, bus_select, + signal_select, attr1, attr2, attr3); + if (ret) + dev_err(sbd_core(), + "%s:%u: error:%d 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n", + __func__, __LINE__, ret, lv1_signal_group, bus_select, + signal_select, attr1, attr2, attr3); + + return ret; +} + +int ps3_set_signal(u64 signal_group, u8 signal_bit, u16 sub_unit, + u8 bus_word) +{ + int ret; + u64 lv1_signal_group; + u64 bus_select; + u64 signal_select; + u64 attr1, attr2, attr3; + + if (signal_group == 0) + return __ps3_set_signal(0, 0, 0, 0, 0, 0); + + lv1_signal_group = + pm_signal_group_to_ps3_lv1_signal_group(signal_group); + bus_select = pm_bus_word_to_ps3_lv1_bus_word(bus_word); + + switch (signal_group) { + case PM_SIG_GROUP_SPU_TRIGGER: + signal_select = 1; + signal_select = signal_select << (63 - signal_bit); + break; + case PM_SIG_GROUP_SPU_EVENT: + signal_select = 1; + signal_select = (signal_select << (63 - signal_bit)) | 0x3; + break; + default: + signal_select = 0; + break; + } + + /* + * 0: physical object. + * 1: logical object. + * This parameter is only used for the PPE and SPE signals. + */ + attr1 = 1; + + /* + * This parameter is used to specify the target physical/logical + * PPE/SPE object. + */ + if (PM_SIG_GROUP_SPU <= signal_group && + signal_group < PM_SIG_GROUP_MFC_MAX) + attr2 = sub_unit; + else + attr2 = lpm_priv->pu_id; + + /* + * This parameter is only used for setting the SPE signal. + */ + attr3 = 0; + + ret = __ps3_set_signal(lv1_signal_group, bus_select, signal_select, + attr1, attr2, attr3); + if (ret) + dev_err(sbd_core(), "%s:%u: __ps3_set_signal failed: %d\n", + __func__, __LINE__, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ps3_set_signal); + +u32 ps3_get_hw_thread_id(int cpu) +{ + return get_hard_smp_processor_id(cpu); +} +EXPORT_SYMBOL_GPL(ps3_get_hw_thread_id); + +/** + * ps3_enable_pm - Enable the entire performance monitoring unit. + * + * When we enable the LPM, all pending writes to counters get committed. + */ + +void ps3_enable_pm(u32 cpu) +{ + int result; + u64 tmp; + int insert_bookmark = 0; + + lpm_priv->tb_count = 0; + + if (use_start_stop_bookmark) { + if (!(lpm_priv->shadow.pm_start_stop & + (PS3_PM_START_STOP_START_MASK + | PS3_PM_START_STOP_STOP_MASK))) { + result = lv1_set_lpm_trigger_control(lpm_priv->lpm_id, + (PS3_PM_START_STOP_PPU_TH0_BOOKMARK_START | + PS3_PM_START_STOP_PPU_TH1_BOOKMARK_START | + PS3_PM_START_STOP_PPU_TH0_BOOKMARK_STOP | + PS3_PM_START_STOP_PPU_TH1_BOOKMARK_STOP), + 0xFFFFFFFFFFFFFFFFULL, &tmp); + + if (result) + dev_err(sbd_core(), "%s:%u: " + "lv1_set_lpm_trigger_control failed: " + "%s\n", __func__, __LINE__, + ps3_result(result)); + + insert_bookmark = !result; + } + } + + result = lv1_start_lpm(lpm_priv->lpm_id); + + if (result) + dev_err(sbd_core(), "%s:%u: lv1_start_lpm failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + if (use_start_stop_bookmark && !result && insert_bookmark) + ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_START); +} +EXPORT_SYMBOL_GPL(ps3_enable_pm); + +/** + * ps3_disable_pm - Disable the entire performance monitoring unit. + */ + +void ps3_disable_pm(u32 cpu) +{ + int result; + u64 tmp; + + ps3_set_bookmark(get_tb() | PS3_PM_BOOKMARK_STOP); + + result = lv1_stop_lpm(lpm_priv->lpm_id, &tmp); + + if (result) { + if(result != LV1_WRONG_STATE) + dev_err(sbd_core(), "%s:%u: lv1_stop_lpm failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return; + } + + lpm_priv->tb_count = tmp; + + dev_dbg(sbd_core(), "%s:%u: tb_count %llu (%llxh)\n", __func__, __LINE__, + lpm_priv->tb_count, lpm_priv->tb_count); +} +EXPORT_SYMBOL_GPL(ps3_disable_pm); + +/** + * ps3_lpm_copy_tb - Copy data from the trace buffer to a kernel buffer. + * @offset: Offset in bytes from the start of the trace buffer. + * @buf: Copy destination. + * @count: Maximum count of bytes to copy. + * @bytes_copied: Pointer to a variable that will receive the number of + * bytes copied to @buf. + * + * On error @buf will contain any successfully copied trace buffer data + * and bytes_copied will be set to the number of bytes successfully copied. + */ + +int ps3_lpm_copy_tb(unsigned long offset, void *buf, unsigned long count, + unsigned long *bytes_copied) +{ + int result; + + *bytes_copied = 0; + + if (!lpm_priv->tb_cache) + return -EPERM; + + if (offset >= lpm_priv->tb_count) + return 0; + + count = min_t(u64, count, lpm_priv->tb_count - offset); + + while (*bytes_copied < count) { + const unsigned long request = count - *bytes_copied; + u64 tmp; + + result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset, + request, &tmp); + if (result) { + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n", + __func__, __LINE__, request, offset); + + dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer " + "failed: %s\n", __func__, __LINE__, + ps3_result(result)); + return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL; + } + + memcpy(buf, lpm_priv->tb_cache, tmp); + buf += tmp; + *bytes_copied += tmp; + offset += tmp; + } + dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__, + *bytes_copied); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb); + +/** + * ps3_lpm_copy_tb_to_user - Copy data from the trace buffer to a user buffer. + * @offset: Offset in bytes from the start of the trace buffer. + * @buf: A __user copy destination. + * @count: Maximum count of bytes to copy. + * @bytes_copied: Pointer to a variable that will receive the number of + * bytes copied to @buf. + * + * On error @buf will contain any successfully copied trace buffer data + * and bytes_copied will be set to the number of bytes successfully copied. + */ + +int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf, + unsigned long count, unsigned long *bytes_copied) +{ + int result; + + *bytes_copied = 0; + + if (!lpm_priv->tb_cache) + return -EPERM; + + if (offset >= lpm_priv->tb_count) + return 0; + + count = min_t(u64, count, lpm_priv->tb_count - offset); + + while (*bytes_copied < count) { + const unsigned long request = count - *bytes_copied; + u64 tmp; + + result = lv1_copy_lpm_trace_buffer(lpm_priv->lpm_id, offset, + request, &tmp); + if (result) { + dev_dbg(sbd_core(), "%s:%u: 0x%lx bytes at 0x%lx\n", + __func__, __LINE__, request, offset); + dev_err(sbd_core(), "%s:%u: lv1_copy_lpm_trace_buffer " + "failed: %s\n", __func__, __LINE__, + ps3_result(result)); + return result == LV1_WRONG_STATE ? -EBUSY : -EINVAL; + } + + result = copy_to_user(buf, lpm_priv->tb_cache, tmp); + + if (result) { + dev_dbg(sbd_core(), "%s:%u: 0x%llx bytes at 0x%p\n", + __func__, __LINE__, tmp, buf); + dev_err(sbd_core(), "%s:%u: copy_to_user failed: %d\n", + __func__, __LINE__, result); + return -EFAULT; + } + + buf += tmp; + *bytes_copied += tmp; + offset += tmp; + } + dev_dbg(sbd_core(), "%s:%u: copied %lxh bytes\n", __func__, __LINE__, + *bytes_copied); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3_lpm_copy_tb_to_user); + +/** + * ps3_get_and_clear_pm_interrupts - + * + * Clearing interrupts for the entire performance monitoring unit. + * Reading pm_status clears the interrupt bits. + */ + +u32 ps3_get_and_clear_pm_interrupts(u32 cpu) +{ + return ps3_read_pm(cpu, pm_status); +} +EXPORT_SYMBOL_GPL(ps3_get_and_clear_pm_interrupts); + +/** + * ps3_enable_pm_interrupts - + * + * Enabling interrupts for the entire performance monitoring unit. + * Enables the interrupt bits in the pm_status register. + */ + +void ps3_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask) +{ + if (mask) + ps3_write_pm(cpu, pm_status, mask); +} +EXPORT_SYMBOL_GPL(ps3_enable_pm_interrupts); + +/** + * ps3_enable_pm_interrupts - + * + * Disabling interrupts for the entire performance monitoring unit. + */ + +void ps3_disable_pm_interrupts(u32 cpu) +{ + ps3_get_and_clear_pm_interrupts(cpu); + ps3_write_pm(cpu, pm_status, 0); +} +EXPORT_SYMBOL_GPL(ps3_disable_pm_interrupts); + +/** + * ps3_lpm_open - Open the logical performance monitor device. + * @tb_type: Specifies the type of trace buffer lv1 should use for this lpm + * instance, specified by one of enum ps3_lpm_tb_type. + * @tb_cache: Optional user supplied buffer to use as the trace buffer cache. + * If NULL, the driver will allocate and manage an internal buffer. + * Unused when when @tb_type is PS3_LPM_TB_TYPE_NONE. + * @tb_cache_size: The size in bytes of the user supplied @tb_cache buffer. + * Unused when @tb_cache is NULL or @tb_type is PS3_LPM_TB_TYPE_NONE. + */ + +int ps3_lpm_open(enum ps3_lpm_tb_type tb_type, void *tb_cache, + u64 tb_cache_size) +{ + int result; + u64 tb_size; + + BUG_ON(!lpm_priv); + BUG_ON(tb_type != PS3_LPM_TB_TYPE_NONE + && tb_type != PS3_LPM_TB_TYPE_INTERNAL); + + if (tb_type == PS3_LPM_TB_TYPE_NONE && tb_cache) + dev_dbg(sbd_core(), "%s:%u: bad in vals\n", __func__, __LINE__); + + if (!atomic_add_unless(&lpm_priv->open, 1, 1)) { + dev_dbg(sbd_core(), "%s:%u: busy\n", __func__, __LINE__); + return -EBUSY; + } + + /* Note tb_cache needs 128 byte alignment. */ + + if (tb_type == PS3_LPM_TB_TYPE_NONE) { + lpm_priv->tb_cache_size = 0; + lpm_priv->tb_cache_internal = NULL; + lpm_priv->tb_cache = NULL; + } else if (tb_cache) { + if (tb_cache != (void *)_ALIGN_UP((unsigned long)tb_cache, 128) + || tb_cache_size != _ALIGN_UP(tb_cache_size, 128)) { + dev_err(sbd_core(), "%s:%u: unaligned tb_cache\n", + __func__, __LINE__); + result = -EINVAL; + goto fail_align; + } + lpm_priv->tb_cache_size = tb_cache_size; + lpm_priv->tb_cache_internal = NULL; + lpm_priv->tb_cache = tb_cache; + } else { + lpm_priv->tb_cache_size = PS3_LPM_DEFAULT_TB_CACHE_SIZE; + lpm_priv->tb_cache_internal = kzalloc( + lpm_priv->tb_cache_size + 127, GFP_KERNEL); + if (!lpm_priv->tb_cache_internal) { + dev_err(sbd_core(), "%s:%u: alloc internal tb_cache " + "failed\n", __func__, __LINE__); + result = -ENOMEM; + goto fail_malloc; + } + lpm_priv->tb_cache = (void *)_ALIGN_UP( + (unsigned long)lpm_priv->tb_cache_internal, 128); + } + + result = lv1_construct_lpm(lpm_priv->node_id, tb_type, 0, 0, + ps3_mm_phys_to_lpar(__pa(lpm_priv->tb_cache)), + lpm_priv->tb_cache_size, &lpm_priv->lpm_id, + &lpm_priv->outlet_id, &tb_size); + + if (result) { + dev_err(sbd_core(), "%s:%u: lv1_construct_lpm failed: %s\n", + __func__, __LINE__, ps3_result(result)); + result = -EINVAL; + goto fail_construct; + } + + lpm_priv->shadow.pm_control = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.pm_start_stop = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.group_control = PS3_LPM_SHADOW_REG_INIT; + lpm_priv->shadow.debug_bus_control = PS3_LPM_SHADOW_REG_INIT; + + dev_dbg(sbd_core(), "%s:%u: lpm_id 0x%llx, outlet_id 0x%llx, " + "tb_size 0x%llx\n", __func__, __LINE__, lpm_priv->lpm_id, + lpm_priv->outlet_id, tb_size); + + return 0; + +fail_construct: + kfree(lpm_priv->tb_cache_internal); + lpm_priv->tb_cache_internal = NULL; +fail_malloc: +fail_align: + atomic_dec(&lpm_priv->open); + return result; +} +EXPORT_SYMBOL_GPL(ps3_lpm_open); + +/** + * ps3_lpm_close - Close the lpm device. + * + */ + +int ps3_lpm_close(void) +{ + dev_dbg(sbd_core(), "%s:%u\n", __func__, __LINE__); + + lv1_destruct_lpm(lpm_priv->lpm_id); + lpm_priv->lpm_id = 0; + + kfree(lpm_priv->tb_cache_internal); + lpm_priv->tb_cache_internal = NULL; + + atomic_dec(&lpm_priv->open); + return 0; +} +EXPORT_SYMBOL_GPL(ps3_lpm_close); + +static int ps3_lpm_probe(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, " -> %s:%u\n", __func__, __LINE__); + + if (lpm_priv) { + dev_info(&dev->core, "%s:%u: called twice\n", + __func__, __LINE__); + return -EBUSY; + } + + lpm_priv = kzalloc(sizeof(*lpm_priv), GFP_KERNEL); + + if (!lpm_priv) + return -ENOMEM; + + lpm_priv->sbd = dev; + lpm_priv->node_id = dev->lpm.node_id; + lpm_priv->pu_id = dev->lpm.pu_id; + lpm_priv->rights = dev->lpm.rights; + + dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__); + + return 0; +} + +static int ps3_lpm_remove(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, " -> %s:%u:\n", __func__, __LINE__); + + ps3_lpm_close(); + + kfree(lpm_priv); + lpm_priv = NULL; + + dev_info(&dev->core, " <- %s:%u:\n", __func__, __LINE__); + return 0; +} + +static struct ps3_system_bus_driver ps3_lpm_driver = { + .match_id = PS3_MATCH_ID_LPM, + .core.name = "ps3-lpm", + .core.owner = THIS_MODULE, + .probe = ps3_lpm_probe, + .remove = ps3_lpm_remove, + .shutdown = ps3_lpm_remove, +}; + +static int __init ps3_lpm_init(void) +{ + pr_debug("%s:%d:\n", __func__, __LINE__); + return ps3_system_bus_driver_register(&ps3_lpm_driver); +} + +static void __exit ps3_lpm_exit(void) +{ + pr_debug("%s:%d:\n", __func__, __LINE__); + ps3_system_bus_driver_unregister(&ps3_lpm_driver); +} + +module_init(ps3_lpm_init); +module_exit(ps3_lpm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 Logical Performance Monitor Driver"); +MODULE_AUTHOR("Sony Corporation"); +MODULE_ALIAS(PS3_MODULE_ALIAS_LPM); diff --git a/drivers/ps3/sys-manager.c b/drivers/ps3/ps3-sys-manager.c index 8461b08ab9f..f2ab435954f 100644 --- a/drivers/ps3/sys-manager.c +++ b/drivers/ps3/ps3-sys-manager.c @@ -24,14 +24,11 @@ #include <linux/reboot.h> #include <asm/firmware.h> +#include <asm/lv1call.h> #include <asm/ps3.h> #include "vuart.h" -MODULE_AUTHOR("Sony Corporation"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("PS3 System Manager"); - /** * ps3_sys_manager - PS3 system manager driver. * @@ -49,7 +46,7 @@ MODULE_DESCRIPTION("PS3 System Manager"); /** * struct ps3_sys_manager_header - System manager message header. * @version: Header version, currently 1. - * @size: Header size in bytes, curently 16. + * @size: Header size in bytes, currently 16. * @payload_size: Message payload size in bytes. * @service_id: Message type, one of enum ps3_sys_manager_service_id. * @request_tag: Unique number to identify reply. @@ -83,7 +80,7 @@ static void __maybe_unused _dump_sm_header( * * Currently all messages received from the system manager are either * (16 bytes header + 8 bytes payload = 24 bytes) or (16 bytes header - * + 16 bytes payload = 32 bytes). This knowlege is used to simplify + * + 16 bytes payload = 32 bytes). This knowledge is used to simplify * the logic. */ @@ -122,7 +119,7 @@ enum ps3_sys_manager_service_id { * enum ps3_sys_manager_attr - Notification attribute (bit position mask). * @PS3_SM_ATTR_POWER: Power button. * @PS3_SM_ATTR_RESET: Reset button, not available on retail console. - * @PS3_SM_ATTR_THERMAL: Sytem thermal alert. + * @PS3_SM_ATTR_THERMAL: System thermal alert. * @PS3_SM_ATTR_CONTROLLER: Remote controller event. * @PS3_SM_ATTR_ALL: Logical OR of all. * @@ -142,9 +139,11 @@ enum ps3_sys_manager_attr { /** * enum ps3_sys_manager_event - External event type, reported by system manager. - * @PS3_SM_EVENT_POWER_PRESSED: payload.value not used. + * @PS3_SM_EVENT_POWER_PRESSED: payload.value = + * enum ps3_sys_manager_button_event. * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec. - * @PS3_SM_EVENT_RESET_PRESSED: payload.value not used. + * @PS3_SM_EVENT_RESET_PRESSED: payload.value = + * enum ps3_sys_manager_button_event. * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec. * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id. * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id. @@ -162,6 +161,17 @@ enum ps3_sys_manager_event { }; /** + * enum ps3_sys_manager_button_event - Button event payload values. + * @PS3_SM_BUTTON_EVENT_HARD: Hardware generated event. + * @PS3_SM_BUTTON_EVENT_SOFT: Software generated event. + */ + +enum ps3_sys_manager_button_event { + PS3_SM_BUTTON_EVENT_HARD = 0, + PS3_SM_BUTTON_EVENT_SOFT = 1, +}; + +/** * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed. */ @@ -174,25 +184,32 @@ enum ps3_sys_manager_next_op { /** * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask). - * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button, IR - * controller, and bluetooth controller. - * @PS3_SM_WAKE_RTC: - * @PS3_SM_WAKE_RTC_ERROR: + * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button. + * @PS3_SM_WAKE_W_O_L: Ether or wireless LAN. * @PS3_SM_WAKE_P_O_R: Power on reset. * * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN. - * System will always wake from the PS3_SM_WAKE_DEFAULT sources. + * The system will always wake from the PS3_SM_WAKE_DEFAULT sources. + * Sources listed here are the only ones available to guests in the + * other-os lpar. */ enum ps3_sys_manager_wake_source { /* version 3 */ PS3_SM_WAKE_DEFAULT = 0, - PS3_SM_WAKE_RTC = 0x00000040, - PS3_SM_WAKE_RTC_ERROR = 0x00000080, - PS3_SM_WAKE_P_O_R = 0x10000000, + PS3_SM_WAKE_W_O_L = 0x00000400, + PS3_SM_WAKE_P_O_R = 0x80000000, }; /** + * user_wake_sources - User specified wakeup sources. + * + * Logical OR of enum ps3_sys_manager_wake_source types. + */ + +static u32 user_wake_sources = PS3_SM_WAKE_DEFAULT; + +/** * enum ps3_sys_manager_cmd - Command from system manager to guest. * * The guest completes the actions needed, then acks or naks the command via @@ -418,8 +435,10 @@ static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev) switch (event.type) { case PS3_SM_EVENT_POWER_PRESSED: - dev_dbg(&dev->core, "%s:%d: POWER_PRESSED\n", - __func__, __LINE__); + dev_dbg(&dev->core, "%s:%d: POWER_PRESSED (%s)\n", + __func__, __LINE__, + (event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft" + : "hard")); ps3_sm_force_power_off = 1; /* * A memory barrier is use here to sync memory since @@ -434,8 +453,10 @@ static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev) __func__, __LINE__, event.value); break; case PS3_SM_EVENT_RESET_PRESSED: - dev_dbg(&dev->core, "%s:%d: RESET_PRESSED\n", - __func__, __LINE__); + dev_dbg(&dev->core, "%s:%d: RESET_PRESSED (%s)\n", + __func__, __LINE__, + (event.value == PS3_SM_BUTTON_EVENT_SOFT ? "soft" + : "hard")); ps3_sm_force_power_off = 0; /* * A memory barrier is use here to sync memory since @@ -452,7 +473,7 @@ static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev) case PS3_SM_EVENT_THERMAL_ALERT: dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n", __func__, __LINE__, event.value); - printk(KERN_INFO "PS3 Thermal Alert Zone %u\n", event.value); + pr_info("PS3 Thermal Alert Zone %u\n", event.value); break; case PS3_SM_EVENT_THERMAL_CLEARED: dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n", @@ -488,7 +509,7 @@ static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev) result = ps3_vuart_read(dev, &cmd, sizeof(cmd)); BUG_ON(result && "need to retry here"); - if(result) + if (result) return result; if (cmd.version != 1) { @@ -521,7 +542,7 @@ static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev) result = ps3_vuart_read(dev, &header, sizeof(struct ps3_sys_manager_header)); - if(result) + if (result) return result; if (header.version != 1) { @@ -566,6 +587,23 @@ fail_id: return -EIO; } +static void ps3_sys_manager_fin(struct ps3_system_bus_device *dev) +{ + ps3_sys_manager_send_request_shutdown(dev); + + pr_emerg("System Halted, OK to turn off power\n"); + + while (ps3_sys_manager_handle_msg(dev)) { + /* pause until next DEC interrupt */ + lv1_pause(0); + } + + while (1) { + /* pause, ignoring DEC interrupt */ + lv1_pause(1); + } +} + /** * ps3_sys_manager_final_power_off - The final platform machine_power_off routine. * @@ -586,13 +624,9 @@ static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev) ps3_vuart_cancel_async(dev); ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN, - PS3_SM_WAKE_DEFAULT); - ps3_sys_manager_send_request_shutdown(dev); + user_wake_sources); - printk(KERN_EMERG "System Halted, OK to turn off power\n"); - - while(1) - ps3_sys_manager_handle_msg(dev); + ps3_sys_manager_fin(dev); } /** @@ -622,15 +656,43 @@ static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev) ps3_vuart_cancel_async(dev); ps3_sys_manager_send_attr(dev, 0); - ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT, - PS3_SM_WAKE_DEFAULT); - ps3_sys_manager_send_request_shutdown(dev); + ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT, + user_wake_sources); + + ps3_sys_manager_fin(dev); +} + +/** + * ps3_sys_manager_get_wol - Get wake-on-lan setting. + */ - printk(KERN_EMERG "System Halted, OK to turn off power\n"); +int ps3_sys_manager_get_wol(void) +{ + pr_debug("%s:%d\n", __func__, __LINE__); - while(1) - ps3_sys_manager_handle_msg(dev); + return (user_wake_sources & PS3_SM_WAKE_W_O_L) != 0; } +EXPORT_SYMBOL_GPL(ps3_sys_manager_get_wol); + +/** + * ps3_sys_manager_set_wol - Set wake-on-lan setting. + */ + +void ps3_sys_manager_set_wol(int state) +{ + static DEFINE_MUTEX(mutex); + + mutex_lock(&mutex); + + pr_debug("%s:%d: %d\n", __func__, __LINE__, state); + + if (state) + user_wake_sources |= PS3_SM_WAKE_W_O_L; + else + user_wake_sources &= ~PS3_SM_WAKE_W_O_L; + mutex_unlock(&mutex); +} +EXPORT_SYMBOL_GPL(ps3_sys_manager_set_wol); /** * ps3_sys_manager_work - Asynchronous read handler. @@ -699,4 +761,7 @@ static int __init ps3_sys_manager_init(void) module_init(ps3_sys_manager_init); /* Module remove not supported. */ +MODULE_AUTHOR("Sony Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 System Manager"); MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER); diff --git a/drivers/ps3/vuart.c b/drivers/ps3/ps3-vuart.c index bea25a1391e..bc1e5139ba2 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/ps3-vuart.c @@ -19,14 +19,15 @@ */ #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/workqueue.h> +#include <linux/bitops.h> #include <asm/ps3.h> #include <asm/firmware.h> #include <asm/lv1call.h> -#include <asm/bitops.h> #include "vuart.h" @@ -108,18 +109,18 @@ static struct ps3_vuart_port_priv *to_port_priv( struct ports_bmp { u64 status; u64 unused[3]; -} __attribute__ ((aligned (32))); +} __attribute__((aligned(32))); #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) static void __maybe_unused _dump_ports_bmp( - const struct ports_bmp* bmp, const char* func, int line) + const struct ports_bmp *bmp, const char *func, int line) { - pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); + pr_debug("%s:%d: ports_bmp: %016llxh\n", func, line, bmp->status); } #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) static void __maybe_unused _dump_port_params(unsigned int port_number, - const char* func, int line) + const char *func, int line) { #if defined(DEBUG) static const char *strings[] = { @@ -159,11 +160,13 @@ int ps3_vuart_get_triggers(struct ps3_system_bus_device *dev, struct vuart_triggers *trig) { int result; - unsigned long size; - unsigned long val; + u64 size; + u64 val; + u64 tx; result = lv1_get_virtual_uart_param(dev->port_number, - PARAM_TX_TRIGGER, &trig->tx); + PARAM_TX_TRIGGER, &tx); + trig->tx = tx; if (result) { dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", @@ -201,7 +204,7 @@ int ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx, unsigned int rx) { int result; - unsigned long size; + u64 size; result = lv1_set_virtual_uart_param(dev->port_number, PARAM_TX_TRIGGER, tx); @@ -248,7 +251,7 @@ static int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev, dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n", __func__, __LINE__, ps3_result(result)); - dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, + dev_dbg(&dev->core, "%s:%d: %llxh\n", __func__, __LINE__, *bytes_waiting); return result; } @@ -295,7 +298,7 @@ static int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev, *status = tmp & priv->interrupt_mask; - dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", + dev_dbg(&dev->core, "%s:%d: m %llxh, s %llxh, m&s %lxh\n", __func__, __LINE__, priv->interrupt_mask, tmp, *status); return result; @@ -363,7 +366,7 @@ int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev) */ static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, - const void* buf, unsigned int bytes, unsigned long *bytes_written) + const void *buf, unsigned int bytes, u64 *bytes_written) { int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); @@ -379,7 +382,7 @@ static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, priv->stats.bytes_written += *bytes_written; - dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__, + dev_dbg(&dev->core, "%s:%d: wrote %llxh/%xh=>%lxh\n", __func__, __LINE__, *bytes_written, bytes, priv->stats.bytes_written); return result; @@ -393,7 +396,7 @@ static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, */ static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf, - unsigned int bytes, unsigned long *bytes_read) + unsigned int bytes, u64 *bytes_read) { int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); @@ -411,7 +414,7 @@ static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf, priv->stats.bytes_read += *bytes_read; - dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, + dev_dbg(&dev->core, "%s:%d: read %llxh/%xh=>%lxh\n", __func__, __LINE__, *bytes_read, bytes, priv->stats.bytes_read); return result; @@ -431,7 +434,7 @@ void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 bytes_waiting; - void* tmp; + void *tmp; result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting); @@ -500,7 +503,7 @@ int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, spin_lock_irqsave(&priv->tx_list.lock, flags); if (list_empty(&priv->tx_list.head)) { - unsigned long bytes_written; + u64 bytes_written; result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); @@ -526,9 +529,8 @@ int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); - if (!lb) { + if (!lb) return -ENOMEM; - } memcpy(lb->data, buf, bytes); lb->head = lb->data; @@ -593,7 +595,7 @@ static int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev, list_add_tail(&lb->link, &priv->rx_list.head); priv->rx_list.bytes_held += bytes; - dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", + dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %llxh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); *bytes_queued = bytes; @@ -697,8 +699,6 @@ int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes) BUG_ON(!bytes); - PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work); - spin_lock_irqsave(&priv->rx_list.lock, flags); if (priv->rx_list.bytes_held >= bytes) { dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", @@ -746,7 +746,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev) list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) { - unsigned long bytes_written; + u64 bytes_written; result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head, &bytes_written); @@ -763,7 +763,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev) if (bytes_written < lb->tail - lb->head) { lb->head += bytes_written; dev_dbg(&dev->core, - "%s:%d cleared buf_%lu, %lxh bytes\n", + "%s:%d cleared buf_%lu, %llxh bytes\n", __func__, __LINE__, lb->dbg_number, bytes_written); goto port_full; @@ -878,7 +878,7 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev) struct vuart_bus_priv { struct ports_bmp *bmp; unsigned int virq; - struct semaphore probe_mutex; + struct mutex probe_mutex; int use_count; struct ps3_system_bus_device *devices[PORT_COUNT]; } static vuart_bus_priv; @@ -926,9 +926,8 @@ static int ps3_vuart_bus_interrupt_get(void) BUG_ON(vuart_bus_priv.use_count > 2); - if (vuart_bus_priv.use_count != 1) { + if (vuart_bus_priv.use_count != 1) return 0; - } BUG_ON(vuart_bus_priv.bmp); @@ -951,7 +950,7 @@ static int ps3_vuart_bus_interrupt_get(void) } result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, - IRQF_DISABLED, "vuart", &vuart_bus_priv); + 0, "vuart", &vuart_bus_priv); if (result) { pr_debug("%s:%d: request_irq failed (%d)\n", @@ -1017,7 +1016,7 @@ static int ps3_vuart_probe(struct ps3_system_bus_device *dev) return -EINVAL; } - down(&vuart_bus_priv.probe_mutex); + mutex_lock(&vuart_bus_priv.probe_mutex); result = ps3_vuart_bus_interrupt_get(); @@ -1051,7 +1050,7 @@ static int ps3_vuart_probe(struct ps3_system_bus_device *dev) INIT_LIST_HEAD(&priv->rx_list.head); spin_lock_init(&priv->rx_list.lock); - INIT_WORK(&priv->rx_list.work.work, NULL); + INIT_WORK(&priv->rx_list.work.work, ps3_vuart_work); priv->rx_list.work.trigger = 0; priv->rx_list.work.dev = dev; @@ -1074,11 +1073,10 @@ static int ps3_vuart_probe(struct ps3_system_bus_device *dev) if (result) { dev_dbg(&dev->core, "%s:%d: drv->probe failed\n", __func__, __LINE__); - down(&vuart_bus_priv.probe_mutex); goto fail_probe; } - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return result; @@ -1091,7 +1089,7 @@ fail_dev_malloc: fail_busy: ps3_vuart_bus_interrupt_put(); fail_setup_interrupt: - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__); return result; } @@ -1130,7 +1128,7 @@ static int ps3_vuart_remove(struct ps3_system_bus_device *dev) BUG_ON(!dev); - down(&vuart_bus_priv.probe_mutex); + mutex_lock(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, dev->match_id); @@ -1138,7 +1136,7 @@ static int ps3_vuart_remove(struct ps3_system_bus_device *dev) if (!dev->core.driver) { dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1161,7 +1159,7 @@ static int ps3_vuart_remove(struct ps3_system_bus_device *dev) priv = NULL; dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1181,7 +1179,7 @@ static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) BUG_ON(!dev); - down(&vuart_bus_priv.probe_mutex); + mutex_lock(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, dev->match_id); @@ -1189,7 +1187,7 @@ static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) if (!dev->core.driver) { dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1213,7 +1211,7 @@ static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); - up(&vuart_bus_priv.probe_mutex); + mutex_unlock(&vuart_bus_priv.probe_mutex); return 0; } @@ -1224,7 +1222,7 @@ static int __init ps3_vuart_bus_init(void) if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; - init_MUTEX(&vuart_bus_priv.probe_mutex); + mutex_init(&vuart_bus_priv.probe_mutex); return 0; } diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 85e21614f86..437fc35beb7 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -23,6 +23,8 @@ #include <linux/delay.h> #include <linux/notifier.h> #include <linux/ioctl.h> +#include <linux/fb.h> +#include <linux/slab.h> #include <asm/firmware.h> #include <asm/ps3av.h> @@ -33,6 +35,8 @@ #define BUFSIZE 4096 /* vuart buf size */ #define PS3AV_BUF_SIZE 512 /* max packet size */ +static int safe_mode; + static int timeout = 5000; /* in msec ( 5 sec ) */ module_param(timeout, int, 0644); @@ -56,8 +60,6 @@ static struct ps3av { struct ps3av_reply_hdr reply_hdr; u8 raw[PS3AV_BUF_SIZE]; } recv_buf; - void (*flip_ctl)(int on, void *data); - void *flip_data; } *ps3av; /* color space */ @@ -75,23 +77,21 @@ static const struct avset_video_mode { u32 aspect; u32 x; u32 y; - u32 interlace; - u32 freq; } video_mode_table[] = { { 0, }, /* auto */ - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480, 1, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480, 0, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720, 0, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080, 1, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080, 0, 60}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576, 1, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576, 0, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720, 0, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080, 1, 50}, - {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080, 0, 50}, - { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768, 0, 60}, - { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024, 0, 60}, - { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200, 0, 60}, + {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_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_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}, + { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024}, + { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200}, }; /* supported CIDs */ @@ -338,7 +338,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, mutex_unlock(&ps3av->mutex); return 0; - err: +err: mutex_unlock(&ps3av->mutex); printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); return res; @@ -477,7 +477,6 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) return 0; } - EXPORT_SYMBOL_GPL(ps3av_set_audio_mode); static int ps3av_set_videomode(void) @@ -491,40 +490,22 @@ static int ps3av_set_videomode(void) return 0; } -static void ps3av_set_videomode_cont(u32 id, u32 old_id) +static void ps3av_set_videomode_packet(u32 id) { struct ps3av_pkt_avb_param avb_param; - int i; + unsigned int i; u32 len = 0, av_video_cs; const struct avset_video_mode *video_mode; int res; video_mode = &video_mode_table[id & PS3AV_MODE_MASK]; - avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ + avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ avb_param.num_of_audio_pkt = 0; avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi + ps3av->av_hw_conf.num_of_avmulti; avb_param.num_of_av_audio_pkt = 0; - /* video signal off */ - ps3av_set_video_disable_sig(); - - /* Retail PS3 product doesn't support this */ - if (id & PS3AV_MODE_HDCP_OFF) { - res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF); - if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) - dev_dbg(&ps3av->dev->core, "Not supported\n"); - else if (res) - dev_dbg(&ps3av->dev->core, - "ps3av_cmd_av_hdmi_mode failed\n"); - } else if (old_id & PS3AV_MODE_HDCP_OFF) { - res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL); - if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) - dev_dbg(&ps3av->dev->core, - "ps3av_cmd_av_hdmi_mode failed\n"); - } - /* video_pkt */ for (i = 0; i < avb_param.num_of_video_pkt; i++) len += ps3av_cmd_set_video_mode(&avb_param.buf[len], @@ -539,7 +520,7 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) #ifndef PS3AV_HDMI_YUV if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) - av_video_cs = RGB8; /* use RGB for HDMI */ + av_video_cs = RGB8; /* use RGB for HDMI */ #endif len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], ps3av->av_port[i], @@ -551,10 +532,46 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) res = ps3av_cmd_avb_param(&avb_param, len); if (res == PS3AV_STATUS_NO_SYNC_HEAD) printk(KERN_WARNING - "%s: Command failed. Please try your request again. \n", + "%s: Command failed. Please try your request again.\n", __func__); else if (res) dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); +} + +static void ps3av_set_videomode_cont(u32 id, u32 old_id) +{ + static int vesa; + int res; + + /* video signal off */ + ps3av_set_video_disable_sig(); + + /* + * AV backend needs non-VESA mode setting at least one time + * when VESA mode is used. + */ + if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) { + /* vesa mode */ + ps3av_set_videomode_packet(PS3AV_MODE_480P); + } + vesa = 1; + + /* Retail PS3 product doesn't support this */ + if (id & PS3AV_MODE_HDCP_OFF) { + res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF); + if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) + dev_dbg(&ps3av->dev->core, "Not supported\n"); + else if (res) + dev_dbg(&ps3av->dev->core, + "ps3av_cmd_av_hdmi_mode failed\n"); + } else if (old_id & PS3AV_MODE_HDCP_OFF) { + res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL); + if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) + dev_dbg(&ps3av->dev->core, + "ps3av_cmd_av_hdmi_mode failed\n"); + } + + ps3av_set_videomode_packet(id); msleep(1500); /* av video mute */ @@ -567,165 +584,249 @@ static void ps3avd(struct work_struct *work) complete(&ps3av->done); } -static int ps3av_vid2table_id(int vid) -{ - int i; - - for (i = 1; i < ARRAY_SIZE(video_mode_table); i++) - if (video_mode_table[i].vid == vid) - return i; - return -1; -} +#define SHIFT_50 0 +#define SHIFT_60 4 +#define SHIFT_VESA 8 + +static const struct { + unsigned mask:19; + unsigned id:4; +} ps3av_preferred_modes[] = { + { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA }, + { PS3AV_RESBIT_1920x1080P << SHIFT_60, PS3AV_MODE_1080P60 }, + { PS3AV_RESBIT_1920x1080P << SHIFT_50, PS3AV_MODE_1080P50 }, + { PS3AV_RESBIT_1920x1080I << SHIFT_60, PS3AV_MODE_1080I60 }, + { PS3AV_RESBIT_1920x1080I << SHIFT_50, PS3AV_MODE_1080I50 }, + { PS3AV_RESBIT_SXGA << SHIFT_VESA, PS3AV_MODE_SXGA }, + { PS3AV_RESBIT_WXGA << SHIFT_VESA, PS3AV_MODE_WXGA }, + { PS3AV_RESBIT_1280x720P << SHIFT_60, PS3AV_MODE_720P60 }, + { PS3AV_RESBIT_1280x720P << SHIFT_50, PS3AV_MODE_720P50 }, + { PS3AV_RESBIT_720x480P << SHIFT_60, PS3AV_MODE_480P }, + { PS3AV_RESBIT_720x576P << SHIFT_50, PS3AV_MODE_576P }, +}; -static int ps3av_resbit2vid(u32 res_50, u32 res_60) +static enum ps3av_mode_num ps3av_resbit2id(u32 res_50, u32 res_60, + u32 res_vesa) { - int vid = -1; + unsigned int i; + u32 res_all; + + /* + * We mask off the resolution bits we care about and combine the + * results in one bitfield, so make sure there's no overlap + */ + BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 & + PS3AV_RES_MASK_60 << SHIFT_60); + BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 & + PS3AV_RES_MASK_VESA << SHIFT_VESA); + BUILD_BUG_ON(PS3AV_RES_MASK_60 << SHIFT_60 & + PS3AV_RES_MASK_VESA << SHIFT_VESA); + res_all = (res_50 & PS3AV_RES_MASK_50) << SHIFT_50 | + (res_60 & PS3AV_RES_MASK_60) << SHIFT_60 | + (res_vesa & PS3AV_RES_MASK_VESA) << SHIFT_VESA; + + if (!res_all) + return 0; + + for (i = 0; i < ARRAY_SIZE(ps3av_preferred_modes); i++) + if (res_all & ps3av_preferred_modes[i].mask) + return ps3av_preferred_modes[i].id; - if (res_50 > res_60) { /* if res_50 == res_60, res_60 will be used */ - if (res_50 & PS3AV_RESBIT_1920x1080P) - vid = PS3AV_CMD_VIDEO_VID_1080P_50HZ; - else if (res_50 & PS3AV_RESBIT_1920x1080I) - vid = PS3AV_CMD_VIDEO_VID_1080I_50HZ; - else if (res_50 & PS3AV_RESBIT_1280x720P) - vid = PS3AV_CMD_VIDEO_VID_720P_50HZ; - else if (res_50 & PS3AV_RESBIT_720x576P) - vid = PS3AV_CMD_VIDEO_VID_576P; - else - vid = -1; - } else { - if (res_60 & PS3AV_RESBIT_1920x1080P) - vid = PS3AV_CMD_VIDEO_VID_1080P_60HZ; - else if (res_60 & PS3AV_RESBIT_1920x1080I) - vid = PS3AV_CMD_VIDEO_VID_1080I_60HZ; - else if (res_60 & PS3AV_RESBIT_1280x720P) - vid = PS3AV_CMD_VIDEO_VID_720P_60HZ; - else if (res_60 & PS3AV_RESBIT_720x480P) - vid = PS3AV_CMD_VIDEO_VID_480P; - else - vid = -1; - } - return vid; + return 0; } -static int ps3av_hdmi_get_vid(struct ps3av_info_monitor *info) +static enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info) { - u32 res_50, res_60; - int vid = -1; + enum ps3av_mode_num id; - if (info->monitor_type != PS3AV_MONITOR_TYPE_HDMI) - return -1; + if (safe_mode) + return PS3AV_DEFAULT_HDMI_MODE_ID_REG_60; /* check native resolution */ - res_50 = info->res_50.native & PS3AV_RES_MASK_50; - res_60 = info->res_60.native & PS3AV_RES_MASK_60; - if (res_50 || res_60) { - vid = ps3av_resbit2vid(res_50, res_60); - return vid; + id = ps3av_resbit2id(info->res_50.native, info->res_60.native, + info->res_vesa.native); + if (id) { + pr_debug("%s: Using native mode %d\n", __func__, id); + return id; } - /* check resolution */ - res_50 = info->res_50.res_bits & PS3AV_RES_MASK_50; - res_60 = info->res_60.res_bits & PS3AV_RES_MASK_60; - if (res_50 || res_60) { - vid = ps3av_resbit2vid(res_50, res_60); - return vid; + /* check supported resolutions */ + id = ps3av_resbit2id(info->res_50.res_bits, info->res_60.res_bits, + info->res_vesa.res_bits); + if (id) { + pr_debug("%s: Using supported mode %d\n", __func__, id); + return id; } if (ps3av->region & PS3AV_REGION_60) - vid = PS3AV_DEFAULT_HDMI_VID_REG_60; + id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60; else - vid = PS3AV_DEFAULT_HDMI_VID_REG_50; - return vid; + id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50; + pr_debug("%s: Using default mode %d\n", __func__, id); + return id; +} + +static void ps3av_monitor_info_dump( + const struct ps3av_pkt_av_get_monitor_info *monitor_info) +{ + const struct ps3av_info_monitor *info = &monitor_info->info; + const struct ps3av_info_audio *audio = info->audio; + char id[sizeof(info->monitor_id)*3+1]; + int i; + + pr_debug("Monitor Info: size %u\n", monitor_info->send_hdr.size); + + pr_debug("avport: %02x\n", info->avport); + for (i = 0; i < sizeof(info->monitor_id); i++) + sprintf(&id[i*3], " %02x", info->monitor_id[i]); + pr_debug("monitor_id: %s\n", id); + pr_debug("monitor_type: %02x\n", info->monitor_type); + pr_debug("monitor_name: %.*s\n", (int)sizeof(info->monitor_name), + info->monitor_name); + + /* resolution */ + pr_debug("resolution_60: bits: %08x native: %08x\n", + info->res_60.res_bits, info->res_60.native); + pr_debug("resolution_50: bits: %08x native: %08x\n", + info->res_50.res_bits, info->res_50.native); + pr_debug("resolution_other: bits: %08x native: %08x\n", + info->res_other.res_bits, info->res_other.native); + pr_debug("resolution_vesa: bits: %08x native: %08x\n", + info->res_vesa.res_bits, info->res_vesa.native); + + /* color space */ + pr_debug("color space rgb: %02x\n", info->cs.rgb); + pr_debug("color space yuv444: %02x\n", info->cs.yuv444); + pr_debug("color space yuv422: %02x\n", info->cs.yuv422); + + /* color info */ + pr_debug("color info red: X %04x Y %04x\n", info->color.red_x, + info->color.red_y); + pr_debug("color info green: X %04x Y %04x\n", info->color.green_x, + info->color.green_y); + pr_debug("color info blue: X %04x Y %04x\n", info->color.blue_x, + info->color.blue_y); + pr_debug("color info white: X %04x Y %04x\n", info->color.white_x, + info->color.white_y); + pr_debug("color info gamma: %08x\n", info->color.gamma); + + /* other info */ + pr_debug("supported_AI: %02x\n", info->supported_ai); + pr_debug("speaker_info: %02x\n", info->speaker_info); + pr_debug("num of audio: %02x\n", info->num_of_audio_block); + + /* audio block */ + for (i = 0; i < info->num_of_audio_block; i++) { + pr_debug( + "audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n", + i, audio->type, audio->max_num_of_ch, audio->fs, + audio->sbit); + audio++; + } +} + +static const struct ps3av_monitor_quirk { + const char *monitor_name; + u32 clear_60; +} ps3av_monitor_quirks[] = { + { + .monitor_name = "DELL 2007WFP", + .clear_60 = PS3AV_RESBIT_1920x1080I + }, { + .monitor_name = "L226WTQ", + .clear_60 = PS3AV_RESBIT_1920x1080I | + PS3AV_RESBIT_1920x1080P + }, { + .monitor_name = "SyncMaster", + .clear_60 = PS3AV_RESBIT_1920x1080I + } +}; + +static void ps3av_fixup_monitor_info(struct ps3av_info_monitor *info) +{ + unsigned int i; + const struct ps3av_monitor_quirk *quirk; + + for (i = 0; i < ARRAY_SIZE(ps3av_monitor_quirks); i++) { + quirk = &ps3av_monitor_quirks[i]; + if (!strncmp(info->monitor_name, quirk->monitor_name, + sizeof(info->monitor_name))) { + pr_info("%s: Applying quirk for %s\n", __func__, + quirk->monitor_name); + info->res_60.res_bits &= ~quirk->clear_60; + info->res_60.native &= ~quirk->clear_60; + break; + } + } } -static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf, - int boot) +static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf) { - int i, res, vid = -1, dvi = 0, rgb = 0; + int i, res, id = 0, dvi = 0, rgb = 0; struct ps3av_pkt_av_get_monitor_info monitor_info; struct ps3av_info_monitor *info; - /* get vid for hdmi */ - for (i = 0; i < av_hw_conf->num_of_hdmi; i++) { + /* get mode id for hdmi */ + for (i = 0; i < av_hw_conf->num_of_hdmi && !id; i++) { res = ps3av_cmd_video_get_monitor_info(&monitor_info, PS3AV_CMD_AVPORT_HDMI_0 + i); if (res < 0) return -1; - ps3av_cmd_av_monitor_info_dump(&monitor_info); + ps3av_monitor_info_dump(&monitor_info); + info = &monitor_info.info; - /* check DVI */ - if (info->monitor_type == PS3AV_MONITOR_TYPE_DVI) { + ps3av_fixup_monitor_info(info); + + switch (info->monitor_type) { + case PS3AV_MONITOR_TYPE_DVI: dvi = PS3AV_MODE_DVI; - break; - } - /* check HDMI */ - vid = ps3av_hdmi_get_vid(info); - if (vid != -1) { - /* got valid vid */ + /* fall through */ + case PS3AV_MONITOR_TYPE_HDMI: + id = ps3av_hdmi_get_id(info); break; } } - if (dvi) { - /* DVI mode */ - vid = PS3AV_DEFAULT_DVI_VID; - } else if (vid == -1) { + if (!id) { /* no HDMI interface or HDMI is off */ if (ps3av->region & PS3AV_REGION_60) - vid = PS3AV_DEFAULT_AVMULTI_VID_REG_60; + id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_60; else - vid = PS3AV_DEFAULT_AVMULTI_VID_REG_50; + id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_50; if (ps3av->region & PS3AV_REGION_RGB) rgb = PS3AV_MODE_RGB; - } else if (boot) { - /* HDMI: using DEFAULT HDMI_VID while booting up */ - info = &monitor_info.info; - if (ps3av->region & PS3AV_REGION_60) { - if (info->res_60.res_bits & PS3AV_RESBIT_720x480P) - vid = PS3AV_DEFAULT_HDMI_VID_REG_60; - else if (info->res_50.res_bits & PS3AV_RESBIT_720x576P) - vid = PS3AV_DEFAULT_HDMI_VID_REG_50; - else { - /* default */ - vid = PS3AV_DEFAULT_HDMI_VID_REG_60; - } - } else { - if (info->res_50.res_bits & PS3AV_RESBIT_720x576P) - vid = PS3AV_DEFAULT_HDMI_VID_REG_50; - else if (info->res_60.res_bits & PS3AV_RESBIT_720x480P) - vid = PS3AV_DEFAULT_HDMI_VID_REG_60; - else { - /* default */ - vid = PS3AV_DEFAULT_HDMI_VID_REG_50; - } - } + pr_debug("%s: Using avmulti mode %d\n", __func__, id); } - return (ps3av_vid2table_id(vid) | dvi | rgb); + return id | dvi | rgb; } static int ps3av_get_hw_conf(struct ps3av *ps3av) { int i, j, k, res; + const struct ps3av_pkt_av_get_hw_conf *hw_conf; /* get av_hw_conf */ res = ps3av_cmd_av_get_hw_conf(&ps3av->av_hw_conf); if (res < 0) return -1; - ps3av_cmd_av_hw_conf_dump(&ps3av->av_hw_conf); + hw_conf = &ps3av->av_hw_conf; + pr_debug("av_h_conf: num of hdmi: %u\n", hw_conf->num_of_hdmi); + pr_debug("av_h_conf: num of avmulti: %u\n", hw_conf->num_of_avmulti); + pr_debug("av_h_conf: num of spdif: %u\n", hw_conf->num_of_spdif); for (i = 0; i < PS3AV_HEAD_MAX; i++) ps3av->head[i] = PS3AV_CMD_VIDEO_HEAD_A + i; for (i = 0; i < PS3AV_OPT_PORT_MAX; i++) ps3av->opt_port[i] = PS3AV_CMD_AVPORT_SPDIF_0 + i; - for (i = 0; i < ps3av->av_hw_conf.num_of_hdmi; i++) + for (i = 0; i < hw_conf->num_of_hdmi; i++) ps3av->av_port[i] = PS3AV_CMD_AVPORT_HDMI_0 + i; - for (j = 0; j < ps3av->av_hw_conf.num_of_avmulti; j++) + for (j = 0; j < hw_conf->num_of_avmulti; j++) ps3av->av_port[i + j] = PS3AV_CMD_AVPORT_AVMULTI_0 + j; - for (k = 0; k < ps3av->av_hw_conf.num_of_spdif; k++) + for (k = 0; k < hw_conf->num_of_spdif; k++) ps3av->av_port[i + j + k] = PS3AV_CMD_AVPORT_SPDIF_0 + k; /* set all audio port */ @@ -738,7 +839,7 @@ static int ps3av_get_hw_conf(struct ps3av *ps3av) } /* set mode using id */ -int ps3av_set_video_mode(u32 id, int boot) +int ps3av_set_video_mode(int id) { int size; u32 option; @@ -751,8 +852,8 @@ int ps3av_set_video_mode(u32 id, int boot) /* auto mode */ option = id & ~PS3AV_MODE_MASK; - if ((id & PS3AV_MODE_MASK) == 0) { - id = ps3av_auto_videomode(&ps3av->av_hw_conf, boot); + if ((id & PS3AV_MODE_MASK) == PS3AV_MODE_AUTO) { + id = ps3av_auto_videomode(&ps3av->av_hw_conf); if (id < 1) { printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); return -EINVAL; @@ -769,74 +870,20 @@ int ps3av_set_video_mode(u32 id, int boot) return 0; } - EXPORT_SYMBOL_GPL(ps3av_set_video_mode); -int ps3av_get_auto_mode(int boot) +int ps3av_get_auto_mode(void) { - return ps3av_auto_videomode(&ps3av->av_hw_conf, boot); + return ps3av_auto_videomode(&ps3av->av_hw_conf); } - EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); -int ps3av_set_mode(u32 id, int boot) -{ - int res; - - res = ps3av_set_video_mode(id, boot); - if (res) - return res; - - res = ps3av_set_audio_mode(PS3AV_CMD_AUDIO_NUM_OF_CH_2, - PS3AV_CMD_AUDIO_FS_48K, - PS3AV_CMD_AUDIO_WORD_BITS_16, - PS3AV_CMD_AUDIO_FORMAT_PCM, - PS3AV_CMD_AUDIO_SOURCE_SERIAL); - if (res) - return res; - - return 0; -} - -EXPORT_SYMBOL_GPL(ps3av_set_mode); - int ps3av_get_mode(void) { return ps3av ? ps3av->ps3av_mode : 0; } - EXPORT_SYMBOL_GPL(ps3av_get_mode); -int ps3av_get_scanmode(int id) -{ - int size; - - id = id & PS3AV_MODE_MASK; - size = ARRAY_SIZE(video_mode_table); - if (id > size - 1 || id < 0) { - printk(KERN_ERR "%s: invalid mode %d\n", __func__, id); - return -EINVAL; - } - return video_mode_table[id].interlace; -} - -EXPORT_SYMBOL_GPL(ps3av_get_scanmode); - -int ps3av_get_refresh_rate(int id) -{ - int size; - - id = id & PS3AV_MODE_MASK; - size = ARRAY_SIZE(video_mode_table); - if (id > size - 1 || id < 0) { - printk(KERN_ERR "%s: invalid mode %d\n", __func__, id); - return -EINVAL; - } - return video_mode_table[id].freq; -} - -EXPORT_SYMBOL_GPL(ps3av_get_refresh_rate); - /* get resolution by video_mode */ int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres) { @@ -852,7 +899,6 @@ int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres) *yres = video_mode_table[id].y; return 0; } - EXPORT_SYMBOL_GPL(ps3av_video_mode2res); /* mute */ @@ -861,39 +907,35 @@ int ps3av_video_mute(int mute) return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON : PS3AV_CMD_MUTE_OFF); } - EXPORT_SYMBOL_GPL(ps3av_video_mute); -int ps3av_audio_mute(int mute) +/* mute analog output only */ +int ps3av_audio_mute_analog(int mute) { - return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON - : PS3AV_CMD_MUTE_OFF); -} + int i, res; -EXPORT_SYMBOL_GPL(ps3av_audio_mute); - -void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), - void *flip_data) -{ - mutex_lock(&ps3av->mutex); - ps3av->flip_ctl = flip_ctl; - ps3av->flip_data = flip_data; - mutex_unlock(&ps3av->mutex); + for (i = 0; i < ps3av->av_hw_conf.num_of_avmulti; i++) { + res = ps3av_cmd_av_audio_mute(1, + &ps3av->av_port[i + ps3av->av_hw_conf.num_of_hdmi], + mute); + if (res < 0) + return -1; + } + return 0; } -EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl); +EXPORT_SYMBOL_GPL(ps3av_audio_mute_analog); -void ps3av_flip_ctl(int on) +int ps3av_audio_mute(int mute) { - mutex_lock(&ps3av->mutex); - if (ps3av->flip_ctl) - ps3av->flip_ctl(on, ps3av->flip_data); - mutex_unlock(&ps3av->mutex); + return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON + : PS3AV_CMD_MUTE_OFF); } +EXPORT_SYMBOL_GPL(ps3av_audio_mute); static int ps3av_probe(struct ps3_system_bus_device *dev) { int res; - u32 id; + int id; dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); dev_dbg(&dev->core, " timeout=%d\n", timeout); @@ -908,15 +950,17 @@ static int ps3av_probe(struct ps3_system_bus_device *dev) return -ENOMEM; mutex_init(&ps3av->mutex); - ps3av->ps3av_mode = 0; + ps3av->ps3av_mode = PS3AV_MODE_AUTO; ps3av->dev = dev; INIT_WORK(&ps3av->work, ps3avd); init_completion(&ps3av->done); complete(&ps3av->done); ps3av->wq = create_singlethread_workqueue("ps3avd"); - if (!ps3av->wq) + if (!ps3av->wq) { + res = -ENOMEM; goto fail; + } switch (ps3_os_area_get_av_multi_out()) { case PS3_PARAM_AV_MULTI_OUT_NTSC: @@ -941,7 +985,20 @@ static int ps3av_probe(struct ps3_system_bus_device *dev) res); ps3av_get_hw_conf(ps3av); - id = ps3av_auto_videomode(&ps3av->av_hw_conf, 1); + +#ifdef CONFIG_FB + if (fb_mode_option && !strcmp(fb_mode_option, "safe")) + safe_mode = 1; +#endif /* CONFIG_FB */ + id = ps3av_auto_videomode(&ps3av->av_hw_conf); + if (id < 0) { + printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); + res = -EINVAL; + goto fail; + } + + safe_mode = 0; + mutex_lock(&ps3av->mutex); ps3av->ps3av_mode = id; mutex_unlock(&ps3av->mutex); @@ -953,7 +1010,7 @@ static int ps3av_probe(struct ps3_system_bus_device *dev) fail: kfree(ps3av); ps3av = NULL; - return -ENOMEM; + return res; } static int ps3av_remove(struct ps3_system_bus_device *dev) @@ -986,7 +1043,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; diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index f72f5ddf18e..f555fedd507 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -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" @@ -512,7 +513,6 @@ static const u32 ps3av_ns_table[][5] = { static void ps3av_cnv_ns(u8 *ns, u32 fs, u32 video_vid) { u32 av_vid, ns_val; - u8 *p = ns; int d; d = ns_val = 0; @@ -551,24 +551,22 @@ static void ps3av_cnv_ns(u8 *ns, u32 fs, u32 video_vid) else ns_val = ps3av_ns_table[PS3AV_CMD_AUDIO_FS_44K-BASE][d]; - *p++ = ns_val & 0x000000FF; - *p++ = (ns_val & 0x0000FF00) >> 8; - *p = (ns_val & 0x00FF0000) >> 16; + *ns++ = ns_val & 0x000000FF; + *ns++ = (ns_val & 0x0000FF00) >> 8; + *ns = (ns_val & 0x00FF0000) >> 16; } #undef BASE static u8 ps3av_cnv_enable(u32 source, const u8 *enable) { - const u8 *p; u8 ret = 0; if (source == PS3AV_CMD_AUDIO_SOURCE_SPDIF) { ret = 0x03; } else if (source == PS3AV_CMD_AUDIO_SOURCE_SERIAL) { - p = enable; - ret = ((p[0] << 4) + (p[1] << 5) + (p[2] << 6) + (p[3] << 7)) | - 0x01; + ret = ((enable[0] << 4) + (enable[1] << 5) + (enable[2] << 6) + + (enable[3] << 7)) | 0x01; } else printk(KERN_ERR "%s failed, source:%x\n", __func__, source); return ret; @@ -576,11 +574,9 @@ static u8 ps3av_cnv_enable(u32 source, const u8 *enable) static u8 ps3av_cnv_fifomap(const u8 *map) { - const u8 *p; u8 ret = 0; - p = map; - ret = p[0] + (p[1] << 2) + (p[2] << 4) + (p[3] << 6); + ret = map[0] + (map[1] << 2) + (map[2] << 4) + (map[3] << 6); return ret; } @@ -665,9 +661,10 @@ u32 ps3av_cmd_set_av_audio_param(void *p, u32 port, } /* default cs val */ -static const u8 ps3av_mode_cs_info[] = { +u8 ps3av_mode_cs_info[] = { 0x00, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00 }; +EXPORT_SYMBOL_GPL(ps3av_mode_cs_info); #define CS_44 0x00 #define CS_48 0x02 @@ -682,7 +679,7 @@ void ps3av_cmd_set_audio_mode(struct ps3av_pkt_audio_mode *audio, u32 avport, u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) { - int spdif_through, spdif_bitstream; + int spdif_through; int i; if (!(ch | fs | format | word_bits | source)) { @@ -692,7 +689,6 @@ void ps3av_cmd_set_audio_mode(struct ps3av_pkt_audio_mode *audio, u32 avport, format = PS3AV_CMD_AUDIO_FORMAT_PCM; source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; } - spdif_through = spdif_bitstream = 0; /* XXX not supported */ /* audio mode */ memset(audio, 0, sizeof(*audio)); @@ -782,16 +778,17 @@ void ps3av_cmd_set_audio_mode(struct ps3av_pkt_audio_mode *audio, u32 avport, break; } + /* non-audio bit */ + spdif_through = audio->audio_cs_info[0] & 0x02; + /* pass through setting */ if (spdif_through && (avport == PS3AV_CMD_AVPORT_SPDIF_0 || - avport == PS3AV_CMD_AVPORT_SPDIF_1)) { + avport == PS3AV_CMD_AVPORT_SPDIF_1 || + avport == PS3AV_CMD_AVPORT_HDMI_0 || + avport == PS3AV_CMD_AVPORT_HDMI_1)) { audio->audio_word_bits = PS3AV_CMD_AUDIO_WORD_BITS_16; - audio->audio_source = PS3AV_CMD_AUDIO_SOURCE_SPDIF; - if (spdif_bitstream) { - audio->audio_format = PS3AV_CMD_AUDIO_FORMAT_BITSTREAM; - audio->audio_cs_info[0] |= CS_BIT; - } + audio->audio_format = PS3AV_CMD_AUDIO_FORMAT_BITSTREAM; } } @@ -868,7 +865,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) { int res; - ps3av_flip_ctl(0); /* flip off */ + mutex_lock(&ps3_gpu_mutex); /* avb packet */ res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb), @@ -882,7 +879,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) res); out: - ps3av_flip_ctl(1); /* flip on */ + mutex_unlock(&ps3_gpu_mutex); return res; } @@ -927,72 +924,6 @@ int ps3av_cmd_video_get_monitor_info(struct ps3av_pkt_av_get_monitor_info *info, return res; } -#ifdef PS3AV_DEBUG -void ps3av_cmd_av_hw_conf_dump(const struct ps3av_pkt_av_get_hw_conf *hw_conf) -{ - printk("av_h_conf:num of hdmi:%d\n", hw_conf->num_of_hdmi); - printk("av_h_conf:num of avmulti:%d\n", hw_conf->num_of_avmulti); - printk("av_h_conf:num of spdif:%d\n", hw_conf->num_of_spdif); -} - -void ps3av_cmd_av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info *monitor_info) -{ - const struct ps3av_info_monitor *info = &monitor_info->info; - const struct ps3av_info_audio *audio = info->audio; - int i; - - printk("Monitor Info: size%d\n", monitor_info->send_hdr.size); - - printk("avport:%02x\n", info->avport); - printk("monitor_id:"); - for (i = 0; i < 10; i++) - printk("%02x ", info->monitor_id[i]); - printk("\nmonitor_type:%02x\n", info->monitor_type); - printk("monitor_name:"); - for (i = 0; i < 16; i++) - printk("%c", info->monitor_name[i]); - - /* resolution */ - printk("\nresolution_60: bits:%08x native:%08x\n", - info->res_60.res_bits, info->res_60.native); - printk("resolution_50: bits:%08x native:%08x\n", - info->res_50.res_bits, info->res_50.native); - printk("resolution_other: bits:%08x native:%08x\n", - info->res_other.res_bits, info->res_other.native); - printk("resolution_vesa: bits:%08x native:%08x\n", - info->res_vesa.res_bits, info->res_vesa.native); - - /* color space */ - printk("color space rgb:%02x\n", info->cs.rgb); - printk("color space yuv444:%02x\n", info->cs.yuv444); - printk("color space yuv422:%02x\n", info->cs.yuv422); - - /* color info */ - printk("color info red:X %04x Y %04x\n", - info->color.red_x, info->color.red_y); - printk("color info green:X %04x Y %04x\n", - info->color.green_x, info->color.green_y); - printk("color info blue:X %04x Y %04x\n", - info->color.blue_x, info->color.blue_y); - printk("color info white:X %04x Y %04x\n", - info->color.white_x, info->color.white_y); - printk("color info gamma: %08x\n", info->color.gamma); - - /* other info */ - printk("supported_AI:%02x\n", info->supported_ai); - printk("speaker_info:%02x\n", info->speaker_info); - printk("num of audio:%02x\n", info->num_of_audio_block); - - /* audio block */ - for (i = 0; i < info->num_of_audio_block; i++) { - printk("audio[%d] type:%02x max_ch:%02x fs:%02x sbit:%02x\n", - i, audio->type, audio->max_num_of_ch, audio->fs, - audio->sbit); - audio++; - } -} -#endif /* PS3AV_DEBUG */ - #define PS3AV_AV_LAYOUT_0 (PS3AV_CMD_AV_LAYOUT_32 \ | PS3AV_CMD_AV_LAYOUT_44 \ | PS3AV_CMD_AV_LAYOUT_48) diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c index 3a9824e3b25..8c3f5adf1bc 100644 --- a/drivers/ps3/ps3stor_lib.c +++ b/drivers/ps3/ps3stor_lib.c @@ -19,10 +19,70 @@ */ #include <linux/dma-mapping.h> +#include <linux/module.h> #include <asm/lv1call.h> #include <asm/ps3stor.h> +/* + * A workaround for flash memory I/O errors when the internal hard disk + * has not been formatted for OtherOS use. Delay disk close until flash + * memory is closed. + */ + +static struct ps3_flash_workaround { + int flash_open; + int disk_open; + struct ps3_system_bus_device *disk_sbd; +} ps3_flash_workaround; + +static int ps3stor_open_hv_device(struct ps3_system_bus_device *sbd) +{ + int error = ps3_open_hv_device(sbd); + + if (error) + return error; + + if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH) + ps3_flash_workaround.flash_open = 1; + + if (sbd->match_id == PS3_MATCH_ID_STOR_DISK) + ps3_flash_workaround.disk_open = 1; + + return 0; +} + +static int ps3stor_close_hv_device(struct ps3_system_bus_device *sbd) +{ + int error; + + if (sbd->match_id == PS3_MATCH_ID_STOR_DISK + && ps3_flash_workaround.disk_open + && ps3_flash_workaround.flash_open) { + ps3_flash_workaround.disk_sbd = sbd; + return 0; + } + + error = ps3_close_hv_device(sbd); + + if (error) + return error; + + if (sbd->match_id == PS3_MATCH_ID_STOR_DISK) + ps3_flash_workaround.disk_open = 0; + + if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH) { + ps3_flash_workaround.flash_open = 0; + + if (ps3_flash_workaround.disk_sbd) { + ps3_close_hv_device(ps3_flash_workaround.disk_sbd); + ps3_flash_workaround.disk_open = 0; + ps3_flash_workaround.disk_sbd = NULL; + } + } + + return 0; +} static int ps3stor_probe_access(struct ps3_storage_device *dev) { @@ -66,11 +126,11 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev) if (n > 1) dev_info(&dev->sbd.core, "%s:%u: %lu accessible regions found. Only the first " - "one will be used", + "one will be used\n", __func__, __LINE__, n); dev->region_idx = __ffs(dev->accessible_regions); dev_info(&dev->sbd.core, - "First accessible region has index %u start %lu size %lu\n", + "First accessible region has index %u start %llu size %llu\n", dev->region_idx, dev->regions[dev->region_idx].start, dev->regions[dev->region_idx].size); @@ -90,7 +150,7 @@ int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler) int error, res, alignment; enum ps3_dma_page_size page_size; - error = ps3_open_hv_device(&dev->sbd); + error = ps3stor_open_hv_device(&dev->sbd); if (error) { dev_err(&dev->sbd.core, "%s:%u: ps3_open_hv_device failed %d\n", __func__, @@ -107,7 +167,7 @@ int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler) goto fail_close_device; } - error = request_irq(dev->irq, handler, IRQF_DISABLED, + error = request_irq(dev->irq, handler, 0, dev->sbd.core.driver->name, dev); if (error) { dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n", @@ -166,7 +226,7 @@ fail_free_irq: fail_sb_event_receive_port_destroy: ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); fail_close_device: - ps3_close_hv_device(&dev->sbd); + ps3stor_close_hv_device(&dev->sbd); fail: return error; } @@ -193,7 +253,7 @@ void ps3stor_teardown(struct ps3_storage_device *dev) "%s:%u: destroy event receive port failed %d\n", __func__, __LINE__, error); - error = ps3_close_hv_device(&dev->sbd); + error = ps3stor_close_hv_device(&dev->sbd); if (error) dev_err(&dev->sbd.core, "%s:%u: ps3_close_hv_device failed %d\n", __func__, @@ -220,7 +280,7 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, const char *op = write ? "write" : "read"; int res; - dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n", + dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n", __func__, __LINE__, op, sectors, start_sector); init_completion(&dev->done); @@ -238,7 +298,7 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, wait_for_completion(&dev->done); if (dev->lv1_status) { - dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__, + dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, __LINE__, op, dev->lv1_status); return dev->lv1_status; } @@ -268,7 +328,7 @@ u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1, { int res; - dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%lx\n", __func__, + dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%llx\n", __func__, __LINE__, cmd); init_completion(&dev->done); @@ -277,19 +337,19 @@ u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1, arg2, arg3, arg4, &dev->tag); if (res) { dev_err(&dev->sbd.core, - "%s:%u: send_device_command 0x%lx failed %d\n", + "%s:%u: send_device_command 0x%llx failed %d\n", __func__, __LINE__, cmd, res); return -1; } wait_for_completion(&dev->done); if (dev->lv1_status) { - dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx failed 0x%lx\n", + dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx failed 0x%llx\n", __func__, __LINE__, cmd, dev->lv1_status); return dev->lv1_status; } - dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx completed\n", __func__, + dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx completed\n", __func__, __LINE__, cmd); return 0; diff --git a/drivers/ps3/sys-manager-core.c b/drivers/ps3/sys-manager-core.c index 31648f7d9ae..0e41737ea83 100644 --- a/drivers/ps3/sys-manager-core.c +++ b/drivers/ps3/sys-manager-core.c @@ -19,6 +19,8 @@ */ #include <linux/kernel.h> +#include <linux/export.h> +#include <asm/lv1call.h> #include <asm/ps3.h> /** @@ -50,10 +52,7 @@ void ps3_sys_manager_power_off(void) if (ps3_sys_manager_ops.power_off) ps3_sys_manager_ops.power_off(ps3_sys_manager_ops.dev); - printk(KERN_EMERG "System Halted, OK to turn off power\n"); - local_irq_disable(); - while (1) - (void)0; + ps3_sys_manager_halt(); } void ps3_sys_manager_restart(void) @@ -61,8 +60,14 @@ void ps3_sys_manager_restart(void) if (ps3_sys_manager_ops.restart) ps3_sys_manager_ops.restart(ps3_sys_manager_ops.dev); - printk(KERN_EMERG "System Halted, OK to turn off power\n"); + ps3_sys_manager_halt(); +} + +void ps3_sys_manager_halt(void) +{ + pr_emerg("System Halted, OK to turn off power\n"); local_irq_disable(); while (1) - (void)0; + lv1_pause(1); } + |
