From 3d5134ee8341bffc4f539049abb9e90d469b448d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 4 Jun 2007 15:15:36 +1000 Subject: [POWERPC] Rewrite IO allocation & mapping on powerpc64 This rewrites pretty much from scratch the handling of MMIO and PIO space allocations on powerpc64. The main goals are: - Get rid of imalloc and use more common code where possible - Simplify the current mess so that PIO space is allocated and mapped in a single place for PCI bridges - Handle allocation constraints of PIO for all bridges including hot plugged ones within the 2GB space reserved for IO ports, so that devices on hotplugged busses will now work with drivers that assume IO ports fit in an int. - Cleanup and separate tracking of the ISA space in the reserved low 64K of IO space. No ISA -> Nothing mapped there. I booted a cell blade with IDE on PIO and MMIO and a dual G5 so far, that's it :-) With this patch, all allocations are done using the code in mm/vmalloc.c, though we use the low level __get_vm_area with explicit start/stop constraints in order to manage separate areas for vmalloc/vmap, ioremap, and PCI IOs. This greatly simplifies a lot of things, as you can see in the diffstat of that patch :-) A new pair of functions pcibios_map/unmap_io_space() now replace all of the previous code that used to manipulate PCI IOs space. The allocation is done at mapping time, which is now called from scan_phb's, just before the devices are probed (instead of after, which is by itself a bug fix). The only other caller is the PCI hotplug code for hot adding PCI-PCI bridges (slots). imalloc is gone, as is the "sub-allocation" thing, but I do beleive that hotplug should still work in the sense that the space allocation is always done by the PHB, but if you unmap a child bus of this PHB (which seems to be possible), then the code should properly tear down all the HPTE mappings for that area of the PHB allocated IO space. I now always reserve the first 64K of IO space for the bridge with the ISA bus on it. I have moved the code for tracking ISA in a separate file which should also make it smarter if we ever are capable of hot unplugging or re-plugging an ISA bridge. This should have a side effect on platforms like powermac where VGA IOs will no longer work. This is done on purpose though as they would have worked semi-randomly before. The idea at this point is to isolate drivers that might need to access those and fix them by providing a proper function to obtain an offset to the legacy IOs of a given bus. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/pci/hotplug/rpadlpar_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index bb3c101c2c5a..deb6b5e35feb 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -159,8 +159,8 @@ static void dlpar_pci_add_bus(struct device_node *dn) /* Claim new bus resources */ pcibios_claim_one_bus(dev->bus); - /* ioremap() for child bus, which may or may not succeed */ - remap_bus_range(dev->subordinate); + /* Map IO space for child bus, which may or may not succeed */ + pcibios_map_io_space(dev->subordinate); /* Add new devices to global lists. Register in proc, sysfs. */ pci_bus_add_devices(phb->bus); @@ -390,7 +390,7 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) } else pcibios_remove_pci_devices(bus); - if (unmap_bus_range(bus)) { + if (pcibios_unmap_io_space(bus)) { printk(KERN_ERR "%s: failed to unmap bus range\n", __FUNCTION__); return -ERANGE; -- cgit v1.2.3 From 848cfdc5c1cd2163ba0c9a6490d9adcb7a7c3518 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Sat, 16 Jun 2007 07:18:14 +1000 Subject: [POWERPC] PS3: Use __maybe_unused Change the PS3 debug routines from using the GCC specific '__attribute__ ((unused))' to the preprocessor macro __maybe_unused. Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/ps3/interrupt.c | 4 ++-- arch/powerpc/platforms/ps3/time.c | 2 +- drivers/ps3/vuart.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index ec9030dbb5f1..b050fc4cd883 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -533,7 +533,7 @@ static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, *p & 0xffff); } -static void __attribute__ ((unused)) _dump_256_bmp(const char *header, +static void __maybe_unused _dump_256_bmp(const char *header, const u64 *p, unsigned cpu, const char* func, int line) { pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", @@ -552,7 +552,7 @@ static void _dump_bmp(struct ps3_private* pd, const char* func, int line) } #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, +static void __maybe_unused _dump_mask(struct ps3_private *pd, const char* func, int line) { unsigned long flags; diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c index 1bae8b19b363..802a9ccacb5e 100644 --- a/arch/powerpc/platforms/ps3/time.c +++ b/arch/powerpc/platforms/ps3/time.c @@ -39,7 +39,7 @@ static void _dump_tm(const struct rtc_time *tm, const char* func, int line) } #define dump_time(_a) _dump_time(_a, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_time(int time, const char* func, +static void __maybe_unused _dump_time(int time, const char *func, int line) { struct rtc_time tm; diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index ec2d36a1bc67..5333fb2f0d86 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -83,7 +83,7 @@ struct ports_bmp { } __attribute__ ((aligned (32))); #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_ports_bmp( +static void __maybe_unused _dump_ports_bmp( const struct ports_bmp* bmp, const char* func, int line) { pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); @@ -107,7 +107,7 @@ static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id, } #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) -static void __attribute__ ((unused)) _dump_port_params(unsigned int port_number, +static void __maybe_unused _dump_port_params(unsigned int port_number, const char* func, int line) { #if defined(DEBUG) -- cgit v1.2.3 From dc23fba7063867ed745cb6f0bd27a0dc5f558dbc Mon Sep 17 00:00:00 2001 From: Masashi Kimoto Date: Sat, 16 Jun 2007 07:19:10 +1000 Subject: [POWERPC] PS3: Add support for HDMI RGB Full Range mode Add support for HDMI RGB Full Range mode, which is available on system software 1.80 or newer. CC: Masashi Kimoto Signed-off-by: Geert Uytterhoeven Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- drivers/ps3/ps3av_cmd.c | 16 ++++++++++++++++ include/asm-powerpc/ps3av.h | 12 +++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 0145ea173c42..7c4fb264dda2 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -143,6 +143,14 @@ static u32 ps3av_vid_video2av(int vid) return PS3AV_CMD_AV_VID_480P; } +static int ps3av_hdmi_range(void) +{ + if (ps3_compare_firmware_version(1, 8, 0) < 0) + return 0; + else + return 1; /* supported */ +} + int ps3av_cmd_init(void) { int res; @@ -350,6 +358,10 @@ u32 ps3av_cmd_set_av_video_cs(void *p, u32 avport, int video_vid, int cs_out, /* should be same as video_mode.video_cs_out */ av_video_cs->av_cs_in = ps3av_cs_video2av(PS3AV_CMD_VIDEO_CS_RGB_8); av_video_cs->bitlen_out = ps3av_cs_video2av_bitlen(cs_out); + if ((id & PS3AV_MODE_WHITE) && ps3av_hdmi_range()) + av_video_cs->super_white = PS3AV_CMD_AV_SUPER_WHITE_ON; + else /* default off */ + av_video_cs->super_white = PS3AV_CMD_AV_SUPER_WHITE_OFF; av_video_cs->aspect = aspect; if (id & PS3AV_MODE_DITHER) { av_video_cs->dither = PS3AV_CMD_AV_DITHER_ON @@ -392,6 +404,10 @@ u32 ps3av_cmd_set_video_mode(void *p, u32 head, int video_vid, int video_fmt, video_mode->pitch = video_mode->width * 4; /* line_length */ video_mode->video_out_format = PS3AV_CMD_VIDEO_OUT_FORMAT_RGB_12BIT; video_mode->video_format = ps3av_video_fmt_table[video_fmt].format; + if ((id & PS3AV_MODE_COLOR) && ps3av_hdmi_range()) + video_mode->video_cl_cnv = PS3AV_CMD_VIDEO_CL_CNV_DISABLE_LUT; + else /* default enable */ + video_mode->video_cl_cnv = PS3AV_CMD_VIDEO_CL_CNV_ENABLE_LUT; video_mode->video_order = ps3av_video_fmt_table[video_fmt].order; pr_debug("%s: video_mode:vid:%x width:%d height:%d pitch:%d out_format:%d format:%x order:%x\n", diff --git a/include/asm-powerpc/ps3av.h b/include/asm-powerpc/ps3av.h index 9efc40f1c778..7f5948efca9b 100644 --- a/include/asm-powerpc/ps3av.h +++ b/include/asm-powerpc/ps3av.h @@ -159,6 +159,9 @@ #define PS3AV_CMD_VIDEO_FMT_X8R8G8B8 0x0000 /* video_out_format */ #define PS3AV_CMD_VIDEO_OUT_FORMAT_RGB_12BIT 0x0000 +/* video_cl_cnv */ +#define PS3AV_CMD_VIDEO_CL_CNV_ENABLE_LUT 0x0000 +#define PS3AV_CMD_VIDEO_CL_CNV_DISABLE_LUT 0x0010 /* video_sync */ #define PS3AV_CMD_VIDEO_SYNC_VSYNC 0x0001 #define PS3AV_CMD_VIDEO_SYNC_CSYNC 0x0004 @@ -311,6 +314,8 @@ #define PS3AV_MODE_MASK 0x000F #define PS3AV_MODE_HDCP_OFF 0x1000 /* Retail PS3 product doesn't support this */ #define PS3AV_MODE_DITHER 0x0800 +#define PS3AV_MODE_COLOR 0x0400 +#define PS3AV_MODE_WHITE 0x0200 #define PS3AV_MODE_FULL 0x0080 #define PS3AV_MODE_DVI 0x0040 #define PS3AV_MODE_RGB 0x0020 @@ -529,9 +534,9 @@ struct ps3av_pkt_video_mode { u32 video_out_format; /* in: out format */ u32 video_format; /* in: input frame buffer format */ u8 reserved3; - u8 reserved4; + u8 video_cl_cnv; /* in: color conversion */ u16 video_order; /* in: input RGB order */ - u32 reserved5; + u32 reserved4; }; /* video: format */ @@ -539,7 +544,8 @@ struct ps3av_pkt_video_format { struct ps3av_send_hdr send_hdr; u32 video_head; /* in: head */ u32 video_format; /* in: frame buffer format */ - u16 reserved; + u8 reserved; + u8 video_cl_cnv; /* in: color conversion */ u16 video_order; /* in: input RGB order */ }; -- cgit v1.2.3 From 7626e78d29651d3075e88f233c0632867ea6a35c Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Sat, 16 Jun 2007 08:01:06 +1000 Subject: [POWERPC] PS3: Vuart rework PS3 vuart updates to reflect the new PS3 unified device support. - Move vuart devices to the PS3 system bus. - Replace use of ps3_vuart_port_device with ps3_system_bus_device. - Make the PS3 vuart bus driver a loadable module. - Add remove() and shutdown() routines. - Move ps3_vuart_work into ps3_vuart_port_priv.tx_list. - Remove redundant spinlock ps3_vuart_work.lock. - No longer free ps3_vuart_port_device.priv on shutdown. - Cleanup Kconfig defs. - Export symbols needed for modular port drivers. - Arrange to use port numbers found in repository. - Fix bugs in ps3_vuart_read_async() and polled reading - Cleanup handling of shared interrupt with ps3_vuart_bus_interrupt_get() and ps3_vuart_bus_interrupt_put() - Add more comments to vuart.c. Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/ps3/Kconfig | 21 +- arch/powerpc/platforms/ps3/interrupt.c | 2 + drivers/ps3/vuart.c | 813 ++++++++++++++++++++------------- drivers/ps3/vuart.h | 71 +-- include/asm-powerpc/ps3.h | 17 - 5 files changed, 520 insertions(+), 404 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 40f0008af4d1..b5122a764813 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -73,18 +73,12 @@ config PS3_USE_LPAR_ADDR config PS3_VUART depends on PPC_PS3 - bool "PS3 Virtual UART support" if PS3_ADVANCED - default y - help - Include support for the PS3 Virtual UART. - - This support is required for several system services - including the System Manager and AV Settings. In - general, all users will say Y. + tristate config PS3_PS3AV + depends on PPC_PS3 tristate "PS3 AV settings driver" if PS3_ADVANCED - depends on PS3_VUART + select PS3_VUART default y help Include support for the PS3 AV Settings driver. @@ -93,13 +87,14 @@ config PS3_PS3AV general, all users will say Y or M. config PS3_SYS_MANAGER - bool "PS3 System Manager driver" if PS3_ADVANCED - depends on PS3_VUART - default y + depends on PPC_PS3 + tristate "PS3 System Manager driver" if PS3_ADVANCED + select PS3_VUART + default m help Include support for the PS3 System Manager. This support is required for system control. In - general, all users will say Y. + general, all users will say Y or M. endmenu diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 51141dc06f91..99a0826c8d90 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -564,6 +564,7 @@ int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, return result; } +EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup); int ps3_vuart_irq_destroy(unsigned int virq) { @@ -583,6 +584,7 @@ int ps3_vuart_irq_destroy(unsigned int virq) return result; } +EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy); /** * ps3_spe_irq_setup - Setup an spe virq. diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index 5333fb2f0d86..bea25a1391ee 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -70,6 +70,34 @@ enum vuart_interrupt_mask { INTERRUPT_MASK_DISCONNECT = 4, }; +/** + * struct ps3_vuart_port_priv - private vuart device data. + */ + +struct ps3_vuart_port_priv { + u64 interrupt_mask; + + struct { + spinlock_t lock; + struct list_head head; + } tx_list; + struct { + struct ps3_vuart_work work; + unsigned long bytes_held; + spinlock_t lock; + struct list_head head; + } rx_list; + struct ps3_vuart_stats stats; +}; + +static struct ps3_vuart_port_priv *to_port_priv( + struct ps3_system_bus_device *dev) +{ + BUG_ON(!dev); + BUG_ON(!dev->driver_priv); + return (struct ps3_vuart_port_priv *)dev->driver_priv; +} + /** * struct ports_bmp - bitmap indicating ports needing service. * @@ -89,23 +117,6 @@ static void __maybe_unused _dump_ports_bmp( pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); } -static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id, - unsigned int *port_number) -{ - switch(match_id) { - case PS3_MATCH_ID_AV_SETTINGS: - *port_number = 0; - return 0; - case PS3_MATCH_ID_SYSTEM_MANAGER: - *port_number = 2; - return 0; - default: - WARN_ON(1); - *port_number = UINT_MAX; - return -EINVAL; - }; -} - #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) @@ -144,14 +155,14 @@ struct vuart_triggers { unsigned long tx; }; -int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, +int ps3_vuart_get_triggers(struct ps3_system_bus_device *dev, struct vuart_triggers *trig) { int result; unsigned long size; unsigned long val; - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_TX_TRIGGER, &trig->tx); if (result) { @@ -160,7 +171,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_BUF_SIZE, &size); if (result) { @@ -169,7 +180,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_TRIGGER, &val); if (result) { @@ -186,13 +197,13 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } -int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, +int ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx, unsigned int rx) { int result; unsigned long size; - result = lv1_set_virtual_uart_param(dev->priv->port_number, + result = lv1_set_virtual_uart_param(dev->port_number, PARAM_TX_TRIGGER, tx); if (result) { @@ -201,7 +212,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } - result = lv1_get_virtual_uart_param(dev->priv->port_number, + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_BUF_SIZE, &size); if (result) { @@ -210,7 +221,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } - result = lv1_set_virtual_uart_param(dev->priv->port_number, + result = lv1_set_virtual_uart_param(dev->port_number, PARAM_RX_TRIGGER, size - rx); if (result) { @@ -225,10 +236,12 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } -static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, +static int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev, u64 *bytes_waiting) { - int result = lv1_get_virtual_uart_param(dev->priv->port_number, + int result; + + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_RX_BYTES, bytes_waiting); if (result) @@ -240,17 +253,24 @@ static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, return result; } -static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, +/** + * ps3_vuart_set_interrupt_mask - Enable/disable the port interrupt sources. + * @dev: The struct ps3_system_bus_device instance. + * @bmp: Logical OR of enum vuart_interrupt_mask values. A zero bit disables. + */ + +static int ps3_vuart_set_interrupt_mask(struct ps3_system_bus_device *dev, unsigned long mask) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); - dev->priv->interrupt_mask = mask; + priv->interrupt_mask = mask; - result = lv1_set_virtual_uart_param(dev->priv->port_number, - PARAM_INTERRUPT_MASK, dev->priv->interrupt_mask); + result = lv1_set_virtual_uart_param(dev->port_number, + PARAM_INTERRUPT_MASK, priv->interrupt_mask); if (result) dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", @@ -259,79 +279,96 @@ static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, return result; } -static int ps3_vuart_get_interrupt_status(struct ps3_vuart_port_device *dev, +static int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev, unsigned long *status) { + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 tmp; - int result = lv1_get_virtual_uart_param(dev->priv->port_number, + + result = lv1_get_virtual_uart_param(dev->port_number, PARAM_INTERRUPT_STATUS, &tmp); if (result) dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", __func__, __LINE__, ps3_result(result)); - *status = tmp & dev->priv->interrupt_mask; + *status = tmp & priv->interrupt_mask; dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", - __func__, __LINE__, dev->priv->interrupt_mask, tmp, *status); + __func__, __LINE__, priv->interrupt_mask, tmp, *status); return result; } -int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev) +int ps3_vuart_enable_interrupt_tx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | INTERRUPT_MASK_TX); } -int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev) +int ps3_vuart_enable_interrupt_rx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | INTERRUPT_MASK_RX); } -int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev) +int ps3_vuart_enable_interrupt_disconnect(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 + : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | INTERRUPT_MASK_DISCONNECT); } -int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev) +int ps3_vuart_disable_interrupt_tx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) - ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_TX) + ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask & ~INTERRUPT_MASK_TX) : 0; } -int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev) +int ps3_vuart_disable_interrupt_rx(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) - ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_RX) + ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask & ~INTERRUPT_MASK_RX) : 0; } -int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev) +int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev) { - return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) - ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + + return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) + ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask & ~INTERRUPT_MASK_DISCONNECT) : 0; } /** * ps3_vuart_raw_write - Low level write helper. + * @dev: The struct ps3_system_bus_device instance. * * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write. */ -static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, +static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, const void* buf, unsigned int bytes, unsigned long *bytes_written) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); - result = lv1_write_virtual_uart(dev->priv->port_number, + result = lv1_write_virtual_uart(dev->port_number, ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); if (result) { @@ -340,28 +377,30 @@ static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, return result; } - dev->priv->stats.bytes_written += *bytes_written; + priv->stats.bytes_written += *bytes_written; dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__, - *bytes_written, bytes, dev->priv->stats.bytes_written); + *bytes_written, bytes, priv->stats.bytes_written); return result; } /** * ps3_vuart_raw_read - Low level read helper. + * @dev: The struct ps3_system_bus_device instance. * * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read. */ -static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, +static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes, unsigned long *bytes_read) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); - result = lv1_read_virtual_uart(dev->priv->port_number, + result = lv1_read_virtual_uart(dev->port_number, ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); if (result) { @@ -370,25 +409,27 @@ static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, return result; } - dev->priv->stats.bytes_read += *bytes_read; + priv->stats.bytes_read += *bytes_read; dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, - *bytes_read, bytes, dev->priv->stats.bytes_read); + *bytes_read, bytes, priv->stats.bytes_read); return result; } /** * ps3_vuart_clear_rx_bytes - Discard bytes received. + * @dev: The struct ps3_system_bus_device instance. * @bytes: Max byte count to discard, zero = all pending. * * Used to clear pending rx interrupt source. Will not block. */ -void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, +void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, unsigned int bytes) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 bytes_waiting; void* tmp; @@ -418,8 +459,9 @@ void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, /* Don't include these bytes in the stats. */ - dev->priv->stats.bytes_read -= bytes_waiting; + priv->stats.bytes_read -= bytes_waiting; } +EXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes); /** * struct list_buffer - An element for a port device fifo buffer list. @@ -435,6 +477,7 @@ struct list_buffer { /** * ps3_vuart_write - the entry point for writing data to a port + * @dev: The struct ps3_system_bus_device instance. * * If the port is idle on entry as much of the incoming data is written to * the port as the port will accept. Otherwise a list buffer is created @@ -442,25 +485,26 @@ struct list_buffer { * then enqueued for transmision via the transmit interrupt. */ -int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, +int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, unsigned int bytes) { static unsigned long dbg_number; int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb; dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); - spin_lock_irqsave(&dev->priv->tx_list.lock, flags); + spin_lock_irqsave(&priv->tx_list.lock, flags); - if (list_empty(&dev->priv->tx_list.head)) { + if (list_empty(&priv->tx_list.head)) { unsigned long bytes_written; result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); if (result) { dev_dbg(&dev->core, @@ -478,7 +522,7 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, bytes -= bytes_written; buf += bytes_written; } else - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); @@ -491,29 +535,86 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, lb->tail = lb->data + bytes; lb->dbg_number = ++dbg_number; - spin_lock_irqsave(&dev->priv->tx_list.lock, flags); - list_add_tail(&lb->link, &dev->priv->tx_list.head); + spin_lock_irqsave(&priv->tx_list.lock, flags); + list_add_tail(&lb->link, &priv->tx_list.head); ps3_vuart_enable_interrupt_tx(dev); - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); return 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_write); + +/** + * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list. + * @dev: The struct ps3_system_bus_device instance. + * @bytes_queued: Number of bytes queued to the buffer list. + * + * Must be called with priv->rx_list.lock held. + */ + +static int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev, + u64 *bytes_queued) +{ + static unsigned long dbg_number; + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + struct list_buffer *lb; + u64 bytes; + + *bytes_queued = 0; + + result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); + BUG_ON(result); + + if (result) + return -EIO; + + if (!bytes) + return 0; + + /* Add some extra space for recently arrived data. */ + + bytes += 128; + + lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); + + if (!lb) + return -ENOMEM; + + ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); + + lb->head = lb->data; + lb->tail = lb->data + bytes; + lb->dbg_number = ++dbg_number; + + 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", + __func__, __LINE__, lb->dbg_number, bytes); + + *bytes_queued = bytes; + + return 0; +} /** - * ps3_vuart_read - the entry point for reading data from a port + * ps3_vuart_read - The entry point for reading data from a port. * - * If enough bytes to satisfy the request are held in the buffer list those - * bytes are dequeued and copied to the caller's buffer. Emptied list buffers - * are retiered. If the request cannot be statified by bytes held in the list - * buffers -EAGAIN is returned. + * Queue data waiting at the port, and if enough bytes to satisfy the request + * are held in the buffer list those bytes are dequeued and copied to the + * caller's buffer. Emptied list buffers are retiered. If the request cannot + * be statified by bytes held in the list buffers -EAGAIN is returned. */ -int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, +int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes) { + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb, *n; unsigned long bytes_read; @@ -521,30 +622,37 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); - spin_lock_irqsave(&dev->priv->rx_list.lock, flags); + spin_lock_irqsave(&priv->rx_list.lock, flags); - if (dev->priv->rx_list.bytes_held < bytes) { - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); - dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", - __func__, __LINE__, - bytes - dev->priv->rx_list.bytes_held); - return -EAGAIN; + /* Queue rx bytes here for polled reads. */ + + while (priv->rx_list.bytes_held < bytes) { + u64 tmp; + + result = ps3_vuart_queue_rx_bytes(dev, &tmp); + if (result || !tmp) { + dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", + __func__, __LINE__, + bytes - priv->rx_list.bytes_held); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); + return -EAGAIN; + } } - list_for_each_entry_safe(lb, n, &dev->priv->rx_list.head, link) { + list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) { bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); memcpy(buf, lb->head, bytes_read); buf += bytes_read; bytes -= bytes_read; - dev->priv->rx_list.bytes_held -= bytes_read; + priv->rx_list.bytes_held -= bytes_read; if (bytes_read < lb->tail - lb->head) { lb->head += bytes_read; dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh " "bytes\n", __func__, __LINE__, lb->dbg_number, bytes_read); - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } @@ -556,16 +664,32 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, kfree(lb); } - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_read); -int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, - unsigned int bytes) +/** + * ps3_vuart_work - Asynchronous read handler. + */ + +static void ps3_vuart_work(struct work_struct *work) +{ + struct ps3_system_bus_device *dev = + ps3_vuart_work_to_system_bus_dev(work); + struct ps3_vuart_port_driver *drv = + ps3_system_bus_dev_to_vuart_drv(dev); + + BUG_ON(!drv); + drv->work(dev); +} + +int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes) { + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; - if(dev->priv->work.trigger) { + if (priv->rx_list.work.trigger) { dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n", __func__, __LINE__); return -EAGAIN; @@ -573,30 +697,32 @@ int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, BUG_ON(!bytes); - PREPARE_WORK(&dev->priv->work.work, func); + PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work); - spin_lock_irqsave(&dev->priv->work.lock, flags); - if(dev->priv->rx_list.bytes_held >= bytes) { + 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", __func__, __LINE__, bytes); - schedule_work(&dev->priv->work.work); - spin_unlock_irqrestore(&dev->priv->work.lock, flags); + schedule_work(&priv->rx_list.work.work); + spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } - dev->priv->work.trigger = bytes; - spin_unlock_irqrestore(&dev->priv->work.lock, flags); + priv->rx_list.work.trigger = bytes; + spin_unlock_irqrestore(&priv->rx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); return 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_read_async); -void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) +void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev) { - dev->priv->work.trigger = 0; + to_port_priv(dev)->rx_list.work.trigger = 0; } +EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async); /** * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler @@ -606,18 +732,19 @@ void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) * adjusts the final list buffer state for a partial write. */ -static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) +static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev) { int result = 0; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb, *n; unsigned long bytes_total = 0; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - spin_lock_irqsave(&dev->priv->tx_list.lock, flags); + spin_lock_irqsave(&priv->tx_list.lock, flags); - list_for_each_entry_safe(lb, n, &dev->priv->tx_list.head, link) { + list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) { unsigned long bytes_written; @@ -651,7 +778,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) ps3_vuart_disable_interrupt_tx(dev); port_full: - spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); + spin_unlock_irqrestore(&priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", __func__, __LINE__, bytes_total); return result; @@ -665,60 +792,37 @@ port_full: * buffer list. Buffer list data is dequeued via ps3_vuart_read. */ -static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) +static int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev) { - static unsigned long dbg_number; - int result = 0; + int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; - struct list_buffer *lb; - unsigned long bytes; + u64 bytes; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); - - if (result) - return -EIO; - - BUG_ON(!bytes); - - /* Add some extra space for recently arrived data. */ - - bytes += 128; - - lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); + spin_lock_irqsave(&priv->rx_list.lock, flags); + result = ps3_vuart_queue_rx_bytes(dev, &bytes); - if (!lb) - return -ENOMEM; - - ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); - - lb->head = lb->data; - lb->tail = lb->data + bytes; - lb->dbg_number = ++dbg_number; - - spin_lock_irqsave(&dev->priv->rx_list.lock, flags); - list_add_tail(&lb->link, &dev->priv->rx_list.head); - dev->priv->rx_list.bytes_held += bytes; - spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); - - dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", - __func__, __LINE__, lb->dbg_number, bytes); + if (result) { + spin_unlock_irqrestore(&priv->rx_list.lock, flags); + return result; + } - spin_lock_irqsave(&dev->priv->work.lock, flags); - if(dev->priv->work.trigger - && dev->priv->rx_list.bytes_held >= dev->priv->work.trigger) { + if (priv->rx_list.work.trigger && priv->rx_list.bytes_held + >= priv->rx_list.work.trigger) { dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", - __func__, __LINE__, dev->priv->work.trigger); - dev->priv->work.trigger = 0; - schedule_work(&dev->priv->work.work); + __func__, __LINE__, priv->rx_list.work.trigger); + priv->rx_list.work.trigger = 0; + schedule_work(&priv->rx_list.work.work); } - spin_unlock_irqrestore(&dev->priv->work.lock, flags); - return 0; + + spin_unlock_irqrestore(&priv->rx_list.lock, flags); + return result; } static int ps3_vuart_handle_interrupt_disconnect( - struct ps3_vuart_port_device *dev) + struct ps3_system_bus_device *dev) { dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); BUG_ON("no support"); @@ -733,9 +837,10 @@ static int ps3_vuart_handle_interrupt_disconnect( * stage handler after one iteration. */ -static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) +static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev) { int result; + struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long status; result = ps3_vuart_get_interrupt_status(dev, &status); @@ -747,21 +852,21 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) status); if (status & INTERRUPT_MASK_DISCONNECT) { - dev->priv->stats.disconnect_interrupts++; + priv->stats.disconnect_interrupts++; result = ps3_vuart_handle_interrupt_disconnect(dev); if (result) ps3_vuart_disable_interrupt_disconnect(dev); } if (status & INTERRUPT_MASK_TX) { - dev->priv->stats.tx_interrupts++; + priv->stats.tx_interrupts++; result = ps3_vuart_handle_interrupt_tx(dev); if (result) ps3_vuart_disable_interrupt_tx(dev); } if (status & INTERRUPT_MASK_RX) { - dev->priv->stats.rx_interrupts++; + priv->stats.rx_interrupts++; result = ps3_vuart_handle_interrupt_rx(dev); if (result) ps3_vuart_disable_interrupt_rx(dev); @@ -771,11 +876,11 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) } struct vuart_bus_priv { - const struct ports_bmp bmp; + struct ports_bmp *bmp; unsigned int virq; struct semaphore probe_mutex; int use_count; - struct ps3_vuart_port_device *devices[PORT_COUNT]; + struct ps3_system_bus_device *devices[PORT_COUNT]; } static vuart_bus_priv; /** @@ -788,17 +893,16 @@ struct vuart_bus_priv { static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) { - struct vuart_bus_priv *bus_priv; + struct vuart_bus_priv *bus_priv = _private; - BUG_ON(!_private); - bus_priv = (struct vuart_bus_priv *)_private; + BUG_ON(!bus_priv); while (1) { unsigned int port; - dump_ports_bmp(&bus_priv->bmp); + dump_ports_bmp(bus_priv->bmp); - port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp.status); + port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp->status); if (port == BITS_PER_LONG) break; @@ -812,100 +916,144 @@ static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) return IRQ_HANDLED; } -static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv) +static int ps3_vuart_bus_interrupt_get(void) { int result; - struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_drv); - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - result = dev->match_id == drv->match_id; + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + vuart_bus_priv.use_count++; + + BUG_ON(vuart_bus_priv.use_count > 2); + + if (vuart_bus_priv.use_count != 1) { + return 0; + } + + BUG_ON(vuart_bus_priv.bmp); + + vuart_bus_priv.bmp = kzalloc(sizeof(struct ports_bmp), GFP_KERNEL); + + if (!vuart_bus_priv.bmp) { + pr_debug("%s:%d: kzalloc failed.\n", __func__, __LINE__); + result = -ENOMEM; + goto fail_bmp_malloc; + } + + result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, vuart_bus_priv.bmp, + &vuart_bus_priv.virq); + + if (result) { + pr_debug("%s:%d: ps3_vuart_irq_setup failed (%d)\n", + __func__, __LINE__, result); + result = -EPERM; + goto fail_alloc_irq; + } + + result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, + IRQF_DISABLED, "vuart", &vuart_bus_priv); - dev_info(&dev->core, "%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, - __LINE__, dev->match_id, dev->core.bus_id, drv->match_id, - drv->core.name, (result ? "match" : "miss")); + if (result) { + pr_debug("%s:%d: request_irq failed (%d)\n", + __func__, __LINE__, result); + goto fail_request_irq; + } + pr_debug(" <- %s:%d: ok\n", __func__, __LINE__); return result; + +fail_request_irq: + ps3_vuart_irq_destroy(vuart_bus_priv.virq); + vuart_bus_priv.virq = NO_IRQ; +fail_alloc_irq: + kfree(vuart_bus_priv.bmp); + vuart_bus_priv.bmp = NULL; +fail_bmp_malloc: + vuart_bus_priv.use_count--; + pr_debug(" <- %s:%d: failed\n", __func__, __LINE__); + return result; +} + +static int ps3_vuart_bus_interrupt_put(void) +{ + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + vuart_bus_priv.use_count--; + + BUG_ON(vuart_bus_priv.use_count < 0); + + if (vuart_bus_priv.use_count != 0) + return 0; + + free_irq(vuart_bus_priv.virq, &vuart_bus_priv); + + ps3_vuart_irq_destroy(vuart_bus_priv.virq); + vuart_bus_priv.virq = NO_IRQ; + + kfree(vuart_bus_priv.bmp); + vuart_bus_priv.bmp = NULL; + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; } -static int ps3_vuart_probe(struct device *_dev) +static int ps3_vuart_probe(struct ps3_system_bus_device *dev) { int result; - unsigned int port_number; - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - struct ps3_vuart_port_driver *drv = - to_ps3_vuart_port_driver(_dev->driver); + struct ps3_vuart_port_driver *drv; + struct ps3_vuart_port_priv *priv = NULL; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + drv = ps3_system_bus_dev_to_vuart_drv(dev); + + dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__, + drv->core.core.name); + BUG_ON(!drv); - down(&vuart_bus_priv.probe_mutex); + if (dev->port_number >= PORT_COUNT) { + BUG(); + return -EINVAL; + } - /* Setup vuart_bus_priv.devices[]. */ + down(&vuart_bus_priv.probe_mutex); - result = ps3_vuart_match_id_to_port(dev->match_id, - &port_number); + result = ps3_vuart_bus_interrupt_get(); - if (result) { - dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n", - __func__, __LINE__, dev->match_id); - result = -EINVAL; - goto fail_match; - } + if (result) + goto fail_setup_interrupt; - if (vuart_bus_priv.devices[port_number]) { + if (vuart_bus_priv.devices[dev->port_number]) { dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, - __LINE__, port_number); + __LINE__, dev->port_number); result = -EBUSY; - goto fail_match; + goto fail_busy; } - vuart_bus_priv.devices[port_number] = dev; + vuart_bus_priv.devices[dev->port_number] = dev; - /* Setup dev->priv. */ + /* Setup dev->driver_priv. */ - dev->priv = kzalloc(sizeof(struct ps3_vuart_port_priv), GFP_KERNEL); + dev->driver_priv = kzalloc(sizeof(struct ps3_vuart_port_priv), + GFP_KERNEL); - if (!dev->priv) { + if (!dev->driver_priv) { result = -ENOMEM; - goto fail_alloc; + goto fail_dev_malloc; } - dev->priv->port_number = port_number; - - INIT_LIST_HEAD(&dev->priv->tx_list.head); - spin_lock_init(&dev->priv->tx_list.lock); + priv = to_port_priv(dev); - INIT_LIST_HEAD(&dev->priv->rx_list.head); - spin_lock_init(&dev->priv->rx_list.lock); + INIT_LIST_HEAD(&priv->tx_list.head); + spin_lock_init(&priv->tx_list.lock); - INIT_WORK(&dev->priv->work.work, NULL); - spin_lock_init(&dev->priv->work.lock); - dev->priv->work.trigger = 0; - dev->priv->work.dev = dev; + INIT_LIST_HEAD(&priv->rx_list.head); + spin_lock_init(&priv->rx_list.lock); - if (++vuart_bus_priv.use_count == 1) { - - result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, - (void*)&vuart_bus_priv.bmp.status, &vuart_bus_priv.virq); - - if (result) { - dev_dbg(&dev->core, - "%s:%d: ps3_vuart_irq_setup failed (%d)\n", - __func__, __LINE__, result); - result = -EPERM; - goto fail_alloc_irq; - } - - result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, - IRQF_DISABLED, "vuart", &vuart_bus_priv); - - if (result) { - dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n", - __func__, __LINE__, result); - goto fail_request_irq; - } - } + INIT_WORK(&priv->rx_list.work.work, NULL); + priv->rx_list.work.trigger = 0; + priv->rx_list.work.dev = dev; /* clear stale pending interrupts */ @@ -936,150 +1084,158 @@ static int ps3_vuart_probe(struct device *_dev) fail_probe: ps3_vuart_set_interrupt_mask(dev, 0); -fail_request_irq: - ps3_vuart_irq_destroy(vuart_bus_priv.virq); - vuart_bus_priv.virq = NO_IRQ; -fail_alloc_irq: - --vuart_bus_priv.use_count; - kfree(dev->priv); - dev->priv = NULL; -fail_alloc: - vuart_bus_priv.devices[port_number] = NULL; -fail_match: + kfree(dev->driver_priv); + dev->driver_priv = NULL; +fail_dev_malloc: + vuart_bus_priv.devices[dev->port_number] = NULL; +fail_busy: + ps3_vuart_bus_interrupt_put(); +fail_setup_interrupt: up(&vuart_bus_priv.probe_mutex); - dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__); + dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__); return result; } -static int ps3_vuart_remove(struct device *_dev) +/** + * ps3_vuart_cleanup - common cleanup helper. + * @dev: The struct ps3_system_bus_device instance. + * + * Cleans interrupts and HV resources. Must be called with + * vuart_bus_priv.probe_mutex held. Used by ps3_vuart_remove and + * ps3_vuart_shutdown. After this call, polled reading will still work. + */ + +static int ps3_vuart_cleanup(struct ps3_system_bus_device *dev) { - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - struct ps3_vuart_port_driver *drv = - to_ps3_vuart_port_driver(_dev->driver); + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + ps3_vuart_cancel_async(dev); + ps3_vuart_set_interrupt_mask(dev, 0); + ps3_vuart_bus_interrupt_put(); + return 0; +} + +/** + * ps3_vuart_remove - Completely clean the device instance. + * @dev: The struct ps3_system_bus_device instance. + * + * Cleans all memory, interrupts and HV resources. After this call the + * device can no longer be used. + */ + +static int ps3_vuart_remove(struct ps3_system_bus_device *dev) +{ + struct ps3_vuart_port_priv *priv = to_port_priv(dev); + struct ps3_vuart_port_driver *drv; + + BUG_ON(!dev); down(&vuart_bus_priv.probe_mutex); - dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, - dev->core.bus_id); + dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, + dev->match_id); - BUG_ON(vuart_bus_priv.use_count < 1); + if (!dev->core.driver) { + dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, + __LINE__); + up(&vuart_bus_priv.probe_mutex); + return 0; + } - if (drv->remove) - drv->remove(dev); - else - dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__, - __LINE__, dev->core.bus_id); + drv = ps3_system_bus_dev_to_vuart_drv(dev); - vuart_bus_priv.devices[dev->priv->port_number] = NULL; + BUG_ON(!drv); - if (--vuart_bus_priv.use_count == 0) { + if (drv->remove) { + drv->remove(dev); + } else { + dev_dbg(&dev->core, "%s:%d: no remove method\n", __func__, + __LINE__); BUG(); - free_irq(vuart_bus_priv.virq, &vuart_bus_priv); - ps3_vuart_irq_destroy(vuart_bus_priv.virq); - vuart_bus_priv.virq = NO_IRQ; } - kfree(dev->priv); - dev->priv = NULL; + ps3_vuart_cleanup(dev); + + vuart_bus_priv.devices[dev->port_number] = NULL; + kfree(priv); + priv = NULL; + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); up(&vuart_bus_priv.probe_mutex); return 0; } -static void ps3_vuart_shutdown(struct device *_dev) -{ - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); - struct ps3_vuart_port_driver *drv = - to_ps3_vuart_port_driver(_dev->driver); - - dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, - dev->core.bus_id); - - if (drv->shutdown) - drv->shutdown(dev); - else - dev_dbg(&dev->core, "%s:%d: %s no shutdown method\n", __func__, - __LINE__, dev->core.bus_id); -} - /** - * ps3_vuart_bus - The vuart bus instance. + * ps3_vuart_shutdown - Cleans interrupts and HV resources. + * @dev: The struct ps3_system_bus_device instance. * - * The vuart is managed as a bus that port devices connect to. + * Cleans interrupts and HV resources. After this call the + * device can still be used in polling mode. This behavior required + * by sys-manager to be able to complete the device power operation + * sequence. */ -struct bus_type ps3_vuart_bus = { - .name = "ps3_vuart", - .match = ps3_vuart_match, - .probe = ps3_vuart_probe, - .remove = ps3_vuart_remove, - .shutdown = ps3_vuart_shutdown, -}; - -int __init ps3_vuart_bus_init(void) +static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) { - int result; + struct ps3_vuart_port_driver *drv; - pr_debug("%s:%d:\n", __func__, __LINE__); + BUG_ON(!dev); - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) - return -ENODEV; + down(&vuart_bus_priv.probe_mutex); - init_MUTEX(&vuart_bus_priv.probe_mutex); - result = bus_register(&ps3_vuart_bus); - BUG_ON(result); + dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, + dev->match_id); - return result; -} + if (!dev->core.driver) { + dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, + __LINE__); + up(&vuart_bus_priv.probe_mutex); + return 0; + } -void __exit ps3_vuart_bus_exit(void) -{ - pr_debug("%s:%d:\n", __func__, __LINE__); - bus_unregister(&ps3_vuart_bus); -} + drv = ps3_system_bus_dev_to_vuart_drv(dev); -core_initcall(ps3_vuart_bus_init); -module_exit(ps3_vuart_bus_exit); + BUG_ON(!drv); -/** - * ps3_vuart_port_release_device - Remove a vuart port device. - */ + if (drv->shutdown) + drv->shutdown(dev); + else if (drv->remove) { + dev_dbg(&dev->core, "%s:%d: no shutdown, calling remove\n", + __func__, __LINE__); + drv->remove(dev); + } else { + dev_dbg(&dev->core, "%s:%d: no shutdown method\n", __func__, + __LINE__); + BUG(); + } -static void ps3_vuart_port_release_device(struct device *_dev) -{ -#if defined(DEBUG) - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); + ps3_vuart_cleanup(dev); - dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); - BUG_ON(dev->priv && "forgot to free"); - memset(&dev->core, 0, sizeof(dev->core)); -#endif + up(&vuart_bus_priv.probe_mutex); + return 0; } -/** - * ps3_vuart_port_device_register - Add a vuart port device. - */ - -int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev) +static int __init ps3_vuart_bus_init(void) { - static unsigned int dev_count = 1; - - BUG_ON(dev->priv && "forgot to free"); + pr_debug("%s:%d:\n", __func__, __LINE__); - dev->core.parent = NULL; - dev->core.bus = &ps3_vuart_bus; - dev->core.release = ps3_vuart_port_release_device; + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; - snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", - dev_count++); + init_MUTEX(&vuart_bus_priv.probe_mutex); - dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__); + return 0; +} - return device_register(&dev->core); +static void __exit ps3_vuart_bus_exit(void) +{ + pr_debug("%s:%d:\n", __func__, __LINE__); } -EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register); +core_initcall(ps3_vuart_bus_init); +module_exit(ps3_vuart_bus_exit); /** * ps3_vuart_port_driver_register - Add a vuart port device driver. @@ -1089,12 +1245,18 @@ int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) { int result; - pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); - drv->core.bus = &ps3_vuart_bus; - result = driver_register(&drv->core); + pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name); + + BUG_ON(!drv->core.match_id); + BUG_ON(!drv->core.core.name); + + drv->core.probe = ps3_vuart_probe; + drv->core.remove = ps3_vuart_remove; + drv->core.shutdown = ps3_vuart_shutdown; + + result = ps3_system_bus_driver_register(&drv->core); return result; } - EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); /** @@ -1103,8 +1265,7 @@ EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) { - pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); - driver_unregister(&drv->core); + pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name); + ps3_system_bus_driver_unregister(&drv->core); } - EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister); diff --git a/drivers/ps3/vuart.h b/drivers/ps3/vuart.h index 1be992d568c8..eb7f6d94a890 100644 --- a/drivers/ps3/vuart.h +++ b/drivers/ps3/vuart.h @@ -34,29 +34,7 @@ struct ps3_vuart_stats { struct ps3_vuart_work { struct work_struct work; unsigned long trigger; - spinlock_t lock; - struct ps3_vuart_port_device* dev; /* to convert work to device */ -}; - -/** - * struct ps3_vuart_port_priv - private vuart device data. - */ - -struct ps3_vuart_port_priv { - unsigned int port_number; - u64 interrupt_mask; - - struct { - spinlock_t lock; - struct list_head head; - } tx_list; - struct { - unsigned long bytes_held; - spinlock_t lock; - struct list_head head; - } rx_list; - struct ps3_vuart_stats stats; - struct ps3_vuart_work work; + struct ps3_system_bus_device *dev; /* to convert work to device */ }; /** @@ -64,32 +42,30 @@ struct ps3_vuart_port_priv { */ struct ps3_vuart_port_driver { - enum ps3_match_id match_id; - struct device_driver core; - int (*probe)(struct ps3_vuart_port_device *); - int (*remove)(struct ps3_vuart_port_device *); - void (*shutdown)(struct ps3_vuart_port_device *); - int (*tx_event)(struct ps3_vuart_port_device *dev); - int (*rx_event)(struct ps3_vuart_port_device *dev); - int (*disconnect_event)(struct ps3_vuart_port_device *dev); - /* int (*suspend)(struct ps3_vuart_port_device *, pm_message_t); */ - /* int (*resume)(struct ps3_vuart_port_device *); */ + struct ps3_system_bus_driver core; + int (*probe)(struct ps3_system_bus_device *); + int (*remove)(struct ps3_system_bus_device *); + void (*shutdown)(struct ps3_system_bus_device *); + void (*work)(struct ps3_system_bus_device *); + /* int (*tx_event)(struct ps3_system_bus_device *dev); */ + /* int (*rx_event)(struct ps3_system_bus_device *dev); */ + /* int (*disconnect_event)(struct ps3_system_bus_device *dev); */ + /* int (*suspend)(struct ps3_system_bus_device *, pm_message_t); */ + /* int (*resume)(struct ps3_system_bus_device *); */ }; int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv); -static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( - struct device_driver *_drv) -{ - return container_of(_drv, struct ps3_vuart_port_driver, core); -} -static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device( - struct device *_dev) +static inline struct ps3_vuart_port_driver * + ps3_system_bus_dev_to_vuart_drv(struct ps3_system_bus_device *_dev) { - return container_of(_dev, struct ps3_vuart_port_device, core); + struct ps3_system_bus_driver *sbd = + ps3_system_bus_dev_to_system_bus_drv(_dev); + BUG_ON(!sbd); + return container_of(sbd, struct ps3_vuart_port_driver, core); } -static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( +static inline struct ps3_system_bus_device *ps3_vuart_work_to_system_bus_dev( struct work_struct *_work) { struct ps3_vuart_work *vw = container_of(_work, struct ps3_vuart_work, @@ -97,14 +73,13 @@ static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( return vw->dev; } -int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, - unsigned int bytes); -int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, +int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, unsigned int bytes); -int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, +int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes); -void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev); -void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, +int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes); +void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev); +void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, unsigned int bytes); #endif diff --git a/include/asm-powerpc/ps3.h b/include/asm-powerpc/ps3.h index 4f753907bbf9..433c38eb61ae 100644 --- a/include/asm-powerpc/ps3.h +++ b/include/asm-powerpc/ps3.h @@ -407,23 +407,6 @@ static inline void *ps3_system_bus_get_driver_data( extern struct bus_type ps3_system_bus_type; -/* vuart routines */ - -struct ps3_vuart_port_priv; - -/** - * struct ps3_vuart_port_device - a device on a vuart port - */ - -struct ps3_vuart_port_device { - enum ps3_match_id match_id; - struct device core; - struct ps3_vuart_port_priv* priv; /* private driver variables */ - -}; - -int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev); - /* system manager */ #ifdef CONFIG_PS3_SYS_MANAGER -- cgit v1.2.3 From 66c63b84b23d39ce191a18833b5a769370114ec9 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Sat, 16 Jun 2007 08:03:54 +1000 Subject: [POWERPC] PS3: System manager re-work PS3 sys-manager updates to reflect the new PS3 unifed device support. Fixups to the PS3 sys-manager driver to properly support sys_reboot(). - Add varable request_tag to struct ps3_sys_manager_header. - Move ctrl_alt_del from PS3_SM_EVENT_POWER_RELEASED to PS3_SM_EVENT_POWER_PRESSED. - Make the PS3 sys-manager driver a loadable module. - Add new file sys-manager-core.c. - Add new struct ps3_sys_manager_ops for dynamic binding. - Put data sent to device on stack. - Add support for PS3_SM_SERVICE_ID_REQUEST_ERROR. Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- drivers/ps3/Makefile | 1 + drivers/ps3/sys-manager-core.c | 68 ++++++++++ drivers/ps3/sys-manager.c | 290 +++++++++++++++++++++++++++-------------- include/asm-powerpc/ps3.h | 15 ++- 4 files changed, 269 insertions(+), 105 deletions(-) create mode 100644 drivers/ps3/sys-manager-core.c (limited to 'drivers') diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index e251d1c1171c..a6d8db79b20c 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_PS3_VUART) += vuart.o obj-$(CONFIG_PS3_PS3AV) += ps3av.o ps3av_cmd.o +obj-$(CONFIG_PPC_PS3) += sys-manager-core.o obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o diff --git a/drivers/ps3/sys-manager-core.c b/drivers/ps3/sys-manager-core.c new file mode 100644 index 000000000000..31648f7d9ae1 --- /dev/null +++ b/drivers/ps3/sys-manager-core.c @@ -0,0 +1,68 @@ +/* + * PS3 System Manager core. + * + * 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 +#include + +/** + * Staticly linked routines that allow late binding of a loaded sys-manager + * module. + */ + +static struct ps3_sys_manager_ops ps3_sys_manager_ops; + +/** + * ps3_register_sys_manager_ops - Bind ps3_sys_manager_ops to a module. + * @ops: struct ps3_sys_manager_ops. + * + * To be called from ps3_sys_manager_probe() and ps3_sys_manager_remove() to + * register call back ops for power control. Copies data to the static + * variable ps3_sys_manager_ops. + */ + +void ps3_sys_manager_register_ops(const struct ps3_sys_manager_ops *ops) +{ + BUG_ON(!ops); + BUG_ON(!ops->dev); + ps3_sys_manager_ops = ops ? *ops : ps3_sys_manager_ops; +} +EXPORT_SYMBOL_GPL(ps3_sys_manager_register_ops); + +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; +} + +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"); + local_irq_disable(); + while (1) + (void)0; +} diff --git a/drivers/ps3/sys-manager.c b/drivers/ps3/sys-manager.c index 3aa2b0dcc369..8461b08ab9fb 100644 --- a/drivers/ps3/sys-manager.c +++ b/drivers/ps3/sys-manager.c @@ -35,7 +35,7 @@ MODULE_DESCRIPTION("PS3 System Manager"); /** * ps3_sys_manager - PS3 system manager driver. * - * The system manager provides an asyncronous system event notification + * The system manager provides an asynchronous system event notification * mechanism for reporting events like thermal alert and button presses to * guests. It also provides support to control system shutdown and startup. * @@ -52,6 +52,7 @@ MODULE_DESCRIPTION("PS3 System Manager"); * @size: Header size in bytes, curently 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. */ struct ps3_sys_manager_header { @@ -61,29 +62,49 @@ struct ps3_sys_manager_header { u16 reserved_1; u32 payload_size; u16 service_id; - u16 reserved_2[3]; + u16 reserved_2; + u32 request_tag; }; +#define dump_sm_header(_h) _dump_sm_header(_h, __func__, __LINE__) +static void __maybe_unused _dump_sm_header( + const struct ps3_sys_manager_header *h, const char *func, int line) +{ + pr_debug("%s:%d: version: %xh\n", func, line, h->version); + pr_debug("%s:%d: size: %xh\n", func, line, h->size); + pr_debug("%s:%d: payload_size: %xh\n", func, line, h->payload_size); + pr_debug("%s:%d: service_id: %xh\n", func, line, h->service_id); + pr_debug("%s:%d: request_tag: %xh\n", func, line, h->request_tag); +} + /** - * @PS3_SM_RX_MSG_LEN - System manager received message length. + * @PS3_SM_RX_MSG_LEN_MIN - Shortest received message length. + * @PS3_SM_RX_MSG_LEN_MAX - Longest received message length. * - * Currently all messages received from the system manager are the same length - * (16 bytes header + 16 bytes payload = 32 bytes). This knowlege is used to - * simplify the logic. + * 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 + * the logic. */ enum { - PS3_SM_RX_MSG_LEN = 32, + PS3_SM_RX_MSG_LEN_MIN = 24, + PS3_SM_RX_MSG_LEN_MAX = 32, }; /** * enum ps3_sys_manager_service_id - Message header service_id. - * @PS3_SM_SERVICE_ID_REQUEST: guest --> sys_manager. - * @PS3_SM_SERVICE_ID_COMMAND: guest <-- sys_manager. - * @PS3_SM_SERVICE_ID_RESPONSE: guest --> sys_manager. - * @PS3_SM_SERVICE_ID_SET_ATTR: guest --> sys_manager. - * @PS3_SM_SERVICE_ID_EXTERN_EVENT: guest <-- sys_manager. - * @PS3_SM_SERVICE_ID_SET_NEXT_OP: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_REQUEST: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_REQUEST_ERROR: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_COMMAND: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_RESPONSE: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_SET_ATTR: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_EXTERN_EVENT: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_SET_NEXT_OP: guest --> sys_manager. + * + * PS3_SM_SERVICE_ID_REQUEST_ERROR is returned for invalid data values in a + * a PS3_SM_SERVICE_ID_REQUEST message. It also seems to be returned when + * a REQUEST message is sent at the wrong time. */ enum ps3_sys_manager_service_id { @@ -93,6 +114,7 @@ enum ps3_sys_manager_service_id { PS3_SM_SERVICE_ID_COMMAND = 3, PS3_SM_SERVICE_ID_EXTERN_EVENT = 4, PS3_SM_SERVICE_ID_SET_NEXT_OP = 5, + PS3_SM_SERVICE_ID_REQUEST_ERROR = 6, PS3_SM_SERVICE_ID_SET_ATTR = 8, }; @@ -184,12 +206,22 @@ enum ps3_sys_manager_cmd { PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */ }; +/** + * ps3_sm_force_power_off - Poweroff helper. + * + * A global variable used to force a poweroff when the power button has + * been pressed irrespective of how init handles the ctrl_alt_del signal. + * + */ + +static unsigned int ps3_sm_force_power_off; + /** * ps3_sys_manager_write - Helper to write a two part message to the vuart. * */ -static int ps3_sys_manager_write(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_write(struct ps3_system_bus_device *dev, const struct ps3_sys_manager_header *header, const void *payload) { int result; @@ -213,15 +245,10 @@ static int ps3_sys_manager_write(struct ps3_vuart_port_device *dev, * */ -static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_send_attr(struct ps3_system_bus_device *dev, enum ps3_sys_manager_attr attr) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_SET_ATTR, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 reserved_1[3]; @@ -232,6 +259,12 @@ static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_SET_ATTR; + memset(&payload, 0, sizeof(payload)); payload.version = 1; payload.attribute = attr; @@ -245,16 +278,11 @@ static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, * Tell the system manager what to do after this lpar is destroyed. */ -static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_send_next_op(struct ps3_system_bus_device *dev, enum ps3_sys_manager_next_op op, enum ps3_sys_manager_wake_source wake_source) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 type; @@ -268,6 +296,12 @@ static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP; + memset(&payload, 0, sizeof(payload)); payload.version = 3; payload.type = op; @@ -286,32 +320,35 @@ static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, * the command is then communicated back to the system manager with a response * message. * - * Currently, the only supported request it the 'shutdown self' request. + * Currently, the only supported request is the 'shutdown self' request. */ -static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_send_request_shutdown( + struct ps3_system_bus_device *dev) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_REQUEST, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 type; u8 gos_id; u8 reserved_1[13]; - } static const payload = { - .version = 1, - .type = 1, /* shutdown */ - .gos_id = 0, /* self */ - }; + } payload; BUILD_BUG_ON(sizeof(payload) != 16); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_REQUEST; + + memset(&payload, 0, sizeof(payload)); + payload.version = 1; + payload.type = 1; /* shutdown */ + payload.gos_id = 0; /* self */ + return ps3_sys_manager_write(dev, &header, &payload); } @@ -323,15 +360,10 @@ static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *d * failure of a command sent by the system manager. */ -static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, +static int ps3_sys_manager_send_response(struct ps3_system_bus_device *dev, u64 status) { - static const struct ps3_sys_manager_header header = { - .version = 1, - .size = 16, - .payload_size = 16, - .service_id = PS3_SM_SERVICE_ID_RESPONSE, - }; + struct ps3_sys_manager_header header; struct { u8 version; u8 reserved_1[3]; @@ -344,6 +376,12 @@ static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__, (status ? "nak" : "ack")); + memset(&header, 0, sizeof(header)); + header.version = 1; + header.size = 16; + header.payload_size = 16; + header.service_id = PS3_SM_SERVICE_ID_RESPONSE; + memset(&payload, 0, sizeof(payload)); payload.version = 1; payload.status = status; @@ -356,7 +394,7 @@ static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, * */ -static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_handle_event(struct ps3_system_bus_device *dev) { int result; struct { @@ -370,7 +408,7 @@ static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) BUILD_BUG_ON(sizeof(event) != 16); result = ps3_vuart_read(dev, &event, sizeof(event)); - BUG_ON(result); + BUG_ON(result && "need to retry here"); if (event.version != 1) { dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n", @@ -382,11 +420,34 @@ static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) case PS3_SM_EVENT_POWER_PRESSED: dev_dbg(&dev->core, "%s:%d: POWER_PRESSED\n", __func__, __LINE__); + ps3_sm_force_power_off = 1; + /* + * A memory barrier is use here to sync memory since + * ps3_sys_manager_final_restart() could be called on + * another cpu. + */ + wmb(); + kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */ break; case PS3_SM_EVENT_POWER_RELEASED: dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n", __func__, __LINE__, event.value); - kill_cad_pid(SIGINT, 1); + break; + case PS3_SM_EVENT_RESET_PRESSED: + dev_dbg(&dev->core, "%s:%d: RESET_PRESSED\n", + __func__, __LINE__); + ps3_sm_force_power_off = 0; + /* + * A memory barrier is use here to sync memory since + * ps3_sys_manager_final_restart() could be called on + * another cpu. + */ + wmb(); + kill_cad_pid(SIGINT, 1); /* ctrl_alt_del */ + break; + case PS3_SM_EVENT_RESET_RELEASED: + dev_dbg(&dev->core, "%s:%d: RESET_RELEASED (%u ms)\n", + __func__, __LINE__, event.value); break; case PS3_SM_EVENT_THERMAL_ALERT: dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n", @@ -411,7 +472,7 @@ static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) * The system manager sends this in reply to a 'request' message from the guest. */ -static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_handle_cmd(struct ps3_system_bus_device *dev) { int result; struct { @@ -425,6 +486,7 @@ static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); result = ps3_vuart_read(dev, &cmd, sizeof(cmd)); + BUG_ON(result && "need to retry here"); if(result) return result; @@ -448,9 +510,10 @@ static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) /** * ps3_sys_manager_handle_msg - First stage msg handler. * + * Can be called directly to manually poll vuart and pump message handler. */ -static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) +static int ps3_sys_manager_handle_msg(struct ps3_system_bus_device *dev) { int result; struct ps3_sys_manager_header header; @@ -464,12 +527,17 @@ static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) if (header.version != 1) { dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n", __func__, __LINE__, header.version); + dump_sm_header(&header); goto fail_header; } BUILD_BUG_ON(sizeof(header) != 16); - BUG_ON(header.size != 16); - BUG_ON(header.payload_size != 16); + + if (header.size != 16 || (header.payload_size != 8 + && header.payload_size != 16)) { + dump_sm_header(&header); + BUG(); + } switch (header.service_id) { case PS3_SM_SERVICE_ID_EXTERN_EVENT: @@ -478,6 +546,11 @@ static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) case PS3_SM_SERVICE_ID_COMMAND: dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__); return ps3_sys_manager_handle_cmd(dev); + case PS3_SM_SERVICE_ID_REQUEST_ERROR: + dev_dbg(&dev->core, "%s:%d: REQUEST_ERROR\n", __func__, + __LINE__); + dump_sm_header(&header); + break; default: dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n", __func__, __LINE__, header.service_id); @@ -494,45 +567,25 @@ fail_id: } /** - * ps3_sys_manager_work - Asyncronous read handler. - * - * Signaled when a complete message arrives at the vuart port. - */ - -static void ps3_sys_manager_work(struct work_struct *work) -{ - struct ps3_vuart_port_device *dev = ps3_vuart_work_to_port_device(work); - - ps3_sys_manager_handle_msg(dev); - ps3_vuart_read_async(dev, ps3_sys_manager_work, PS3_SM_RX_MSG_LEN); -} - -struct { - struct ps3_vuart_port_device *dev; -} static drv_priv; - -/** - * ps3_sys_manager_restart - The final platform machine_restart routine. + * ps3_sys_manager_final_power_off - The final platform machine_power_off routine. * - * This routine never returns. The routine disables asyncronous vuart reads + * This routine never returns. The routine disables asynchronous vuart reads * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge * the shutdown command sent from the system manager. Soon after the * acknowledgement is sent the lpar is destroyed by the HV. This routine - * should only be called from ps3_restart(). + * should only be called from ps3_power_off() through + * ps3_sys_manager_ops.power_off. */ -void ps3_sys_manager_restart(void) +static void ps3_sys_manager_final_power_off(struct ps3_system_bus_device *dev) { - struct ps3_vuart_port_device *dev = drv_priv.dev; - - BUG_ON(!drv_priv.dev); + BUG_ON(!dev); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); 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_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN, PS3_SM_WAKE_DEFAULT); ps3_sys_manager_send_request_shutdown(dev); @@ -543,26 +596,33 @@ void ps3_sys_manager_restart(void) } /** - * ps3_sys_manager_power_off - The final platform machine_power_off routine. + * ps3_sys_manager_final_restart - The final platform machine_restart routine. * - * This routine never returns. The routine disables asyncronous vuart reads + * This routine never returns. The routine disables asynchronous vuart reads * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge * the shutdown command sent from the system manager. Soon after the * acknowledgement is sent the lpar is destroyed by the HV. This routine - * should only be called from ps3_power_off(). + * should only be called from ps3_restart() through ps3_sys_manager_ops.restart. */ -void ps3_sys_manager_power_off(void) +static void ps3_sys_manager_final_restart(struct ps3_system_bus_device *dev) { - struct ps3_vuart_port_device *dev = drv_priv.dev; - - BUG_ON(!drv_priv.dev); + BUG_ON(!dev); dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + /* Check if we got here via a power button event. */ + + if (ps3_sm_force_power_off) { + dev_dbg(&dev->core, "%s:%d: forcing poweroff\n", + __func__, __LINE__); + ps3_sys_manager_final_power_off(dev); + } + ps3_vuart_cancel_async(dev); - ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN, + 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); @@ -572,31 +632,60 @@ void ps3_sys_manager_power_off(void) ps3_sys_manager_handle_msg(dev); } -static int ps3_sys_manager_probe(struct ps3_vuart_port_device *dev) +/** + * ps3_sys_manager_work - Asynchronous read handler. + * + * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port. + */ + +static void ps3_sys_manager_work(struct ps3_system_bus_device *dev) +{ + ps3_sys_manager_handle_msg(dev); + ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); +} + +static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev) { int result; + struct ps3_sys_manager_ops ops; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - BUG_ON(drv_priv.dev); - drv_priv.dev = dev; + ops.power_off = ps3_sys_manager_final_power_off; + ops.restart = ps3_sys_manager_final_restart; + ops.dev = dev; + + /* ps3_sys_manager_register_ops copies ops. */ + + ps3_sys_manager_register_ops(&ops); result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL); BUG_ON(result); - result = ps3_vuart_read_async(dev, ps3_sys_manager_work, - PS3_SM_RX_MSG_LEN); + result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); BUG_ON(result); return result; } +static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + return 0; +} + +static void ps3_sys_manager_shutdown(struct ps3_system_bus_device *dev) +{ + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); +} + static struct ps3_vuart_port_driver ps3_sys_manager = { - .match_id = PS3_MATCH_ID_SYSTEM_MANAGER, - .core = { - .name = "ps3_sys_manager", - }, + .core.match_id = PS3_MATCH_ID_SYSTEM_MANAGER, + .core.core.name = "ps3_sys_manager", .probe = ps3_sys_manager_probe, + .remove = ps3_sys_manager_remove, + .shutdown = ps3_sys_manager_shutdown, + .work = ps3_sys_manager_work, }; static int __init ps3_sys_manager_init(void) @@ -608,3 +697,6 @@ static int __init ps3_sys_manager_init(void) } module_init(ps3_sys_manager_init); +/* Module remove not supported. */ + +MODULE_ALIAS(PS3_MODULE_ALIAS_SYSTEM_MANAGER); diff --git a/include/asm-powerpc/ps3.h b/include/asm-powerpc/ps3.h index 433c38eb61ae..a35aea2db9c5 100644 --- a/include/asm-powerpc/ps3.h +++ b/include/asm-powerpc/ps3.h @@ -409,13 +409,15 @@ extern struct bus_type ps3_system_bus_type; /* system manager */ -#ifdef CONFIG_PS3_SYS_MANAGER -void ps3_sys_manager_restart(void); +struct ps3_sys_manager_ops { + struct ps3_system_bus_device *dev; + void (*power_off)(struct ps3_system_bus_device *dev); + void (*restart)(struct ps3_system_bus_device *dev); +}; + +void ps3_sys_manager_register_ops(const struct ps3_sys_manager_ops *ops); void ps3_sys_manager_power_off(void); -#else -static inline void ps3_sys_manager_restart(void) {} -static inline void ps3_sys_manager_power_off(void) {} -#endif +void ps3_sys_manager_restart(void); struct ps3_prealloc { const char *name; @@ -426,4 +428,5 @@ struct ps3_prealloc { extern struct ps3_prealloc ps3fb_videomemory; + #endif -- cgit v1.2.3 From 13a5e30cf7407415387b5592b15ef4b352d28283 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Sat, 16 Jun 2007 08:05:01 +1000 Subject: [POWERPC] PS3: Rework AV settings driver Make the PS3 ps3av driver a loadable module. - Replace static data with kmalloc()'ed. o Allocate struct ps3av dynamically, as it contains data used as vuart receive/transmit buffers o Move static recv_buf from ps3av_do_pkt() to struct ps3av - Move ps3av_vuart_{read,write}() from drivers/ps3/ps3av_cmd.c to drivers/ps3/ps3av.c and make them static as they're used in that file only. - Make device a PS3 system-bus device. - Update copyright formatting. - Make two new routines ps3av_register_flip_ctl() and ps3av_flip_ctl() to support late binding of the frame buffer flip control routine. Signed-off-by: Geert Uytterhoeven Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- drivers/ps3/Makefile | 3 +- drivers/ps3/ps3av.c | 372 +++++++++++++++++++++++--------------------- drivers/ps3/ps3av_cmd.c | 35 +---- include/asm-powerpc/ps3av.h | 36 ++--- 4 files changed, 220 insertions(+), 226 deletions(-) (limited to 'drivers') diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index a6d8db79b20c..b8c5547adbde 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PS3_VUART) += vuart.o -obj-$(CONFIG_PS3_PS3AV) += ps3av.o ps3av_cmd.o +obj-$(CONFIG_PS3_PS3AV) += ps3av_mod.o +ps3av_mod-objs += ps3av.o ps3av_cmd.o obj-$(CONFIG_PPC_PS3) += sys-manager-core.o obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 1393e64335f9..85e21614f868 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -1,32 +1,30 @@ /* - * Copyright (C) 2006 Sony Computer Entertainment Inc. - * Copyright 2006, 2007 Sony Corporation + * PS3 AV backend support. * - * AV backend support for PS3 + * 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 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. + * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * 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 #include #include #include -#include -#include #include #include -#include #include #include @@ -39,13 +37,12 @@ static int timeout = 5000; /* in msec ( 5 sec ) */ module_param(timeout, int, 0644); static struct ps3av { - int available; struct mutex mutex; struct work_struct work; struct completion done; struct workqueue_struct *wq; int open_count; - struct ps3_vuart_port_device *dev; + struct ps3_system_bus_device *dev; int region; struct ps3av_pkt_av_get_hw_conf av_hw_conf; @@ -55,11 +52,13 @@ static struct ps3av { u32 audio_port; int ps3av_mode; int ps3av_mode_old; -} ps3av; - -static struct ps3_vuart_port_device ps3av_dev = { - .match_id = PS3_MATCH_ID_AV_SETTINGS -}; + union { + 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 */ #define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8 @@ -169,7 +168,7 @@ static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr) if (hdr->cid & PS3AV_EVENT_CMD_MASK) { table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK); if (table) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "recv event packet cid:%08x port:0x%x size:%d\n", hdr->cid, ps3av_event_get_port_id(hdr->cid), hdr->size); @@ -182,6 +181,41 @@ static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr) return 0; } + +#define POLLING_INTERVAL 25 /* in msec */ + +static int ps3av_vuart_write(struct ps3_system_bus_device *dev, + const void *buf, unsigned long size) +{ + int error; + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + error = ps3_vuart_write(dev, buf, size); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); + return error ? error : size; +} + +static int ps3av_vuart_read(struct ps3_system_bus_device *dev, void *buf, + unsigned long size, int timeout) +{ + int error; + int loopcnt = 0; + + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL; + while (loopcnt++ <= timeout) { + error = ps3_vuart_read(dev, buf, size); + if (!error) + return size; + if (error != -EAGAIN) { + printk(KERN_ERR "%s: ps3_vuart_read failed %d\n", + __func__, error); + return error; + } + msleep(POLLING_INTERVAL); + } + return -EWOULDBLOCK; +} + static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, struct ps3av_reply_hdr *recv_buf, int write_len, int read_len) @@ -190,13 +224,13 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, u32 cmd; int event; - if (!ps3av.available) + if (!ps3av) return -ENODEV; /* send pkt */ - res = ps3av_vuart_write(ps3av.dev, send_buf, write_len); + res = ps3av_vuart_write(ps3av->dev, send_buf, write_len); if (res < 0) { - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "%s: ps3av_vuart_write() failed (result=%d)\n", __func__, res); return res; @@ -206,20 +240,20 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, cmd = send_buf->cid; do { /* read header */ - res = ps3av_vuart_read(ps3av.dev, recv_buf, PS3AV_HDR_SIZE, + res = ps3av_vuart_read(ps3av->dev, recv_buf, PS3AV_HDR_SIZE, timeout); if (res != PS3AV_HDR_SIZE) { - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "%s: ps3av_vuart_read() failed (result=%d)\n", __func__, res); return res; } /* read body */ - res = ps3av_vuart_read(ps3av.dev, &recv_buf->cid, + res = ps3av_vuart_read(ps3av->dev, &recv_buf->cid, recv_buf->size, timeout); if (res < 0) { - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "%s: ps3av_vuart_read() failed (result=%d)\n", __func__, res); return res; @@ -230,7 +264,7 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, } while (event); if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) { - dev_dbg(&ps3av_dev.core, "%s: reply err (result=%x)\n", + dev_dbg(&ps3av->dev->core, "%s: reply err (result=%x)\n", __func__, recv_buf->cid); return -EINVAL; } @@ -245,7 +279,7 @@ static int ps3av_process_reply_packet(struct ps3av_send_hdr *cmd_buf, int return_len; if (recv_buf->version != PS3AV_VERSION) { - dev_dbg(&ps3av_dev.core, "reply_packet invalid version:%x\n", + dev_dbg(&ps3av->dev->core, "reply_packet invalid version:%x\n", recv_buf->version); return -EFAULT; } @@ -267,16 +301,11 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, struct ps3av_send_hdr *buf) { int res = 0; - static union { - struct ps3av_reply_hdr reply_hdr; - u8 raw[PS3AV_BUF_SIZE]; - } recv_buf; - u32 *table; - BUG_ON(!ps3av.available); + BUG_ON(!ps3av); - mutex_lock(&ps3av.mutex); + mutex_lock(&ps3av->mutex); table = ps3av_search_cmd_table(cid, PS3AV_CID_MASK); BUG_ON(!table); @@ -288,7 +317,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, ps3av_set_hdr(cid, send_len, buf); /* send packet via vuart */ - res = ps3av_send_cmd_pkt(buf, &recv_buf.reply_hdr, send_len, + res = ps3av_send_cmd_pkt(buf, &ps3av->recv_buf.reply_hdr, send_len, usr_buf_size); if (res < 0) { printk(KERN_ERR @@ -298,7 +327,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, } /* process reply packet */ - res = ps3av_process_reply_packet(buf, &recv_buf.reply_hdr, + res = ps3av_process_reply_packet(buf, &ps3av->recv_buf.reply_hdr, usr_buf_size); if (res < 0) { printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n", @@ -306,11 +335,11 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, goto err; } - mutex_unlock(&ps3av.mutex); + mutex_unlock(&ps3av->mutex); return 0; err: - mutex_unlock(&ps3av.mutex); + mutex_unlock(&ps3av->mutex); printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); return res; } @@ -319,11 +348,11 @@ static int ps3av_set_av_video_mute(u32 mute) { int i, num_of_av_port, res; - num_of_av_port = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; + num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; /* video mute on */ for (i = 0; i < num_of_av_port; i++) { - res = ps3av_cmd_av_video_mute(1, &ps3av.av_port[i], mute); + res = ps3av_cmd_av_video_mute(1, &ps3av->av_port[i], mute); if (res < 0) return -1; } @@ -335,13 +364,13 @@ static int ps3av_set_video_disable_sig(void) { int i, num_of_hdmi_port, num_of_av_port, res; - num_of_hdmi_port = ps3av.av_hw_conf.num_of_hdmi; - num_of_av_port = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; + num_of_hdmi_port = ps3av->av_hw_conf.num_of_hdmi; + num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; /* tv mute */ for (i = 0; i < num_of_hdmi_port; i++) { - res = ps3av_cmd_av_tv_mute(ps3av.av_port[i], + res = ps3av_cmd_av_tv_mute(ps3av->av_port[i], PS3AV_CMD_MUTE_ON); if (res < 0) return -1; @@ -350,11 +379,11 @@ static int ps3av_set_video_disable_sig(void) /* video mute on */ for (i = 0; i < num_of_av_port; i++) { - res = ps3av_cmd_av_video_disable_sig(ps3av.av_port[i]); + res = ps3av_cmd_av_video_disable_sig(ps3av->av_port[i]); if (res < 0) return -1; if (i < num_of_hdmi_port) { - res = ps3av_cmd_av_tv_mute(ps3av.av_port[i], + res = ps3av_cmd_av_tv_mute(ps3av->av_port[i], PS3AV_CMD_MUTE_OFF); if (res < 0) return -1; @@ -369,17 +398,17 @@ static int ps3av_set_audio_mute(u32 mute) { int i, num_of_av_port, num_of_opt_port, res; - num_of_av_port = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti; - num_of_opt_port = ps3av.av_hw_conf.num_of_spdif; + num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti; + num_of_opt_port = ps3av->av_hw_conf.num_of_spdif; for (i = 0; i < num_of_av_port; i++) { - res = ps3av_cmd_av_audio_mute(1, &ps3av.av_port[i], mute); + res = ps3av_cmd_av_audio_mute(1, &ps3av->av_port[i], mute); if (res < 0) return -1; } for (i = 0; i < num_of_opt_port; i++) { - res = ps3av_cmd_audio_mute(1, &ps3av.opt_port[i], mute); + res = ps3av_cmd_audio_mute(1, &ps3av->opt_port[i], mute); if (res < 0) return -1; } @@ -394,40 +423,40 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) struct ps3av_pkt_audio_mode audio_mode; u32 len = 0; - num_of_audio = ps3av.av_hw_conf.num_of_hdmi + - ps3av.av_hw_conf.num_of_avmulti + - ps3av.av_hw_conf.num_of_spdif; + num_of_audio = ps3av->av_hw_conf.num_of_hdmi + + ps3av->av_hw_conf.num_of_avmulti + + ps3av->av_hw_conf.num_of_spdif; avb_param.num_of_video_pkt = 0; avb_param.num_of_audio_pkt = PS3AV_AVB_NUM_AUDIO; /* always 0 */ avb_param.num_of_av_video_pkt = 0; - avb_param.num_of_av_audio_pkt = ps3av.av_hw_conf.num_of_hdmi; + avb_param.num_of_av_audio_pkt = ps3av->av_hw_conf.num_of_hdmi; - vid = video_mode_table[ps3av.ps3av_mode].vid; + vid = video_mode_table[ps3av->ps3av_mode].vid; /* audio mute */ ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON); /* audio inactive */ - res = ps3av_cmd_audio_active(0, ps3av.audio_port); + res = ps3av_cmd_audio_active(0, ps3av->audio_port); if (res < 0) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "ps3av_cmd_audio_active OFF failed\n"); /* audio_pkt */ for (i = 0; i < num_of_audio; i++) { - ps3av_cmd_set_audio_mode(&audio_mode, ps3av.av_port[i], ch, fs, - word_bits, format, source); - if (i < ps3av.av_hw_conf.num_of_hdmi) { + ps3av_cmd_set_audio_mode(&audio_mode, ps3av->av_port[i], ch, + fs, word_bits, format, source); + if (i < ps3av->av_hw_conf.num_of_hdmi) { /* hdmi only */ len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len], - ps3av.av_port[i], + ps3av->av_port[i], &audio_mode, vid); } /* audio_mode pkt should be sent separately */ res = ps3av_cmd_audio_mode(&audio_mode); if (res < 0) - dev_dbg(&ps3av_dev.core, + dev_dbg(&ps3av->dev->core, "ps3av_cmd_audio_mode failed, port:%x\n", i); } @@ -435,15 +464,16 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) len += offsetof(struct ps3av_pkt_avb_param, buf); res = ps3av_cmd_avb_param(&avb_param, len); if (res < 0) - dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n"); + dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); /* audio mute */ ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF); /* audio active */ - res = ps3av_cmd_audio_active(1, ps3av.audio_port); + res = ps3av_cmd_audio_active(1, ps3av->audio_port); if (res < 0) - dev_dbg(&ps3av_dev.core, "ps3av_cmd_audio_active ON failed\n"); + dev_dbg(&ps3av->dev->core, + "ps3av_cmd_audio_active ON failed\n"); return 0; } @@ -456,7 +486,7 @@ static int ps3av_set_videomode(void) ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON); /* wake up ps3avd to do the actual video mode setting */ - queue_work(ps3av.wq, &ps3av.work); + queue_work(ps3av->wq, &ps3av->work); return 0; } @@ -473,8 +503,8 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) 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_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 */ @@ -484,21 +514,21 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) 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"); + dev_dbg(&ps3av->dev->core, "Not supported\n"); else if (res) - dev_dbg(&ps3av_dev.core, + 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, + 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], - ps3av.head[i], video_mode->vid, + ps3av->head[i], video_mode->vid, video_mode->fmt, id); /* av_video_pkt */ for (i = 0; i < avb_param.num_of_av_video_pkt; i++) { @@ -507,12 +537,12 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) else av_video_cs = video_mode->cs; #ifndef PS3AV_HDMI_YUV - if (ps3av.av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || - ps3av.av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) + 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 */ #endif len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], - ps3av.av_port[i], + ps3av->av_port[i], video_mode->vid, av_video_cs, video_mode->aspect, id); } @@ -524,7 +554,7 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) "%s: Command failed. Please try your request again. \n", __func__); else if (res) - dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n"); + dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); msleep(1500); /* av video mute */ @@ -533,8 +563,8 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id) static void ps3avd(struct work_struct *work) { - ps3av_set_videomode_cont(ps3av.ps3av_mode, ps3av.ps3av_mode_old); - complete(&ps3av.done); + ps3av_set_videomode_cont(ps3av->ps3av_mode, ps3av->ps3av_mode_old); + complete(&ps3av->done); } static int ps3av_vid2table_id(int vid) @@ -601,7 +631,7 @@ static int ps3av_hdmi_get_vid(struct ps3av_info_monitor *info) return vid; } - if (ps3av.region & PS3AV_REGION_60) + if (ps3av->region & PS3AV_REGION_60) vid = PS3AV_DEFAULT_HDMI_VID_REG_60; else vid = PS3AV_DEFAULT_HDMI_VID_REG_50; @@ -643,16 +673,16 @@ static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf, vid = PS3AV_DEFAULT_DVI_VID; } else if (vid == -1) { /* no HDMI interface or HDMI is off */ - if (ps3av.region & PS3AV_REGION_60) + if (ps3av->region & PS3AV_REGION_60) vid = PS3AV_DEFAULT_AVMULTI_VID_REG_60; else vid = PS3AV_DEFAULT_AVMULTI_VID_REG_50; - if (ps3av.region & PS3AV_REGION_RGB) + 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 (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) @@ -715,14 +745,14 @@ int ps3av_set_video_mode(u32 id, int boot) size = ARRAY_SIZE(video_mode_table); if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) { - dev_dbg(&ps3av_dev.core, "%s: error id :%d\n", __func__, id); + dev_dbg(&ps3av->dev->core, "%s: error id :%d\n", __func__, id); return -EINVAL; } /* auto mode */ option = id & ~PS3AV_MODE_MASK; if ((id & PS3AV_MODE_MASK) == 0) { - id = ps3av_auto_videomode(&ps3av.av_hw_conf, boot); + id = ps3av_auto_videomode(&ps3av->av_hw_conf, boot); if (id < 1) { printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); return -EINVAL; @@ -731,11 +761,11 @@ int ps3av_set_video_mode(u32 id, int boot) } /* set videomode */ - wait_for_completion(&ps3av.done); - ps3av.ps3av_mode_old = ps3av.ps3av_mode; - ps3av.ps3av_mode = id; + wait_for_completion(&ps3av->done); + ps3av->ps3av_mode_old = ps3av->ps3av_mode; + ps3av->ps3av_mode = id; if (ps3av_set_videomode()) - ps3av.ps3av_mode = ps3av.ps3av_mode_old; + ps3av->ps3av_mode = ps3av->ps3av_mode_old; return 0; } @@ -744,7 +774,7 @@ EXPORT_SYMBOL_GPL(ps3av_set_video_mode); int ps3av_get_auto_mode(int boot) { - return ps3av_auto_videomode(&ps3av.av_hw_conf, boot); + return ps3av_auto_videomode(&ps3av->av_hw_conf, boot); } EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); @@ -772,7 +802,7 @@ EXPORT_SYMBOL_GPL(ps3av_set_mode); int ps3av_get_mode(void) { - return ps3av.ps3av_mode; + return ps3av ? ps3av->ps3av_mode : 0; } EXPORT_SYMBOL_GPL(ps3av_get_mode); @@ -842,82 +872,65 @@ int ps3av_audio_mute(int mute) EXPORT_SYMBOL_GPL(ps3av_audio_mute); -int ps3av_dev_open(void) +void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), + void *flip_data) { - int status = 0; - - mutex_lock(&ps3av.mutex); - if (!ps3av.open_count++) { - status = lv1_gpu_open(0); - if (status) { - printk(KERN_ERR "%s: lv1_gpu_open failed %d\n", - __func__, status); - ps3av.open_count--; - } - } - mutex_unlock(&ps3av.mutex); - - return status; + mutex_lock(&ps3av->mutex); + ps3av->flip_ctl = flip_ctl; + ps3av->flip_data = flip_data; + mutex_unlock(&ps3av->mutex); } +EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl); -EXPORT_SYMBOL_GPL(ps3av_dev_open); - -int ps3av_dev_close(void) +void ps3av_flip_ctl(int on) { - int status = 0; - - mutex_lock(&ps3av.mutex); - if (ps3av.open_count <= 0) { - printk(KERN_ERR "%s: GPU already closed\n", __func__); - status = -1; - } else if (!--ps3av.open_count) { - status = lv1_gpu_close(); - if (status) - printk(KERN_WARNING "%s: lv1_gpu_close failed %d\n", - __func__, status); - } - mutex_unlock(&ps3av.mutex); - - return status; + mutex_lock(&ps3av->mutex); + if (ps3av->flip_ctl) + ps3av->flip_ctl(on, ps3av->flip_data); + mutex_unlock(&ps3av->mutex); } -EXPORT_SYMBOL_GPL(ps3av_dev_close); - -static int ps3av_probe(struct ps3_vuart_port_device *dev) +static int ps3av_probe(struct ps3_system_bus_device *dev) { int res; u32 id; - dev_dbg(&ps3av_dev.core, "init ...\n"); - dev_dbg(&ps3av_dev.core, " timeout=%d\n", timeout); + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + dev_dbg(&dev->core, " timeout=%d\n", timeout); - memset(&ps3av, 0, sizeof(ps3av)); - - mutex_init(&ps3av.mutex); - ps3av.ps3av_mode = 0; - ps3av.dev = dev; + if (ps3av) { + dev_err(&dev->core, "Only one ps3av device is supported\n"); + return -EBUSY; + } - INIT_WORK(&ps3av.work, ps3avd); - init_completion(&ps3av.done); - complete(&ps3av.done); - ps3av.wq = create_singlethread_workqueue("ps3avd"); - if (!ps3av.wq) + ps3av = kzalloc(sizeof(*ps3av), GFP_KERNEL); + if (!ps3av) return -ENOMEM; - ps3av.available = 1; + mutex_init(&ps3av->mutex); + ps3av->ps3av_mode = 0; + ps3av->dev = dev; + + INIT_WORK(&ps3av->work, ps3avd); + init_completion(&ps3av->done); + complete(&ps3av->done); + ps3av->wq = create_singlethread_workqueue("ps3avd"); + if (!ps3av->wq) + goto fail; + switch (ps3_os_area_get_av_multi_out()) { case PS3_PARAM_AV_MULTI_OUT_NTSC: - ps3av.region = PS3AV_REGION_60; + ps3av->region = PS3AV_REGION_60; break; case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR: case PS3_PARAM_AV_MULTI_OUT_SECAM: - ps3av.region = PS3AV_REGION_50; + ps3av->region = PS3AV_REGION_50; break; case PS3_PARAM_AV_MULTI_OUT_PAL_RGB: - ps3av.region = PS3AV_REGION_50 | PS3AV_REGION_RGB; + ps3av->region = PS3AV_REGION_50 | PS3AV_REGION_RGB; break; default: - ps3av.region = PS3AV_REGION_60; + ps3av->region = PS3AV_REGION_60; break; } @@ -927,39 +940,47 @@ static int ps3av_probe(struct ps3_vuart_port_device *dev) printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __func__, res); - ps3av_get_hw_conf(&ps3av); - id = ps3av_auto_videomode(&ps3av.av_hw_conf, 1); - mutex_lock(&ps3av.mutex); - ps3av.ps3av_mode = id; - mutex_unlock(&ps3av.mutex); + ps3av_get_hw_conf(ps3av); + id = ps3av_auto_videomode(&ps3av->av_hw_conf, 1); + mutex_lock(&ps3av->mutex); + ps3av->ps3av_mode = id; + mutex_unlock(&ps3av->mutex); - dev_dbg(&ps3av_dev.core, "init...done\n"); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); return 0; + +fail: + kfree(ps3av); + ps3av = NULL; + return -ENOMEM; } -static int ps3av_remove(struct ps3_vuart_port_device *dev) +static int ps3av_remove(struct ps3_system_bus_device *dev) { - if (ps3av.available) { + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + if (ps3av) { ps3av_cmd_fin(); - if (ps3av.wq) - destroy_workqueue(ps3av.wq); - ps3av.available = 0; + if (ps3av->wq) + destroy_workqueue(ps3av->wq); + kfree(ps3av); + ps3av = NULL; } + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); return 0; } -static void ps3av_shutdown(struct ps3_vuart_port_device *dev) +static void ps3av_shutdown(struct ps3_system_bus_device *dev) { + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); ps3av_remove(dev); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); } static struct ps3_vuart_port_driver ps3av_driver = { - .match_id = PS3_MATCH_ID_AV_SETTINGS, - .core = { - .name = "ps3_av", - }, + .core.match_id = PS3_MATCH_ID_AV_SETTINGS, + .core.core.name = "ps3_av", .probe = ps3av_probe, .remove = ps3av_remove, .shutdown = ps3av_shutdown, @@ -972,6 +993,8 @@ static int ps3av_module_init(void) if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) return -ENODEV; + pr_debug(" -> %s:%d\n", __func__, __LINE__); + error = ps3_vuart_port_driver_register(&ps3av_driver); if (error) { printk(KERN_ERR @@ -980,20 +1003,21 @@ static int ps3av_module_init(void) return error; } - error = ps3_vuart_port_device_register(&ps3av_dev); - if (error) - printk(KERN_ERR - "%s: ps3_vuart_port_device_register failed %d\n", - __func__, error); - + pr_debug(" <- %s:%d\n", __func__, __LINE__); return error; } static void __exit ps3av_module_exit(void) { - device_unregister(&ps3av_dev.core); + pr_debug(" -> %s:%d\n", __func__, __LINE__); ps3_vuart_port_driver_unregister(&ps3av_driver); + pr_debug(" <- %s:%d\n", __func__, __LINE__); } subsys_initcall(ps3av_module_init); module_exit(ps3av_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 AV Settings Driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_ALIAS(PS3_MODULE_ALIAS_AV_SETTINGS); diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 7c4fb264dda2..f72f5ddf18e4 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -868,7 +868,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) { int res; - ps3fb_flip_ctl(0); /* flip off */ + ps3av_flip_ctl(0); /* flip off */ /* avb packet */ res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb), @@ -882,7 +882,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len) res); out: - ps3fb_flip_ctl(1); /* flip on */ + ps3av_flip_ctl(1); /* flip on */ return res; } @@ -1003,34 +1003,3 @@ void ps3av_cmd_av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info * | PS3AV_CMD_AV_LAYOUT_176 \ | PS3AV_CMD_AV_LAYOUT_192) -/************************* vuart ***************************/ - -#define POLLING_INTERVAL 25 /* in msec */ - -int ps3av_vuart_write(struct ps3_vuart_port_device *dev, const void *buf, - unsigned long size) -{ - int error = ps3_vuart_write(dev, buf, size); - return error ? error : size; -} - -int ps3av_vuart_read(struct ps3_vuart_port_device *dev, void *buf, - unsigned long size, int timeout) -{ - int error; - int loopcnt = 0; - - timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL; - while (loopcnt++ <= timeout) { - error = ps3_vuart_read(dev, buf, size); - if (!error) - return size; - if (error != -EAGAIN) { - printk(KERN_ERR "%s: ps3_vuart_read failed %d\n", - __func__, error); - return error; - } - msleep(POLLING_INTERVAL); - } - return -EWOULDBLOCK; -} diff --git a/include/asm-powerpc/ps3av.h b/include/asm-powerpc/ps3av.h index 7f5948efca9b..7df4250802de 100644 --- a/include/asm-powerpc/ps3av.h +++ b/include/asm-powerpc/ps3av.h @@ -1,20 +1,23 @@ /* - * Copyright (C) 2006 Sony Computer Entertainment Inc. - * Copyright 2006, 2007 Sony Corporation + * PS3 AV backend support. * - * 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. + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. * - * 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. + * 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. * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * 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 */ + #ifndef _ASM_POWERPC_PS3AV_H_ #define _ASM_POWERPC_PS3AV_H_ @@ -704,12 +707,6 @@ static inline void ps3av_cmd_av_monitor_info_dump(const struct ps3av_pkt_av_get_ extern int ps3av_cmd_video_get_monitor_info(struct ps3av_pkt_av_get_monitor_info *, u32); -struct ps3_vuart_port_device; -extern int ps3av_vuart_write(struct ps3_vuart_port_device *dev, - const void *buf, unsigned long size); -extern int ps3av_vuart_read(struct ps3_vuart_port_device *dev, void *buf, - unsigned long size, int timeout); - extern int ps3av_set_video_mode(u32, int); extern int ps3av_set_audio_mode(u32, u32, u32, u32, u32); extern int ps3av_get_auto_mode(int); @@ -722,5 +719,8 @@ extern int ps3av_video_mute(int); extern int ps3av_audio_mute(int); extern int ps3av_dev_open(void); extern int ps3av_dev_close(void); +extern void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data), + void *flip_data); +extern void ps3av_flip_ctl(int on); #endif /* _ASM_POWERPC_PS3AV_H_ */ -- cgit v1.2.3 From 9e6b99bd4494dadebb189d2db4d1f55ae726b0bb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 16 Jun 2007 08:05:38 +1000 Subject: [POWERPC] PS3: Frame buffer system-bus rework Convert the ps3fb device from a platform device to a PS3 system bus device. Fix the remove and shutdown methods to support kexec and to make ps3fb a loadable module. Signed-off-by: Geert Uytterhoeven Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/ps3/setup.c | 9 +- drivers/video/Kconfig | 4 +- drivers/video/ps3fb.c | 290 ++++++++++++++++--------------------- include/asm-powerpc/ps3fb.h | 12 -- 4 files changed, 134 insertions(+), 181 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index 96ad4263bd29..ba38319ed8af 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c @@ -107,7 +107,7 @@ static void ps3_panic(char *str) while(1); } -#ifdef CONFIG_FB_PS3 +#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE) static void prealloc(struct ps3_prealloc *p) { if (!p->size) @@ -125,10 +125,11 @@ static void prealloc(struct ps3_prealloc *p) } struct ps3_prealloc ps3fb_videomemory = { - .name = "ps3fb videomemory", - .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, - .align = 1024*1024 /* the GPU requires 1 MiB alignment */ + .name = "ps3fb videomemory", + .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024, + .align = 1024*1024 /* the GPU requires 1 MiB alignment */ }; +EXPORT_SYMBOL_GPL(ps3fb_videomemory); #define prealloc_ps3fb_videomemory() prealloc(&ps3fb_videomemory) static int __init early_parse_ps3fb(char *p) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 403dac787ebf..9b7a76be36a0 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1790,8 +1790,8 @@ config FB_IBM_GXT4500 adaptor, found on some IBM System P (pSeries) machines. config FB_PS3 - bool "PS3 GPU framebuffer driver" - depends on (FB = y) && PS3_PS3AV + tristate "PS3 GPU framebuffer driver" + depends on FB && PS3_PS3AV select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index 9cf92ba5d6e3..08b7ffbbbbd8 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,9 @@ #include #include + +#define DEVICE_NAME "ps3fb" + #ifdef PS3FB_DEBUG #define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args) #else @@ -126,7 +128,6 @@ struct gpu_driver_info { struct ps3fb_priv { unsigned int irq_no; - void *dev; u64 context_handle, memory_handle; void *xdr_ea; @@ -171,7 +172,7 @@ static const struct ps3fb_res_table ps3fb_res[] = { { 0, 0, 0, 0 , 0} }; /* default resolution */ -#define GPU_RES_INDEX 0 /* 720 x 480 */ +#define GPU_RES_INDEX 0 /* 720 x 480 */ static const struct fb_videomode ps3fb_modedb[] = { /* 60 Hz broadcast modes (modes "1" to "5") */ @@ -298,10 +299,9 @@ static const struct fb_videomode ps3fb_modedb[] = { #define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET) static int ps3fb_mode; -module_param(ps3fb_mode, bool, 0); - -static char *mode_option __initdata; +module_param(ps3fb_mode, int, 0); +static char *mode_option __devinitdata; static int ps3fb_get_res_table(u32 xres, u32 yres) { @@ -681,15 +681,15 @@ int ps3fb_wait_for_vsync(u32 crtc) EXPORT_SYMBOL_GPL(ps3fb_wait_for_vsync); -void ps3fb_flip_ctl(int on) +void ps3fb_flip_ctl(int on, void *data) { + struct ps3fb_priv *priv = data; if (on) - atomic_dec_if_positive(&ps3fb.ext_flip); + atomic_dec_if_positive(&priv->ext_flip); else - atomic_inc(&ps3fb.ext_flip); + atomic_inc(&priv->ext_flip); } -EXPORT_SYMBOL_GPL(ps3fb_flip_ctl); /* * ioctl @@ -851,37 +851,9 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -#ifndef MODULE -static int __init ps3fb_setup(char *options) -{ - char *this_opt; - int mode = 0; - - if (!options || !*options) - return 0; /* no options */ - - while ((this_opt = strsep(&options, ",")) != NULL) { - if (!*this_opt) - continue; - if (!strncmp(this_opt, "mode:", 5)) - mode = simple_strtoul(this_opt + 5, NULL, 0); - else - mode_option = this_opt; - } - return mode; -} -#endif /* MODULE */ - - /* - * Initialisation - */ -static void ps3fb_platform_release(struct device *device) -{ - /* This is called when the reference count goes to zero. */ -} - -static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) +static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, + struct ps3_system_bus_device *dev) { int error; @@ -897,7 +869,6 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) return -EINVAL; } - ps3fb.dev = dev; error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, &ps3fb.irq_no); if (error) { @@ -907,7 +878,7 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev) } error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, - "ps3fb vsync", ps3fb.dev); + DEVICE_NAME, dev); if (error) { printk(KERN_ERR "%s: request_irq failed %d\n", __func__, error); @@ -966,16 +937,45 @@ static struct fb_ops ps3fb_ops = { }; static struct fb_fix_screeninfo ps3fb_fix __initdata = { - .id = "PS3 FB", + .id = DEVICE_NAME, .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, }; -static int __init ps3fb_probe(struct platform_device *dev) +static int ps3fb_set_sync(void) +{ + int status; + +#ifdef HEAD_A + status = lv1_gpu_context_attribute(0x0, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, + 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); + if (status) { + printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_SYNC " + "failed: %d\n", __func__, status); + return -1; + } +#endif +#ifdef HEAD_B + status = lv1_gpu_context_attribute(0x0, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, + 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); + + if (status) { + printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE " + "failed: %d\n", __func__, status); + return -1; + } +#endif + return 0; +} + +static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev) { struct fb_info *info; int retval = -ENOMEM; + u32 xres, yres; u64 ddr_lpar = 0; u64 lpar_dma_control = 0; u64 lpar_driver_info = 0; @@ -986,6 +986,30 @@ static int __init ps3fb_probe(struct platform_device *dev) unsigned long offset; struct task_struct *task; + status = ps3_open_hv_device(dev); + if (status) { + printk(KERN_ERR "%s: ps3_open_hv_device failed\n", __func__); + goto err; + } + + if (!ps3fb_mode) + ps3fb_mode = ps3av_get_mode(); + DPRINTK("ps3av_mode:%d\n", ps3fb_mode); + + if (ps3fb_mode > 0 && + !ps3av_video_mode2res(ps3fb_mode, &xres, &yres)) { + ps3fb.res_index = ps3fb_get_res_table(xres, yres); + DPRINTK("res_index:%d\n", ps3fb.res_index); + } else + ps3fb.res_index = GPU_RES_INDEX; + + atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ + atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ + init_waitqueue_head(&ps3fb.wait_vsync); + ps3fb.num_frames = 1; + + ps3fb_set_sync(); + /* get gpu context handle */ status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0, &ps3fb.memory_handle, &ddr_lpar); @@ -1029,7 +1053,7 @@ static int __init ps3fb_probe(struct platform_device *dev) * leakage into userspace */ memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size); - info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); + info = framebuffer_alloc(sizeof(u32) * 16, &dev->core); if (!info) goto err_free_irq; @@ -1061,19 +1085,20 @@ static int __init ps3fb_probe(struct platform_device *dev) if (retval < 0) goto err_fb_dealloc; - platform_set_drvdata(dev, info); + dev->core.driver_data = info; printk(KERN_INFO "fb%d: PS3 frame buffer device, using %ld KiB of video memory\n", info->node, ps3fb_videomemory.size >> 10); - task = kthread_run(ps3fbd, info, "ps3fbd"); + task = kthread_run(ps3fbd, info, DEVICE_NAME); if (IS_ERR(task)) { retval = PTR_ERR(task); goto err_unregister_framebuffer; } ps3fb.task = task; + ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb); return 0; @@ -1084,7 +1109,7 @@ err_fb_dealloc: err_framebuffer_release: framebuffer_release(info); err_free_irq: - free_irq(ps3fb.irq_no, ps3fb.dev); + free_irq(ps3fb.irq_no, dev); ps3_irq_plug_destroy(ps3fb.irq_no); err_iounmap_dinfo: iounmap((u8 __iomem *)ps3fb.dinfo); @@ -1096,26 +1121,30 @@ err: return retval; } -static void ps3fb_shutdown(struct platform_device *dev) +static int ps3fb_shutdown(struct ps3_system_bus_device *dev) { - ps3fb_flip_ctl(0); /* flip off */ + int status; + struct fb_info *info = dev->core.driver_data; + + DPRINTK(" -> %s:%d\n", __func__, __LINE__); + + ps3fb_flip_ctl(0, &ps3fb); /* flip off */ ps3fb.dinfo->irq.mask = 0; - free_irq(ps3fb.irq_no, ps3fb.dev); - ps3_irq_plug_destroy(ps3fb.irq_no); - iounmap((u8 __iomem *)ps3fb.dinfo); -} -void ps3fb_cleanup(void) -{ - int status; + if (info) { + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } + ps3av_register_flip_ctl(NULL, NULL); if (ps3fb.task) { struct task_struct *task = ps3fb.task; ps3fb.task = NULL; kthread_stop(task); } if (ps3fb.irq_no) { - free_irq(ps3fb.irq_no, ps3fb.dev); + free_irq(ps3fb.irq_no, dev); ps3_irq_plug_destroy(ps3fb.irq_no); } iounmap((u8 __iomem *)ps3fb.dinfo); @@ -1128,134 +1157,69 @@ void ps3fb_cleanup(void) if (status) DPRINTK("lv1_gpu_memory_free failed: %d\n", status); - ps3av_dev_close(); -} + ps3_close_hv_device(dev); + DPRINTK(" <- %s:%d\n", __func__, __LINE__); -EXPORT_SYMBOL_GPL(ps3fb_cleanup); - -static int ps3fb_remove(struct platform_device *dev) -{ - struct fb_info *info = platform_get_drvdata(dev); - - if (info) { - unregister_framebuffer(info); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } - ps3fb_cleanup(); return 0; } -static struct platform_driver ps3fb_driver = { - .probe = ps3fb_probe, - .remove = ps3fb_remove, - .shutdown = ps3fb_shutdown, - .driver = { .name = "ps3fb" } -}; - -static struct platform_device ps3fb_device = { - .name = "ps3fb", - .id = 0, - .dev = { .release = ps3fb_platform_release } +static struct ps3_system_bus_driver ps3fb_driver = { + .match_id = PS3_MATCH_ID_GRAPHICS, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3fb_probe, + .remove = ps3fb_shutdown, + .shutdown = ps3fb_shutdown, }; -int ps3fb_set_sync(void) +static int __init ps3fb_setup(void) { - int status; + char *options; -#ifdef HEAD_A - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute DISPLAY_SYNC failed: %d\n", - __func__, status); - return -1; - } -#endif -#ifdef HEAD_B - status = lv1_gpu_context_attribute(0x0, - L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, - 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); - - if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute DISPLAY_MODE failed: %d\n", - __func__, status); - return -1; - } -#endif +#ifdef MODULE return 0; -} - -EXPORT_SYMBOL_GPL(ps3fb_set_sync); - -static int __init ps3fb_init(void) -{ - int error; -#ifndef MODULE - int mode; - char *option = NULL; - - if (fb_get_options("ps3fb", &option)) - goto err; #endif - if (!ps3fb_videomemory.address) - goto err; - - error = ps3av_dev_open(); - if (error) { - printk(KERN_ERR "%s: ps3av_dev_open failed\n", __func__); - goto err; - } + if (fb_get_options(DEVICE_NAME, &options)) + return -ENXIO; - ps3fb_mode = ps3av_get_mode(); - DPRINTK("ps3av_mode:%d\n", ps3fb_mode); -#ifndef MODULE - mode = ps3fb_setup(option); /* check boot option */ - if (mode) - ps3fb_mode = mode; -#endif - if (ps3fb_mode > 0) { - u32 xres, yres; - ps3av_video_mode2res(ps3fb_mode, &xres, &yres); - ps3fb.res_index = ps3fb_get_res_table(xres, yres); - DPRINTK("res_index:%d\n", ps3fb.res_index); - } else - ps3fb.res_index = GPU_RES_INDEX; + if (!options || !*options) + return 0; - atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ - atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ - init_waitqueue_head(&ps3fb.wait_vsync); - ps3fb.num_frames = 1; + while (1) { + char *this_opt = strsep(&options, ","); - error = platform_driver_register(&ps3fb_driver); - if (!error) { - error = platform_device_register(&ps3fb_device); - if (error) - platform_driver_unregister(&ps3fb_driver); + if (!this_opt) + break; + if (!*this_opt) + continue; + if (!strncmp(this_opt, "mode:", 5)) + ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0); + else + mode_option = this_opt; } + return 0; +} - ps3fb_set_sync(); - - return error; +static int __init ps3fb_init(void) +{ + if (!ps3fb_videomemory.address || ps3fb_setup()) + return -ENXIO; -err: - return -ENXIO; + return ps3_system_bus_driver_register(&ps3fb_driver); } -module_init(ps3fb_init); - -#ifdef MODULE static void __exit ps3fb_exit(void) { - platform_device_unregister(&ps3fb_device); - platform_driver_unregister(&ps3fb_driver); + DPRINTK(" -> %s:%d\n", __func__, __LINE__); + ps3_system_bus_driver_unregister(&ps3fb_driver); + DPRINTK(" <- %s:%d\n", __func__, __LINE__); } +module_init(ps3fb_init); module_exit(ps3fb_exit); MODULE_LICENSE("GPL"); -#endif /* MODULE */ +MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS); diff --git a/include/asm-powerpc/ps3fb.h b/include/asm-powerpc/ps3fb.h index ad81cf431964..3f121fe4010d 100644 --- a/include/asm-powerpc/ps3fb.h +++ b/include/asm-powerpc/ps3fb.h @@ -41,16 +41,4 @@ struct ps3fb_ioctl_res { __u32 num_frames; /* num of frame buffers */ }; -#ifdef __KERNEL__ - -#ifdef CONFIG_FB_PS3 -extern void ps3fb_flip_ctl(int on); -extern void ps3fb_cleanup(void); -#else -static inline void ps3fb_flip_ctl(int on) {} -static inline void ps3fb_cleanup(void) {} -#endif - -#endif /* __KERNEL__ */ - #endif /* _ASM_POWERPC_PS3FB_H_ */ -- cgit v1.2.3 From 80071802cb9c622dbd44bc6ba292f0683891ef44 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 22 Jun 2007 00:14:21 +1000 Subject: [POWERPC] PS3: Storage Driver Core Add storage driver core support for the PS3. PS3 storage devices are a special kind of PS3 system bus device. Signed-off-by: Geert Uytterhoeven Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/ps3/Kconfig | 4 + drivers/ps3/Makefile | 1 + drivers/ps3/ps3stor_lib.c | 302 +++++++++++++++++++++++++++++++++++++ include/asm-powerpc/ps3stor.h | 71 +++++++++ 4 files changed, 378 insertions(+) create mode 100644 drivers/ps3/ps3stor_lib.c create mode 100644 include/asm-powerpc/ps3stor.h (limited to 'drivers') diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 9c5a15d9c9ec..a05079b07696 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -98,4 +98,8 @@ config PS3_SYS_MANAGER This support is required for system control. In general, all users will say Y or M. +config PS3_STORAGE + depends on PPC_PS3 + tristate + endmenu diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index b8c5547adbde..746031de2195 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_PS3_PS3AV) += ps3av_mod.o ps3av_mod-objs += ps3av.o ps3av_cmd.o obj-$(CONFIG_PPC_PS3) += sys-manager-core.o obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o +obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c new file mode 100644 index 000000000000..3a9824e3b251 --- /dev/null +++ b/drivers/ps3/ps3stor_lib.c @@ -0,0 +1,302 @@ +/* + * PS3 Storage Library + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include + + +static int ps3stor_probe_access(struct ps3_storage_device *dev) +{ + int res, error; + unsigned int i; + unsigned long n; + + if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) { + /* special case: CD-ROM is assumed always accessible */ + dev->accessible_regions = 1; + return 0; + } + + error = -EPERM; + for (i = 0; i < dev->num_regions; i++) { + dev_dbg(&dev->sbd.core, + "%s:%u: checking accessibility of region %u\n", + __func__, __LINE__, i); + + dev->region_idx = i; + res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1, + 0); + if (res) { + dev_dbg(&dev->sbd.core, "%s:%u: read failed, " + "region %u is not accessible\n", __func__, + __LINE__, i); + continue; + } + + dev_dbg(&dev->sbd.core, "%s:%u: region %u is accessible\n", + __func__, __LINE__, i); + set_bit(i, &dev->accessible_regions); + + /* We can access at least one region */ + error = 0; + } + if (error) + return error; + + n = hweight_long(dev->accessible_regions); + if (n > 1) + dev_info(&dev->sbd.core, + "%s:%u: %lu accessible regions found. Only the first " + "one will be used", + __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", + dev->region_idx, dev->regions[dev->region_idx].start, + dev->regions[dev->region_idx].size); + + return 0; +} + + +/** + * ps3stor_setup - Setup a storage device before use + * @dev: Pointer to a struct ps3_storage_device + * @handler: Pointer to an interrupt handler + * + * Returns 0 for success, or an error code + */ +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); + if (error) { + dev_err(&dev->sbd.core, + "%s:%u: ps3_open_hv_device failed %d\n", __func__, + __LINE__, error); + goto fail; + } + + error = ps3_sb_event_receive_port_setup(&dev->sbd, PS3_BINDING_CPU_ANY, + &dev->irq); + if (error) { + dev_err(&dev->sbd.core, + "%s:%u: ps3_sb_event_receive_port_setup failed %d\n", + __func__, __LINE__, error); + goto fail_close_device; + } + + error = request_irq(dev->irq, handler, IRQF_DISABLED, + dev->sbd.core.driver->name, dev); + if (error) { + dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n", + __func__, __LINE__, error); + goto fail_sb_event_receive_port_destroy; + } + + alignment = min(__ffs(dev->bounce_size), + __ffs((unsigned long)dev->bounce_buf)); + if (alignment < 12) { + dev_err(&dev->sbd.core, + "%s:%u: bounce buffer not aligned (%lx at 0x%p)\n", + __func__, __LINE__, dev->bounce_size, dev->bounce_buf); + error = -EINVAL; + goto fail_free_irq; + } else if (alignment < 16) + page_size = PS3_DMA_4K; + else + page_size = PS3_DMA_64K; + dev->sbd.d_region = &dev->dma_region; + ps3_dma_region_init(&dev->sbd, &dev->dma_region, page_size, + PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size); + res = ps3_dma_region_create(&dev->dma_region); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: cannot create DMA region\n", + __func__, __LINE__); + error = -ENOMEM; + goto fail_free_irq; + } + + dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf)); + dev->bounce_dma = dma_map_single(&dev->sbd.core, dev->bounce_buf, + dev->bounce_size, DMA_BIDIRECTIONAL); + if (!dev->bounce_dma) { + dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n", + __func__, __LINE__); + error = -ENODEV; + goto fail_free_dma; + } + + error = ps3stor_probe_access(dev); + if (error) { + dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n", + __func__, __LINE__); + goto fail_unmap_dma; + } + return 0; + +fail_unmap_dma: + dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, + DMA_BIDIRECTIONAL); +fail_free_dma: + ps3_dma_region_free(&dev->dma_region); +fail_free_irq: + free_irq(dev->irq, dev); +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); +fail: + return error; +} +EXPORT_SYMBOL_GPL(ps3stor_setup); + + +/** + * ps3stor_teardown - Tear down a storage device after use + * @dev: Pointer to a struct ps3_storage_device + */ +void ps3stor_teardown(struct ps3_storage_device *dev) +{ + int error; + + dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, + DMA_BIDIRECTIONAL); + ps3_dma_region_free(&dev->dma_region); + + free_irq(dev->irq, dev); + + error = ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); + if (error) + dev_err(&dev->sbd.core, + "%s:%u: destroy event receive port failed %d\n", + __func__, __LINE__, error); + + error = ps3_close_hv_device(&dev->sbd); + if (error) + dev_err(&dev->sbd.core, + "%s:%u: ps3_close_hv_device failed %d\n", __func__, + __LINE__, error); +} +EXPORT_SYMBOL_GPL(ps3stor_teardown); + + +/** + * ps3stor_read_write_sectors - read/write from/to a storage device + * @dev: Pointer to a struct ps3_storage_device + * @lpar: HV logical partition address + * @start_sector: First sector to read/write + * @sectors: Number of sectors to read/write + * @write: Flag indicating write (non-zero) or read (zero) + * + * Returns 0 for success, -1 in case of failure to submit the command, or + * an LV1 status value in case of other errors + */ +u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, + u64 start_sector, u64 sectors, int write) +{ + unsigned int region_id = dev->regions[dev->region_idx].id; + const char *op = write ? "write" : "read"; + int res; + + dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n", + __func__, __LINE__, op, sectors, start_sector); + + init_completion(&dev->done); + res = write ? lv1_storage_write(dev->sbd.dev_id, region_id, + start_sector, sectors, 0, lpar, + &dev->tag) + : lv1_storage_read(dev->sbd.dev_id, region_id, + start_sector, sectors, 0, lpar, + &dev->tag); + if (res) { + dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, + __LINE__, op, res); + return -1; + } + + wait_for_completion(&dev->done); + if (dev->lv1_status) { + dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__, + __LINE__, op, dev->lv1_status); + return dev->lv1_status; + } + + dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, __LINE__, + op); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3stor_read_write_sectors); + + +/** + * ps3stor_send_command - send a device command to a storage device + * @dev: Pointer to a struct ps3_storage_device + * @cmd: Command number + * @arg1: First command argument + * @arg2: Second command argument + * @arg3: Third command argument + * @arg4: Fourth command argument + * + * Returns 0 for success, -1 in case of failure to submit the command, or + * an LV1 status value in case of other errors + */ +u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1, + u64 arg2, u64 arg3, u64 arg4) +{ + int res; + + dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%lx\n", __func__, + __LINE__, cmd); + + init_completion(&dev->done); + + res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, arg1, + arg2, arg3, arg4, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, + "%s:%u: send_device_command 0x%lx 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", + __func__, __LINE__, cmd, dev->lv1_status); + return dev->lv1_status; + } + + dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx completed\n", __func__, + __LINE__, cmd); + + return 0; +} +EXPORT_SYMBOL_GPL(ps3stor_send_command); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PS3 Storage Bus Library"); +MODULE_AUTHOR("Sony Corporation"); diff --git a/include/asm-powerpc/ps3stor.h b/include/asm-powerpc/ps3stor.h new file mode 100644 index 000000000000..6fcaf714fa50 --- /dev/null +++ b/include/asm-powerpc/ps3stor.h @@ -0,0 +1,71 @@ +/* + * PS3 Storage Devices + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ASM_POWERPC_PS3STOR_H_ +#define _ASM_POWERPC_PS3STOR_H_ + +#include + +#include + + +struct ps3_storage_region { + unsigned int id; + u64 start; + u64 size; +}; + +struct ps3_storage_device { + struct ps3_system_bus_device sbd; + + struct ps3_dma_region dma_region; + unsigned int irq; + u64 blk_size; + + u64 tag; + u64 lv1_status; + struct completion done; + + unsigned long bounce_size; + void *bounce_buf; + u64 bounce_lpar; + dma_addr_t bounce_dma; + + unsigned int num_regions; + unsigned long accessible_regions; + unsigned int region_idx; /* first accessible region */ + struct ps3_storage_region regions[0]; /* Must be last */ +}; + +static inline struct ps3_storage_device *to_ps3_storage_device(struct device *dev) +{ + return container_of(dev, struct ps3_storage_device, sbd.core); +} + +extern int ps3stor_setup(struct ps3_storage_device *dev, + irq_handler_t handler); +extern void ps3stor_teardown(struct ps3_storage_device *dev); +extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, + u64 start_sector, u64 sectors, + int write); +extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, + u64 arg1, u64 arg2, u64 arg3, u64 arg4); + +#endif /* _ASM_POWERPC_PS3STOR_H_ */ -- cgit v1.2.3 From 90faf4fa7964a08d36160f7bc3c252866ad1275a Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Mon, 9 Jul 2007 23:51:06 -0500 Subject: [POWERPC] CPM_UART: Use inline function form of eieio Signed-off-by: Kumar Gala --- drivers/serial/cpm_uart/cpm_uart_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b63ff8dd7304..cefde58dbad2 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -678,7 +678,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) } bdp->cbd_datlen = count; bdp->cbd_sc |= BD_SC_READY; - __asm__("eieio"); + eieio(); /* Get next BD. */ if (bdp->cbd_sc & BD_SC_WRAP) bdp = pinfo->tx_bd_base; -- cgit v1.2.3 From 80128ff79d282cf71b1819dbca9b8dd47d8ed3e8 Mon Sep 17 00:00:00 2001 From: Vitaly Bordug Date: Mon, 9 Jul 2007 11:37:35 -0700 Subject: [POWERPC] 8xx: mpc885ads pcmcia support Adds support for PowerQuicc on-chip PCMCIA. The driver is implemented as of_device, so only arch/powerpc stuff is capable to use it, which now implies only mpc885ads reference board. To cope with the code that should be hooked inside driver, but is really board specific (like set_voltage), global structure mpc8xx_pcmcia_ops holds necessary function pointers that are filled in the BSP code. [akpm@linux-foundation.org: whitespace diddles] Signed-off-by: Vitaly Bordug Acked-by: Arnd Bergmann Acked-by: Olof Johansson Cc: Dominik Brodowski Cc: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc885ads.dts | 11 + arch/powerpc/platforms/8xx/m8xx_setup.c | 5 + arch/powerpc/platforms/8xx/mpc885ads_setup.c | 71 ++++++ arch/powerpc/sysdev/fsl_soc.c | 13 + arch/powerpc/sysdev/mpc8xx_pic.h | 11 +- drivers/pcmcia/Kconfig | 17 +- drivers/pcmcia/m8xx_pcmcia.c | 351 +++++++++++++-------------- include/asm-powerpc/mpc8xx.h | 4 + include/linux/fsl_devices.h | 5 + 9 files changed, 294 insertions(+), 194 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index 110bf6170603..aee01087a932 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -112,6 +112,17 @@ compatible = "CPM"; }; + pcmcia@0080 { + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + compatible = "fsl,pq-pcmcia"; + device_type = "pcmcia"; + reg = <80 80>; + interrupt-parent = ; + interrupts = ; + }; + cpm@ff000000 { linux,phandle = ; #address-cells = <1>; diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c index 0901dbada350..f1693550c70c 100644 --- a/arch/powerpc/platforms/8xx/m8xx_setup.c +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,10 @@ #include "sysdev/mpc8xx_pic.h" +#ifdef CONFIG_PCMCIA_M8XX +struct mpc8xx_pcmcia_ops m8xx_pcmcia_ops; +#endif + void m8xx_calibrate_decr(void); extern void m8xx_wdt_handler_install(bd_t *bp); extern int cpm_pic_init(void); diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index c36e475d93dc..dc27dab48df0 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -51,6 +52,70 @@ static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi); static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi); static void init_scc3_ioports(struct fs_platform_info* ptr); +#ifdef CONFIG_PCMCIA_M8XX +static void pcmcia_hw_setup(int slot, int enable) +{ + unsigned *bcsr_io; + + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + if (enable) + clrbits32(bcsr_io, BCSR1_PCCEN); + else + setbits32(bcsr_io, BCSR1_PCCEN); + + iounmap(bcsr_io); +} + +static int pcmcia_set_voltage(int slot, int vcc, int vpp) +{ + u32 reg = 0; + unsigned *bcsr_io; + + bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); + + switch(vcc) { + case 0: + break; + case 33: + reg |= BCSR1_PCCVCC0; + break; + case 50: + reg |= BCSR1_PCCVCC1; + break; + default: + return 1; + } + + switch(vpp) { + case 0: + break; + case 33: + case 50: + if(vcc == vpp) + reg |= BCSR1_PCCVPP1; + else + return 1; + break; + case 120: + if ((vcc == 33) || (vcc == 50)) + reg |= BCSR1_PCCVPP0; + else + return 1; + default: + return 1; + } + + /* first, turn off all power */ + clrbits32(bcsr_io, 0x00610000); + + /* enable new powersettings */ + setbits32(bcsr_io, reg); + + iounmap(bcsr_io); + return 0; +} +#endif + void __init mpc885ads_board_setup(void) { cpm8xx_t *cp; @@ -115,6 +180,12 @@ void __init mpc885ads_board_setup(void) immr_unmap(io_port); #endif + +#ifdef CONFIG_PCMCIA_M8XX + /*Set up board specific hook-ups*/ + m8xx_pcmcia_ops.hw_ctrl = pcmcia_hw_setup; + m8xx_pcmcia_ops.voltage_set = pcmcia_set_voltage; +#endif } diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index cad175724359..c0ddc80d8160 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -1028,6 +1028,19 @@ err: arch_initcall(fs_enet_of_init); +static int __init fsl_pcmcia_of_init(void) +{ + struct device_node *np = NULL; + /* + * Register all the devices which type is "pcmcia" + */ + while ((np = of_find_compatible_node(np, + "pcmcia", "fsl,pq-pcmcia")) != NULL) + of_platform_device_create(np, "m8xx-pcmcia", NULL); + return 0; +} + +arch_initcall(fsl_pcmcia_of_init); static const char *smc_regs = "regs"; static const char *smc_pram = "pram"; diff --git a/arch/powerpc/sysdev/mpc8xx_pic.h b/arch/powerpc/sysdev/mpc8xx_pic.h index afa2ee6717c1..9fe00eebdc8b 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.h +++ b/arch/powerpc/sysdev/mpc8xx_pic.h @@ -4,9 +4,16 @@ #include #include -extern struct hw_interrupt_type mpc8xx_pic; - int mpc8xx_pic_init(void); unsigned int mpc8xx_get_irq(void); +/* + * Some internal interrupt registers use an 8-bit mask for the interrupt + * level instead of a number. + */ +static inline uint mk_int_int_mask(uint mask) +{ + return (1 << (7 - (mask/2))); +} + #endif /* _PPC_KERNEL_PPC8xx_H */ diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 35f88649d3b7..c0c77f82d051 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -180,14 +180,15 @@ config TCIC PCMCIA cards are plugged into. If unsure, say N. config PCMCIA_M8XX - tristate "MPC8xx PCMCIA support" - depends on PCMCIA && PPC && 8xx - select PCCARD_IODYN - help - Say Y here to include support for PowerPC 8xx series PCMCIA - controller. - - This driver is also available as a module called m8xx_pcmcia. + tristate "MPC8xx PCMCIA support" + depends on PCMCIA && PPC && 8xx + select PCCARD_IODYN + select PCCARD_NONSTATIC + help + Say Y here to include support for PowerPC 8xx series PCMCIA + controller. + + This driver is also available as a module called m8xx_pcmcia. config HD64465_PCMCIA tristate "HD64465 host bridge support" diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 9721ed7bf502..3b40f9623cc9 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -10,7 +10,7 @@ * Further fixes, v2.6 kernel port * * - * Some fixes, additions (C) 2005 Montavista Software, Inc. + * Some fixes, additions (C) 2005-2007 Montavista Software, Inc. * * * "The ExCA standard specifies that socket controllers should provide @@ -40,10 +40,6 @@ #include #include -#include -#include -#include - #include #include #include @@ -51,11 +47,18 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include #include #include @@ -146,27 +149,17 @@ MODULE_LICENSE("Dual MPL/GPL"); #define PCMCIA_MEM_WIN_BASE 0xe0000000 /* base address for memory window 0 */ #define PCMCIA_MEM_WIN_SIZE 0x04000000 /* each memory window is 64 MByte */ #define PCMCIA_IO_WIN_BASE _IO_BASE /* base address for io window 0 */ - -#define PCMCIA_SCHLVL PCMCIA_INTERRUPT /* Status Change Interrupt Level */ - /* ------------------------------------------------------------------------- */ -/* 2.4.x and newer has this always in HZ */ -#define M8XX_BUSFREQ ((((bd_t *)&(__res))->bi_busfreq)) - -static int pcmcia_schlvl = PCMCIA_SCHLVL; +static int pcmcia_schlvl; static DEFINE_SPINLOCK(events_lock); - #define PCMCIA_SOCKET_KEY_5V 1 #define PCMCIA_SOCKET_KEY_LV 2 /* look up table for pgcrx registers */ -static u32 *m8xx_pgcrx[2] = { - &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pgcra, - &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pgcrb -}; +static u32 *m8xx_pgcrx[2]; /* * This structure is used to address each window in the PCMCIA controller. @@ -228,11 +221,16 @@ struct event_table { u32 eventbit; }; +static const char driver_name[] = "m8xx-pcmcia"; + struct socket_info { void (*handler)(void *info, u32 events); void *info; u32 slot; + pcmconf8xx_t *pcmcia; + u32 bus_freq; + int hwirq; socket_state_t state; struct pccard_mem_map mem_win[PCMCIA_MEM_WIN_NO]; @@ -408,78 +406,21 @@ static void hardware_disable(int slot) #if defined(CONFIG_MPC885ADS) #define PCMCIA_BOARD_MSG "MPC885ADS" +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V -static int voltage_set(int slot, int vcc, int vpp) +static inline void hardware_enable(int slot) { - u32 reg = 0; - unsigned *bcsr_io; - - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - - switch(vcc) { - case 0: - break; - case 33: - reg |= BCSR1_PCCVCC0; - break; - case 50: - reg |= BCSR1_PCCVCC1; - break; - default: - goto out_unmap; - } - - switch(vpp) { - case 0: - break; - case 33: - case 50: - if(vcc == vpp) - reg |= BCSR1_PCCVPP1; - else - goto out_unmap; - break; - case 120: - if ((vcc == 33) || (vcc == 50)) - reg |= BCSR1_PCCVPP0; - else - goto out_unmap; - default: - goto out_unmap; - } - - /* first, turn off all power */ - out_be32(bcsr_io, in_be32(bcsr_io) & ~(BCSR1_PCCVCC_MASK | BCSR1_PCCVPP_MASK)); - - /* enable new powersettings */ - out_be32(bcsr_io, in_be32(bcsr_io) | reg); - - iounmap(bcsr_io); - return 0; - -out_unmap: - iounmap(bcsr_io); - return 1; + m8xx_pcmcia_ops.hw_ctrl(slot, 1); } -#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V - -static void hardware_enable(int slot) +static inline void hardware_disable(int slot) { - unsigned *bcsr_io; - - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - out_be32(bcsr_io, in_be32(bcsr_io) & ~BCSR1_PCCEN); - iounmap(bcsr_io); + m8xx_pcmcia_ops.hw_ctrl(slot, 0); } -static void hardware_disable(int slot) +static inline int voltage_set(int slot, int vcc, int vpp) { - unsigned *bcsr_io; - - bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); - out_be32(bcsr_io, in_be32(bcsr_io) | BCSR1_PCCEN); - iounmap(bcsr_io); + return m8xx_pcmcia_ops.voltage_set(slot, vcc, vpp); } #endif @@ -604,48 +545,6 @@ static int voltage_set(int slot, int vcc, int vpp) #endif /* CONFIG_PRxK */ -static void m8xx_shutdown(void) -{ - u32 m, i; - struct pcmcia_win *w; - - for(i = 0; i < PCMCIA_SOCKETS_NO; i++){ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; - - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, M8XX_PCMCIA_MASK(i)); - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per) & ~M8XX_PCMCIA_MASK(i)); - - /* turn off interrupt and disable CxOE */ - out_be32(M8XX_PGCRX(i), M8XX_PGCRX_CXOE); - - /* turn off memory windows */ - for(m = 0; m < PCMCIA_MEM_WIN_NO; m++) { - out_be32(&w->or, 0); /* set to not valid */ - w++; - } - - /* turn off voltage */ - voltage_set(i, 0, 0); - - /* disable external hardware */ - hardware_disable(i); - } - - free_irq(pcmcia_schlvl, NULL); -} - -static struct device_driver m8xx_driver = { - .name = "m8xx-pcmcia", - .bus = &platform_bus_type, - .suspend = pcmcia_socket_dev_suspend, - .resume = pcmcia_socket_dev_resume, -}; - -static struct platform_device m8xx_device = { - .name = "m8xx-pcmcia", - .id = 0, -}; - static u32 pending_events[PCMCIA_SOCKETS_NO]; static DEFINE_SPINLOCK(pending_event_lock); @@ -654,13 +553,14 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev) struct socket_info *s; struct event_table *e; unsigned int i, events, pscr, pipr, per; + pcmconf8xx_t *pcmcia = socket[0].pcmcia; dprintk("Interrupt!\n"); /* get interrupt sources */ - pscr = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr); - pipr = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr); - per = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per); + pscr = in_be32(&pcmcia->pcmc_pscr); + pipr = in_be32(&pcmcia->pcmc_pipr); + per = in_be32(&pcmcia->pcmc_per); for(i = 0; i < PCMCIA_SOCKETS_NO; i++) { s = &socket[i]; @@ -724,7 +624,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev) per &= ~M8XX_PCMCIA_RDY_L(0); per &= ~M8XX_PCMCIA_RDY_L(1); - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, per); + out_be32(&pcmcia->pcmc_per, per); if (events) pcmcia_parse_events(&socket[i].socket, events); @@ -732,7 +632,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev) } /* clear the interrupt sources */ - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, pscr); + out_be32(&pcmcia->pcmc_pscr, pscr); dprintk("Interrupt done.\n"); @@ -753,7 +653,7 @@ static u32 m8xx_get_graycode(u32 size) return k; } -static u32 m8xx_get_speed(u32 ns, u32 is_io) +static u32 m8xx_get_speed(u32 ns, u32 is_io, u32 bus_freq) { u32 reg, clocks, psst, psl, psht; @@ -781,7 +681,7 @@ static u32 m8xx_get_speed(u32 ns, u32 is_io) #define ADJ 180 /* 80 % longer accesstime - to be sure */ - clocks = ((M8XX_BUSFREQ / 1000) * ns) / 1000; + clocks = ((bus_freq / 1000) * ns) / 1000; clocks = (clocks * ADJ) / (100*1000); if(clocks >= PCMCIA_BMT_LIMIT) { printk( "Max access time limit reached\n"); @@ -806,8 +706,9 @@ static int m8xx_get_status(struct pcmcia_socket *sock, unsigned int *value) int lsock = container_of(sock, struct socket_info, socket)->slot; struct socket_info *s = &socket[lsock]; unsigned int pipr, reg; + pcmconf8xx_t *pcmcia = s->pcmcia; - pipr = in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr); + pipr = in_be32(&pcmcia->pcmc_pipr); *value = ((pipr & (M8XX_PCMCIA_CD1(lsock) | M8XX_PCMCIA_CD2(lsock))) == 0) ? SS_DETECT : 0; @@ -918,6 +819,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) struct event_table *e; unsigned int reg; unsigned long flags; + pcmconf8xx_t *pcmcia = socket[0].pcmcia; dprintk( "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x)\n", lsock, state->flags, @@ -927,6 +829,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) if(voltage_set(lsock, state->Vcc, state->Vpp)) return -EINVAL; + /* Take care of reset... */ if(state->flags & SS_RESET) out_be32(M8XX_PGCRX(lsock), in_be32(M8XX_PGCRX(lsock)) | M8XX_PGCRX_CXRESET); /* active high */ @@ -982,7 +885,8 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * If io_irq is non-zero we should enable irq. */ if(state->io_irq) { - out_be32(M8XX_PGCRX(lsock), in_be32(M8XX_PGCRX(lsock)) | mk_int_int_mask(state->io_irq) << 24); + out_be32(M8XX_PGCRX(lsock), + in_be32(M8XX_PGCRX(lsock)) | mk_int_int_mask(s->hwirq) << 24); /* * Strange thing here: * The manual does not tell us which interrupt @@ -1027,7 +931,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * Writing ones will clear the bits. */ - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, reg); + out_be32(&pcmcia->pcmc_pscr, reg); /* * Write the mask. @@ -1036,15 +940,8 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * Ones will enable the interrupt. */ - /* - reg |= ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per - & M8XX_PCMCIA_MASK(lsock); - */ - - reg |= in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per) & - (M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); - - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, reg); + reg |= in_be32(&pcmcia->pcmc_per) & (M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); + out_be32(&pcmcia->pcmc_per, reg); spin_unlock_irqrestore(&events_lock, flags); @@ -1062,6 +959,8 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) struct socket_info *s = &socket[lsock]; struct pcmcia_win *w; unsigned int reg, winnr; + pcmconf8xx_t *pcmcia = s->pcmcia; + #define M8XX_SIZE (io->stop - io->start + 1) #define M8XX_BASE (PCMCIA_IO_WIN_BASE + io->start) @@ -1086,7 +985,7 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) /* setup registers */ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; w += winnr; out_be32(&w->or, 0); /* turn off window first */ @@ -1095,12 +994,13 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) reg <<= 27; reg |= M8XX_PCMCIA_POR_IO |(lsock << 2); - reg |= m8xx_get_speed(io->speed, 1); + reg |= m8xx_get_speed(io->speed, 1, s->bus_freq); if(io->flags & MAP_WRPROT) reg |= M8XX_PCMCIA_POR_WRPROT; - if(io->flags & (MAP_16BIT | MAP_AUTOSZ)) + /*if(io->flags & (MAP_16BIT | MAP_AUTOSZ))*/ + if(io->flags & MAP_16BIT) reg |= M8XX_PCMCIA_POR_16BIT; if(io->flags & MAP_ACTIVE) @@ -1117,7 +1017,7 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) /* setup registers */ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; w += winnr; out_be32(&w->or, 0); /* turn off window */ @@ -1144,6 +1044,7 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *m struct pcmcia_win *w; struct pccard_mem_map *old; unsigned int reg, winnr; + pcmconf8xx_t *pcmcia = s->pcmcia; dprintk( "SetMemMap(%d, %d, %#2.2x, %d ns, " "%#5.5lx, %#5.5x)\n", lsock, mem->map, mem->flags, @@ -1166,12 +1067,12 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *m /* Setup the window in the pcmcia controller */ - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; + w = (void *) &pcmcia->pcmc_pbr0; w += winnr; reg |= lsock << 2; - reg |= m8xx_get_speed(mem->speed, 0); + reg |= m8xx_get_speed(mem->speed, 0, s->bus_freq); if(mem->flags & MAP_ATTRIB) reg |= M8XX_PCMCIA_POR_ATTRMEM; @@ -1236,60 +1137,69 @@ static int m8xx_sock_init(struct pcmcia_socket *sock) } -static int m8xx_suspend(struct pcmcia_socket *sock) +static int m8xx_sock_suspend(struct pcmcia_socket *sock) { return m8xx_set_socket(sock, &dead_socket); } static struct pccard_operations m8xx_services = { .init = m8xx_sock_init, - .suspend = m8xx_suspend, + .suspend = m8xx_sock_suspend, .get_status = m8xx_get_status, .set_socket = m8xx_set_socket, .set_io_map = m8xx_set_io_map, .set_mem_map = m8xx_set_mem_map, }; -static int __init m8xx_init(void) +static int __init m8xx_probe(struct of_device *ofdev, const struct of_device_id *match) { struct pcmcia_win *w; - unsigned int i,m; + unsigned int i, m, hwirq; + pcmconf8xx_t *pcmcia; + int status; + struct device_node *np = ofdev->node; pcmcia_info("%s\n", version); - if (driver_register(&m8xx_driver)) - return -1; + pcmcia = of_iomap(np, 0); + if(pcmcia == NULL) + return -EINVAL; + + pcmcia_schlvl = irq_of_parse_and_map(np, 0); + hwirq = irq_map[pcmcia_schlvl].hwirq; + if (pcmcia_schlvl < 0) + return -EINVAL; + + m8xx_pgcrx[0] = &pcmcia->pcmc_pgcra; + m8xx_pgcrx[1] = &pcmcia->pcmc_pgcrb; + pcmcia_info(PCMCIA_BOARD_MSG " using " PCMCIA_SLOT_MSG - " with IRQ %u.\n", pcmcia_schlvl); + " with IRQ %u (%d). \n", pcmcia_schlvl, hwirq); /* Configure Status change interrupt */ - if(request_irq(pcmcia_schlvl, m8xx_interrupt, 0, - "m8xx_pcmcia", NULL)) { + if(request_irq(pcmcia_schlvl, m8xx_interrupt, IRQF_SHARED, + driver_name, socket)) { pcmcia_error("Cannot allocate IRQ %u for SCHLVL!\n", pcmcia_schlvl); return -1; } - w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0; - - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr, - M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1)); + w = (void *) &pcmcia->pcmc_pbr0; - out_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per, - in_be32(&((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per) & - ~(M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1))); + out_be32(&pcmcia->pcmc_pscr, M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1)); + clrbits32(&pcmcia->pcmc_per, M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); -/* connect interrupt and disable CxOE */ + /* connect interrupt and disable CxOE */ - out_be32(M8XX_PGCRX(0), M8XX_PGCRX_CXOE | (mk_int_int_mask(pcmcia_schlvl) << 16)); - out_be32(M8XX_PGCRX(1), M8XX_PGCRX_CXOE | (mk_int_int_mask(pcmcia_schlvl) << 16)); + out_be32(M8XX_PGCRX(0), M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16)); + out_be32(M8XX_PGCRX(1), M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16)); -/* intialize the fixed memory windows */ + /* intialize the fixed memory windows */ for(i = 0; i < PCMCIA_SOCKETS_NO; i++){ - for(m = 0; m < PCMCIA_MEM_WIN_NO; m++) { + for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) { out_be32(&w->br, PCMCIA_MEM_WIN_BASE + (PCMCIA_MEM_WIN_SIZE * (m + i * PCMCIA_MEM_WIN_NO))); @@ -1300,16 +1210,14 @@ static int __init m8xx_init(void) } } -/* turn off voltage */ + /* turn off voltage */ voltage_set(0, 0, 0); voltage_set(1, 0, 0); -/* Enable external hardware */ + /* Enable external hardware */ hardware_enable(0); hardware_enable(1); - platform_device_register(&m8xx_device); - for (i = 0 ; i < PCMCIA_SOCKETS_NO; i++) { socket[i].slot = i; socket[i].socket.owner = THIS_MODULE; @@ -1317,30 +1225,105 @@ static int __init m8xx_init(void) socket[i].socket.irq_mask = 0x000; socket[i].socket.map_size = 0x1000; socket[i].socket.io_offset = 0; - socket[i].socket.pci_irq = i ? 7 : 9; + socket[i].socket.pci_irq = pcmcia_schlvl; socket[i].socket.ops = &m8xx_services; - socket[i].socket.resource_ops = &pccard_iodyn_ops; + socket[i].socket.resource_ops = &pccard_nonstatic_ops; socket[i].socket.cb_dev = NULL; - socket[i].socket.dev.parent = &m8xx_device.dev; + socket[i].socket.dev.parent = &ofdev->dev; + socket[i].pcmcia = pcmcia; + socket[i].bus_freq = ppc_proc_freq; + socket[i].hwirq = hwirq; + + } - for (i = 0; i < PCMCIA_SOCKETS_NO; i++) - pcmcia_register_socket(&socket[i].socket); + for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { + status = pcmcia_register_socket(&socket[i].socket); + if (status < 0) + pcmcia_error("Socket register failed\n"); + } return 0; } -static void __exit m8xx_exit(void) +static int m8xx_remove(struct of_device* ofdev) { - int i; + u32 m, i; + struct pcmcia_win *w; + pcmconf8xx_t *pcmcia = socket[0].pcmcia; + + for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { + w = (void *) &pcmcia->pcmc_pbr0; + + out_be32(&pcmcia->pcmc_pscr, M8XX_PCMCIA_MASK(i)); + out_be32(&pcmcia->pcmc_per, + in_be32(&pcmcia->pcmc_per) & ~M8XX_PCMCIA_MASK(i)); + /* turn off interrupt and disable CxOE */ + out_be32(M8XX_PGCRX(i), M8XX_PGCRX_CXOE); + + /* turn off memory windows */ + for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) { + out_be32(&w->or, 0); /* set to not valid */ + w++; + } + + /* turn off voltage */ + voltage_set(i, 0, 0); + + /* disable external hardware */ + hardware_disable(i); + } for (i = 0; i < PCMCIA_SOCKETS_NO; i++) pcmcia_unregister_socket(&socket[i].socket); - m8xx_shutdown(); + free_irq(pcmcia_schlvl, NULL); - platform_device_unregister(&m8xx_device); - driver_unregister(&m8xx_driver); + return 0; +} + +#ifdef CONFIG_PM +static int m8xx_suspend(struct platform_device *pdev, pm_message_t state) +{ + return pcmcia_socket_dev_suspend(&pdev->dev, state); +} + +static int m8xx_resume(struct platform_device *pdev) +{ + return pcmcia_socket_dev_resume(&pdev->dev); +} +#else +#define m8xx_suspend NULL +#define m8xx_resume NULL +#endif + +static struct of_device_id m8xx_pcmcia_match[] = { + { + .type = "pcmcia", + .compatible = "fsl,pq-pcmcia", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); + +static struct of_platform_driver m8xx_pcmcia_driver = { + .name = (char *) driver_name, + .match_table = m8xx_pcmcia_match, + .probe = m8xx_probe, + .remove = m8xx_remove, + .suspend = m8xx_suspend, + .resume = m8xx_resume, +}; + +static int __init m8xx_init(void) +{ + return of_register_platform_driver(&m8xx_pcmcia_driver); +} + +static void __exit m8xx_exit(void) +{ + of_unregister_platform_driver(&m8xx_pcmcia_driver); } module_init(m8xx_init); diff --git a/include/asm-powerpc/mpc8xx.h b/include/asm-powerpc/mpc8xx.h index 580371120e1a..2be014b6f57c 100644 --- a/include/asm-powerpc/mpc8xx.h +++ b/include/asm-powerpc/mpc8xx.h @@ -23,6 +23,10 @@ #include #endif +#ifdef CONFIG_PCMCIA_M8XX +extern struct mpc8xx_pcmcia_ops m8xx_pcmcia_ops; +#endif + #endif /* CONFIG_8xx */ #endif /* __CONFIG_8xx_DEFS */ #endif /* __KERNEL__ */ diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 73710d617775..12e631f0fb77 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -120,5 +120,10 @@ struct fsl_spi_platform_data { u32 sysclk; }; +struct mpc8xx_pcmcia_ops { + void(*hw_ctrl)(int slot, int enable); + int(*voltage_set)(int slot, int vcc, int vpp); +}; + #endif /* _FSL_DEVICE_H_ */ #endif /* __KERNEL__ */ -- cgit v1.2.3 From 36a700307ece3ca090d1f8f071db085087ddc627 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 29 May 2007 16:06:54 +1000 Subject: [POWERPC] Fix viodasd geometry calculations Commit a885c8c4316e1c1d2d2c8755da3f3d14f852528d that introduced the getgeo block device method changed the fallback number of sectors and introduced a bug into the fallback cylinder number calculation. Thanks to Rusty Russell for noticing this. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- drivers/block/viodasd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index 68592c336011..dae39911a11d 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -252,10 +252,10 @@ static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) struct gendisk *disk = bdev->bd_disk; struct viodasd_device *d = disk->private_data; - geo->sectors = d->sectors ? d->sectors : 0; + geo->sectors = d->sectors ? d->sectors : 32; geo->heads = d->tracks ? d->tracks : 64; geo->cylinders = d->cylinders ? d->cylinders : - get_capacity(disk) / (geo->cylinders * geo->heads); + get_capacity(disk) / (geo->sectors * geo->heads); return 0; } -- cgit v1.2.3 From f716a425c15ebadf60286cd4fb60d1d6f46e3cf9 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Wed, 30 May 2007 11:29:06 +1000 Subject: [POWERPC] VIOTAPE: Use designated initializers for fops member structures. Replace the old-style member initializers with the newer designated initializers. Signed-off-by: Robert P. J. Day Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- drivers/char/viotape.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index 13faf8d17482..db57277117bb 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -873,12 +873,12 @@ free_op: } const struct file_operations viotap_fops = { - owner: THIS_MODULE, - read: viotap_read, - write: viotap_write, - ioctl: viotap_ioctl, - open: viotap_open, - release: viotap_release, + .owner = THIS_MODULE, + .read = viotap_read, + .write = viotap_write, + .ioctl = viotap_ioctl, + .open = viotap_open, + .release = viotap_release, }; /* Handle interrupt events for tape */ -- cgit v1.2.3 From 1558f9b44e1c6b5eba46f58214851fd24285641f Mon Sep 17 00:00:00 2001 From: Ishizaki Kou Date: Thu, 31 May 2007 19:30:33 +1000 Subject: [POWERPC] of_serial: Ignore unknown serial port Current of_serial driver assumes unknown serial port to be 8250. But this behavior may cause fatal problems when the serial port is not 8250. This corrects probe routine to ignore unknown serial port. Signed-off-by: Kou Ishizaki Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- drivers/serial/of_serial.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 7ffdaeaf0545..d7752af1c7ec 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -75,15 +75,13 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, goto out; switch (port_type) { - case PORT_UNKNOWN: - dev_info(&ofdev->dev, "Unknown serial port found, " - "attempting to use 8250 driver\n"); - /* fallthrough */ case PORT_8250 ... PORT_MAX_8250: ret = serial8250_register_port(&port); break; default: /* need to add code for these */ + case PORT_UNKNOWN: + dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); ret = -ENODEV; break; } -- cgit v1.2.3 From e34b9c94b6e8d63f94387f5148434db5f8f79084 Mon Sep 17 00:00:00 2001 From: Ishizaki Kou Date: Thu, 31 May 2007 19:33:04 +1000 Subject: [POWERPC] of_serial: add port type checking Currently, of_serial driver supports only 8250 compatible ports, but prepares that others can easily be added. Yet unregister routine assumes port is 8250. This corrects unregister routine to check port type. Signed-off-by: Kou Ishizaki Acked-by: Arnd Bergmann Signed-off-by: Paul Mackerras --- drivers/serial/of_serial.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index d7752af1c7ec..a64d85821996 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -17,6 +17,11 @@ #include #include +struct of_serial_info { + int type; + int line; +}; + /* * Fill a struct uart_port for a given device node */ @@ -62,6 +67,7 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev, static int __devinit of_platform_serial_probe(struct of_device *ofdev, const struct of_device_id *id) { + struct of_serial_info *info; struct uart_port port; int port_type; int ret; @@ -69,6 +75,10 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, if (of_find_property(ofdev->node, "used-by-rtas", NULL)) return -EBUSY; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + port_type = (unsigned long)id->data; ret = of_platform_serial_setup(ofdev, port_type, &port); if (ret) @@ -88,9 +98,12 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, if (ret < 0) goto out; - ofdev->dev.driver_data = (void *)(unsigned long)ret; + info->type = port_type; + info->line = ret; + ofdev->dev.driver_data = info; return 0; out: + kfree(info); irq_dispose_mapping(port.irq); return ret; } @@ -100,8 +113,16 @@ out: */ static int of_platform_serial_remove(struct of_device *ofdev) { - int line = (unsigned long)ofdev->dev.driver_data; - serial8250_unregister_port(line); + struct of_serial_info *info = ofdev->dev.driver_data; + switch (info->type) { + case PORT_8250 ... PORT_MAX_8250: + serial8250_unregister_port(info->line); + break; + default: + /* need to add code for these */ + break; + } + kfree(info); return 0; } -- cgit v1.2.3 From 57cc705f3920d39d96ed9b8373aa7ff2cb93434f Mon Sep 17 00:00:00 2001 From: Wade Farnsworth Date: Thu, 7 Jun 2007 02:57:43 +1000 Subject: [POWERPC] Fix drivers/rtc/Kconfig for powerpc The powerpc arch uses CONFIG_PPC instead of CONFIG_POWERPC. This fixes drivers/rtc/Kconfig to use the correct config option. Signed-off-by: Wade Farnsworth Acked-by: Jon Loeliger Signed-off-by: Paul Mackerras --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4e4c10a7fd3a..83b071b6ece4 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -246,7 +246,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ - || M32R || ATARI || POWERPC || MIPS) + || M32R || ATARI || PPC || MIPS) help Say "yes" here to get direct support for the real time clock found in every PC or ACPI-based system, and some other boards. -- cgit v1.2.3 From 32efddf8526879a77d58aa635c5d5dd45bb87fd9 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Mon, 9 Jul 2007 11:37:37 -0700 Subject: [POWERPC] Remove dead code for preventing pread() and pwrite() calls Remove the deactivated code for checking for pread() and pwrite() calls on the PPC-based BRIQ. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras --- drivers/char/briq_panel.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c index ed53f541d9e8..b6f2639f903d 100644 --- a/drivers/char/briq_panel.c +++ b/drivers/char/briq_panel.c @@ -91,11 +91,6 @@ static ssize_t briq_panel_read(struct file *file, char __user *buf, size_t count unsigned short c; unsigned char cp; -#if 0 /* Can't seek (pread) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; -#endif - if (!vfd_is_open) return -ENODEV; @@ -139,11 +134,6 @@ static ssize_t briq_panel_write(struct file *file, const char __user *buf, size_ size_t indx = len; int i, esc = 0; -#if 0 /* Can't seek (pwrite) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; -#endif - if (!vfd_is_open) return -EBUSY; -- cgit v1.2.3 From 5605d4def1b33721c268067620b163af0a9e2474 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 9 Jul 2007 11:37:38 -0700 Subject: [POWERPC] Make drivers/char/hvc_console.c:khvcd() static Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Paul Mackerras --- drivers/char/hvc_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 322bc5f7d86b..b3ab42e0dd4a 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -674,7 +674,7 @@ static const cpumask_t cpus_in_xmon = CPU_MASK_NONE; * calling hvc_poll() who determines whether a console adapter support * interrupts. */ -int khvcd(void *unused) +static int khvcd(void *unused) { int poll_mask; struct hvc_struct *hp; -- cgit v1.2.3