+- update ps3 patches
authorOlaf Hering <olh@suse.de>
Fri, 25 May 2007 16:12:28 +0000 (16:12 +0000)
committerOlaf Hering <olh@suse.de>
Fri, 25 May 2007 16:12:28 +0000 (16:12 +0000)
+  new storage driver provides /dev/ps3da instead of /dev/sda

suse-commit: c29488737fe741e79a010b8f23a65a051ffa4cb3

63 files changed:
Documentation/fb/deferred_io.txt [new file with mode: 0644]
arch/powerpc/platforms/cell/spu_base.c
arch/powerpc/platforms/cell/spufs/context.c
arch/powerpc/platforms/cell/spufs/file.c
arch/powerpc/platforms/cell/spufs/inode.c
arch/powerpc/platforms/cell/spufs/spufs.h
arch/powerpc/platforms/ps3/Kconfig
arch/powerpc/platforms/ps3/Makefile
arch/powerpc/platforms/ps3/device-init.c
arch/powerpc/platforms/ps3/htab.c
arch/powerpc/platforms/ps3/interrupt.c
arch/powerpc/platforms/ps3/mm.c
arch/powerpc/platforms/ps3/platform.h
arch/powerpc/platforms/ps3/setup.c
arch/powerpc/platforms/ps3/system-bus-rework.txt [new file with mode: 0644]
arch/powerpc/platforms/ps3/system-bus.c
drivers/block/Kconfig
drivers/block/Makefile
drivers/block/ps3_storage.c
drivers/block/ps3disk.c [new file with mode: 0644]
drivers/char/Makefile
drivers/char/ps3flash.c [new file with mode: 0644]
drivers/char/vt.c
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/gelic_net.c
drivers/net/gelic_net.h [new file with mode: 0644]
drivers/net/gelic_wireless.c [new file with mode: 0644]
drivers/ps3/Makefile
drivers/ps3/ps3av.c
drivers/ps3/ps3av_cmd.c
drivers/ps3/ps3stor_lib.c [new file with mode: 0644]
drivers/ps3/sys-manager-core.c [new file with mode: 0644]
drivers/ps3/sys-manager.c
drivers/ps3/vuart.c
drivers/scsi/Makefile
drivers/scsi/ps3rom.c [new file with mode: 0644]
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-ps3.c
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/arcfb.c
drivers/video/console/fbcon.c
drivers/video/epson1355fb.c
drivers/video/fb_defio.c [new file with mode: 0644]
drivers/video/fb_sys_fops.c [new file with mode: 0644]
drivers/video/fbmem.c
drivers/video/ps3fb.c
drivers/video/pvr2fb.c
drivers/video/syscopyarea.c [new file with mode: 0644]
drivers/video/sysfillrect.c [new file with mode: 0644]
drivers/video/sysimgblt.c [new file with mode: 0644]
include/asm-powerpc/lv1call.h
include/asm-powerpc/ps3.h
include/asm-powerpc/ps3av.h
include/asm-powerpc/ps3fb.h
include/asm-powerpc/ps3stor.h [new file with mode: 0644]
include/linux/fb.h
include/net/bluetooth/hci.h
mm/rmap.c
net/bluetooth/hci_core.c

diff --git a/Documentation/fb/deferred_io.txt b/Documentation/fb/deferred_io.txt
new file mode 100644 (file)
index 0000000..73cf9fb
--- /dev/null
@@ -0,0 +1,75 @@
+Deferred IO
+-----------
+
+Deferred IO is a way to delay and repurpose IO. It uses host memory as a
+buffer and the MMU pagefault as a pretrigger for when to perform the device
+IO. The following example may be a useful explaination of how one such setup
+works:
+
+- userspace app like Xfbdev mmaps framebuffer
+- deferred IO and driver sets up nopage and page_mkwrite handlers
+- userspace app tries to write to mmaped vaddress
+- we get pagefault and reach nopage handler
+- nopage handler finds and returns physical page
+- we get page_mkwrite where we add this page to a list
+- schedule a workqueue task to be run after a delay
+- app continues writing to that page with no additional cost. this is
+  the key benefit.
+- the workqueue task comes in and mkcleans the pages on the list, then
+ completes the work associated with updating the framebuffer. this is
+  the real work talking to the device.
+- app tries to write to the address (that has now been mkcleaned)
+- get pagefault and the above sequence occurs again
+
+As can be seen from above, one benefit is roughly to allow bursty framebuffer
+writes to occur at minimum cost. Then after some time when hopefully things
+have gone quiet, we go and really update the framebuffer which would be
+a relatively more expensive operation.
+
+For some types of nonvolatile high latency displays, the desired image is
+the final image rather than the intermediate stages which is why it's okay
+to not update for each write that is occuring.
+
+It may be the case that this is useful in other scenarios as well. Paul Mundt
+has mentioned a case where it is beneficial to use the page count to decide
+whether to coalesce and issue SG DMA or to do memory bursts.
+
+Another one may be if one has a device framebuffer that is in an usual format,
+say diagonally shifting RGB, this may then be a mechanism for you to allow
+apps to pretend to have a normal framebuffer but reswizzle for the device
+framebuffer at vsync time based on the touched pagelist.
+
+How to use it: (for applications)
+---------------------------------
+No changes needed. mmap the framebuffer like normal and just use it.
+
+How to use it: (for fbdev drivers)
+----------------------------------
+The following example may be helpful.
+
+1. Setup your structure. Eg:
+
+static struct fb_deferred_io hecubafb_defio = {
+       .delay          = HZ,
+       .deferred_io    = hecubafb_dpy_deferred_io,
+};
+
+The delay is the minimum delay between when the page_mkwrite trigger occurs
+and when the deferred_io callback is called. The deferred_io callback is
+explained below.
+
+2. Setup your deferred IO callback. Eg:
+static void hecubafb_dpy_deferred_io(struct fb_info *info,
+                               struct list_head *pagelist)
+
+The deferred_io callback is where you would perform all your IO to the display
+device. You receive the pagelist which is the list of pages that were written
+to during the delay. You must not modify this list. This callback is called
+from a workqueue.
+
+3. Call init
+       info->fbdefio = &hecubafb_defio;
+       fb_deferred_io_init(info);
+
+4. Call cleanup
+       fb_deferred_io_cleanup(info);
index 9c45d3c..e595b6b 100644 (file)
@@ -36,6 +36,8 @@
 #include <asm/xmon.h>
 
 const struct spu_management_ops *spu_management_ops;
+EXPORT_SYMBOL_GPL(spu_management_ops);
+
 const struct spu_priv1_ops *spu_priv1_ops;
 
 static struct list_head spu_list[MAX_NUMNODES];
@@ -44,7 +46,6 @@ static DEFINE_MUTEX(spu_mutex);
 static spinlock_t spu_list_lock = SPIN_LOCK_UNLOCKED;
 
 EXPORT_SYMBOL_GPL(spu_priv1_ops);
-EXPORT_SYMBOL_GPL(spu_management_ops);
 
 void spu_invalidate_slbs(struct spu *spu)
 {
@@ -571,8 +572,21 @@ int spu_irq_class_1_bottom(struct spu *spu)
        return ret;
 }
 
+static int spu_shutdown(struct sys_device *sysdev)
+{
+       struct spu *spu = container_of(sysdev, struct spu, sysdev);
+
+       // what else here???
+
+       spu_free_irqs(spu);
+       spu_destroy_spu(spu);
+       kfree(spu);
+       return 0;
+}
+
 struct sysdev_class spu_sysdev_class = {
-       set_kset_name("spu")
+       set_kset_name("spu"),
+       .shutdown = spu_shutdown,
 };
 
 int spu_add_sysdev_attr(struct sysdev_attribute *attr)
@@ -731,6 +745,9 @@ static int __init init_spu_base(void)
 {
        int i, ret;
 
+       for (i = 0; i < MAX_NUMNODES; i++)
+               INIT_LIST_HEAD(&spu_list[i]);
+
        if (!spu_management_ops)
                return 0;
 
@@ -739,9 +756,6 @@ static int __init init_spu_base(void)
        if (ret)
                return ret;
 
-       for (i = 0; i < MAX_NUMNODES; i++)
-               INIT_LIST_HEAD(&spu_list[i]);
-
        ret = spu_enumerate_spus(create_spu);
 
        if (ret) {
index b3954ab..04ad2e3 100644 (file)
@@ -41,7 +41,6 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang)
                goto out_free;
        }
        spin_lock_init(&ctx->mmio_lock);
-       spin_lock_init(&ctx->mapping_lock);
        kref_init(&ctx->kref);
        mutex_init(&ctx->state_mutex);
        init_MUTEX(&ctx->run_sema);
index deb340e..505266a 100644 (file)
@@ -44,26 +44,8 @@ spufs_mem_open(struct inode *inode, struct file *file)
 {
        struct spufs_inode_info *i = SPUFS_I(inode);
        struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
        file->private_data = ctx;
-       if (!i->i_openers++)
-               ctx->local_store = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
-static int
-spufs_mem_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->local_store = NULL;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->local_store = inode->i_mapping;
        smp_wmb();
        return 0;
 }
@@ -167,7 +149,6 @@ spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
 
 static const struct file_operations spufs_mem_fops = {
        .open    = spufs_mem_open,
-       .release = spufs_mem_release,
        .read    = spufs_mem_read,
        .write   = spufs_mem_write,
        .llseek  = generic_file_llseek,
@@ -257,35 +238,16 @@ static int spufs_cntl_open(struct inode *inode, struct file *file)
        struct spufs_inode_info *i = SPUFS_I(inode);
        struct spu_context *ctx = i->i_ctx;
 
-       spin_lock(&ctx->mapping_lock);
        file->private_data = ctx;
-       if (!i->i_openers++)
-               ctx->cntl = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->cntl = inode->i_mapping;
        smp_wmb();
        return simple_attr_open(inode, file, spufs_cntl_get,
                                        spufs_cntl_set, "0x%08lx");
 }
 
-static int
-spufs_cntl_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       simple_attr_close(inode, file);
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->cntl = NULL;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
 static const struct file_operations spufs_cntl_fops = {
        .open = spufs_cntl_open,
-       .release = spufs_cntl_release,
+       .release = simple_attr_close,
        .read = simple_attr_read,
        .write = simple_attr_write,
        .mmap = spufs_cntl_mmap,
@@ -761,30 +723,12 @@ static int spufs_signal1_open(struct inode *inode, struct file *file)
 {
        struct spufs_inode_info *i = SPUFS_I(inode);
        struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
        file->private_data = ctx;
-       if (!i->i_openers++)
-               ctx->signal1 = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->signal1 = inode->i_mapping;
        smp_wmb();
        return nonseekable_open(inode, file);
 }
 
-static int
-spufs_signal1_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->signal1 = NULL;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
 static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
                        size_t len, loff_t *pos)
 {
@@ -877,7 +821,6 @@ static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
 
 static const struct file_operations spufs_signal1_fops = {
        .open = spufs_signal1_open,
-       .release = spufs_signal1_release,
        .read = spufs_signal1_read,
        .write = spufs_signal1_write,
        .mmap = spufs_signal1_mmap,
@@ -887,30 +830,12 @@ static int spufs_signal2_open(struct inode *inode, struct file *file)
 {
        struct spufs_inode_info *i = SPUFS_I(inode);
        struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
        file->private_data = ctx;
-       if (!i->i_openers++)
-               ctx->signal2 = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->signal2 = inode->i_mapping;
        smp_wmb();
        return nonseekable_open(inode, file);
 }
 
-static int
-spufs_signal2_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->signal2 = NULL;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
 static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
                        size_t len, loff_t *pos)
 {
@@ -1007,7 +932,6 @@ static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
 
 static const struct file_operations spufs_signal2_fops = {
        .open = spufs_signal2_open,
-       .release = spufs_signal2_release,
        .read = spufs_signal2_read,
        .write = spufs_signal2_write,
        .mmap = spufs_signal2_mmap,
@@ -1107,32 +1031,13 @@ static int spufs_mss_open(struct inode *inode, struct file *file)
        struct spu_context *ctx = i->i_ctx;
 
        file->private_data = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!i->i_openers++)
-               ctx->mss = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->mss = inode->i_mapping;
        smp_wmb();
        return nonseekable_open(inode, file);
 }
 
-static int
-spufs_mss_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->mss = NULL;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
 static const struct file_operations spufs_mss_fops = {
        .open    = spufs_mss_open,
-       .release = spufs_mss_release,
        .mmap    = spufs_mss_mmap,
 };
 
@@ -1167,32 +1072,14 @@ static int spufs_psmap_open(struct inode *inode, struct file *file)
        struct spufs_inode_info *i = SPUFS_I(inode);
        struct spu_context *ctx = i->i_ctx;
 
-       spin_lock(&ctx->mapping_lock);
        file->private_data = i->i_ctx;
-       if (!i->i_openers++)
-               ctx->psmap = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->psmap = inode->i_mapping;
        smp_wmb();
        return nonseekable_open(inode, file);
 }
 
-static int
-spufs_psmap_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->psmap = NULL;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
 static const struct file_operations spufs_psmap_fops = {
        .open    = spufs_psmap_open,
-       .release = spufs_psmap_release,
        .mmap    = spufs_psmap_mmap,
 };
 
@@ -1239,29 +1126,12 @@ static int spufs_mfc_open(struct inode *inode, struct file *file)
        if (atomic_read(&inode->i_count) != 1)
                return -EBUSY;
 
-       spin_lock(&ctx->mapping_lock);
        file->private_data = ctx;
-       if (!i->i_openers++)
-               ctx->mfc = inode->i_mapping;
-       spin_unlock(&ctx->mapping_lock);
+       ctx->mfc = inode->i_mapping;
        smp_wmb();
        return nonseekable_open(inode, file);
 }
 
-static int
-spufs_mfc_release(struct inode *inode, struct file *file)
-{
-       struct spufs_inode_info *i = SPUFS_I(inode);
-       struct spu_context *ctx = i->i_ctx;
-
-       spin_lock(&ctx->mapping_lock);
-       if (!--i->i_openers)
-               ctx->mfc = NULL;
-       spin_unlock(&ctx->mapping_lock);
-       smp_wmb();
-       return 0;
-}
-
 /* interrupt-level mfc callback function. */
 void spufs_mfc_callback(struct spu *spu)
 {
@@ -1529,7 +1399,6 @@ static int spufs_mfc_fasync(int fd, struct file *file, int on)
 
 static const struct file_operations spufs_mfc_fops = {
        .open    = spufs_mfc_open,
-       .release = spufs_mfc_release,
        .read    = spufs_mfc_read,
        .write   = spufs_mfc_write,
        .poll    = spufs_mfc_poll,
index f4ac7b5..d80b94b 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/prom.h>
 #include <asm/semaphore.h>
 #include <asm/spu.h>
+#include <asm/spu_priv1.h>
 #include <asm/uaccess.h>
 
 #include "spufs.h"
@@ -54,7 +55,6 @@ spufs_alloc_inode(struct super_block *sb)
 
        ei->i_gang = NULL;
        ei->i_ctx = NULL;
-       ei->i_openers = 0;
 
        return &ei->vfs_inode;
 }
@@ -654,6 +654,10 @@ static int __init spufs_init(void)
 {
        int ret;
 
+       ret = -ENODEV;
+       if (!spu_management_ops)
+               goto out;
+
        ret = -ENOMEM;
        spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
                        sizeof(struct spufs_inode_info), 0,
index 6e5fcd1..419bf79 100644 (file)
@@ -50,12 +50,11 @@ struct spu_context {
        spinlock_t mmio_lock;             /* protects mmio access */
        struct address_space *local_store; /* local store mapping.  */
        struct address_space *mfc;         /* 'mfc' area mappings. */
-       struct address_space *cntl;        /* 'control' area mappings. */
-       struct address_space *signal1;     /* 'signal1' area mappings. */
-       struct address_space *signal2;     /* 'signal2' area mappings. */
-       struct address_space *mss;         /* 'mss' area mappings. */
-       struct address_space *psmap;       /* 'psmap' area mappings. */
-       spinlock_t mapping_lock;
+       struct address_space *cntl;        /* 'control' area mappings. */
+       struct address_space *signal1;     /* 'signal1' area mappings. */
+       struct address_space *signal2;     /* 'signal2' area mappings. */
+       struct address_space *mss;         /* 'mss' area mappings. */
+       struct address_space *psmap;       /* 'psmap' area mappings. */
        u64 object_id;             /* user space pointer for oprofile */
 
        enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state;
@@ -151,7 +150,6 @@ struct spufs_inode_info {
        struct spu_context *i_ctx;
        struct spu_gang *i_gang;
        struct inode vfs_inode;
-       int i_openers;
 };
 #define SPUFS_I(inode) \
        container_of(inode, struct spufs_inode_info, vfs_inode)
index 1a481a6..0977be9 100644 (file)
@@ -57,18 +57,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.
@@ -77,13 +71,53 @@ 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
+       depends on PPC_PS3
+       tristate "PS3 System Manager driver" if PS3_ADVANCED
+       select PS3_VUART
        default y
        help
          Include support for the PS3 System Manager.
 
          This support is required for system control.  In
-         general, all users will say Y.
+         general, all users will say Y or M.
+
+config PS3_STORAGE
+       depends on PPC_PS3
+       tristate
+
+config PS3_DISK
+       tristate "PS3 Disk Storage Driver"
+       depends on PPC_PS3
+       select PS3_STORAGE
+       default y
+       help
+         Include support for the PS3 Disk Storage.
+
+         This support is required to access the PS3 hard disk.
+         In general, all users will say Y or M.
+
+config PS3_ROM
+       tristate "PS3 ROM Storage Driver"
+       depends on PPC_PS3
+       select BLK_DEV_SR
+       select PS3_STORAGE
+       default y
+       help
+         Include support for the PS3 ROM Storage.
+
+         This support is required to access the PS3 BD/DVD/CD-ROM drive.
+         In general, all users will say Y or M.
+
+config PS3_FLASH
+       tristate "PS3 FLASH ROM Storage Driver"
+       depends on PPC_PS3
+       select PS3_STORAGE
+       default y
+       help
+         Include support for the PS3 FLASH ROM Storage.
+
+         This support is required to access the PS3 FLASH ROM, which
+         contains the boot loader and some boot options.
+         In general, all users will say Y or M.
 
 endmenu
index ed179c5..ac1bdf8 100644 (file)
@@ -1,7 +1,7 @@
 obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o
 obj-y += interrupt.o exports.o os-area.o
 obj-y += system-bus.o
-obj-y += device-init.o
 
 obj-$(CONFIG_SMP) += smp.o
 obj-$(CONFIG_SPU_BASE) += spu.o
+obj-y += device-init.o
index a832889..f8137bf 100644 (file)
 
 #define DEBUG 1
 
+#include <linux/delay.h>
+#include <linux/freezer.h>
 #include <linux/kernel.h>
+#include <linux/kthread.h>
 #include <linux/init.h>
+
 #include <asm/firmware.h>
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
 
 #include "platform.h"
 
@@ -40,7 +46,10 @@ ps3_register_gelic (void)
        dev = kzalloc(sizeof(struct ps3_system_bus_device)
                + sizeof(struct ps3_dma_region), GFP_KERNEL);
 
-       dev->match_id = PS3_MATCH_ID_GELIC;
+       ps3_system_bus_device_init(dev,
+                                  PS3_MATCH_ID_GELIC,
+                                  NULL,
+                                  NULL);
 
        result = ps3_repository_find_first_device(PS3_BUS_TYPE_SB,
                PS3_DEV_TYPE_SB_GELIC, &repo);
@@ -74,9 +83,9 @@ ps3_register_gelic (void)
                + sizeof(struct ps3_system_bus_device));
 
        ps3_dma_region_init(dev->d_region, &dev->did, PS3_DMA_64K,
-               PS3_DMA_OTHER);
+                           PS3_DMA_OTHER, NULL, 0, PS3_IOBUS_SB);
 
-       result = ps3_system_bus_device_register(dev);
+       result = ps3_system_bus_device_register(dev, PS3_IOBUS_SB);
 
        if (result) {
                pr_debug("%s:%d ps3_system_bus_device_register failed\n",
@@ -88,7 +97,7 @@ ps3_register_gelic (void)
        return result;
 
 fail:
-#if defined(DEBUG)
+#ifdef DEBUG
        memset(dev, 0xad, sizeof(struct ps3_system_bus_device)
                + sizeof(struct ps3_dma_region));
 #endif
@@ -117,9 +126,10 @@ ps3_register_ohci_0 (void)
 
        p = kzalloc(sizeof(struct ohci_layout), GFP_KERNEL);
 
-       p->dev.d_region = &p->d_region;
-       p->dev.m_region = &p->m_region;
-       p->dev.match_id = PS3_MATCH_ID_OHCI;
+       ps3_system_bus_device_init(&p->dev,
+                                  PS3_MATCH_ID_OHCI,
+                                  &p->d_region,
+                                  &p->m_region);
 
        result = ps3_repository_find_first_device(PS3_BUS_TYPE_SB,
                PS3_DEV_TYPE_SB_USB, &repo);
@@ -149,12 +159,12 @@ ps3_register_ohci_0 (void)
        BUG_ON(len != 0x10000);
 
        ps3_dma_region_init(p->dev.d_region, &p->dev.did, PS3_DMA_64K,
-               PS3_DMA_INTERNAL);
+                           PS3_DMA_INTERNAL, NULL, 0, PS3_IOBUS_SB);
 
        ps3_mmio_region_init(p->dev.m_region, &p->dev.did, bus_addr,
-               len, PS3_MMIO_4K);
+                            len, PS3_MMIO_4K, PS3_IOBUS_SB);
 
-       result = ps3_system_bus_device_register(&p->dev);
+       result = ps3_system_bus_device_register(&p->dev, PS3_IOBUS_SB);
 
        if (result)
                pr_debug("%s:%d ps3_system_bus_device_register failed\n",
@@ -164,7 +174,7 @@ ps3_register_ohci_0 (void)
        return result;
 
 fail:
-#if defined(DEBUG)
+#ifdef DEBUG
        memset(p, 0xad, sizeof(struct ohci_layout));
 #endif
        kfree(p);
@@ -192,9 +202,10 @@ ps3_register_ohci_1 (void)
 
        p = kzalloc(sizeof(struct ohci_layout), GFP_KERNEL);
 
-       p->dev.d_region = &p->d_region;
-       p->dev.m_region = &p->m_region;
-       p->dev.match_id = PS3_MATCH_ID_OHCI;
+       ps3_system_bus_device_init(&p->dev,
+                                  PS3_MATCH_ID_OHCI,
+                                  &p->d_region,
+                                  &p->m_region);
 
        result = ps3_repository_find_first_device(PS3_BUS_TYPE_SB,
                PS3_DEV_TYPE_SB_USB, &repo);
@@ -233,12 +244,12 @@ ps3_register_ohci_1 (void)
        BUG_ON(len != 0x10000);
 
        ps3_dma_region_init(p->dev.d_region, &p->dev.did, PS3_DMA_64K,
-               PS3_DMA_INTERNAL);
+                           PS3_DMA_INTERNAL, NULL, 0, PS3_IOBUS_SB);
 
        ps3_mmio_region_init(p->dev.m_region, &p->dev.did, bus_addr,
-               len, PS3_MMIO_4K);
+                            len, PS3_MMIO_4K, PS3_IOBUS_SB);
 
-       result = ps3_system_bus_device_register(&p->dev);
+       result = ps3_system_bus_device_register(&p->dev, PS3_IOBUS_SB);
 
        if (result)
                pr_debug("%s:%d ps3_system_bus_device_register failed\n",
@@ -248,7 +259,7 @@ ps3_register_ohci_1 (void)
        return result;
 
 fail:
-#if defined(DEBUG)
+#ifdef DEBUG
        memset(p, 0xad, sizeof(struct ohci_layout));
 #endif
        kfree(p);
@@ -276,9 +287,10 @@ ps3_register_ehci_0 (void)
 
        p = kzalloc(sizeof(struct ehci_layout), GFP_KERNEL);
 
-       p->dev.d_region = &p->d_region;
-       p->dev.m_region = &p->m_region;
-       p->dev.match_id = PS3_MATCH_ID_EHCI;
+       ps3_system_bus_device_init(&p->dev,
+                                  PS3_MATCH_ID_EHCI,
+                                  &p->d_region,
+                                  &p->m_region);
 
        result = ps3_repository_find_first_device(PS3_BUS_TYPE_SB,
                PS3_DEV_TYPE_SB_USB, &repo);
@@ -308,12 +320,12 @@ ps3_register_ehci_0 (void)
        BUG_ON(len != 0x10000);
 
        ps3_dma_region_init(p->dev.d_region, &p->dev.did, PS3_DMA_64K,
-               PS3_DMA_INTERNAL);
+                           PS3_DMA_INTERNAL, NULL, 0, PS3_IOBUS_SB);
 
        ps3_mmio_region_init(p->dev.m_region, &p->dev.did, bus_addr,
-               len, PS3_MMIO_4K);
+                            len, PS3_MMIO_4K, PS3_IOBUS_SB);
 
-       result = ps3_system_bus_device_register(&p->dev);
+       result = ps3_system_bus_device_register(&p->dev, PS3_IOBUS_SB);
 
        if (result)
                pr_debug("%s:%d ps3_system_bus_device_register failed\n",
@@ -323,7 +335,7 @@ ps3_register_ehci_0 (void)
        return result;
 
 fail:
-#if defined(DEBUG)
+#ifdef DEBUG
        memset(p, 0xad, sizeof(struct ehci_layout));
 #endif
        kfree(p);
@@ -351,9 +363,10 @@ ps3_register_ehci_1 (void)
 
        p = kzalloc(sizeof(struct ehci_layout), GFP_KERNEL);
 
-       p->dev.d_region = &p->d_region;
-       p->dev.m_region = &p->m_region;
-       p->dev.match_id = PS3_MATCH_ID_EHCI;
+       ps3_system_bus_device_init(&p->dev,
+                                  PS3_MATCH_ID_EHCI,
+                                  &p->d_region,
+                                  &p->m_region);
 
        result = ps3_repository_find_first_device(PS3_BUS_TYPE_SB,
                PS3_DEV_TYPE_SB_USB, &repo);
@@ -392,12 +405,12 @@ ps3_register_ehci_1 (void)
        BUG_ON(len != 0x10000);
 
        ps3_dma_region_init(p->dev.d_region, &p->dev.did, PS3_DMA_64K,
-               PS3_DMA_INTERNAL);
+                           PS3_DMA_INTERNAL, NULL, 0, PS3_IOBUS_SB);
 
        ps3_mmio_region_init(p->dev.m_region, &p->dev.did, bus_addr,
-               len, PS3_MMIO_4K);
+                            len, PS3_MMIO_4K, PS3_IOBUS_SB);
 
-       result = ps3_system_bus_device_register(&p->dev);
+       result = ps3_system_bus_device_register(&p->dev, PS3_IOBUS_SB);
 
        if (result)
                pr_debug("%s:%d ps3_system_bus_device_register failed\n",
@@ -407,7 +420,7 @@ ps3_register_ehci_1 (void)
        return result;
 
 fail:
-#if defined(DEBUG)
+#ifdef DEBUG
        memset(p, 0xad, sizeof(struct ehci_layout));
 #endif
        kfree(p);
@@ -415,28 +428,458 @@ fail:
        return result;
 }
 
-static int __devinit
-ps3_register_sys_manager (void)
+static int __devinit ps3_register_sound(void)
 {
        int result;
-       static struct ps3_vuart_port_device dev = {
-               .match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
-       };
+
+       struct snd_ps3_layout {
+               struct ps3_system_bus_device dev;
+               struct ps3_dma_region d_region;
+               struct ps3_mmio_region m_region;
+       } *p;
 
        pr_debug(" -> %s:%d\n", __func__, __LINE__);
 
-       result = ps3_vuart_port_device_register(&dev);
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       ps3_system_bus_device_init(&p->dev, PS3_MATCH_ID_SOUND,
+                                  &p->d_region,
+                                  &p->m_region);
+
+#warning need to get the device specific data here
+
+       result = ps3_system_bus_device_register(&p->dev, PS3_IOBUS_IOC0);
+
+       if (result)
+               kfree(p);
+
+       pr_debug(" <- %s:%d\n", __func__, __LINE__);
+       return result;
+}
+
+static int __devinit ps3_register_sys_manager(void)
+{
+       int result;
+       struct ps3_vuart_port_device *p;
+
+       pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       p->match_id = PS3_MATCH_ID_SYSTEM_MANAGER;
+
+#if defined(CONFIG_PS3_SYS_MANAGER) || defined(CONFIG_PS3_SYS_MANAGER_MODULE)
+       result = ps3_vuart_port_device_register(p);
 
        if (result)
                pr_debug("%s:%d ps3_vuart_port_device_register failed\n",
                        __func__, __LINE__);
+#endif
 
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
        return result;
 }
 
-int __init
-ps3_register_known_devices (void)
+#ifdef DEBUG
+static const char *ps3stor_dev_type(enum ps3_dev_type dev_type)
+{
+       switch (dev_type) {
+       case PS3_DEV_TYPE_STOR_DISK:
+               return "disk";
+
+       case PS3_DEV_TYPE_STOR_ROM:
+               return "rom";
+
+       case PS3_DEV_TYPE_STOR_FLASH:
+               return "flash";
+
+       case PS3_DEV_TYPE_NONE:
+               return "not present";
+
+       default:
+               return "unknown";
+       }
+}
+#else
+static inline const char *ps3stor_dev_type(enum ps3_dev_type dev_type)
+{
+    return NULL;
+}
+#endif /* DEBUG */
+
+#define NOTIFICATION_DEVID     ((u64)(-1L))
+#define NOTIFICATION_TIMEOUT   HZ
+
+static u64 ps3stor_wait_for_completion(u64 devid, u64 tag,
+                                      unsigned int timeout)
+{
+       unsigned int retries = 0;
+       u64 res = -1, status;
+
+       for (retries = 0; retries < timeout; retries++) {
+               res = lv1_storage_check_async_status(NOTIFICATION_DEVID, tag,
+                                                    &status);
+               if (!res)
+                       break;
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+       }
+       if (res)
+               pr_debug("%s:%u: check_async_status returns %ld status %lx\n",
+                        __func__, __LINE__, res, status);
+
+       return res;
+}
+
+static int ps3stor_probe_notification(struct ps3_storage_device *dev,
+                                     enum ps3_dev_type dev_type)
+{
+       int error = -ENODEV, res;
+       u64 *buf;
+       u64 lpar;
+
+       pr_info("%s:%u: Requesting notification\n", __func__, __LINE__);
+
+       buf = kzalloc(512, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       lpar = ps3_mm_phys_to_lpar(__pa(buf));
+
+       /* 2-1) open special event device */
+       res = lv1_open_device(dev->sbd.did.bus_id, NOTIFICATION_DEVID, 0);
+       if (res) {
+               printk(KERN_ERR "%s:%u: open notification device failed %d\n",
+                      __func__, __LINE__, res);
+               goto fail_free;
+       }
+
+       /* 2-2) write info to request notify */
+       buf[0] = 0;
+       buf[1] = (1 << 1); /* region update info only */
+       res = lv1_storage_write(NOTIFICATION_DEVID, 0, 0, 1, 0, lpar,
+                               &dev->tag);
+       if (res) {
+               printk(KERN_ERR "%s:%u: notify request write failed %d\n",
+                      __func__, __LINE__, res);
+               goto fail_close;
+       }
+
+       /* wait for completion in one second */
+       res = ps3stor_wait_for_completion(NOTIFICATION_DEVID, dev->tag,
+                                         NOTIFICATION_TIMEOUT);
+       if (res) {
+               /* write not completed */
+               printk(KERN_ERR "%s:%u: write not completed %d\n", __func__,
+                      __LINE__, res);
+               goto fail_close;
+       }
+
+       /* 2-3) read to wait region notification for each device */
+       while (1) {
+               memset(buf, 0, 512);
+               lv1_storage_read(NOTIFICATION_DEVID, 0, 0, 1, 0, lpar,
+                                &dev->tag);
+               res = ps3stor_wait_for_completion(NOTIFICATION_DEVID, dev->tag,
+                                                 NOTIFICATION_TIMEOUT);
+               if (res) {
+                       /* read not completed */
+                       printk(KERN_ERR "%s:%u: read not completed %d\n",
+                              __func__, __LINE__, res);
+                       break;
+               }
+
+               /* 2-4) verify the notification */
+               if (buf[0] != 1 || buf[1] != dev->sbd.did.bus_id) {
+                       /* other info notified */
+                       pr_debug("%s:%u: notification info %ld dev=%lx type=%lx\n",
+                                __func__, __LINE__, buf[0], buf[2], buf[3]);
+                       break;
+               }
+
+               if (buf[2] == dev->sbd.did.dev_id && buf[3] == dev_type) {
+                       pr_debug("%s:%u: device ready\n", __func__, __LINE__);
+                       error = 0;
+                       break;
+               }
+       }
+
+fail_close:
+       lv1_close_device(dev->sbd.did.bus_id, NOTIFICATION_DEVID);
+
+fail_free:
+       kfree(buf);
+       return error;
+}
+
+static int ps3stor_probe_dev(struct ps3_repository_device *repo)
+{
+       int error;
+       u64 port, blk_size, num_blocks;
+       unsigned int num_regions, i;
+       struct ps3_storage_device *dev;
+       enum ps3_dev_type dev_type;
+       enum ps3_match_id match_id;
+
+       pr_info("%s:%u: Probing new storage device %u\n", __func__, __LINE__,
+                repo->dev_index);
+
+       error = ps3_repository_read_dev_id(repo->bus_index, repo->dev_index,
+                                          &repo->did.dev_id);
+       if (error) {
+               printk(KERN_ERR "%s:%u: read_dev_id failed %d\n", __func__,
+                      __LINE__, error);
+               return -ENODEV;
+       }
+
+       error = ps3_repository_read_dev_type(repo->bus_index, repo->dev_index,
+                                            &dev_type);
+       if (error) {
+               printk(KERN_ERR "%s:%u: read_dev_type failed %d\n", __func__,
+                      __LINE__, error);
+               return -ENODEV;
+       }
+
+       pr_debug("%s:%u: index %u:%u: id %u:%u dev_type %u (%s)\n", __func__,
+                __LINE__, repo->bus_index, repo->dev_index, repo->did.bus_id,
+                repo->did.dev_id, dev_type, ps3stor_dev_type(dev_type));
+
+       switch (dev_type) {
+       case PS3_DEV_TYPE_STOR_DISK:
+               match_id = PS3_MATCH_ID_STOR_DISK;
+               break;
+
+       case PS3_DEV_TYPE_STOR_ROM:
+               match_id = PS3_MATCH_ID_STOR_ROM;
+               break;
+
+       case PS3_DEV_TYPE_STOR_FLASH:
+               match_id = PS3_MATCH_ID_STOR_FLASH;
+               break;
+
+       default:
+               return 0;
+       }
+
+       error = ps3_repository_read_stor_dev_info(repo->bus_index,
+                                                 repo->dev_index, &port,
+                                                 &blk_size, &num_blocks,
+                                                 &num_regions);
+       if (error) {
+               printk(KERN_ERR "%s:%u: _read_stor_dev_info failed %d\n",
+                      __func__, __LINE__, error);
+               return -ENODEV;
+       }
+       pr_debug("%s:%u: index %u:%u: port %lu blk_size %lu num_blocks %lu "
+                "num_regions %u\n",
+                __func__, __LINE__, repo->bus_index, repo->dev_index, port,
+                blk_size, num_blocks, num_regions);
+
+       dev = kzalloc(sizeof(struct ps3_storage_device)+
+                     num_regions*sizeof(struct ps3_storage_region),
+                     GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->sbd.did = repo->did;
+       ps3_system_bus_device_init(&dev->sbd, match_id, &dev->dma_region,
+                                  NULL);
+       dev->blk_size = blk_size;
+       dev->num_regions = num_regions;
+
+       error = ps3_repository_find_interrupt(repo,
+                                             PS3_INTERRUPT_TYPE_EVENT_PORT,
+                                             &dev->sbd.interrupt_id);
+       if (error) {
+               printk(KERN_ERR "%s:%u: find_interrupt failed %d\n", __func__,
+                       __LINE__, error);
+               goto cleanup;
+       }
+
+#if defined(CONFIG_PS3_STORAGE_OLD) || defined(CONFIG_PS3_STORAGE_OLD_MODULE)
+       switch (match_id) {
+#if defined(CONFIG_PS3_DISK) || defined(CONFIG_PS3_DISK_MODULE)
+       case PS3_MATCH_ID_STOR_DISK:
+               break;
+#endif
+#if defined(CONFIG_PS3_ROM) || defined(CONFIG_PS3_ROM_MODULE)
+       case PS3_MATCH_ID_STOR_ROM:
+               break;
+#endif
+#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
+       case PS3_MATCH_ID_STOR_FLASH:
+               break;
+#endif
+
+       default:
+               /*
+                * FIXME As this driver conflicts with the old storage driver,
+                *       we cannot do a full probe here
+                */
+               printk(KERN_ERR
+                      "Ignoring storage device, let the old driver handle it\n");
+               goto cleanup;
+       }
+#endif
+
+       /* FIXME Do we really need this? I guess for kboot only? */
+       error = ps3stor_probe_notification(dev, dev_type);
+       if (error) {
+               printk(KERN_ERR "%s:%u: probe_notification failed %d\n",
+                      __func__, __LINE__, error);
+               goto cleanup;
+       }
+
+       for (i = 0; i < num_regions; i++) {
+               unsigned int id;
+               u64 start, size;
+
+               error = ps3_repository_read_stor_dev_region(repo->bus_index,
+                                                           repo->dev_index, i,
+                                                           &id, &start,
+                                                           &size);
+               if (error) {
+                       printk(KERN_ERR
+                              "%s:%u: read_stor_dev_region failed %d\n",
+                              __func__, __LINE__, error);
+                       goto cleanup;
+               }
+               pr_debug("%s:%u: region %u: id %u start %lu size %lu\n",
+                        __func__, __LINE__, i, id, start, size);
+
+               dev->regions[i].id = id;
+               dev->regions[i].start = start;
+               dev->regions[i].size = size;
+       }
+
+       error = ps3_system_bus_device_register(&dev->sbd, PS3_IOBUS_SB);
+       if (error) {
+               printk(KERN_ERR
+                      "%s:%u: ps3_system_bus_device_register failed %d\n",
+                      __func__, __LINE__, error);
+               goto cleanup;
+       }
+       return 0;
+
+cleanup:
+       kfree(dev);
+       return -ENODEV;
+}
+
+static int ps3stor_thread(void *data)
+{
+       struct ps3_repository_device *repo = data;
+       int error;
+       unsigned int n, ms = 250;
+
+       pr_debug("%s:%u: kthread started\n", __func__, __LINE__);
+
+       do {
+               try_to_freeze();
+
+//             pr_debug("%s:%u: Checking for new storage devices...\n",
+//                      __func__, __LINE__);
+               error = ps3_repository_read_bus_num_dev(repo->bus_index, &n);
+               if (error) {
+                       printk(KERN_ERR "%s:%u: read_bus_num_dev failed %d\n",
+                              __func__, __LINE__, error);
+                       break;
+               }
+
+               if (n > repo->dev_index) {
+                       pr_debug("%s:%u: Found %u storage devices (%u new)\n",
+                                __func__, __LINE__, n, n - repo->dev_index);
+
+                       while (repo->dev_index < n && !error) {
+                               error = ps3stor_probe_dev(repo);
+                               repo->dev_index++;
+                       }
+
+                       ms = 250;
+               }
+
+               msleep_interruptible(ms);
+               if (ms < 60000)
+                       ms <<= 1;
+       } while (!kthread_should_stop());
+
+       pr_debug("%s:%u: kthread finished\n", __func__, __LINE__);
+
+       return 0;
+}
+
+static int __devinit ps3_register_storage_devices(void)
+{
+       int error;
+       static struct ps3_repository_device repo;
+       struct task_struct *task;
+
+       if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+               return -ENODEV;
+
+       error = ps3_repository_find_bus(PS3_BUS_TYPE_STORAGE, 0,
+                                       &repo.bus_index);
+       if (error) {
+               printk(KERN_ERR "%s: Cannot find storage bus (%d)\n", __func__,
+                      error);
+               return -ENODEV;
+       }
+       pr_debug("%s:%u: Storage bus has index %u\n", __func__, __LINE__,
+                repo.bus_index);
+
+       error = ps3_repository_read_bus_id(repo.bus_index, &repo.did.bus_id);
+       if (error) {
+               printk(KERN_ERR "%s: read_bus_id failed %d\n", __func__,
+                      error);
+               return -ENODEV;
+       }
+
+       pr_debug("%s:%u: Storage bus has id %u\n", __func__, __LINE__,
+                repo.did.bus_id);
+
+       task = kthread_run(ps3stor_thread, &repo, "ps3stor-probe");
+       if (IS_ERR(task)) {
+               error = PTR_ERR(task);
+               printk(KERN_ERR "%s: kthread_run failed %d\n", __func__,
+                      error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int __devinit ps3_register_fb(void)
+{
+       int error;
+       struct ps3_system_bus_device *dev;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       ps3_system_bus_device_init(dev, PS3_MATCH_ID_GFX, NULL, NULL);
+
+       pr_debug(" -> %s:%d\n", __func__, __LINE__);
+
+       error = ps3_system_bus_device_register(dev, PS3_IOBUS_IOC0);
+       if (error) {
+               printk(KERN_ERR
+                      "%s:%u: ps3_system_bus_device_register failed %d\n",
+                      __func__, __LINE__, error);
+               goto cleanup;
+       }
+       return 0;
+
+cleanup:
+       kfree(dev);
+       return -ENODEV;
+}
+
+static int __init ps3_register_known_devices(void)
 {
        int result;
 
@@ -447,14 +890,16 @@ ps3_register_known_devices (void)
 
        //ps3_repository_dump_bus_info();
 
+       result = ps3_register_fb();
        result = ps3_register_ohci_0();
        result = ps3_register_ehci_0();
        result = ps3_register_ohci_1();
        result = ps3_register_ehci_1();
-#if defined(CONFIG_PS3_SYS_MANAGER)
+       result = ps3_register_sound();
+
        result = ps3_register_sys_manager();
-#endif
        result = ps3_register_gelic();
+       result = ps3_register_storage_devices();
 
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
        return result;
index e12e59f..c9f35e3 100644 (file)
@@ -234,10 +234,18 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va,
 
 static void ps3_hpte_clear(void)
 {
-       /* Make sure to clean up the frame buffer device first */
-       ps3fb_cleanup();
+       int result;
 
-       lv1_unmap_htab(htab_addr);
+       DBG(" -> %s:%d\n", __func__, __LINE__);
+
+       result = lv1_unmap_htab(htab_addr);
+       BUG_ON(result);
+
+       ps3_mm_shutdown();
+
+       ps3_mm_vas_destroy();
+
+       DBG(" <- %s:%d\n", __func__, __LINE__);
 }
 
 void __init ps3_hpte_init(unsigned long htab_size)
index 9da82c2..755fe5b 100644 (file)
@@ -90,6 +90,92 @@ struct ps3_private {
 static DEFINE_PER_CPU(struct ps3_private, ps3_private);
 
 /**
+ * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp.
+ * @virq: The assigned Linux virq.
+ *
+ * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
+ */
+
+static void ps3_chip_mask(unsigned int virq)
+{
+       struct ps3_private *pd = get_irq_chip_data(virq);
+       u64 bit = 0x8000000000000000UL >> virq;
+       u64 *p = &pd->bmp.mask;
+       u64 old;
+       unsigned long flags;
+
+       pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
+
+       local_irq_save(flags);
+       asm volatile(
+                    "1:        ldarx %0,0,%3\n"
+                    "andc      %0,%0,%2\n"
+                    "stdcx.    %0,0,%3\n"
+                    "bne-      1b"
+                    : "=&r" (old), "+m" (*p)
+                    : "r" (bit), "r" (p)
+                    : "cc" );
+
+       lv1_did_update_interrupt_mask(pd->node, pd->cpu);
+       local_irq_restore(flags);
+}
+
+/**
+ * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp.
+ * @virq: The assigned Linux virq.
+ *
+ * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
+ */
+
+static void ps3_chip_unmask(unsigned int virq)
+{
+       struct ps3_private *pd = get_irq_chip_data(virq);
+       u64 bit = 0x8000000000000000UL >> virq;
+       u64 *p = &pd->bmp.mask;
+       u64 old;
+       unsigned long flags;
+
+       pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
+
+       local_irq_save(flags);
+       asm volatile(
+                    "1:        ldarx %0,0,%3\n"
+                    "or        %0,%0,%2\n"
+                    "stdcx.    %0,0,%3\n"
+                    "bne-      1b"
+                    : "=&r" (old), "+m" (*p)
+                    : "r" (bit), "r" (p)
+                    : "cc" );
+
+       lv1_did_update_interrupt_mask(pd->node, pd->cpu);
+       local_irq_restore(flags);
+}
+
+/**
+ * ps3_chip_eoi - HV end-of-interrupt.
+ * @virq: The assigned Linux virq.
+ *
+ * Calls lv1_end_of_interrupt_ext().
+ */
+
+static void ps3_chip_eoi(unsigned int virq)
+{
+       const struct ps3_private *pd = get_irq_chip_data(virq);
+       lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq);
+}
+
+/**
+ * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip.
+ */
+
+static struct irq_chip ps3_irq_chip = {
+       .typename = "ps3",
+       .mask = ps3_chip_mask,
+       .unmask = ps3_chip_unmask,
+       .eoi = ps3_chip_eoi,
+};
+
+/**
  * ps3_virq_setup - virq related setup.
  * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
  * serviced on.
@@ -133,6 +219,8 @@ int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
                goto fail_set;
        }
 
+       ps3_chip_mask(*virq);
+
        return result;
 
 fail_set:
@@ -224,6 +312,8 @@ int ps3_irq_plug_destroy(unsigned int virq)
        pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
                pd->node, pd->cpu, virq);
 
+       ps3_chip_mask(virq);
+
        result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
 
        if (result)
@@ -281,7 +371,9 @@ int ps3_event_receive_port_destroy(unsigned int virq)
 {
        int result;
 
-       pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq);
+       pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
+
+       ps3_chip_mask(virq);
 
        result = lv1_destruct_event_receive_port(virq_to_hw(virq));
 
@@ -289,17 +381,13 @@ int ps3_event_receive_port_destroy(unsigned int virq)
                pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
                        __func__, __LINE__, ps3_result(result));
 
-       /* lv1_destruct_event_receive_port() destroys the IRQ plug,
-        * so don't call ps3_irq_plug_destroy() here.
+       /* Can't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
+        * calls from interrupt context (smp_call_function).
         */
 
-       result = ps3_virq_destroy(virq);
-       BUG_ON(result);
-
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
        return result;
 }
-EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy);
 
 int ps3_send_event_locally(unsigned int virq)
 {
@@ -371,6 +459,13 @@ int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did,
        result = ps3_event_receive_port_destroy(virq);
        BUG_ON(result);
 
+       /* ps3_event_receive_port_destroy() destroys the IRQ plug,
+        * so don't call ps3_irq_plug_destroy() here.
+        */
+
+       result = ps3_virq_destroy(virq);
+       BUG_ON(result);
+
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
        return result;
 }
@@ -411,16 +506,23 @@ EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
 int ps3_io_irq_destroy(unsigned int virq)
 {
        int result;
+       unsigned long outlet = virq_to_hw(virq);
 
-       result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
+       ps3_chip_mask(virq);
 
-       if (result)
-               pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
-                       __func__, __LINE__, ps3_result(result));
+       /* lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
+        * so call ps3_irq_plug_destroy() first.
+        */
 
        result = ps3_irq_plug_destroy(virq);
        BUG_ON(result);
 
+       result = lv1_destruct_io_irq_outlet(outlet);
+
+       if (result)
+               pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
+                       __func__, __LINE__, ps3_result(result));
+
        return result;
 }
 EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
@@ -465,6 +567,7 @@ int ps3_vuart_irq_destroy(unsigned int virq)
 {
        int result;
 
+       ps3_chip_mask(virq);
        result = lv1_deconfigure_virtual_uart_irq();
 
        if (result) {
@@ -513,9 +616,14 @@ int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
 
 int ps3_spe_irq_destroy(unsigned int virq)
 {
-       int result = ps3_irq_plug_destroy(virq);
+       int result;
+
+       ps3_chip_mask(virq);
+
+       result = ps3_irq_plug_destroy(virq);
        BUG_ON(result);
-       return 0;
+
+       return result;
 }
 
 
@@ -564,67 +672,6 @@ static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd,
 static void dump_bmp(struct ps3_private* pd) {};
 #endif /* defined(DEBUG) */
 
-static void ps3_chip_mask(unsigned int virq)
-{
-       struct ps3_private *pd = get_irq_chip_data(virq);
-       u64 bit = 0x8000000000000000UL >> virq;
-       u64 *p = &pd->bmp.mask;
-       u64 old;
-       unsigned long flags;
-
-       pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
-
-       local_irq_save(flags);
-       asm volatile(
-                    "1:        ldarx %0,0,%3\n"
-                    "andc      %0,%0,%2\n"
-                    "stdcx.    %0,0,%3\n"
-                    "bne-      1b"
-                    : "=&r" (old), "+m" (*p)
-                    : "r" (bit), "r" (p)
-                    : "cc" );
-
-       lv1_did_update_interrupt_mask(pd->node, pd->cpu);
-       local_irq_restore(flags);
-}
-
-static void ps3_chip_unmask(unsigned int virq)
-{
-       struct ps3_private *pd = get_irq_chip_data(virq);
-       u64 bit = 0x8000000000000000UL >> virq;
-       u64 *p = &pd->bmp.mask;
-       u64 old;
-       unsigned long flags;
-
-       pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
-
-       local_irq_save(flags);
-       asm volatile(
-                    "1:        ldarx %0,0,%3\n"
-                    "or        %0,%0,%2\n"
-                    "stdcx.    %0,0,%3\n"
-                    "bne-      1b"
-                    : "=&r" (old), "+m" (*p)
-                    : "r" (bit), "r" (p)
-                    : "cc" );
-
-       lv1_did_update_interrupt_mask(pd->node, pd->cpu);
-       local_irq_restore(flags);
-}
-
-static void ps3_chip_eoi(unsigned int virq)
-{
-       const struct ps3_private *pd = get_irq_chip_data(virq);
-       lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq);
-}
-
-static struct irq_chip irq_chip = {
-       .typename = "ps3",
-       .mask = ps3_chip_mask,
-       .unmask = ps3_chip_unmask,
-       .eoi = ps3_chip_eoi,
-};
-
 static void ps3_host_unmap(struct irq_host *h, unsigned int virq)
 {
        set_irq_chip_data(virq, NULL);
@@ -636,7 +683,7 @@ static int ps3_host_map(struct irq_host *h, unsigned int virq,
        pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
                virq);
 
-       set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
+       set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
 
        return 0;
 }
index b8b0de5..ab71579 100644 (file)
@@ -17,6 +17,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
+#define DEBUG
 
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -32,7 +33,7 @@
 #if defined(DEBUG)
 #define DBG(fmt...) udbg_printf(fmt)
 #else
-#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
+#define DBG(fmt...) do { if (0) printk(fmt);} while (0)
 #endif
 
 enum {
@@ -222,9 +223,15 @@ fail:
 
 void ps3_mm_vas_destroy(void)
 {
+       int result;
+
+       DBG("%s:%d: map.vas_id    = %lu\n", __func__, __LINE__, map.vas_id);
+
        if (map.vas_id) {
-               lv1_select_virtual_address_space(0);
-               lv1_destruct_virtual_address_space(map.vas_id);
+               result = lv1_select_virtual_address_space(0);
+               BUG_ON(result);
+               result = lv1_destruct_virtual_address_space(map.vas_id);
+               BUG_ON(result);
                map.vas_id = 0;
        }
 }
@@ -285,8 +292,12 @@ zero_region:
 
 void ps3_mm_region_destroy(struct mem_region *r)
 {
+       int result;
+
+       DBG("%s:%d: r->base = %lxh\n", __func__, __LINE__, r->base);
        if (r->base) {
-               lv1_release_memory(r->base);
+               result = lv1_release_memory(r->base);
+               BUG_ON(result);
                r->size = r->base = r->offset = 0;
                map.total = map.rm.size;
        }
@@ -339,17 +350,19 @@ core_initcall(ps3_mm_add_memory);
 /*============================================================================*/
 
 /**
- * dma_lpar_to_bus - Translate an lpar address to ioc mapped bus address.
+ * dma_sb_lpar_to_bus - Translate an lpar address to ioc mapped bus address.
  * @r: pointer to dma region structure
  * @lpar_addr: HV lpar address
  */
 
-static unsigned long dma_lpar_to_bus(struct ps3_dma_region *r,
+static unsigned long dma_sb_lpar_to_bus(struct ps3_dma_region *r,
        unsigned long lpar_addr)
 {
-       BUG_ON(lpar_addr >= map.r1.base + map.r1.size);
-       return r->bus_addr + (lpar_addr <= map.rm.size ? lpar_addr
-               : lpar_addr - map.r1.offset);
+       if (lpar_addr >= map.rm.size)
+               lpar_addr -= map.r1.offset;
+       BUG_ON(lpar_addr < r->offset);
+       BUG_ON(lpar_addr >= r->offset + r->len);
+       return r->bus_addr + lpar_addr - r->offset;
 }
 
 #define dma_dump_region(_a) _dma_dump_region(_a, __func__, __LINE__)
@@ -361,6 +374,7 @@ static void _dma_dump_region(const struct ps3_dma_region *r, const char* func,
        DBG("%s:%d: page_size  %u\n", func, line, r->page_size);
        DBG("%s:%d: bus_addr   %lxh\n", func, line, r->bus_addr);
        DBG("%s:%d: len        %lxh\n", func, line, r->len);
+       DBG("%s:%d: offset     %lxh\n", func, line, r->offset);
 }
 
 /**
@@ -395,6 +409,7 @@ static void _dma_dump_chunk (const struct dma_chunk* c, const char* func,
        DBG("%s:%d: r.bus_addr   %lxh\n", func, line, c->region->bus_addr);
        DBG("%s:%d: r.page_size  %u\n", func, line, c->region->page_size);
        DBG("%s:%d: r.len        %lxh\n", func, line, c->region->len);
+       DBG("%s:%d: r.offset     %lxh\n", func, line, c->region->offset);
        DBG("%s:%d: c.lpar_addr  %lxh\n", func, line, c->lpar_addr);
        DBG("%s:%d: c.bus_addr   %lxh\n", func, line, c->bus_addr);
        DBG("%s:%d: c.len        %lxh\n", func, line, c->len);
@@ -405,33 +420,62 @@ static struct dma_chunk * dma_find_chunk(struct ps3_dma_region *r,
 {
        struct dma_chunk *c;
        unsigned long aligned_bus = _ALIGN_DOWN(bus_addr, 1 << r->page_size);
-       unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size);
+       unsigned long aligned_len = _ALIGN_UP(len+bus_addr-aligned_bus,
+                                             1 << r->page_size);
 
        list_for_each_entry(c, &r->chunk_list.head, link) {
                /* intersection */
-               if (aligned_bus >= c->bus_addr
-                       && aligned_bus < c->bus_addr + c->len
-                       && aligned_bus + aligned_len <= c->bus_addr + c->len) {
+               if (aligned_bus >= c->bus_addr &&
+                   aligned_bus + aligned_len <= c->bus_addr + c->len)
                        return c;
-               }
+
                /* below */
-               if (aligned_bus + aligned_len <= c->bus_addr) {
+               if (aligned_bus + aligned_len <= c->bus_addr)
                        continue;
-               }
+
                /* above */
-               if (aligned_bus >= c->bus_addr + c->len) {
+               if (aligned_bus >= c->bus_addr + c->len)
                        continue;
-               }
 
                /* we don't handle the multi-chunk case for now */
-
                dma_dump_chunk(c);
                BUG();
        }
        return NULL;
 }
 
-static int dma_free_chunk(struct dma_chunk *c)
+static struct dma_chunk * dma_find_chunk_lpar(struct ps3_dma_region *r,
+       unsigned long lpar_addr, unsigned long len)
+{
+       struct dma_chunk *c;
+       unsigned long aligned_lpar = _ALIGN_DOWN(lpar_addr, 1 << r->page_size);
+       unsigned long aligned_len = _ALIGN_UP(len + lpar_addr - aligned_lpar,
+                                             1 << r->page_size);
+
+       list_for_each_entry(c, &r->chunk_list.head, link) {
+               /* intersection */
+               if (c->lpar_addr <= aligned_lpar &&
+                   aligned_lpar < c->lpar_addr + c->len) {
+                       if (aligned_lpar + aligned_len <= c->lpar_addr + c->len)
+                               return c;
+                       else {
+                               dma_dump_chunk(c);
+                               BUG();
+                       }
+               }
+               /* below */
+               if (aligned_lpar + aligned_len <= c->lpar_addr) {
+                       continue;
+               }
+               /* above */
+               if (c->lpar_addr + c->len <= aligned_lpar) {
+                       continue;
+               }
+       }
+       return NULL;
+}
+
+static int dma_sb_free_chunk(struct dma_chunk *c)
 {
        int result = 0;
 
@@ -445,8 +489,39 @@ static int dma_free_chunk(struct dma_chunk *c)
        return result;
 }
 
+static int dma_ioc0_free_chunk(struct dma_chunk *c)
+{
+       int result = 0;
+       int iopage;
+       unsigned long offset;
+       struct ps3_dma_region * r = c->region;
+
+       DBG("%s:start\n", __func__);
+       for (iopage = 0; iopage < (c->len >> r->page_size); iopage++) {
+               offset = (1 << r->page_size) * iopage;
+               /* put INVALID entry */
+               result = lv1_put_iopte(0,
+                                      c->bus_addr + offset,
+                                      c->lpar_addr + offset,
+                                      r->ioid,
+                                      0);
+               DBG("%s: bus=%#lx, lpar=%#lx, ioid=%d\n", __func__,
+                   c->bus_addr + offset,
+                   c->lpar_addr + offset,
+                   r->ioid);
+
+               if (result) {
+                       DBG("%s:%d: lv1_map_device_dma_region failed: %s\n",
+                           __func__, __LINE__, ps3_result(result));
+               }
+       }
+       kfree(c);
+       DBG("%s:end\n", __func__);
+       return result;
+}
+
 /**
- * dma_map_pages - Maps dma pages into the io controller bus address space.
+ * dma_sb_map_pages - Maps dma pages into the io controller bus address space.
  * @r: Pointer to a struct ps3_dma_region.
  * @phys_addr: Starting physical address of the area to map.
  * @len: Length in bytes of the area to map.
@@ -456,8 +531,8 @@ static int dma_free_chunk(struct dma_chunk *c)
  * make the HV call to add the pages into the io controller address space.
  */
 
-static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
-       unsigned long len, struct dma_chunk **c_out)
+static int dma_sb_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
+           unsigned long len, struct dma_chunk **c_out, u64 iopte_flag)
 {
        int result;
        struct dma_chunk *c;
@@ -471,13 +546,13 @@ static int dma_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
 
        c->region = r;
        c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
-       c->bus_addr = dma_lpar_to_bus(r, c->lpar_addr);
+       c->bus_addr = dma_sb_lpar_to_bus(r, c->lpar_addr);
        c->len = len;
 
+       BUG_ON(iopte_flag != 0xf800000000000000UL);
        result = lv1_map_device_dma_region(c->region->did.bus_id,
-               c->region->did.dev_id, c->lpar_addr, c->bus_addr, c->len,
-               0xf800000000000000UL);
-
+                                          c->region->did.dev_id, c->lpar_addr,
+                                          c->bus_addr, c->len, iopte_flag);
        if (result) {
                DBG("%s:%d: lv1_map_device_dma_region failed: %s\n",
                        __func__, __LINE__, ps3_result(result));
@@ -497,26 +572,119 @@ fail_alloc:
        return result;
 }
 
+static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr,
+                             unsigned long len, struct dma_chunk **c_out,
+                             u64 iopte_flag)
+{
+       int result;
+       struct dma_chunk *c, *last;
+       int iopage, pages;
+       unsigned long offset;
+
+       DBG(KERN_ERR "%s: phy=%#lx, lpar%#lx, len=%#lx\n", __func__,
+           phys_addr, ps3_mm_phys_to_lpar(phys_addr), len);
+       c = kzalloc(sizeof(struct dma_chunk), GFP_ATOMIC);
+
+       if (!c) {
+               result = -ENOMEM;
+               goto fail_alloc;
+       }
+
+       c->region = r;
+       c->len = len;
+       c->lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
+       /* allocate IO address */
+       if (list_empty(&r->chunk_list.head)) {
+               /* first one */
+               c->bus_addr = r->bus_addr;
+       } else {
+               /* derive from last bus addr*/
+               last  = list_entry(r->chunk_list.head.next,
+                                  struct dma_chunk, link);
+               c->bus_addr = last->bus_addr + last->len;
+               DBG("%s: last bus=%#lx, len=%#lx\n", __func__,
+                   last->bus_addr, last->len);
+       }
+
+       /* FIXME: check whether length exceeds region size */
+
+       /* build ioptes for the area */
+       pages = len >> r->page_size;
+       DBG("%s: pgsize=%#x len=%#lx pages=%#x iopteflag=%#lx\n", __func__,
+           r->page_size, r->len, pages, iopte_flag);
+       for (iopage = 0; iopage < pages; iopage++) {
+               offset = (1 << r->page_size) * iopage;
+               result = lv1_put_iopte(0,
+                                      c->bus_addr + offset,
+                                      c->lpar_addr + offset,
+                                      r->ioid,
+                                      iopte_flag);
+               if (result) {
+                       printk("%s:%d: lv1_map_device_dma_region failed: %s\n",
+                           __func__, __LINE__, ps3_result(result));
+                       goto fail_map;
+               }
+               DBG("%s: pg=%d bus=%#lx, lpar=%#lx, ioid=%#x\n", __func__,
+                   iopage, c->bus_addr + offset, c->lpar_addr + offset,
+                   r->ioid);
+       }
+
+       /* be sure that last allocated one is inserted at head */
+       list_add(&c->link, &r->chunk_list.head);
+
+       *c_out = c;
+       DBG("%s: end\n", __func__);
+       return 0;
+
+fail_map:
+       for (iopage--; 0 <= iopage; iopage--) {
+               lv1_put_iopte(0,
+                             c->bus_addr + offset,
+                             c->lpar_addr + offset,
+                             r->ioid,
+                             0);
+       }
+       kfree(c);
+fail_alloc:
+       *c_out = NULL;
+       return result;
+}
+
 /**
- * dma_region_create - Create a device dma region.
+ * dma_sb_region_create - Create a device dma region.
  * @r: Pointer to a struct ps3_dma_region.
  *
  * This is the lowest level dma region create routine, and is the one that
  * will make the HV call to create the region.
  */
 
-static int dma_region_create(struct ps3_dma_region* r)
+static int dma_sb_region_create(struct ps3_dma_region* r)
 {
        int result;
 
-       r->len = _ALIGN_UP(map.total, 1 << r->page_size);
+       pr_info(" -> %s:%d:\n", __func__, __LINE__);
+
+       BUG_ON(!r);
+
+       if(!r->did.bus_id) {
+               pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__,
+                       r->did.bus_id, r->did.dev_id);
+               return 0;
+       }
+
+       DBG("%s:%u: len = 0x%lx, page_size = %u, offset = 0x%lx\n", __func__,
+           __LINE__, r->len, r->page_size, r->offset);
+
+       BUG_ON(!r->len);
+       BUG_ON(!r->page_size);
+       BUG_ON(!r->region_ops);
+
        INIT_LIST_HEAD(&r->chunk_list.head);
        spin_lock_init(&r->chunk_list.lock);
 
        result = lv1_allocate_device_dma_region(r->did.bus_id, r->did.dev_id,
-               r->len, r->page_size, r->region_type, &r->bus_addr);
-
-       dma_dump_region(r);
+               roundup_pow_of_two(r->len), r->page_size, r->region_type,
+               &r->bus_addr);
 
        if (result) {
                DBG("%s:%d: lv1_allocate_device_dma_region failed: %s\n",
@@ -527,6 +695,27 @@ static int dma_region_create(struct ps3_dma_region* r)
        return result;
 }
 
+static int dma_ioc0_region_create(struct ps3_dma_region* r)
+{
+       int result;
+
+       INIT_LIST_HEAD(&r->chunk_list.head);
+       spin_lock_init(&r->chunk_list.lock);
+
+       result = lv1_allocate_io_segment(0,
+                                        r->len,
+                                        r->page_size,
+                                        &r->bus_addr);
+       if (result) {
+               DBG("%s:%d: lv1_allocate_io_segment failed: %s\n",
+                       __func__, __LINE__, ps3_result(result));
+               r->len = r->bus_addr = 0;
+       }
+       DBG("%s: len=%#lx, pg=%d, bus=%#lx\n", __func__,
+           r->len, r->page_size, r->bus_addr);
+       return result;
+}
+
 /**
  * dma_region_free - Free a device dma region.
  * @r: Pointer to a struct ps3_dma_region.
@@ -535,15 +724,23 @@ static int dma_region_create(struct ps3_dma_region* r)
  * will make the HV call to free the region.
  */
 
-static int dma_region_free(struct ps3_dma_region* r)
+static int dma_sb_region_free(struct ps3_dma_region* r)
 {
        int result;
        struct dma_chunk *c;
        struct dma_chunk *tmp;
 
+       BUG_ON(!r);
+
+       if(!r->did.bus_id) {
+               pr_info("%s:%d: %u:%u no dma\n", __func__, __LINE__,
+                       r->did.bus_id, r->did.dev_id);
+               return 0;
+       }
+
        list_for_each_entry_safe(c, tmp, &r->chunk_list.head, link) {
                list_del(&c->link);
-               dma_free_chunk(c);
+               dma_sb_free_chunk(c);
        }
 
        result = lv1_free_device_dma_region(r->did.bus_id, r->did.dev_id,
@@ -553,13 +750,36 @@ static int dma_region_free(struct ps3_dma_region* r)
                DBG("%s:%d: lv1_free_device_dma_region failed: %s\n",
                        __func__, __LINE__, ps3_result(result));
 
-       r->len = r->bus_addr = 0;
+       r->bus_addr = 0;
+
+       return result;
+}
+
+static int dma_ioc0_region_free(struct ps3_dma_region* r)
+{
+       int result;
+       struct dma_chunk *c, *n;
+
+       DBG("%s: start\n", __func__);
+       list_for_each_entry_safe(c, n, &r->chunk_list.head, link) {
+               list_del(&c->link);
+               dma_ioc0_free_chunk(c);
+       }
+
+       result = lv1_release_io_segment(0, r->bus_addr);
+
+       if (result)
+               DBG("%s:%d: lv1_free_device_dma_region failed: %s\n",
+                       __func__, __LINE__, ps3_result(result));
+
+       r->bus_addr = 0;
+       DBG("%s: end\n", __func__);
 
        return result;
 }
 
 /**
- * dma_map_area - Map an area of memory into a device dma region.
+ * dma_sb_map_area - Map an area of memory into a device dma region.
  * @r: Pointer to a struct ps3_dma_region.
  * @virt_addr: Starting virtual address of the area to map.
  * @len: Length in bytes of the area to map.
@@ -569,16 +789,19 @@ static int dma_region_free(struct ps3_dma_region* r)
  * This is the common dma mapping routine.
  */
 
-static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
-       unsigned long len, unsigned long *bus_addr)
+static int dma_sb_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
+          unsigned long len, unsigned long *bus_addr,
+          u64 iopte_flag)
 {
        int result;
        unsigned long flags;
        struct dma_chunk *c;
        unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
                : virt_addr;
-
-       *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
+       unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size);
+       unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys,
+                                             1 << r->page_size);
+       *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
 
        if (!USE_DYNAMIC_DMA) {
                unsigned long lpar_addr = ps3_mm_phys_to_lpar(phys_addr);
@@ -598,17 +821,18 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
        c = dma_find_chunk(r, *bus_addr, len);
 
        if (c) {
+               DBG("%s:%d: reusing mapped chunk", __func__, __LINE__);
+               dma_dump_chunk(c);
                c->usage_count++;
                spin_unlock_irqrestore(&r->chunk_list.lock, flags);
                return 0;
        }
 
-       result = dma_map_pages(r, _ALIGN_DOWN(phys_addr, 1 << r->page_size),
-               _ALIGN_UP(len, 1 << r->page_size), &c);
+       result = dma_sb_map_pages(r, aligned_phys, aligned_len, &c, iopte_flag);
 
        if (result) {
                *bus_addr = 0;
-               DBG("%s:%d: dma_map_pages failed (%d)\n",
+               DBG("%s:%d: dma_sb_map_pages failed (%d)\n",
                        __func__, __LINE__, result);
                spin_unlock_irqrestore(&r->chunk_list.lock, flags);
                return result;
@@ -620,8 +844,57 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
        return result;
 }
 
+static int dma_ioc0_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
+            unsigned long len, unsigned long *bus_addr,
+            u64 iopte_flag)
+{
+       int result;
+       unsigned long flags;
+       struct dma_chunk *c;
+       unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
+               : virt_addr;
+       unsigned long aligned_phys = _ALIGN_DOWN(phys_addr, 1 << r->page_size);
+       unsigned long aligned_len = _ALIGN_UP(len + phys_addr - aligned_phys,
+                                             1 << r->page_size);
+
+       DBG(KERN_ERR "%s: vaddr=%#lx, len=%#lx\n", __func__,
+           virt_addr, len);
+       DBG(KERN_ERR "%s: ph=%#lx a_ph=%#lx a_l=%#lx\n", __func__,
+           phys_addr, aligned_phys, aligned_len);
+
+       spin_lock_irqsave(&r->chunk_list.lock, flags);
+       c = dma_find_chunk_lpar(r, ps3_mm_phys_to_lpar(phys_addr), len);
+
+       if (c) {
+               /* FIXME */
+               BUG();
+               *bus_addr = c->bus_addr + phys_addr - aligned_phys;
+               c->usage_count++;
+               spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+               return 0;
+       }
+
+       result = dma_ioc0_map_pages(r, aligned_phys, aligned_len, &c,
+                                   iopte_flag);
+
+       if (result) {
+               *bus_addr = 0;
+               DBG("%s:%d: dma_ioc0_map_pages failed (%d)\n",
+                       __func__, __LINE__, result);
+               spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+               return result;
+       }
+       *bus_addr = c->bus_addr + phys_addr - aligned_phys;
+       DBG("%s: va=%#lx pa=%#lx a_pa=%#lx bus=%#lx\n", __func__,
+           virt_addr, phys_addr, aligned_phys, *bus_addr);
+       c->usage_count = 1;
+
+       spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+       return result;
+}
+
 /**
- * dma_unmap_area - Unmap an area of memory from a device dma region.
+ * dma_sb_unmap_area - Unmap an area of memory from a device dma region.
  * @r: Pointer to a struct ps3_dma_region.
  * @bus_addr: The starting ioc bus address of the area to unmap.
  * @len: Length in bytes of the area to unmap.
@@ -629,7 +902,7 @@ static int dma_map_area(struct ps3_dma_region *r, unsigned long virt_addr,
  * This is the common dma unmap routine.
  */
 
-int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
+int dma_sb_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
        unsigned long len)
 {
        unsigned long flags;
@@ -641,7 +914,8 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
        if (!c) {
                unsigned long aligned_bus = _ALIGN_DOWN(bus_addr,
                        1 << r->page_size);
-               unsigned long aligned_len = _ALIGN_UP(len, 1 << r->page_size);
+               unsigned long aligned_len = _ALIGN_UP(len + bus_addr - aligned_bus,
+                                                     1 << r->page_size);
                DBG("%s:%d: not found: bus_addr %lxh\n",
                        __func__, __LINE__, bus_addr);
                DBG("%s:%d: not found: len %lxh\n",
@@ -657,94 +931,165 @@ int dma_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
 
        if (!c->usage_count) {
                list_del(&c->link);
-               dma_free_chunk(c);
+               dma_sb_free_chunk(c);
        }
 
        spin_unlock_irqrestore(&r->chunk_list.lock, flags);
        return 0;
 }
 
+int dma_ioc0_unmap_area(struct ps3_dma_region *r, unsigned long bus_addr,
+                       unsigned long len)
+{
+       unsigned long flags;
+       struct dma_chunk *c;
+
+       DBG("%s: start a=%#lx l=%#lx\n", __func__, bus_addr, len);
+       spin_lock_irqsave(&r->chunk_list.lock, flags);
+       c = dma_find_chunk(r, bus_addr, len);
+
+       if (!c) {
+               unsigned long aligned_bus = _ALIGN_DOWN(bus_addr,
+                                                       1 << r->page_size);
+               unsigned long aligned_len = _ALIGN_UP(len + bus_addr - aligned_bus,
+                                                     1 << r->page_size);
+               DBG("%s:%d: not found: bus_addr %lxh\n",
+                   __func__, __LINE__, bus_addr);
+               DBG("%s:%d: not found: len %lxh\n",
+                   __func__, __LINE__, len);
+               DBG("%s:%d: not found: aligned_bus %lxh\n",
+                   __func__, __LINE__, aligned_bus);
+               DBG("%s:%d: not found: aligned_len %lxh\n",
+                   __func__, __LINE__, aligned_len);
+               BUG();
+       }
+
+       c->usage_count--;
+
+       if (!c->usage_count) {
+               list_del(&c->link);
+               dma_ioc0_free_chunk(c);
+       }
+
+       spin_unlock_irqrestore(&r->chunk_list.lock, flags);
+       DBG("%s: end\n", __func__);
+       return 0;
+}
+
 /**
- * dma_region_create_linear - Setup a linear dma maping for a device.
+ * dma_sb_region_create_linear - Setup a linear dma mapping for a device.
  * @r: Pointer to a struct ps3_dma_region.
  *
  * This routine creates an HV dma region for the device and maps all available
  * ram into the io controller bus address space.
  */
 
-static int dma_region_create_linear(struct ps3_dma_region *r)
+static int dma_sb_region_create_linear(struct ps3_dma_region *r)
 {
        int result;
-       unsigned long tmp;
-
-       /* force 16M dma pages for linear mapping */
-
-       if (r->page_size != PS3_DMA_16M) {
-               pr_info("%s:%d: forcing 16M pages for linear map\n",
-                       __func__, __LINE__);
-               r->page_size = PS3_DMA_16M;
+       unsigned long virt_addr, len, tmp;
+
+       if (r->len > 16*1024*1024) {    // FIXME
+               /* force 16M dma pages for linear mapping */
+               if (r->page_size != PS3_DMA_16M) {
+                       pr_info("%s:%d: forcing 16M pages for linear map\n",
+                               __func__, __LINE__);
+                       r->page_size = PS3_DMA_16M;
+                       r->len = _ALIGN_UP(r->len, 1 << r->page_size);
+               }
        }
 
-       result = dma_region_create(r);
-       BUG_ON(result);
-
-       result = dma_map_area(r, map.rm.base, map.rm.size, &tmp);
+       result = dma_sb_region_create(r);
        BUG_ON(result);
 
-       if (USE_LPAR_ADDR)
-               result = dma_map_area(r, map.r1.base, map.r1.size,
-                       &tmp);
-       else
-               result = dma_map_area(r, map.rm.size, map.r1.size,
-                       &tmp);
+       if (r->offset < map.rm.size) {
+               /* Map (part of) 1st RAM chunk */
+               virt_addr = map.rm.base + r->offset;
+               len = map.rm.size - r->offset;
+               if (len > r->len)
+                       len = r->len;
+               result = dma_sb_map_area(r, virt_addr, len, &tmp,
+                       IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
+               BUG_ON(result);
+       }
 
-       BUG_ON(result);
+       if (r->offset + r->len > map.rm.size) {
+               /* Map (part of) 2nd RAM chunk */
+               virt_addr = USE_LPAR_ADDR ? map.r1.base : map.rm.size;
+               len = r->len;
+               if (r->offset >= map.rm.size)
+                       virt_addr += r->offset - map.rm.size;
+               else
+                       len -= map.rm.size - r->offset;
+               result = dma_sb_map_area(r, virt_addr, len, &tmp,
+                       IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
+               BUG_ON(result);
+       }
 
        return result;
 }
 
 /**
- * dma_region_free_linear - Free a linear dma mapping for a device.
+ * dma_sb_region_free_linear - Free a linear dma mapping for a device.
  * @r: Pointer to a struct ps3_dma_region.
  *
  * This routine will unmap all mapped areas and free the HV dma region.
  */
 
-static int dma_region_free_linear(struct ps3_dma_region *r)
+static int dma_sb_region_free_linear(struct ps3_dma_region *r)
 {
        int result;
+       unsigned long bus_addr, len, lpar_addr;
+
+       if (r->offset < map.rm.size) {
+               /* Unmap (part of) 1st RAM chunk */
+               lpar_addr = map.rm.base + r->offset;
+               len = map.rm.size - r->offset;
+               if (len > r->len)
+                       len = r->len;
+               bus_addr = dma_sb_lpar_to_bus(r, lpar_addr);
+               result = dma_sb_unmap_area(r, bus_addr, len);
+               BUG_ON(result);
+       }
 
-       result = dma_unmap_area(r, dma_lpar_to_bus(r, 0), map.rm.size);
-       BUG_ON(result);
-
-       result = dma_unmap_area(r, dma_lpar_to_bus(r, map.r1.base),
-               map.r1.size);
-       BUG_ON(result);
+       if (r->offset + r->len > map.rm.size) {
+               /* Unmap (part of) 2nd RAM chunk */
+               lpar_addr = map.r1.base;
+               len = r->len;
+               if (r->offset >= map.rm.size)
+                       lpar_addr += r->offset - map.rm.size;
+               else
+                       len -= map.rm.size - r->offset;
+               bus_addr = dma_sb_lpar_to_bus(r, lpar_addr);
+               result = dma_sb_unmap_area(r, bus_addr, len);
+               BUG_ON(result);
+       }
 
-       result = dma_region_free(r);
+       result = dma_sb_region_free(r);
        BUG_ON(result);
 
        return result;
 }
 
 /**
- * dma_map_area_linear - Map an area of memory into a device dma region.
+ * dma_sb_map_area_linear - Map an area of memory into a device dma region.
  * @r: Pointer to a struct ps3_dma_region.
  * @virt_addr: Starting virtual address of the area to map.
  * @len: Length in bytes of the area to map.
  * @bus_addr: A pointer to return the starting ioc bus address of the area to
  * map.
  *
- * This routine just returns the coresponding bus address.  Actual mapping
+ * This routine just returns the corresponding bus address.  Actual mapping
  * occurs in dma_region_create_linear().
  */
 
-static int dma_map_area_linear(struct ps3_dma_region *r,
-       unsigned long virt_addr, unsigned long len, unsigned long *bus_addr)
+static int dma_sb_map_area_linear(struct ps3_dma_region *r,
+       unsigned long virt_addr, unsigned long len, unsigned long *bus_addr,
+       u64 iopte_flag)
 {
        unsigned long phys_addr = is_kernel_addr(virt_addr) ? __pa(virt_addr)
                : virt_addr;
-       *bus_addr = dma_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
+       *bus_addr = dma_sb_lpar_to_bus(r, ps3_mm_phys_to_lpar(phys_addr));
        return 0;
 }
 
@@ -754,42 +1099,97 @@ static int dma_map_area_linear(struct ps3_dma_region *r,
  * @bus_addr: The starting ioc bus address of the area to unmap.
  * @len: Length in bytes of the area to unmap.
  *
- * This routine does nothing.  Unmapping occurs in dma_region_free_linear().
+ * This routine does nothing.  Unmapping occurs in dma_sb_region_free_linear().
  */
 
-static int dma_unmap_area_linear(struct ps3_dma_region *r,
+static int dma_sb_unmap_area_linear(struct ps3_dma_region *r,
        unsigned long bus_addr, unsigned long len)
 {
        return 0;
+};
+
+static const struct ps3_dma_region_ops ps3_dma_sb_region_ops =  {
+       .create = dma_sb_region_create,
+       .free = dma_sb_region_free,
+       .map = dma_sb_map_area,
+       .unmap = dma_sb_unmap_area
+};
+
+static const struct ps3_dma_region_ops ps3_dma_sb_region_linear_ops = {
+       .create = dma_sb_region_create_linear,
+       .free = dma_sb_region_free_linear,
+       .map = dma_sb_map_area_linear,
+       .unmap = dma_sb_unmap_area_linear
+};
+
+static const struct ps3_dma_region_ops ps3_dma_ioc0_region_ops = {
+       .create = dma_ioc0_region_create,
+       .free = dma_ioc0_region_free,
+       .map = dma_ioc0_map_area,
+       .unmap = dma_ioc0_unmap_area
+};
+
+void ps3_dma_region_init(struct ps3_dma_region *r,
+       const struct ps3_device_id *did, enum ps3_dma_page_size page_size,
+       enum ps3_dma_region_type region_type, void *addr, unsigned long len,
+       enum ps3_iobus_type iobus_type)
+{
+       unsigned long lpar_addr;
+
+       lpar_addr = addr ? ps3_mm_phys_to_lpar(__pa(addr)) : 0;
+
+       r->did = *did;
+       r->page_size = page_size;
+       r->region_type = region_type;
+       r->offset = lpar_addr;
+       if (r->offset >= map.rm.size)
+               r->offset -= map.r1.offset;
+       r->len = len ? len : _ALIGN_UP(map.total, 1 << r->page_size);
+
+       switch (iobus_type) {
+       case PS3_IOBUS_SB:
+               r->region_ops =  (USE_DYNAMIC_DMA)
+                       ? &ps3_dma_sb_region_ops
+                       : &ps3_dma_sb_region_linear_ops;
+               break;
+       case PS3_IOBUS_IOC0:
+               r->region_ops = &ps3_dma_ioc0_region_ops;
+               break;
+       default:
+               BUG();
+       }
 }
+EXPORT_SYMBOL(ps3_dma_region_init);
 
 int ps3_dma_region_create(struct ps3_dma_region *r)
 {
-       return (USE_DYNAMIC_DMA)
-               ? dma_region_create(r)
-               : dma_region_create_linear(r);
+       BUG_ON(!r);
+       BUG_ON(!r->region_ops);
+       BUG_ON(!r->region_ops->create);
+       return r->region_ops->create(r);
 }
+EXPORT_SYMBOL(ps3_dma_region_create);
 
 int ps3_dma_region_free(struct ps3_dma_region *r)
 {
-       return (USE_DYNAMIC_DMA)
-               ? dma_region_free(r)
-               : dma_region_free_linear(r);
+       BUG_ON(!r);
+       BUG_ON(!r->region_ops);
+       BUG_ON(!r->region_ops->free);
+       return r->region_ops->free(r);
 }
+EXPORT_SYMBOL(ps3_dma_region_free);
 
 int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr,
-       unsigned long len, unsigned long *bus_addr)
+       unsigned long len, unsigned long *bus_addr,
+       u64 iopte_flag)
 {
-       return (USE_DYNAMIC_DMA)
-               ? dma_map_area(r, virt_addr, len, bus_addr)
-               : dma_map_area_linear(r, virt_addr, len, bus_addr);
+       return r->region_ops->map(r, virt_addr, len, bus_addr, iopte_flag);
 }
 
 int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr,
        unsigned long len)
 {
-       return (USE_DYNAMIC_DMA) ? dma_unmap_area(r, bus_addr, len)
-               : dma_unmap_area_linear(r, bus_addr, len);
+       return r->region_ops->unmap(r, bus_addr, len);
 }
 
 /*============================================================================*/
@@ -826,6 +1226,9 @@ void __init ps3_mm_init(void)
        /* arrange to do this in ps3_mm_add_memory */
        ps3_mm_region_create(&map.r1, map.total - map.rm.size);
 
+       /* correct map.total for the real total amount of memory we use */
+       map.total = map.rm.size + map.r1.size;
+
        // FIXME Temporary solution for the storage and sound drivers
        ps3_mem_total = map.rm.size + map.r1.size;
        ps3_rm_limit = map.rm.size;
index 18b66de..24af37a 100644 (file)
@@ -82,6 +82,7 @@ enum ps3_dev_type {
        PS3_DEV_TYPE_STOR_ROM = TYPE_ROM,       /* 5 */
        PS3_DEV_TYPE_SB_GPIO = 6,
        PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC,     /* 14 */
+       PS3_DEV_TYPE_NONE = 255,
 };
 
 int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
@@ -218,4 +219,14 @@ int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id);
 int ps3_repository_read_spu_resource_id(unsigned int res_index,
        enum ps3_spu_resource_type* resource_type, unsigned int *resource_id);
 
+/* Page table entries */
+#define IOPTE_PP_W             0x8000000000000000ul /* protection: write */
+#define IOPTE_PP_R             0x4000000000000000ul /* protection: read */
+#define IOPTE_M                        0x2000000000000000ul /* coherency required */
+#define IOPTE_SO_R             0x1000000000000000ul /* ordering: writes */
+#define IOPTE_SO_RW             0x1800000000000000ul /* ordering: r & w */
+#define IOPTE_RPN_Mask         0x07fffffffffff000ul /* RPN */
+#define IOPTE_H                        0x0000000000000800ul /* cache hint */
+#define IOPTE_IOID_Mask                0x00000000000007fful /* ioid */
+
 #endif
index b8f8018..4865264 100644 (file)
@@ -99,6 +99,9 @@ static void ps3_panic(char *str)
        while(1);
 }
 
+#if defined(CONFIG_FB_PS3) || defined(CONFIG_PS3_FLASH) || \
+    defined(CONFIG_PS3_FLASH_MODULE) || defined(CONFIG_PS3_STORAGE_OLD) || \
+    defined(CONFIG_PS3_STORAGE_MODULE_OLD)
 static void prealloc(struct ps3_prealloc *p)
 {
        if (!p->size)
@@ -114,13 +117,15 @@ static void prealloc(struct ps3_prealloc *p)
        printk(KERN_INFO "%s: %lu bytes at %p\n", p->name, p->size,
               p->address);
 }
+#endif
 
-#ifdef CONFIG_FB_PS3
+#if defined(CONFIG_FB_PS3) || defined(CONFIG_FB_PS3_MODULE)
 struct ps3_prealloc ps3fb_videomemory = {
        .name = "ps3fb videomemory",
        .size = CONFIG_FB_PS3_DEFAULT_SIZE_M*1024*1024,
        .align = 1024*1024              /* the GPU requires 1 MiB alignment */
 };
+EXPORT_SYMBOL_GPL(ps3fb_videomemory);
 #define prealloc_ps3fb_videomemory()   prealloc(&ps3fb_videomemory)
 
 static int __init early_parse_ps3fb(char *p)
@@ -137,16 +142,17 @@ early_param("ps3fb", early_parse_ps3fb);
 #define prealloc_ps3fb_videomemory()   do { } while (0)
 #endif
 
-#if defined(CONFIG_PS3_STORAGE) || defined(CONFIG_PS3_STORAGE_MODULE)
-struct ps3_prealloc ps3_stor_bounce_buffer = {
-       .name = "ps3_stor bounce buffer",
+#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) || \
+    defined(CONFIG_PS3_STORAGE_OLD) || defined(CONFIG_PS3_STORAGE_OLD_MODULE)
+struct ps3_prealloc ps3flash_bounce_buffer = {
+       .name = "ps3flash bounce buffer",
        .size = 256*1024,
        .align = 256*1024
 };
-EXPORT_SYMBOL_GPL(ps3_stor_bounce_buffer);
-#define prealloc_ps3_stor_bounce_buffer()      prealloc(&ps3_stor_bounce_buffer)
+EXPORT_SYMBOL_GPL(ps3flash_bounce_buffer);
+#define prealloc_ps3flash_bounce_buffer()      prealloc(&ps3flash_bounce_buffer)
 #else
-#define prealloc_ps3_stor_bounce_buffer()      do { } while (0)
+#define prealloc_ps3flash_bounce_buffer()      do { } while (0)
 #endif
 
 static int ps3_set_dabr(u64 dabr)
@@ -178,7 +184,7 @@ static void __init ps3_setup_arch(void)
 #endif
 
        prealloc_ps3fb_videomemory();
-       prealloc_ps3_stor_bounce_buffer();
+       prealloc_ps3flash_bounce_buffer();
 
        ppc_md.power_save = ps3_power_save;
 
@@ -215,31 +221,28 @@ static int __init ps3_probe(void)
 #if defined(CONFIG_KEXEC)
 static void ps3_kexec_cpu_down(int crash_shutdown, int secondary)
 {
-       DBG(" -> %s:%d\n", __func__, __LINE__);
+       int result;
+       u64 ppe_id;
+       u64 thread_id = secondary ? 1 : 0;
+
+       DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, secondary);
+       ps3_smp_cleanup_cpu(thread_id);
+
+       lv1_get_logical_ppe_id(&ppe_id);
+       result = lv1_configure_irq_state_bitmap(ppe_id, secondary ? 0 : 1, 0);
 
-       if (secondary) {
-               int cpu;
-               for_each_online_cpu(cpu)
-                       if (cpu)
-                               ps3_smp_cleanup_cpu(cpu);
-       } else
-               ps3_smp_cleanup_cpu(0);
+       /* seems to fail on second call */
+       DBG("%s:%d: lv1_configure_irq_state_bitmap (%d) %s\n", __func__,
+               __LINE__, secondary, ps3_result(result));
 
        DBG(" <- %s:%d\n", __func__, __LINE__);
 }
 
 static void ps3_machine_kexec(struct kimage *image)
 {
-       unsigned long ppe_id;
-
        DBG(" -> %s:%d\n", __func__, __LINE__);
 
-       lv1_get_logical_ppe_id(&ppe_id);
-       lv1_configure_irq_state_bitmap(ppe_id, 0, 0);
-       ps3_mm_shutdown();
-       ps3_mm_vas_destroy();
-
-       default_machine_kexec(image);
+       default_machine_kexec(image); // needs ipi, never returns.
 
        DBG(" <- %s:%d\n", __func__, __LINE__);
 }
diff --git a/arch/powerpc/platforms/ps3/system-bus-rework.txt b/arch/powerpc/platforms/ps3/system-bus-rework.txt
new file mode 100644 (file)
index 0000000..7877348
--- /dev/null
@@ -0,0 +1,40 @@
+Status of the system-bus re-work
+
+o=working
+x=not working
+NA=no support planned
+                               1st     shut    2nd     insmod  rmmod
+                               boot    down    boot
+
+CONFIG_USB_EHCI_HCD            o       o       o       o(5)    o
+CONFIG_USB_OHCI_HCD            o       o       o       o(5)    o
+CONFIG_GELIC_NET               o       o       o
+CONFIG_FB_PS3                  o       o(2)    o       o       x(6)
+CONFIG_SND_PS3                 o       o       o       o       o
+CONFIG_PS3_PS3AV               o       o       o       ?
+CONFIG_PS3_SYS_MANAGER         o       o       o       o       NA(4)
+CONFIG_PS3_VUART               o       o       o
+CONFIG_PS3_FLASH               o       o       o       o(5)    o
+CONFIG_PS3_DISK                        o       o       o       o(5)    o
+CONFIG_PS3_ROM                 o       o       o       o(5)    o
+CONFIG_PS3_STORAGE             o       o       o       o       NA(4)
+CONFIG_SPU_FS                  o       o       o
+CONFIG_PS3_STORAGE_OLD         o       o       o       NA      NA
+
+-- commands --
+
+64 bit kexec   o
+32 bit kexec   x(3)
+reboot:        o
+halt           o
+shutdown       o
+poweroff       o
+power button   o
+
+-- notes --
+
+(2) fbcon trouble, temp fix: ps3-hack-fbcon-shutdown.diff
+(3) not working, WIP
+(4) loaded as 'permanent'.
+(5) modprobe binding trouble
+(6) need to fix fbcon to support remove
index f910ad3..54257e0 100644 (file)
@@ -36,6 +36,202 @@ static struct device ps3_system_bus = {
         .bus_id         = "ps3_system",
 };
 
+// FIXME: need device usage counters!
+struct {
+       struct mutex mutex;
+       int sb_11; // usb 0
+       int sb_12; // usb 1
+       int gpu;
+} static usage_hack;
+
+static int ps3_open_hv_device_sb(struct ps3_system_bus_device *dev)
+{
+       int result;
+
+       BUG_ON(!dev->did.bus_id);
+       mutex_lock(&usage_hack.mutex);
+
+       if(dev->did.bus_id == 1 && dev->did.dev_id == 1) {
+               usage_hack.sb_11++;
+               if (usage_hack.sb_11 > 1) {
+                       result = 0;
+                       goto done;
+               }
+       }
+
+       if(dev->did.bus_id == 1 && dev->did.dev_id == 2) {
+               usage_hack.sb_12++;
+               if (usage_hack.sb_12 > 1) {
+                       result = 0;
+                       goto done;
+               }
+       }
+
+       result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0);
+
+       if (result) {
+               pr_debug("%s:%d: lv1_open_device failed: %s\n", __func__,
+                       __LINE__, ps3_result(result));
+                       result = -EPERM;
+       }
+
+done:
+       mutex_unlock(&usage_hack.mutex);
+       return result;
+}
+
+static int ps3_close_hv_device_sb(struct ps3_system_bus_device *dev)
+{
+       int result;
+
+       BUG_ON(!dev->did.bus_id);
+       mutex_lock(&usage_hack.mutex);
+
+       if(dev->did.bus_id == 1 && dev->did.dev_id == 1) {
+               usage_hack.sb_11--;
+               if (usage_hack.sb_11) {
+                       result = 0;
+                       goto done;
+               }
+       }
+
+       if(dev->did.bus_id == 1 && dev->did.dev_id == 2) {
+               usage_hack.sb_12--;
+               if (usage_hack.sb_12) {
+                       result = 0;
+                       goto done;
+               }
+       }
+
+       result = lv1_close_device(dev->did.bus_id, dev->did.dev_id);
+       BUG_ON(result);
+
+done:
+       mutex_unlock(&usage_hack.mutex);
+       return result;
+}
+
+static int ps3_open_hv_device_gpu(struct ps3_system_bus_device *dev)
+{
+       int result;
+
+       mutex_lock(&usage_hack.mutex);
+
+       usage_hack.gpu++;
+       if (usage_hack.gpu > 1) {
+               result = 0;
+               goto done;
+       }
+
+       result = lv1_gpu_open(0);
+
+       if (result) {
+               pr_debug("%s:%d: lv1_gpu_open failed: %s\n", __func__,
+                       __LINE__, ps3_result(result));
+                       result = -EPERM;
+       }
+
+done:
+       mutex_unlock(&usage_hack.mutex);
+       return result;
+}
+
+static int ps3_close_hv_device_gpu(struct ps3_system_bus_device *dev)
+{
+       int result;
+
+       mutex_lock(&usage_hack.mutex);
+
+       usage_hack.gpu--;
+       if (usage_hack.gpu) {
+               result = 0;
+               goto done;
+       }
+
+       result = lv1_gpu_close();
+       BUG_ON(result);
+
+done:
+       mutex_unlock(&usage_hack.mutex);
+       return result;
+}
+
+int ps3_open_hv_device(struct ps3_system_bus_device *dev)
+{
+       BUG_ON(!dev);
+       pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);
+
+       switch(dev->match_id) {
+       case PS3_MATCH_ID_EHCI:
+       case PS3_MATCH_ID_OHCI:
+       case PS3_MATCH_ID_GELIC:
+       case PS3_MATCH_ID_STOR_DISK:
+       case PS3_MATCH_ID_STOR_ROM:
+       case PS3_MATCH_ID_STOR_FLASH:
+               return ps3_open_hv_device_sb(dev);
+
+       case PS3_MATCH_ID_SOUND:
+       case PS3_MATCH_ID_GFX:
+               return ps3_open_hv_device_gpu(dev);
+
+       case PS3_MATCH_ID_AV_SETTINGS:
+       case PS3_MATCH_ID_SYSTEM_MANAGER:
+               pr_debug("%s:%d: unsupported match_id: %u\n", __func__,
+                       __LINE__, dev->match_id);
+               pr_debug("%s:%d: bus_id: %u\n", __func__,
+                       __LINE__, dev->did.bus_id);
+               BUG();
+               return -EINVAL;
+
+       default:
+               break;
+       }
+
+       pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,
+               dev->match_id);
+       BUG();
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(ps3_open_hv_device);
+
+int ps3_close_hv_device(struct ps3_system_bus_device *dev)
+{
+       BUG_ON(!dev);
+       pr_debug("%s:%d: match_id: %u\n", __func__, __LINE__, dev->match_id);
+
+       switch(dev->match_id) {
+       case PS3_MATCH_ID_EHCI:
+       case PS3_MATCH_ID_OHCI:
+       case PS3_MATCH_ID_GELIC:
+       case PS3_MATCH_ID_STOR_DISK:
+       case PS3_MATCH_ID_STOR_ROM:
+       case PS3_MATCH_ID_STOR_FLASH:
+               return ps3_close_hv_device_sb(dev);
+
+       case PS3_MATCH_ID_SOUND:
+       case PS3_MATCH_ID_GFX:
+               return ps3_close_hv_device_gpu(dev);
+
+       case PS3_MATCH_ID_AV_SETTINGS:
+       case PS3_MATCH_ID_SYSTEM_MANAGER:
+               pr_debug("%s:%d: unsupported match_id: %u\n", __func__,
+                       __LINE__, dev->match_id);
+               pr_debug("%s:%d: bus_id: %u\n", __func__,
+                       __LINE__, dev->did.bus_id);
+               BUG();
+               return -EINVAL;
+
+       default:
+               break;
+       }
+
+       pr_debug("%s:%d: unknown match_id: %u\n", __func__, __LINE__,
+               dev->match_id);
+       BUG();
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(ps3_close_hv_device);
+
 #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__)
 static void _dump_mmio_region(const struct ps3_mmio_region* r,
        const char* func, int line)
@@ -47,7 +243,7 @@ static void _dump_mmio_region(const struct ps3_mmio_region* r,
        pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr);
 }
 
-int ps3_mmio_region_create(struct ps3_mmio_region *r)
+static int ps3_sb_mmio_region_create(struct ps3_mmio_region *r)
 {
        int result;
 
@@ -63,12 +259,25 @@ int ps3_mmio_region_create(struct ps3_mmio_region *r)
        dump_mmio_region(r);
        return result;
 }
+
+static int ps3_ioc0_mmio_region_create(struct ps3_mmio_region *r)
+{
+       /* device specific; do nothing currently */
+       return 0;
+}
+
+int ps3_mmio_region_create(struct ps3_mmio_region *r)
+{
+       return r->mmio_ops->create(r);
+}
 EXPORT_SYMBOL_GPL(ps3_mmio_region_create);
 
-int ps3_free_mmio_region(struct ps3_mmio_region *r)
+static int ps3_sb_free_mmio_region(struct ps3_mmio_region *r)
 {
        int result;
 
+       dump_mmio_region(r);
+;
        result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id,
                r->lpar_addr);
 
@@ -79,8 +288,53 @@ int ps3_free_mmio_region(struct ps3_mmio_region *r)
        r->lpar_addr = 0;
        return result;
 }
+
+static int ps3_ioc0_free_mmio_region(struct ps3_mmio_region *r)
+{
+       /* device specific; do nothing currently */
+       return 0;
+}
+
+
+int ps3_free_mmio_region(struct ps3_mmio_region *r)
+{
+       return r->mmio_ops->free(r);
+}
+
 EXPORT_SYMBOL_GPL(ps3_free_mmio_region);
 
+static const struct ps3_mmio_region_ops ps3_mmio_sb_region_ops = {
+       .create = ps3_sb_mmio_region_create,
+       .free = ps3_sb_free_mmio_region
+};
+
+static const struct ps3_mmio_region_ops ps3_mmio_ioc0_region_ops = {
+       .create = ps3_ioc0_mmio_region_create,
+       .free = ps3_ioc0_free_mmio_region
+};
+
+void ps3_mmio_region_init(struct ps3_mmio_region *r,
+       const struct ps3_device_id* did, unsigned long bus_addr,
+       unsigned long len, enum ps3_mmio_page_size page_size,
+       enum ps3_iobus_type iobus_type)
+{
+       r->did = *did;
+       r->bus_addr = bus_addr;
+       r->len = len;
+       r->page_size = page_size;
+       switch (iobus_type) {
+       case PS3_IOBUS_SB:
+               r->mmio_ops = &ps3_mmio_sb_region_ops;
+               break;
+       case PS3_IOBUS_IOC0:
+               r->mmio_ops = &ps3_mmio_ioc0_region_ops;
+               break;
+       default:
+               BUG();
+       }
+}
+EXPORT_SYMBOL_GPL(ps3_mmio_region_init);
+
 static int ps3_system_bus_match(struct device *_dev,
        struct device_driver *_drv)
 {
@@ -98,97 +352,84 @@ static int ps3_system_bus_match(struct device *_dev,
 
 static int ps3_system_bus_probe(struct device *_dev)
 {
-       int result;
+       int result = 0;
        struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
-       struct ps3_system_bus_driver *drv =
-               to_ps3_system_bus_driver(_dev->driver);
-
-       result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0);
+       struct ps3_system_bus_driver *drv;
 
-       if (result && (result != LV1_BUSY || (dev->match_id != PS3_MATCH_ID_EHCI
-               && dev->match_id != PS3_MATCH_ID_OHCI))) {
-               pr_debug("%s:%d: lv1_open_device failed: %s\n",
-                       __func__, __LINE__, ps3_result(result));
-               result = -EACCES;
-               goto clean_none;
-       }
-
-       if (dev->d_region->did.bus_id) {
-               result = ps3_dma_region_create(dev->d_region);
-
-               if (result) {
-                       pr_debug("%s:%d: ps3_dma_region_create failed (%d)\n",
-                               __func__, __LINE__, result);
-                       BUG_ON("check region type");
-                       result = -EINVAL;
-                       goto clean_device;
-               }
-       }
+       BUG_ON(!dev);
+       pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id);
 
+       drv = to_ps3_system_bus_driver(_dev->driver);
        BUG_ON(!drv);
 
-       if (drv->probe)
+       if(drv->probe)
                result = drv->probe(dev);
        else
                pr_info("%s:%d: %s no probe method\n", __func__, __LINE__,
                        dev->core.bus_id);
 
-       if (result) {
-               pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__);
-               goto clean_dma;
-       }
-
-       return result;
-
-clean_dma:
-       ps3_dma_region_free(dev->d_region);
-clean_device:
-       lv1_close_device(dev->did.bus_id, dev->did.dev_id);
-clean_none:
+       pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id);
        return result;
 }
 
 static int ps3_system_bus_remove(struct device *_dev)
 {
+       int result = 0;
        struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
-       struct ps3_system_bus_driver *drv =
-               to_ps3_system_bus_driver(_dev->driver);
+       struct ps3_system_bus_driver *drv;
+
+       BUG_ON(!dev);
+       pr_info(" -> %s:%d: %s\n", __func__, __LINE__, _dev->bus_id);
+
+       drv = to_ps3_system_bus_driver(_dev->driver);
+       BUG_ON(!drv);
 
        if (drv->remove)
-               drv->remove(dev);
+               result = drv->remove(dev);
        else
-               pr_info("%s:%d: %s no remove method\n", __func__, __LINE__,
-                       dev->core.bus_id);
+               dev_dbg(&dev->core, "%s:%d %s: no remove method\n",
+                       __func__, __LINE__, drv->core.name);
 
-       ps3_dma_region_free(dev->d_region);
-       ps3_free_mmio_region(dev->m_region);
-       lv1_close_device(dev->did.bus_id, dev->did.dev_id);
-
-       return 0;
+       pr_info(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id);
+       return result;
 }
 
-static const char *ps3_system_bus_modalias(enum ps3_match_id match_id)
-{
-       const char *p;
-       switch (match_id) {
-               case PS3_MATCH_ID_EHCI:
-                       p = "ps3-ehci";
-                       break;
-               case PS3_MATCH_ID_OHCI:
-                       p = "ps3-ohci";
-                       break;
-               case PS3_MATCH_ID_GELIC:
-                       p = "gelic_net";
-                       break;
-               case PS3_MATCH_ID_AV_SETTINGS:
-                       p = "ps3av";
-                       break;
-               case PS3_MATCH_ID_SYSTEM_MANAGER:
-               default:
-                       p = "unknown";
-                       break;
+static void ps3_system_bus_shutdown(struct device *_dev)
+{
+       struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+       struct ps3_system_bus_driver *drv;
+
+       BUG_ON(!dev);
+
+       dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
+               dev->match_id);
+
+       if (!dev->core.driver) {
+               dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
+                       __LINE__);
+               return;
+       }
+
+       drv = to_ps3_system_bus_driver(dev->core.driver);
+
+       BUG_ON(!drv);
+
+       dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__,
+               dev->core.bus_id, drv->core.name);
+
+       if (drv->shutdown)
+               drv->shutdown(dev);
+       else if (drv->remove) {
+               dev_dbg(&dev->core, "%s:%d %s: no shutdown, calling remove\n",
+                       __func__, __LINE__, drv->core.name);
+               drv->remove(dev);
+       } else {
+               dev_dbg(&dev->core, "%s:%d %s: no shutdown method\n",
+                       __func__, __LINE__, drv->core.name);
+               BUG();
        }
-       return p;
+
+       dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
 }
 
 static int ps3_system_bus_uevent(struct device *_dev, char **envp,
@@ -198,8 +439,8 @@ static int ps3_system_bus_uevent(struct device *_dev, char **envp,
        int i=0, length = 0;
 
        if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-                          &length, "MODALIAS=%s",
-                          ps3_system_bus_modalias(dev->match_id)))
+                          &length, "MODALIAS=ps3:%d",
+                          dev->match_id))
                return -ENOMEM;
 
        envp[i] = NULL;
@@ -210,7 +451,7 @@ static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
                             char *buf)
 {
        struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
-        int len = snprintf(buf, PAGE_SIZE, "%s\n", ps3_system_bus_modalias(dev->match_id));
+        int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id);
 
         return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
 }
@@ -223,9 +464,10 @@ static struct device_attribute ps3_system_bus_dev_attrs[] = {
 struct bus_type ps3_system_bus_type = {
        .name = "ps3_system_bus",
        .match = ps3_system_bus_match,
+       .uevent = ps3_system_bus_uevent,
        .probe = ps3_system_bus_probe,
        .remove = ps3_system_bus_remove,
-       .uevent = ps3_system_bus_uevent,
+       .shutdown = ps3_system_bus_shutdown,
        .dev_attrs = ps3_system_bus_dev_attrs,
 };
 
@@ -235,10 +477,18 @@ int __init ps3_system_bus_init(void)
 
        if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
                return -ENODEV;
+
+       printk(" -> %s:%d\n", __func__, __LINE__);
+
+       mutex_init(&usage_hack.mutex);
+
        result = device_register(&ps3_system_bus);
        BUG_ON(result);
+
        result = bus_register(&ps3_system_bus_type);
        BUG_ON(result);
+
+       printk(" <- %s:%d\n", __func__, __LINE__);
        return result;
 }
 
@@ -248,16 +498,13 @@ core_initcall(ps3_system_bus_init);
  * Returns the virtual address of the buffer and sets dma_handle
  * to the dma address (mapping) of the first page.
  */
-
 static void * ps3_alloc_coherent(struct device *_dev, size_t size,
-       dma_addr_t *dma_handle, gfp_t flag)
+                                     dma_addr_t *dma_handle, gfp_t flag)
 {
        int result;
        struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
        unsigned long virt_addr;
 
-       BUG_ON(!dev->d_region->bus_addr);
-
        flag &= ~(__GFP_DMA | __GFP_HIGHMEM);
        flag |= __GFP_ZERO;
 
@@ -268,7 +515,8 @@ static void * ps3_alloc_coherent(struct device *_dev, size_t size,
                goto clean_none;
        }
 
-       result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle);
+       result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle,
+                            IOPTE_PP_W | IOPTE_PP_R | IOPTE_SO_RW | IOPTE_M);
 
        if (result) {
                pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -302,7 +550,7 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
  * byte within the page as vaddr.
  */
 
-static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size,
+static dma_addr_t ps3_sb_map_single(struct device *_dev, void *ptr, size_t size,
        enum dma_data_direction direction)
 {
        struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
@@ -310,7 +558,8 @@ static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size,
        unsigned long bus_addr;
 
        result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
-               &bus_addr);
+                            &bus_addr,
+                            IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW | IOPTE_M);
 
        if (result) {
                pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -320,6 +569,39 @@ static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size,
        return bus_addr;
 }
 
+static dma_addr_t ps3_ioc0_map_single(struct device *_dev, void *ptr, size_t size,
+                                     enum dma_data_direction direction)
+{
+       struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
+       int result;
+       unsigned long bus_addr;
+       u64 iopte_flag;
+
+       iopte_flag = IOPTE_M;
+       switch (direction) {
+       case DMA_BIDIRECTIONAL:
+               iopte_flag |= IOPTE_PP_R | IOPTE_PP_W | IOPTE_SO_RW;
+               break;
+       case DMA_TO_DEVICE:
+               iopte_flag |= IOPTE_PP_R | IOPTE_SO_R;
+               break;
+       case DMA_FROM_DEVICE:
+               iopte_flag |= IOPTE_PP_W | IOPTE_SO_RW;
+               break;
+       default:
+               /* not happned */
+               BUG();
+       };
+       result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
+                            &bus_addr, iopte_flag);
+
+       if (result) {
+               pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
+                       __func__, __LINE__, result);
+       }
+       return bus_addr;
+}
+
 static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr,
        size_t size, enum dma_data_direction direction)
 {
@@ -334,7 +616,7 @@ static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr,
        }
 }
 
-static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
+static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
        enum dma_data_direction direction)
 {
        struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev);
@@ -347,7 +629,7 @@ static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
        for (i = 0; i < nents; i++, sg++) {
                int result = ps3_dma_map(dev->d_region,
                        page_to_phys(sg->page) + sg->offset, sg->length,
-                       &sg->dma_address);
+                                        &sg->dma_address, 0);
 
                if (result) {
                        pr_debug("%s:%d: ps3_dma_map failed (%d)\n",
@@ -362,7 +644,14 @@ static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
 #endif
 }
 
-static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg,
+static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg, int nents,
+                          enum dma_data_direction direction)
+{
+       BUG();
+       return 0;
+}
+
+static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg,
        int nents, enum dma_data_direction direction)
 {
 #if defined(CONFIG_PS3_DYNAMIC_DMA)
@@ -370,18 +659,34 @@ static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg,
 #endif
 }
 
+static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg,
+                           int nents, enum dma_data_direction direction)
+{
+       BUG();
+}
+
 static int ps3_dma_supported(struct device *_dev, u64 mask)
 {
        return mask >= DMA_32BIT_MASK;
 }
 
-static struct dma_mapping_ops ps3_dma_ops = {
+static struct dma_mapping_ops ps3_sb_dma_ops = {
+       .alloc_coherent = ps3_alloc_coherent,
+       .free_coherent = ps3_free_coherent,
+       .map_single = ps3_sb_map_single,
+       .unmap_single = ps3_unmap_single,
+       .map_sg = ps3_sb_map_sg,
+       .unmap_sg = ps3_sb_unmap_sg,
+       .dma_supported = ps3_dma_supported
+};
+
+static struct dma_mapping_ops ps3_ioc0_dma_ops = {
        .alloc_coherent = ps3_alloc_coherent,
        .free_coherent = ps3_free_coherent,
-       .map_single = ps3_map_single,
+       .map_single = ps3_ioc0_map_single,
        .unmap_single = ps3_unmap_single,
-       .map_sg = ps3_map_sg,
-       .unmap_sg = ps3_unmap_sg,
+       .map_sg = ps3_ioc0_map_sg,
+       .unmap_sg = ps3_ioc0_unmap_sg,
        .dma_supported = ps3_dma_supported
 };
 
@@ -403,25 +708,37 @@ static void ps3_system_bus_release_device(struct device *_dev)
  * object and frees the object in ps3_system_bus_release_device().
  */
 
-int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
+int ps3_system_bus_device_register(struct ps3_system_bus_device *dev,
+       enum ps3_iobus_type iobus_type)
 {
        int result;
-       static unsigned int dev_count = 1;
+       static unsigned int dev_ioc0_count = 1;
+       static unsigned int dev_sb_count = 1;
 
-       dev->core.parent = NULL;
+       if (!dev->core.parent)
+               dev->core.parent = &ps3_system_bus;
        dev->core.bus = &ps3_system_bus_type;
        dev->core.release = ps3_system_bus_release_device;
+       switch (iobus_type) {
+       case PS3_IOBUS_SB:
+               dev->core.archdata.dma_ops = &ps3_sb_dma_ops;
+               snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x",
+                        dev_sb_count++);
+
+               break;
+
+       case PS3_IOBUS_IOC0:
+               dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops;
+               snprintf(dev->core.bus_id, sizeof(dev->core.bus_id),
+                       "ioc0_%02x", dev_ioc0_count++);
+               break;
+       default:
+               BUG();
+       };
 
        dev->core.archdata.of_node = NULL;
-       dev->core.archdata.dma_ops = &ps3_dma_ops;
        dev->core.archdata.numa_node = 0;
 
-       if (!dev->core.parent)
-               dev->core.parent = &ps3_system_bus;
-
-       snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x",
-               dev_count++);
-
        pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id);
 
        result = device_register(&dev->core);
@@ -430,13 +747,16 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
 
 EXPORT_SYMBOL_GPL(ps3_system_bus_device_register);
 
-int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv)
+int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv,
+       enum ps3_iobus_type iobus_type)
 {
        int result;
 
+       printk(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);
        drv->core.bus = &ps3_system_bus_type;
 
        result = driver_register(&drv->core);
+       printk(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);
        return result;
 }
 
@@ -444,7 +764,9 @@ EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register);
 
 void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv)
 {
+       printk(" -> %s:%d: %s\n", __func__, __LINE__, drv->core.name);
        driver_unregister(&drv->core);
+       printk(" <- %s:%d: %s\n", __func__, __LINE__, drv->core.name);
 }
 
 EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister);
index b64d48f..67daaff 100644 (file)
@@ -459,7 +459,7 @@ config ATA_OVER_ETH
        This driver provides Support for ATA over Ethernet block
        devices like the Coraid EtherDrive (R) Storage Blade.
 
-config PS3_STORAGE
+config PS3_STORAGE_OLD
        tristate "Support PS3 internal HDD/CDROM drives"
        depends on PPC_PS3 && BLK_DEV_SD && BLK_DEV_SR
        help
@@ -468,7 +468,7 @@ config PS3_STORAGE
 
 config PS3_STORAGE_EXPECTED_NUM_DRIVES
        int "Number of expected storage drives"
-       depends on PS3_STORAGE
+       depends on PS3_STORAGE_OLD
        default "3"
        help
          This specifies number of expected numbers of internal
@@ -478,7 +478,7 @@ config PS3_STORAGE_EXPECTED_NUM_DRIVES
 
 config PS3_STORAGE_MAX_SPINUP_WAIT_TIME
        int "Maximum time of wait time"
-       depends on PS3_STORAGE
+       depends on PS3_STORAGE_OLD
        default "10"
        help
          This specifies maximum time to wait for storage
index 67cdc31..715ea60 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o
 obj-$(CONFIG_VIODASD)          += viodasd.o
 obj-$(CONFIG_BLK_DEV_SX8)      += sx8.o
 obj-$(CONFIG_BLK_DEV_UB)       += ub.o
-obj-$(CONFIG_PS3_STORAGE)      += ps3_storage.o
+obj-$(CONFIG_PS3_STORAGE_OLD)  += ps3_storage.o
 
+obj-$(CONFIG_PS3_DISK)         += ps3disk.o
 obj-$(CONFIG_CIPHER_TWOFISH)   += loop_fish2.o
index 657403e..eca180f 100644 (file)
@@ -1414,8 +1414,8 @@ static u64 ps3_free_dma_region(const struct ps3_device_id *did, u64 dma)
 static void *ps3_stor_alloc_separate_memory(int alloc_size, u64 *lpar_addr)
 {
        void * va;
-       BUG_ON(alloc_size != ps3_stor_bounce_buffer.size);
-       va = ps3_stor_bounce_buffer.address;
+       BUG_ON(alloc_size != ps3flash_bounce_buffer.size);
+       va = ps3flash_bounce_buffer.address;
        *lpar_addr = ps3_mm_phys_to_lpar(__pa(va));
        return va;
 }
@@ -1713,12 +1713,24 @@ static unsigned int ps3_stor_enum_storage_drives(void)
 {
        unsigned int devices = 0;
 
+#if defined(CONFIG_PS3_DISK) || defined(CONFIG_PS3_DISK_MODULE)
+       printk("NOT looking for disk devices!!!\n");
+#else
        printk("Looking for disk devices...\n");
        devices += ps3_stor_enum_storage_type(PS3_DEV_TYPE_STOR_DISK);
+#endif
+#if defined(CONFIG_PS3_ROM) || defined(CONFIG_PS3_ROM_MODULE)
+       printk("Not looking for ROM devices...\n");
+#else
        printk("Looking for ROM devices...\n");
        devices += ps3_stor_enum_storage_type(PS3_DEV_TYPE_STOR_ROM);
+#endif
+#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE)
+       printk("NOT looking for FLASH devices!!!\n");
+#else
        printk("Looking for FLASH devices...\n");
        devices += ps3_stor_enum_storage_type(PS3_DEV_TYPE_STOR_FLASH);
+#endif
 
        return devices;
 }
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
new file mode 100644 (file)
index 0000000..2230170
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * PS3 Disk Storage Driver
+ *
+ * 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.
+ */
+
+#undef DEBUG
+
+#include <linux/dma-mapping.h>
+#include <linux/blkdev.h>
+#include <linux/freezer.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+#define DEVICE_NAME            "ps3disk"
+
+#define BOUNCE_SIZE            (64*1024)
+
+// FIXME Use a fixed major assigned by LANANA?
+#define PS3DISK_MAJOR          0
+
+#define PS3DISK_MAX_DISKS      16
+#define PS3DISK_MINORS         16
+
+#define KERNEL_SECTOR_SIZE     512
+
+
+#define PS3DISK_NAME           "ps3d%c"
+
+
+struct ps3disk_private {
+       spinlock_t lock;
+       struct task_struct *thread;
+       struct request_queue *queue;
+       struct gendisk *gendisk;
+       unsigned int blocking_factor;
+};
+#define ps3disk_priv(dev)      ((dev)->sbd.core.driver_data)
+
+static int ps3disk_major = PS3DISK_MAJOR;
+
+static int ps3disk_open(struct inode *inode, struct file *file)
+{
+       struct ps3_storage_device *dev = inode->i_bdev->bd_disk->private_data;
+
+       file->private_data = dev;
+       return 0;
+}
+
+
+
+static struct block_device_operations ps3disk_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ps3disk_open,
+};
+
+static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
+                                  struct request *req, int gather)
+{
+       unsigned int sectors = 0, offset = 0;
+       struct bio *bio;
+       sector_t sector;
+       struct bio_vec *bvec;
+       unsigned int i = 0, j;
+       size_t size;
+       void *buf;
+
+       rq_for_each_bio(bio, req) {
+               sector = bio->bi_sector;
+               dev_dbg(&dev->sbd.core,
+                       "%s:%u: bio %u: %u segs %u sectors from %lu\n",
+                       __func__, __LINE__, i, bio_segments(bio),
+                       bio_sectors(bio), sector);
+               bio_for_each_segment(bvec, bio, j) {
+                       size = bio_cur_sectors(bio)*KERNEL_SECTOR_SIZE;
+                       buf = __bio_kmap_atomic(bio, j, KM_USER0);
+                       if (gather)
+                               memcpy(dev->bounce_buf+offset, buf, size);
+                       else
+                               memcpy(buf, dev->bounce_buf+offset, size);
+                       offset += size;
+                       __bio_kunmap_atomic(bio, KM_USER0);
+               }
+               sectors += bio_sectors(bio);
+               i++;
+       }
+}
+
+static void ps3disk_handle_request_sg(struct ps3_storage_device *dev,
+                                     struct request *req)
+{
+       struct ps3disk_private *priv = ps3disk_priv(dev);
+       int uptodate = 1;
+       int write = rq_data_dir(req);
+       u64 res;
+
+#ifdef DEBUG
+       unsigned int n = 0;
+       struct bio *bio;
+       const char *op = write ? "write" : "read";
+       rq_for_each_bio(bio, req)
+               n++;
+       dev_dbg(&dev->sbd.core,
+               "%s:%u: %s req has %u bios for %lu sectors %lu hard sectors\n",
+               __func__, __LINE__, op, n, req->nr_sectors,
+               req->hard_nr_sectors);
+#endif
+
+       if (write)
+               ps3disk_scatter_gather(dev, req, 1);
+
+       res = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
+                                        req->sector*priv->blocking_factor,
+                                        req->nr_sectors*priv->blocking_factor,
+                                        write);
+       if (res)
+               uptodate = 0;
+       else if (!write)
+               ps3disk_scatter_gather(dev, req, 0);
+
+       spin_lock_irq(&priv->lock);
+       if (!end_that_request_first(req, uptodate, req->nr_sectors)) {
+               blkdev_dequeue_request(req);
+               end_that_request_last(req, uptodate);
+       }
+       spin_unlock_irq(&priv->lock);
+}
+
+static int ps3disk_thread(void *data)
+{
+       struct ps3_storage_device *dev = data;
+       struct ps3disk_private *priv = ps3disk_priv(dev);
+       request_queue_t *q = priv->queue;
+       struct request *req;
+
+       dev_dbg(&dev->sbd.core, "%s thread init\n", __func__);
+
+       current->flags |= PF_NOFREEZE;
+
+       while (!kthread_should_stop()) {
+               spin_lock_irq(&priv->lock);
+               set_current_state(TASK_INTERRUPTIBLE);
+               req = elv_next_request(q);
+               if (!req) {
+                       spin_unlock_irq(&priv->lock);
+                       schedule();
+                       continue;
+               }
+               if (!blk_fs_request(req)) {
+                       blk_dump_rq_flags(req, DEVICE_NAME " bad request");
+                       end_request(req, 0);
+                       spin_unlock_irq(&priv->lock);
+                       continue;
+               }
+               spin_unlock_irq(&priv->lock);
+               ps3disk_handle_request_sg(dev, req);
+       }
+
+       dev_dbg(&dev->sbd.core, "%s thread exit\n", __func__);
+       return 0;
+}
+
+static void ps3disk_request(request_queue_t *q)
+{
+       struct ps3_storage_device *dev = q->queuedata;
+       struct ps3disk_private *priv = ps3disk_priv(dev);
+       wake_up_process(priv->thread);
+}
+
+static unsigned long ps3disk_mask;
+
+static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev)
+{
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3disk_private *priv;
+       int res, error;
+       unsigned int devidx;
+       struct request_queue *queue;
+       struct gendisk *gendisk;
+       struct task_struct *task;
+
+       error = ps3stor_probe_access(dev);
+       if (error) {
+               dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n",
+                       __func__, __LINE__);
+               return error;
+       }
+
+       if (dev->blk_size < KERNEL_SECTOR_SIZE) {
+               dev_err(&dev->sbd.core,
+                       "%s:%u: cannot handle block size %lu\n", __func__,
+                       __LINE__, dev->blk_size);
+               return -EINVAL;
+       }
+
+       BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG);
+       devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS);
+       if (devidx >= PS3DISK_MAX_DISKS) {
+               dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__,
+                       __LINE__);
+               return -ENOSPC;
+       }
+       __set_bit(devidx, &ps3disk_mask);
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               error = -ENOMEM;
+               goto fail;
+       }
+
+       ps3disk_priv(dev) = priv;
+       spin_lock_init(&priv->lock);
+
+       dev->bounce_size = BOUNCE_SIZE;
+       dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+       if (!dev->bounce_buf) {
+               error = -ENOMEM;
+               goto fail_free_priv;
+       }
+
+       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_free_bounce;
+       }
+
+       error = ps3_sb_event_receive_port_setup(PS3_BINDING_CPU_ANY,
+                                               &dev->sbd.did,
+                                               dev->sbd.interrupt_id,
+                                               &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, ps3stor_interrupt, IRQF_DISABLED,
+                           DEVICE_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;
+       }
+
+       dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf));
+
+       dev->sbd.d_region = &dev->dma_region;
+       ps3_dma_region_init(&dev->dma_region, &dev->sbd.did, PS3_DMA_4K,
+                           PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size,
+                           PS3_IOBUS_SB);
+       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_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;
+       }
+
+       queue = blk_init_queue(ps3disk_request, &priv->lock);
+       if (!queue) {
+               dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
+                       __func__, __LINE__);
+               error = -ENOMEM;
+               goto fail_unmap_dma;
+       }
+
+       priv->queue = queue;
+       queue->queuedata = dev;
+
+       blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH);
+
+       blk_queue_max_sectors(queue, dev->bounce_size/KERNEL_SECTOR_SIZE);
+       blk_queue_segment_boundary(queue, -1UL);
+       blk_queue_dma_alignment(queue, dev->blk_size-1);
+       blk_queue_hardsect_size(queue, dev->blk_size);
+
+       blk_queue_ordered(queue, 0, NULL);      // FIXME no barriers
+
+       blk_queue_max_phys_segments(queue, -1);
+       blk_queue_max_hw_segments(queue, -1);
+       blk_queue_max_segment_size(queue, dev->bounce_size);
+
+       gendisk = alloc_disk(PS3DISK_MINORS);
+       if (!gendisk) {
+               dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__,
+                       __LINE__);
+               error = -ENOMEM;
+               goto fail_cleanup_queue;
+       }
+
+       priv->gendisk = gendisk;
+       gendisk->major = ps3disk_major;
+       gendisk->first_minor = devidx * PS3DISK_MINORS;
+       gendisk->fops = &ps3disk_fops;
+       gendisk->queue = queue;
+       gendisk->private_data = dev;
+       snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME,
+                devidx+'a');
+       priv->blocking_factor = dev->blk_size/KERNEL_SECTOR_SIZE;
+       set_capacity(gendisk,
+                    dev->regions[dev->region_idx].size*priv->blocking_factor);
+
+       task = kthread_run(ps3disk_thread, dev, DEVICE_NAME);
+       if (IS_ERR(task)) {
+               error = PTR_ERR(task);
+               goto fail_free_disk;
+       }
+       priv->thread = task;
+
+       add_disk(gendisk);
+       return 0;
+
+fail_free_disk:
+       put_disk(priv->gendisk);
+fail_cleanup_queue:
+       blk_cleanup_queue(queue);
+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.did, dev->sbd.interrupt_id,
+                                         dev->irq);
+fail_close_device:
+       ps3_close_hv_device(&dev->sbd);
+fail_free_bounce:
+       kfree(dev->bounce_buf);
+fail_free_priv:
+       kfree(priv);
+fail:
+       __clear_bit(devidx, &ps3disk_mask);
+       return error;
+}
+
+static int ps3disk_remove(struct ps3_system_bus_device *_dev)
+{
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3disk_private *priv = ps3disk_priv(dev);
+       int error;
+
+       if (priv->thread)
+               kthread_stop(priv->thread);
+       if (priv->gendisk) {
+               __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS,
+                           &ps3disk_mask);
+               del_gendisk(priv->gendisk);
+               put_disk(priv->gendisk);
+       }
+       if (priv->queue)
+               blk_cleanup_queue(priv->queue);
+       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.did,
+                                                 dev->sbd.interrupt_id,
+                                                 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);
+
+       kfree(dev->bounce_buf);
+       kfree(priv);
+       return 0;
+}
+
+
+static struct ps3_system_bus_driver ps3disk = {
+       .match_id       = PS3_MATCH_ID_STOR_DISK,
+       .core.name      = DEVICE_NAME,
+       .probe          = ps3disk_probe,
+       .remove         = ps3disk_remove,
+       .shutdown       = ps3disk_remove,
+};
+
+
+static int __init ps3disk_init(void)
+{
+       int error;
+
+       error = register_blkdev(ps3disk_major, DEVICE_NAME);
+       if (error <= 0) {
+               printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__,
+                      __LINE__, error);
+               return error;
+       }
+       if (!ps3disk_major)
+               ps3disk_major = error;
+
+       pr_info("%s:%u: registered block device major %d\n", __func__,
+               __LINE__, ps3disk_major);
+
+       return ps3_system_bus_driver_register(&ps3disk, PS3_IOBUS_SB);
+}
+
+static void __exit ps3disk_exit(void)
+{
+       unregister_blkdev(ps3disk_major, DEVICE_NAME);
+
+       return ps3_system_bus_driver_unregister(&ps3disk);
+}
+
+module_init(ps3disk_init);
+module_exit(ps3disk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 Disk Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
+
index 145dac7..97f5cea 100644 (file)
@@ -106,6 +106,8 @@ obj-$(CONFIG_HANGCHECK_TIMER)       += hangcheck-timer.o
 obj-$(CONFIG_TCG_TPM)          += tpm/
 obj-$(CONFIG_CRASHER)          += crasher.o
 
+obj-$(CONFIG_PS3_FLASH)                += ps3flash.o
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
new file mode 100644 (file)
index 0000000..1c5ca39
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * PS3 FLASH ROM Storage Driver
+ *
+ * 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.
+ */
+
+#undef DEBUG
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+#define DEVICE_NAME            "ps3flash"
+
+
+#define FLASH_BLOCK_SIZE       (256*1024)
+
+
+struct ps3flash_private {
+       struct mutex mutex;
+};
+#define ps3flash_priv(dev)     ((dev)->sbd.core.driver_data)
+
+static struct ps3_storage_device *ps3flash_dev;
+
+enum {
+       PS3FLASH_READ   = 0,
+       PS3FLASH_WRITE  = 1,
+};
+
+static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev,
+                                    u64 start_sector, u64 sectors,
+                                    unsigned int sector_offset)
+{
+       u64 max_sectors, lpar, res;
+
+       max_sectors = dev->bounce_size / dev->blk_size;
+       if (sectors > max_sectors) {
+               dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %lu\n",
+                       __func__, __LINE__, max_sectors);
+               sectors = max_sectors;
+       }
+
+       lpar = dev->bounce_lpar + sector_offset * dev->blk_size;
+       res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, 0);
+       return res ? -EIO : sectors;
+}
+
+static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev,
+                                   u64 start_sector)
+{
+       u64 sectors, res;
+
+       sectors = dev->bounce_size / dev->blk_size;
+       res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, start_sector,
+                                        sectors, 1);
+       return res ? -EIO : sectors;
+}
+
+
+static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
+{
+       struct ps3_storage_device *dev = ps3flash_dev;
+       u64 size = dev->regions[dev->region_idx].size*dev->blk_size;
+
+       switch (origin) {
+       case 1:
+               offset += file->f_pos;
+               break;
+       case 2:
+               offset += size;
+               break;
+       }
+       if (offset < 0)
+               return -EINVAL;
+
+       file->f_pos = offset;
+       return file->f_pos;
+}
+
+static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count,
+                            loff_t *pos)
+{
+       struct ps3_storage_device *dev = ps3flash_dev;
+       struct ps3flash_private *priv = ps3flash_priv(dev);
+       u64 size, start_sector, end_sector, offset;
+       ssize_t sectors_read;
+       size_t remaining, n;
+
+       dev_dbg(&dev->sbd.core,
+               "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n",
+               __func__, __LINE__, count, *pos, buf);
+
+       size = dev->regions[dev->region_idx].size*dev->blk_size;
+       if (*pos >= size || !count)
+               return 0;
+
+       if (*pos+count > size) {
+               dev_dbg(&dev->sbd.core,
+                       "%s:%u Truncating count from %zu to %llu\n", __func__,
+                       __LINE__, count, size-*pos);
+               count = size-*pos;
+       }
+
+       start_sector = do_div_llr(*pos, dev->blk_size, &offset);
+       end_sector = DIV_ROUND_UP(*pos+count, dev->blk_size);
+
+       remaining = count;
+       do {
+               mutex_lock(&priv->mutex);
+
+               sectors_read = ps3flash_read_sectors(dev, start_sector,
+                                                    end_sector-start_sector,
+                                                    0);
+               if (sectors_read < 0) {
+                       mutex_unlock(&priv->mutex);
+                       return sectors_read;
+               }
+
+               n = min(remaining, sectors_read*dev->blk_size-offset);
+               dev_dbg(&dev->sbd.core,
+                       "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n",
+                       __func__, __LINE__, n, dev->bounce_buf+offset, buf);
+               if (copy_to_user(buf, dev->bounce_buf+offset, n)) {
+                       mutex_unlock(&priv->mutex);
+                       return -EFAULT;
+               }
+
+               mutex_unlock(&priv->mutex);
+
+               *pos += n;
+               buf += n;
+               remaining -= n;
+               start_sector += sectors_read;
+               offset = 0;
+       } while (remaining > 0);
+
+       return count;
+}
+
+static ssize_t ps3flash_write(struct file *file, const char __user *buf,
+                             size_t count, loff_t *pos)
+{
+       struct ps3_storage_device *dev = ps3flash_dev;
+       struct ps3flash_private *priv = ps3flash_priv(dev);
+       u64 size, chunk_sectors, start_write_sector, end_write_sector,
+           end_read_sector, start_read_sector, head, tail, offset;
+       ssize_t res;
+       size_t remaining, n;
+
+       dev_dbg(&dev->sbd.core,
+               "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n",
+               __func__, __LINE__, count, *pos, buf);
+
+       size = dev->regions[dev->region_idx].size*dev->blk_size;
+       if (*pos >= size || !count)
+               return 0;
+
+       if (*pos+count > size) {
+               dev_dbg(&dev->sbd.core,
+                       "%s:%u Truncating count from %zu to %llu\n", __func__,
+                       __LINE__, count, size-*pos);
+               count = size-*pos;
+       }
+
+       chunk_sectors = dev->bounce_size / dev->blk_size;
+
+       start_write_sector = do_div_llr(*pos, dev->bounce_size, &offset) *
+                            chunk_sectors;
+       end_write_sector = DIV_ROUND_UP(*pos+count, dev->bounce_size) *
+                          chunk_sectors;
+
+       end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size);
+       start_read_sector = (*pos+count) / dev->blk_size;
+
+       /*
+        * As we have to write in 256 KiB chunks, while we can read in blk_size
+        * (usually 512 bytes) chunks, we perform the following steps:
+        *   1. Read from start_write_sector to end_read_sector ("head")
+        *   2. Read from start_read_sector to end_write_sector ("tail")
+        *   3. Copy data to buffer
+        *   4. Write from start_write_sector to end_write_sector
+        * All of this is complicated by using only one 256 KiB bounce buffer.
+        */
+
+       head = end_read_sector-start_write_sector;
+       tail = end_write_sector-start_read_sector;
+
+       remaining = count;
+       do {
+               mutex_lock(&priv->mutex);
+
+               if (end_read_sector >= start_read_sector) {
+                       /* Merge head and tail */
+                       dev_dbg(&dev->sbd.core,
+                               "Merged head and tail: %lu sectors at %lu\n",
+                               chunk_sectors, start_write_sector);
+                       res = ps3flash_read_sectors(dev, start_write_sector,
+                                                   chunk_sectors, 0);
+                       if (res < 0)
+                               goto fail;
+
+
+               } else {
+                       if (head) {
+                               /* Read head */
+                       dev_dbg(&dev->sbd.core, "head: %lu sectors at %lu\n",
+                               head, start_write_sector);
+                               res = ps3flash_read_sectors(dev,
+                                                           start_write_sector,
+                                                           head, 0);
+                               if (res < 0)
+                                       goto fail;
+                       }
+                       if (start_read_sector <
+                           start_write_sector+chunk_sectors) {
+                               /* Read tail */
+                               dev_dbg(&dev->sbd.core,
+                                       "tail: %lu sectors at %lu\n", tail,
+                                       start_read_sector-start_write_sector);
+                               res = ps3flash_read_sectors(dev,
+                                                           start_read_sector,
+                                                           tail,
+                                                           start_read_sector-start_write_sector);
+                               if (res < 0)
+                                       goto fail;
+                       }
+               }
+
+               if (start_read_sector < start_write_sector+chunk_sectors) {
+
+
+
+               }
+
+               n = min(remaining, dev->bounce_size-offset);
+               dev_dbg(&dev->sbd.core,
+                       "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n",
+                       __func__, __LINE__, n, buf, dev->bounce_buf+offset);
+               if (copy_from_user(dev->bounce_buf+offset, buf, n)) {
+                       res = -EFAULT;
+                       goto fail;
+               }
+
+               res = ps3flash_write_chunk(dev, start_write_sector);
+               if (res < 0)
+                       goto fail;
+
+               mutex_unlock(&priv->mutex);
+
+               *pos += n;
+               buf += n;
+               remaining -= n;
+               start_write_sector += chunk_sectors;
+               head = 0;
+               offset = 0;
+       } while (remaining > 0);
+
+       return count;
+
+fail:
+       mutex_unlock(&priv->mutex);
+       return res;
+}
+
+
+static const struct file_operations ps3flash_fops = {
+       .owner  = THIS_MODULE,
+       .llseek = ps3flash_llseek,
+       .read   = ps3flash_read,
+       .write  = ps3flash_write,
+};
+
+static struct miscdevice ps3flash_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = DEVICE_NAME,
+       .fops   = &ps3flash_fops,
+};
+
+static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
+{
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3flash_private *priv;
+       int res, error;
+       unsigned long tmp;
+
+       error = ps3stor_probe_access(dev);
+       if (error) {
+               dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n",
+                       __func__, __LINE__);
+               return error;
+       }
+
+       if (!ps3flash_bounce_buffer.address)
+               return -ENOMEM;
+
+       if (ps3flash_dev) {
+               dev_dbg(&dev->sbd.core,
+                       "Only one FLASH device is supported\n");
+               return -EBUSY;
+       }
+
+       ps3flash_dev = dev;
+
+       tmp = dev->regions[dev->region_idx].start*dev->blk_size;
+       if (tmp % FLASH_BLOCK_SIZE) {
+               dev_err(&dev->sbd.core,
+                       "%s:%u region start %lu is not aligned\n", __func__,
+                       __LINE__, tmp);
+               error = -EINVAL;
+               goto fail;
+       }
+       tmp = dev->regions[dev->region_idx].size*dev->blk_size;
+       if (tmp % FLASH_BLOCK_SIZE) {
+               dev_err(&dev->sbd.core,
+                       "%s:%u region size %lu is not aligned\n", __func__,
+                       __LINE__, tmp);
+               error = -EINVAL;
+               goto fail;
+       }
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               error = -ENOMEM;
+               goto fail;
+       }
+
+       ps3flash_priv(dev) = priv;
+       mutex_init(&priv->mutex);
+
+       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_free_priv;
+       }
+
+       error = ps3_sb_event_receive_port_setup(PS3_BINDING_CPU_ANY,
+                                               &dev->sbd.did,
+                                               dev->sbd.interrupt_id,
+                                               &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, ps3stor_interrupt, IRQF_DISABLED,
+                           DEVICE_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;
+       }
+
+       /* use static buffer, kmalloc cannot allocate 256 KiB */
+       dev->bounce_size = ps3flash_bounce_buffer.size;
+       dev->bounce_buf = ps3flash_bounce_buffer.address;
+       dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf));
+
+       dev->sbd.d_region = &dev->dma_region;
+       ps3_dma_region_init(&dev->dma_region, &dev->sbd.did, PS3_DMA_64K,
+                           PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size,
+                           PS3_IOBUS_SB);
+       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_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 = misc_register(&ps3flash_misc);
+       if (error) {
+               dev_err(&dev->sbd.core, "%s:%u: misc_register failed %d\n",
+                       __func__, __LINE__, error);
+               goto fail_unmap_dma;
+       }
+
+       dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",
+                __func__, __LINE__, ps3flash_misc.minor);
+       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.did, dev->sbd.interrupt_id,
+                                         dev->irq);
+fail_close_device:
+       ps3_close_hv_device(&dev->sbd);
+fail_free_priv:
+       kfree(priv);
+fail:
+       ps3flash_dev = NULL;
+       return error;
+}
+
+static int ps3flash_remove(struct ps3_system_bus_device *_dev)
+{
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       int error;
+
+       misc_deregister(&ps3flash_misc);
+
+       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.did,
+                                                 dev->sbd.interrupt_id,
+                                                 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);
+
+       kfree(ps3flash_priv(dev));
+       ps3flash_dev = NULL;
+
+       return 0;
+}
+
+
+static struct ps3_system_bus_driver ps3flash = {
+       .match_id       = PS3_MATCH_ID_STOR_FLASH,
+       .core.name      = DEVICE_NAME,
+       .probe          = ps3flash_probe,
+       .remove         = ps3flash_remove,
+       .shutdown       = ps3flash_remove,
+};
+
+
+static int __init ps3flash_init(void)
+{
+       return ps3_system_bus_driver_register(&ps3flash, PS3_IOBUS_SB);
+}
+
+static void __exit ps3flash_exit(void)
+{
+       return ps3_system_bus_driver_unregister(&ps3flash);
+}
+
+module_init(ps3flash_init);
+module_exit(ps3flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
index dc0bc32..6db1e32 100644 (file)
@@ -2214,6 +2214,12 @@ struct tty_driver *console_driver;
 
 #ifdef CONFIG_VT_CONSOLE
 
+void vt_console_stop(void)
+{
+       printable = 0;
+}
+EXPORT_SYMBOL_GPL(vt_console_stop);
+
 /*
  *     Console on virtual terminal
  *
index 30dce05..311f577 100644 (file)
@@ -2270,6 +2270,13 @@ config GELIC_NET
          To compile this driver as a module, choose M here: the
          module will be called gelic_net.
 
+config GELIC_WIRELESS
+       bool "Gelic net Wireless Extension"
+       depends on GELIC_NET
+       select WIRELESS_EXT
+       help
+         Wireless Extension support for Gelic Gigabit Ethernet driver
+
 config GIANFAR
        tristate "Gianfar Ethernet"
        depends on 85xx || 83xx || PPC_86xx
index d8fcd8b..76b6681 100644 (file)
@@ -61,6 +61,9 @@ obj-$(CONFIG_BNX2) += bnx2.o
 spidernet-y += spider_net.o spider_net_ethtool.o
 obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o
 obj-$(CONFIG_GELIC_NET) += gelic_net.o
+ifeq ($(CONFIG_GELIC_WIRELESS),y)
+  obj-$(CONFIG_GELIC_NET) += gelic_wireless.o
+endif
 obj-$(CONFIG_TC35815) += tc35815.o
 obj-$(CONFIG_SKGE) += skge.o
 obj-$(CONFIG_SKY2) += sky2.o
index a9f2d11..2808d76 100644 (file)
@@ -1,7 +1,8 @@
 /*
  *  PS3 Platfom gelic network driver.
  *
- * Copyright (C) 2006 Sony Computer Entertainment Inc.
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corporation
  *
  *  this file is based on: spider_net.c
  *
  */
 
 #define DEBUG 1
+#undef GELIC_RING_CHAIN
+
+#include <linux/kernel.h>
+#include <linux/module.h>
 
-#include <linux/compiler.h>
-#include <linux/crc32.h>
-#include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
-#include <linux/firmware.h>
 #include <linux/if_vlan.h>
+
 #include <linux/in.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
 #include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/mii.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/device.h>
-#include <linux/pci.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
 #include <linux/tcp.h>
-#include <linux/types.h>
-#include <linux/wait.h>
-#include <linux/workqueue.h>
-#include <asm/bitops.h>
-#include <asm/pci-bridge.h>
+
+#include <linux/dma-mapping.h>
 #include <net/checksum.h>
-#include <asm/io.h>
 #include <asm/firmware.h>
 #include <asm/ps3.h>
 #include <asm/lv1call.h>
 
+#include "gelic_net.h"
+
 #define GELIC_NET_DRV_NAME "Gelic Network Driver"
 #define GELIC_NET_DRV_VERSION "1.0"
 
-#define GELIC_NET_DEBUG
-
-#ifdef GELIC_NET_DEBUG
-#define DPRINTK(fmt,arg...)   printk(KERN_DEBUG fmt ,##arg)
-#define DPRINTKK(fmt,arg...)  printk(KERN_DEBUG fmt ,##arg)
-#else
-#define DPRINTK(fmt,arg...)
-#define DPRINTKK(fmt,arg...)
-#endif
-
-#define GELIC_NET_ETHTOOL               /* use ethtool */
-
-/* ioctl */
-#define GELIC_NET_GET_MODE              (SIOCDEVPRIVATE + 0)
-#define GELIC_NET_SET_MODE              (SIOCDEVPRIVATE + 1)
-
-/* descriptors */
-#define GELIC_NET_RX_DESCRIPTORS        128 /* num of descriptors */
-#define GELIC_NET_TX_DESCRIPTORS        128 /* num of descriptors */
-
-#define GELIC_NET_MAX_MTU               2308
-#define GELIC_NET_MIN_MTU               64
-#define GELIC_NET_RXBUF_ALIGN           128
-#define GELIC_NET_RX_CSUM_DEFAULT       1 /* hw chksum */
-#define GELIC_NET_WATCHDOG_TIMEOUT      5*HZ
-#define GELIC_NET_NAPI_WEIGHT           64
-#define GELIC_NET_BROADCAST_ADDR        0xffffffffffff
-#define GELIC_NET_VLAN_POS              (VLAN_ETH_ALEN * 2)
-#define GELIC_NET_VLAN_MAX              4
-#define GELIC_NET_MC_COUNT_MAX          32 /* multicast address list */
-
-enum gelic_net_int0_status {
-       GELIC_NET_GDTDCEINT  = 24,
-       GELIC_NET_GRFANMINT  = 28,
-};
-
-/* GHIINT1STS bits */
-enum gelic_net_int1_status {
-       GELIC_NET_GDADCEINT = 14,
-};
-
-/* interrupt mask */
-#define GELIC_NET_TXINT                   (1L << (GELIC_NET_GDTDCEINT + 32))
-
-#define GELIC_NET_RXINT0                  (1L << (GELIC_NET_GRFANMINT + 32))
-#define GELIC_NET_RXINT1                  (1L << GELIC_NET_GDADCEINT)
-#define GELIC_NET_RXINT                   (GELIC_NET_RXINT0 | GELIC_NET_RXINT1)
-
- /* descriptor data_status bits */
-#define GELIC_NET_RXIPCHK                 29
-#define GELIC_NET_TCPUDPIPCHK             28
-#define GELIC_NET_DATA_STATUS_CHK_MASK    (1 << GELIC_NET_RXIPCHK | \
-                                           1 << GELIC_NET_TCPUDPIPCHK)
-
-/* descriptor data_error bits */
-#define GELIC_NET_RXIPCHKERR              27
-#define GELIC_NET_RXTCPCHKERR             26
-#define GELIC_NET_DATA_ERROR_CHK_MASK     (1 << GELIC_NET_RXIPCHKERR | \
-                                           1 << GELIC_NET_RXTCPCHKERR)
-
-#define GELIC_NET_DMAC_CMDSTAT_NOCS       0xa0080000 /* middle of frame */
-#define GELIC_NET_DMAC_CMDSTAT_TCPCS      0xa00a0000
-#define GELIC_NET_DMAC_CMDSTAT_UDPCS      0xa00b0000
-#define GELIC_NET_DMAC_CMDSTAT_END_FRAME  0x00040000 /* end of frame */
-
-#define GELIC_NET_DMAC_CMDSTAT_CHAIN_END  0x00000002 /* RXDCEIS:DMA stopped */
-
-#define GELIC_NET_DESCR_IND_PROC_SHIFT    28
-#define GELIC_NET_DESCR_IND_PROC_MASKO    0x0fffffff
-
-/* ignore ipsec ans multicast */
-#define GELIC_NET_DATA_ERROR_MASK         0xfdefbfff
-/* ignore unmatched sp on sp, drop_packet, multicast address frame*/
-#define GELIC_NET_DATA_ERROR_FLG          0x7def8000
-
-enum gelic_net_descr_status {
-       GELIC_NET_DESCR_COMPLETE            = 0x00, /* used in rx and tx */
-       GELIC_NET_DESCR_RESPONSE_ERROR      = 0x01, /* used in rx and tx */
-       GELIC_NET_DESCR_PROTECTION_ERROR    = 0x02, /* used in rx and tx */
-       GELIC_NET_DESCR_FRAME_END           = 0x04, /* used in rx */
-       GELIC_NET_DESCR_FORCE_END           = 0x05, /* used in rx and tx */
-       GELIC_NET_DESCR_CARDOWNED           = 0x0a, /* used in rx and tx */
-       GELIC_NET_DESCR_NOT_IN_USE                  /* any other value */
-};
-#define GELIC_NET_DMAC_CMDSTAT_NOT_IN_USE 0xb0000000
-
-#define GELIC_NET_DESCR_SIZE              32
-struct gelic_net_descr {
-       /* as defined by the hardware */
-       uint32_t buf_addr;
-       uint32_t buf_size;
-       uint32_t next_descr_addr;
-       uint32_t dmac_cmd_status;
-       uint32_t result_size;
-       uint32_t valid_size;    /* all zeroes for tx */
-       uint32_t data_status;
-       uint32_t data_error;    /* all zeroes for tx */
-
-       /* used in the driver */
-       struct sk_buff *skb;
-       dma_addr_t bus_addr;
-       struct gelic_net_descr *next;
-       struct gelic_net_descr *prev;
-       struct vlan_ethhdr vlan;
-} __attribute__((aligned(32)));
-
-struct gelic_net_descr_chain {
-       /* we walk from tail to head */
-       struct gelic_net_descr *head;
-       struct gelic_net_descr *tail;
-       spinlock_t lock;
-};
-
-struct gelic_net_card {
-       struct net_device *netdev;
-       uint64_t ghiintmask;
-       struct ps3_system_bus_device *dev;
-       uint32_t vlan_id[GELIC_NET_VLAN_MAX];
-       int vlan_index;
-
-       struct gelic_net_descr_chain tx_chain;
-       struct gelic_net_descr_chain rx_chain;
-       spinlock_t chain_lock;
-
-       struct net_device_stats netdev_stats;
-       int rx_csum;
-       spinlock_t intmask_lock;
-
-       struct work_struct tx_timeout_task;
-       atomic_t tx_timeout_task_counter;
-       wait_queue_head_t waitq;
-
-       struct gelic_net_descr *tx_top, *rx_top;
-
-       struct gelic_net_descr descr[0];
-};
-
-static int ps3_gelic_param = 1; /* vlan desc support */
-#ifdef CONFIG_GELIC_NET_MODULE
-module_param(ps3_gelic_param, int, S_IRUGO);
-#endif
-
-struct gelic_net_card *gcard;
-static uint64_t gelic_irq_status;
-
-static int dmac_status = 0;
-
-/* for lv1_net_control */
-#define GELIC_NET_GET_MAC_ADDRESS               0x0000000000000001
-#define GELIC_NET_GET_ETH_PORT_STATUS           0x0000000000000002
-#define GELIC_NET_SET_NEGOTIATION_MODE          0x0000000000000003
-#define GELIC_NET_GET_VLAN_ID                   0x0000000000000004
-
-#define GELIC_NET_LINK_UP                       0x0000000000000001
-#define GELIC_NET_FULL_DUPLEX                   0x0000000000000002
-#define GELIC_NET_AUTO_NEG                      0x0000000000000004
-#define GELIC_NET_SPEED_10                      0x0000000000000010
-#define GELIC_NET_SPEED_100                     0x0000000000000020
-#define GELIC_NET_SPEED_1000                    0x0000000000000040
-
-#define GELIC_NET_VLAN_ALL                      0x0000000000000001
-#define GELIC_NET_VLAN_WIRED                    0x0000000000000002
-#define GELIC_NET_VLAN_WIRELESS                 0x0000000000000003
-#define GELIC_NET_VLAN_PSP                      0x0000000000000004
-#define GELIC_NET_VLAN_PORT0                    0x0000000000000010
-#define GELIC_NET_VLAN_PORT1                    0x0000000000000011
-#define GELIC_NET_VLAN_PORT2                    0x0000000000000012
-#define GELIC_NET_VLAN_DAEMON_CLIENT_BSS        0x0000000000000013
-#define GELIC_NET_VLAN_LIBERO_CLIENT_BSS        0x0000000000000014
-#define GELIC_NET_VLAN_NO_ENTRY                 -6
-
-#define GELIC_NET_PORT                          2 /* for port status */
-
-
 MODULE_AUTHOR("SCE Inc.");
 MODULE_DESCRIPTION("Gelic Network driver");
 MODULE_LICENSE("GPL");
 
-static int rx_descriptors = GELIC_NET_RX_DESCRIPTORS;
-static int tx_descriptors = GELIC_NET_TX_DESCRIPTORS;
-
+static inline struct device * ctodev(struct gelic_net_card * card)
+{
+       return &card->dev->core;
+}
+static inline unsigned int bus_id(struct gelic_net_card *card)
+{
+       return card->dev->did.bus_id;
+}
+static inline unsigned int dev_id(struct gelic_net_card *card)
+{
+       return card->dev->did.dev_id;
+}
 
 /* set irq_mask */
-static int
-gelic_net_set_irq_mask(struct gelic_net_card *card, uint64_t mask)
+static int gelic_net_set_irq_mask(struct gelic_net_card *card, u64 mask)
 {
-       uint64_t status = 0;
+       int status;
 
-       status = lv1_net_set_interrupt_mask(card->dev->did.bus_id,
-               card->dev->did.dev_id, mask, 0);
-       if (status) {
-               printk("lv1_net_set_interrupt_mask failed, status=%ld\n",
-                       status);
-       }
+       status = lv1_net_set_interrupt_mask(bus_id(card), dev_id(card),
+                                           mask, 0);
+       if (status)
+               dev_info(ctodev(card),
+                        "lv1_net_set_interrupt_mask failed %d\n", status);
        return status;
 }
-
+static inline void gelic_net_rx_irq_on(struct gelic_net_card *card)
+{
+       gelic_net_set_irq_mask(card, card->ghiintmask | GELIC_NET_RXINT);
+}
+static inline void gelic_net_rx_irq_off(struct gelic_net_card *card)
+{
+       gelic_net_set_irq_mask(card, card->ghiintmask & ~GELIC_NET_RXINT);
+}
 /**
  * gelic_net_get_descr_status -- returns the status of a descriptor
  * @descr: descriptor to look at
  *
  * returns the status as in the dmac_cmd_status field of the descriptor
  */
-enum gelic_net_descr_status
+static enum gelic_net_descr_status
 gelic_net_get_descr_status(struct gelic_net_descr *descr)
 {
-       uint32_t cmd_status;
+       u32 cmd_status;
 
-       rmb();
        cmd_status = descr->dmac_cmd_status;
-       rmb();
        cmd_status >>= GELIC_NET_DESCR_IND_PROC_SHIFT;
        return cmd_status;
 }
@@ -286,19 +114,17 @@ gelic_net_get_descr_status(struct gelic_net_descr *descr)
  * changes the status to the specified value. Doesn't change other bits
  * in the status
  */
-static void
-gelic_net_set_descr_status(struct gelic_net_descr *descr,
-                           enum gelic_net_descr_status status)
+static void gelic_net_set_descr_status(struct gelic_net_descr *descr,
+                                      enum gelic_net_descr_status status)
 {
-       uint32_t cmd_status;
+       u32 cmd_status;
 
        /* read the status */
-       mb();
        cmd_status = descr->dmac_cmd_status;
        /* clean the upper 4 bits */
        cmd_status &= GELIC_NET_DESCR_IND_PROC_MASKO;
        /* add the status to it */
-       cmd_status |= ((uint32_t)status)<<GELIC_NET_DESCR_IND_PROC_SHIFT;
+       cmd_status |= ((u32)status) << GELIC_NET_DESCR_IND_PROC_SHIFT;
        /* and write it back */
        descr->dmac_cmd_status = cmd_status;
        wmb();
@@ -309,15 +135,14 @@ gelic_net_set_descr_status(struct gelic_net_descr *descr,
  * @card: card structure
  * @descr_in: address of desc
  */
-static void
-gelic_net_free_chain(struct gelic_net_card *card,
-                     struct gelic_net_descr *descr_in)
+static void gelic_net_free_chain(struct gelic_net_card *card,
+                                struct gelic_net_descr *descr_in)
 {
        struct gelic_net_descr *descr;
 
-       for (descr = descr_in; descr && !descr->bus_addr; descr = descr->next) {
-               dma_unmap_single(&card->dev->core, descr->bus_addr,
-                                GELIC_NET_DESCR_SIZE, PCI_DMA_BIDIRECTIONAL);
+       for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) {
+               dma_unmap_single(ctodev(card), descr->bus_addr,
+                                GELIC_NET_DESCR_SIZE, DMA_BIDIRECTIONAL);
                descr->bus_addr = 0;
        }
 }
@@ -334,57 +159,58 @@ gelic_net_free_chain(struct gelic_net_card *card,
  *
  * returns 0 on success, <0 on failure
  */
-static int
-gelic_net_init_chain(struct gelic_net_card *card,
-                      struct gelic_net_descr_chain *chain,
-                      struct gelic_net_descr *start_descr, int no)
+static int gelic_net_init_chain(struct gelic_net_card *card,
+                               struct gelic_net_descr_chain *chain,
+                               struct gelic_net_descr *start_descr, int no)
 {
        int i;
        struct gelic_net_descr *descr;
 
-       spin_lock_init(&chain->lock);
        descr = start_descr;
        memset(descr, 0, sizeof(*descr) * no);
 
        /* set up the hardware pointers in each descriptor */
-       for (i=0; i<no; i++, descr++) {
+       for (i = 0; i < no; i++, descr++) {
                gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);
                descr->bus_addr =
-                       dma_map_single(&card->dev->core, descr,
+                       dma_map_single(ctodev(card), descr,
                                       GELIC_NET_DESCR_SIZE,
-                                      PCI_DMA_BIDIRECTIONAL);
+                                      DMA_BIDIRECTIONAL);
 
-               if (descr->bus_addr == DMA_ERROR_CODE)
+               if (!descr->bus_addr)
                        goto iommu_error;
 
                descr->next = descr + 1;
                descr->prev = descr - 1;
        }
-       /* do actual chain list */
-       (descr-1)->next = start_descr;
-       start_descr->prev = (descr-1);
+       /* make them as ring */
+       (descr - 1)->next = start_descr;
+       start_descr->prev = (descr - 1);
 
+       /* chain bus addr of hw descriptor */
        descr = start_descr;
-       for (i=0; i < no; i++, descr++) {
-               if (descr->next) {
-                       descr->next_descr_addr = descr->next->bus_addr;
-               } else {
-                       descr->next_descr_addr = 0;
-               }
+       for (i = 0; i < no; i++, descr++) {
+               descr->next_descr_addr = descr->next->bus_addr;
        }
 
        chain->head = start_descr;
        chain->tail = start_descr;
-       (descr-1)->next_descr_addr = 0; /* last descr */
+
+
+#ifdef GELIC_RING_CHAIN
+       (descr - 1)->next_descr_addr = start_descr->bus_addr;
+#else
+       /* do not chain last hw descriptor */
+       (descr - 1)->next_descr_addr = 0;
+#endif
        return 0;
 
 iommu_error:
-       descr = start_descr;
-       for (i=0; i < no; i++, descr++)
+       for (i--, descr--; 0 <= i; i--, descr--)
                if (descr->bus_addr)
-                       dma_unmap_single(&card->dev->core, descr->bus_addr,
+                       dma_unmap_single(ctodev(card), descr->bus_addr,
                                         GELIC_NET_DESCR_SIZE,
-                                        PCI_DMA_BIDIRECTIONAL);
+                                        DMA_BIDIRECTIONAL);
        return -ENOMEM;
 }
 
@@ -398,17 +224,14 @@ iommu_error:
  * allocates a new rx skb, iommu-maps it and attaches it to the descriptor.
  * Activate the descriptor state-wise
  */
-static int
-gelic_net_prepare_rx_descr(struct gelic_net_card *card,
-                           struct gelic_net_descr *descr)
+static int gelic_net_prepare_rx_descr(struct gelic_net_card *card,
+                                     struct gelic_net_descr *descr)
 {
-       dma_addr_t buf;
-       int error = 0;
        int offset;
-       int bufsize;
+       unsigned int bufsize;
 
-       if( gelic_net_get_descr_status(descr) !=  GELIC_NET_DESCR_NOT_IN_USE) {
-               printk("%s: ERROR status \n", __FUNCTION__);
+       if (gelic_net_get_descr_status(descr) !=  GELIC_NET_DESCR_NOT_IN_USE) {
+               dev_info(ctodev(card), "%s: ERROR status \n", __func__);
        }
        /* we need to round up the buffer size to a multiple of 128 */
        bufsize = (GELIC_NET_MAX_MTU + GELIC_NET_RXBUF_ALIGN - 1) &
@@ -416,11 +239,12 @@ gelic_net_prepare_rx_descr(struct gelic_net_card *card,
 
        /* and we need to have it 128 byte aligned, therefore we allocate a
         * bit more */
-       /* allocate an skb */
-       descr->skb = dev_alloc_skb(bufsize + GELIC_NET_RXBUF_ALIGN - 1);
+       descr->skb = netdev_alloc_skb(card->netdev,
+               bufsize + GELIC_NET_RXBUF_ALIGN - 1);
        if (!descr->skb) {
-               if (net_ratelimit())
-                       printk("Not enough memory to allocate rx buffer\n");
+               descr->buf_addr = 0; /* tell DMAC don't touch memory */
+               dev_info(ctodev(card),
+                        "%s:allocate skb failed !!\n", __func__);
                return -ENOMEM;
        }
        descr->buf_size = bufsize;
@@ -434,88 +258,71 @@ gelic_net_prepare_rx_descr(struct gelic_net_card *card,
        if (offset)
                skb_reserve(descr->skb, GELIC_NET_RXBUF_ALIGN - offset);
        /* io-mmu-map the skb */
-       buf = dma_map_single(&card->dev->core, descr->skb->data,
-                                       GELIC_NET_MAX_MTU,
-                                       PCI_DMA_BIDIRECTIONAL);
-       descr->buf_addr = buf;
-       if (buf == DMA_ERROR_CODE) {
+       descr->buf_addr = dma_map_single(ctodev(card), descr->skb->data,
+                                        GELIC_NET_MAX_MTU,
+                                        DMA_BIDIRECTIONAL);
+       wmb();
+       if (!descr->buf_addr) {
                dev_kfree_skb_any(descr->skb);
-               printk("Could not iommu-map rx buffer\n");
+               descr->skb = NULL;
+               dev_info(ctodev(card),
+                        "%s:Could not iommu-map rx buffer\n", __func__);
                gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);
+               return -ENOMEM;
        } else {
                gelic_net_set_descr_status(descr, GELIC_NET_DESCR_CARDOWNED);
+               return 0;
        }
-
-       return error;
 }
 
 /**
- * gelic_net_release_rx_chain - free all rx descr
+ * gelic_net_release_rx_chain - free all skb of rx descr
  * @card: card structure
  *
  */
-static void
-gelic_net_release_rx_chain(struct gelic_net_card *card)
+static void gelic_net_release_rx_chain(struct gelic_net_card *card)
 {
-       struct gelic_net_descr_chain *chain = &card->rx_chain;
-
-       while(chain->tail != chain->head) {
-               if (chain->tail->skb) {
-                       dma_unmap_single(&card->dev->core,
-                                               chain->tail->buf_addr,
-                                               chain->tail->skb->len,
-                                               PCI_DMA_BIDIRECTIONAL);
-                       chain->tail->buf_addr = 0;
-                       dev_kfree_skb_any(chain->tail->skb);
-                       chain->tail->skb = NULL;
-                       chain->tail->dmac_cmd_status =
-                                               GELIC_NET_DESCR_NOT_IN_USE;
-                       chain->tail = chain->tail->next;
+       struct gelic_net_descr * descr = card->rx_chain.head;
+
+       do {
+               if (descr->skb) {
+                       dma_unmap_single(ctodev(card),
+                                        descr->buf_addr,
+                                        descr->skb->len,
+                                        DMA_BIDIRECTIONAL);
+                       descr->buf_addr = 0;
+                       dev_kfree_skb_any(descr->skb);
+                       descr->skb = NULL;
+                       descr->dmac_cmd_status = GELIC_NET_DESCR_NOT_IN_USE;
                }
-       }
-}
-
-/**
- * gelic_net_enable_rxdmac - enables a receive DMA controller
- * @card: card structure
- *
- * gelic_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
- * in the GDADMACCNTR register
- */
-static void
-gelic_net_enable_rxdmac(struct gelic_net_card *card)
-{
-       uint64_t status;
-
-       status = lv1_net_start_rx_dma(card->dev->did.bus_id,
-                               card->dev->did.dev_id,
-                               (uint64_t)card->rx_chain.tail->bus_addr, 0);
-       if (status) {
-               printk("lv1_net_start_rx_dma failed, status=%ld\n", status);
-       }
+               descr = descr->next;
+       } while (descr != card->rx_chain.head);
 }
 
 /**
- * gelic_net_refill_rx_chain - refills descriptors/skbs in the rx chains
+ * gelic_net_fill_rx_chain - fills descriptors/skbs in the rx chains
  * @card: card structure
  *
- * refills descriptors in all chains (last used chain first): allocates skbs
+ * fills all descriptors in the rx chain: allocates skbs
  * and iommu-maps them.
+ * returns 0 on success, <0 on failure
  */
-static void
-gelic_net_refill_rx_chain(struct gelic_net_card *card)
+static int gelic_net_fill_rx_chain(struct gelic_net_card *card)
 {
-       struct gelic_net_descr_chain *chain;
-       int count = 0;
+       struct gelic_net_descr *descr = card->rx_chain.head;
+       int ret;
 
-       chain = &card->rx_chain;
-       while (chain->head && gelic_net_get_descr_status(chain->head) ==
-               GELIC_NET_DESCR_NOT_IN_USE) {
-               if (gelic_net_prepare_rx_descr(card, chain->head))
-                       break;
-               count++;
-               chain->head = chain->head->next;
-       }
+       do {
+               if (!descr->skb)
+                       if ((ret = gelic_net_prepare_rx_descr(card, descr)))
+                               goto rewind;
+               descr = descr->next;
+       } while (descr != card->rx_chain.head);
+
+       return 0;
+rewind:
+       gelic_net_release_rx_chain(card);
+       return ret;
 }
 
 /**
@@ -524,15 +331,14 @@ gelic_net_refill_rx_chain(struct gelic_net_card *card)
  *
  * returns 0 on success, <0 on failure
  */
-static int
-gelic_net_alloc_rx_skbs(struct gelic_net_card *card)
+static int gelic_net_alloc_rx_skbs(struct gelic_net_card *card)
 {
        struct gelic_net_descr_chain *chain;
-
+       int ret;
        chain = &card->rx_chain;
-       gelic_net_refill_rx_chain(card);
+       ret = gelic_net_fill_rx_chain(card);
        chain->head = card->rx_top->prev; /* point to the last */
-       return 0;
+       return ret;
 }
 
 /**
@@ -542,30 +348,23 @@ gelic_net_alloc_rx_skbs(struct gelic_net_card *card)
  *
  * releases a used tx descriptor (unmapping, freeing of skb)
  */
-static void
-gelic_net_release_tx_descr(struct gelic_net_card *card,
+static void gelic_net_release_tx_descr(struct gelic_net_card *card,
                            struct gelic_net_descr *descr)
 {
        struct sk_buff *skb;
 
-  if (!ps3_gelic_param) {
-       /* unmap the skb */
-       skb = descr->skb;
-       dma_unmap_single(&card->dev->core, descr->buf_addr, skb->len,
-                        PCI_DMA_BIDIRECTIONAL);
 
-       dev_kfree_skb_any(skb);
-  } else {
-       if ((descr->data_status & 0x00000001) == 1) { /* end of frame */
+       if (descr->data_status & (1 << GELIC_NET_TXDESC_TAIL)) {
+               /* 2nd descriptor */
                skb = descr->skb;
-               dma_unmap_single(&card->dev->core, descr->buf_addr, skb->len,
-                        PCI_DMA_BIDIRECTIONAL);
+               dma_unmap_single(ctodev(card), descr->buf_addr, skb->len,
+                                DMA_BIDIRECTIONAL);
                dev_kfree_skb_any(skb);
        } else {
-               dma_unmap_single(&card->dev->core, descr->buf_addr,
-                       descr->buf_size, PCI_DMA_BIDIRECTIONAL);
+               dma_unmap_single(ctodev(card), descr->buf_addr,
+                                descr->buf_size, DMA_BIDIRECTIONAL);
        }
-  }
+
        descr->buf_addr = 0;
        descr->buf_size = 0;
        descr->next_descr_addr = 0;
@@ -574,7 +373,6 @@ gelic_net_release_tx_descr(struct gelic_net_card *card,
        descr->data_status = 0;
        descr->data_error = 0;
        descr->skb = NULL;
-       card->tx_chain.tail = card->tx_chain.tail->next;
 
        /* set descr status */
        descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOT_IN_USE;
@@ -587,22 +385,25 @@ gelic_net_release_tx_descr(struct gelic_net_card *card,
  *
  * releases the tx descriptors that gelic has finished with
  */
-static void
-gelic_net_release_tx_chain(struct gelic_net_card *card, int stop)
+static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop)
 {
-       struct gelic_net_descr_chain *tx_chain = &card->tx_chain;
+       struct gelic_net_descr_chain *tx_chain;
        enum gelic_net_descr_status status;
        int release = 0;
 
-       for (;tx_chain->head != tx_chain->tail && tx_chain->tail;) {
+       for (tx_chain = &card->tx_chain;
+            tx_chain->head != tx_chain->tail && tx_chain->tail;
+            tx_chain->tail = tx_chain->tail->next) {
                status = gelic_net_get_descr_status(tx_chain->tail);
                switch (status) {
                case GELIC_NET_DESCR_RESPONSE_ERROR:
                case GELIC_NET_DESCR_PROTECTION_ERROR:
                case GELIC_NET_DESCR_FORCE_END:
-                       printk("%s: forcing end of tx descriptor "
-                              "with status x%02x\n",
-                              card->netdev->name, status);
+                       if (printk_ratelimit())
+                               dev_info(ctodev(card),
+                                        "%s: forcing end of tx descriptor " \
+                                        "with status %x\n",
+                                        __func__, status);
                        card->netdev_stats.tx_dropped++;
                        break;
 
@@ -613,23 +414,17 @@ gelic_net_release_tx_chain(struct gelic_net_card *card, int stop)
                        break;
 
                case GELIC_NET_DESCR_CARDOWNED:
-               default: /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */
+                       /* pending tx request */
+               default:
+                       /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */
                        goto out;
                }
                gelic_net_release_tx_descr(card, tx_chain->tail);
                release = 1;
        }
 out:
-       /* status NOT_IN_USE or chain end */
-       if (!tx_chain->tail) {
-               /* release all chains */
-               if(card->tx_chain.head) printk("ERROR tx_chain.head is NULL\n");
-               card->tx_chain.tail = card->tx_top;
-               card->tx_chain.head = card->tx_top;
-       }
-       if (!stop && release && netif_queue_stopped(card->netdev)) {
+       if (!stop && release && netif_queue_stopped(card->netdev))
                netif_wake_queue(card->netdev);
-       }
 }
 
 /**
@@ -640,77 +435,91 @@ out:
  * netdev interface. It also sets up multicast, allmulti and promisc
  * flags appropriately
  */
-static void
-gelic_net_set_multi(struct net_device *netdev)
+static void gelic_net_set_multi(struct net_device *netdev)
 {
-       int i;
-       uint8_t *p;
-       uint64_t addr, status;
-       struct dev_mc_list *mc;
        struct gelic_net_card *card = netdev_priv(netdev);
+       struct dev_mc_list *mc;
+       unsigned int i;
+       uint8_t *p;
+       u64 addr;
+       int status;
 
        /* clear all multicast address */
-       status = lv1_net_remove_multicast_address(card->dev->did.bus_id,
-                               card->dev->did.dev_id, 0, 1);
-       if (status) {
-               printk("lv1_net_remove_multicast_address failed, status=%ld\n",\
+       status = lv1_net_remove_multicast_address(bus_id(card), dev_id(card),
+                                                 0, 1);
+       if (status)
+               dev_err(ctodev(card),
+                       "lv1_net_remove_multicast_address failed %d\n",
                        status);
-       }
        /* set broadcast address */
-       status = lv1_net_add_multicast_address(card->dev->did.bus_id,
-                       card->dev->did.dev_id, GELIC_NET_BROADCAST_ADDR, 0);
-       if (status) {
-               printk("lv1_net_add_multicast_address failed, status=%ld\n",\
+       status = lv1_net_add_multicast_address(bus_id(card), dev_id(card),
+                                              GELIC_NET_BROADCAST_ADDR, 0);
+       if (status)
+               dev_err(ctodev(card),
+                       "lv1_net_add_multicast_address failed, %d\n",
                        status);
-       }
 
        if (netdev->flags & IFF_ALLMULTI
                || netdev->mc_count > GELIC_NET_MC_COUNT_MAX) { /* list max */
-               status = lv1_net_add_multicast_address(card->dev->did.bus_id,
-                               card->dev->did.dev_id,
-                               0, 1);
-               if (status) {
-                       printk("lv1_net_add_multicast_address failed, status=%ld\n",\
+               status = lv1_net_add_multicast_address(bus_id(card), dev_id(card),
+                                                      0, 1);
+               if (status)
+                       dev_err(ctodev(card),
+                               "lv1_net_add_multicast_address failed, %d\n",
                                status);
-               }
-               return ;
+               return;
        }
 
-       /* set multicalst address */
-       for ( mc = netdev->mc_list; mc; mc = mc->next) {
+       /* set multicast address */
+       for (mc = netdev->mc_list; mc; mc = mc->next) {
                addr = 0;
                p = mc->dmi_addr;
                for (i = 0; i < ETH_ALEN; i++) {
                        addr <<= 8;
                        addr |= *p++;
                }
-               status = lv1_net_add_multicast_address(card->dev->did.bus_id,
-                               card->dev->did.dev_id,
+               status = lv1_net_add_multicast_address(bus_id(card), dev_id(card),
                                addr, 0);
-               if (status) {
-                       printk("lv1_net_add_multicast_address failed, status=%ld\n",\
+               if (status)
+                       dev_err(ctodev(card),
+                               "lv1_net_add_multicast_address failed, %d\n",
                                status);
-               }
        }
 }
 
 /**
+ * gelic_net_enable_rxdmac - enables the receive DMA controller
+ * @card: card structure
+ *
+ * gelic_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
+ * in the GDADMACCNTR register
+ */
+static void gelic_net_enable_rxdmac(struct gelic_net_card *card)
+{
+       int status;
+
+       status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),
+                               card->rx_chain.tail->bus_addr, 0);
+       if (status)
+               printk("lv1_net_start_rx_dma failed, status=%d\n", status);
+}
+
+/**
  * gelic_net_disable_rxdmac - disables the receive DMA controller
  * @card: card structure
  *
  * gelic_net_disable_rxdmac terminates processing on the DMA controller by
  * turing off DMA and issueing a force end
  */
-static void
-gelic_net_disable_rxdmac(struct gelic_net_card *card)
+static void gelic_net_disable_rxdmac(struct gelic_net_card *card)
 {
-       uint64_t status;
+       int status;
 
-       status = lv1_net_stop_rx_dma(card->dev->did.bus_id,
-               card->dev->did.dev_id, 0);
-       if (status) {
-               printk("lv1_net_stop_rx_dma faild, status=%ld\n", status);
-       }
+       /* this hvc blocks until the DMA in progress really stopped */
+       status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card), 0);
+       if (status)
+               dev_err(ctodev(card),
+                       "lv1_net_stop_rx_dma faild, %d\n", status);
 }
 
 /**
@@ -720,16 +529,15 @@ gelic_net_disable_rxdmac(struct gelic_net_card *card)
  * gelic_net_disable_txdmac terminates processing on the DMA controller by
  * turing off DMA and issueing a force end
  */
-static void
-gelic_net_disable_txdmac(struct gelic_net_card *card)
+static void gelic_net_disable_txdmac(struct gelic_net_card *card)
 {
-       uint64_t status;
+       int status;
 
-       status = lv1_net_stop_tx_dma(card->dev->did.bus_id,
-               card->dev->did.dev_id, 0);
-       if (status) {
-               printk("lv1_net_stop_tx_dma faild, status=%ld\n", status);
-       }
+       /* this hvc blocks until the DMA in progress really stopped */
+       status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card), 0);
+       if (status)
+               dev_err(ctodev(card),
+                       "lv1_net_stop_tx_dma faild, status=%d\n", status);
 }
 
 /**
@@ -738,11 +546,13 @@ gelic_net_disable_txdmac(struct gelic_net_card *card)
  *
  * always returns 0
  */
-int
-gelic_net_stop(struct net_device *netdev)
+static int gelic_net_stop(struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
 
+#ifdef CONFIG_GELIC_WIRELESS
+       gelicw_down(netdev);
+#endif
        netif_poll_disable(netdev);
        netif_stop_queue(netdev);
 
@@ -779,28 +589,19 @@ gelic_net_stop(struct net_device *netdev)
 static struct gelic_net_descr *
 gelic_net_get_next_tx_descr(struct gelic_net_card *card)
 {
-       if (card->tx_chain.head == NULL) return NULL;
-       /* check, if head points to not-in-use descr */
-  if (!ps3_gelic_param) {
-       if ( card->tx_chain.tail != card->tx_chain.head->next
-               && gelic_net_get_descr_status(card->tx_chain.head) ==
-                    GELIC_NET_DESCR_NOT_IN_USE ) {
-               return card->tx_chain.head;
-       } else {
+       if (!card->tx_chain.head)
                return NULL;
-       }
-  } else {
-       if ( card->tx_chain.tail != card->tx_chain.head->next
-               && card->tx_chain.tail != card->tx_chain.head->next->next
-               && gelic_net_get_descr_status(card->tx_chain.head) ==
-                    GELIC_NET_DESCR_NOT_IN_USE
-               && gelic_net_get_descr_status(card->tx_chain.head->next) ==
-                    GELIC_NET_DESCR_NOT_IN_USE ) {
+       /*  see if we can two consecutive free descrs */
+       if (card->tx_chain.tail != card->tx_chain.head->next &&
+           gelic_net_get_descr_status(card->tx_chain.head) ==
+           GELIC_NET_DESCR_NOT_IN_USE &&
+           card->tx_chain.tail != card->tx_chain.head->next->next &&
+           gelic_net_get_descr_status(card->tx_chain.head->next) ==
+            GELIC_NET_DESCR_NOT_IN_USE )
                return card->tx_chain.head;
-       } else {
+       else
                return NULL;
-       }
-  }
+
 }
 
 /**
@@ -813,45 +614,37 @@ gelic_net_get_next_tx_descr(struct gelic_net_card *card)
  * depending on hardware checksum settings. This function assumes a wmb()
  * has executed before.
  */
-static void
-gelic_net_set_txdescr_cmdstat(struct gelic_net_descr *descr,
-                              struct sk_buff *skb, int middle)
+static void gelic_net_set_txdescr_cmdstat(struct gelic_net_descr *descr,
+                                         struct sk_buff *skb, int middle)
 {
-       uint32_t nocs, tcpcs, udpcs;
-
-       if (middle) {
-               nocs =  GELIC_NET_DMAC_CMDSTAT_NOCS;
-               tcpcs = GELIC_NET_DMAC_CMDSTAT_TCPCS;
-               udpcs = GELIC_NET_DMAC_CMDSTAT_UDPCS;
-       }else {
-               nocs =  GELIC_NET_DMAC_CMDSTAT_NOCS
-                       | GELIC_NET_DMAC_CMDSTAT_END_FRAME;
-               tcpcs = GELIC_NET_DMAC_CMDSTAT_TCPCS
-                       | GELIC_NET_DMAC_CMDSTAT_END_FRAME;
-               udpcs = GELIC_NET_DMAC_CMDSTAT_UDPCS
-                       | GELIC_NET_DMAC_CMDSTAT_END_FRAME;
-       }
+       u32 eofr;
 
-       if (skb->ip_summed != CHECKSUM_PARTIAL) {
-               descr->dmac_cmd_status = nocs;
-       } else {
+       if (middle)
+               eofr = 0;
+       else
+               eofr = GELIC_NET_DMAC_CMDSTAT_END_FRAME;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOCS | eofr;
+       else {
                /* is packet ip?
                 * if yes: tcp? udp? */
                if (skb->protocol == htons(ETH_P_IP)) {
-                       if (skb->nh.iph->protocol == IPPROTO_TCP) {
-                               descr->dmac_cmd_status = tcpcs;
-                       } else if (skb->nh.iph->protocol == IPPROTO_UDP) {
-                               descr->dmac_cmd_status = udpcs;
-                       } else { /* the stack should checksum non-tcp and non-udp
-                                   packets on his own: NETIF_F_IP_CSUM */
-                               descr->dmac_cmd_status = nocs;
-                       }
+                       if (skb->nh.iph->protocol == IPPROTO_TCP)
+                               descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_TCPCS | eofr;
+                       else if (skb->nh.iph->protocol == IPPROTO_UDP)
+                               descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_UDPCS | eofr;
+                       else    /*
+                                * the stack should checksum non-tcp and non-udp
+                                * packets on his own: NETIF_F_IP_CSUM
+                                */
+                               descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOCS | eofr;
                }
        }
 }
 
 /**
- * gelic_net_prepare_tx_descr - get dma address of skb_data
+ * gelic_net_prepare_tx_descr_v - get dma address of skb_data
  * @card: card structure
  * @descr: descriptor structure
  * @skb: packet to use
@@ -859,42 +652,36 @@ gelic_net_set_txdescr_cmdstat(struct gelic_net_descr *descr,
  * returns 0 on success, <0 on failure.
  *
  */
-static int
-gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
-                           struct gelic_net_descr *descr,
-                           struct sk_buff *skb)
+static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
+                                       struct gelic_net_descr *descr,
+                                       struct sk_buff *skb)
 {
-       dma_addr_t buf;
-       uint8_t *hdr;
-       struct vlan_ethhdr *v_hdr;
-       int vlan_len;
+       dma_addr_t buf[2];
+       unsigned int vlan_len;
 
-       if (skb->len < GELIC_NET_VLAN_POS) {
-               printk("error: skb->len:%d\n", skb->len);
+       if (skb->len < GELIC_NET_VLAN_POS)
                return -EINVAL;
-       }
-       hdr = skb->data;
-       v_hdr = (struct vlan_ethhdr *)skb->data;
-       memcpy(&descr->vlan, v_hdr, GELIC_NET_VLAN_POS);
+
+       memcpy(&descr->vlan, skb->data, GELIC_NET_VLAN_POS);
        if (card->vlan_index != -1) {
                descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/
                descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]);
                vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */
-       } else {
+       } else
                vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */
-       }
 
        /* first descr */
-       buf = dma_map_single(&card->dev->core, &descr->vlan,
-                                        vlan_len, PCI_DMA_BIDIRECTIONAL);
+       buf[0] = dma_map_single(ctodev(card), &descr->vlan,
+                            vlan_len, DMA_BIDIRECTIONAL);
 
-       if (buf == DMA_ERROR_CODE) {
-               printk("could not iommu-map packet (%p, %i). "
-                         "Dropping packet\n", v_hdr, vlan_len);
+       if (!buf[0]) {
+               dev_err(ctodev(card),
+                       "dma map 1 failed (%p, %i). Dropping packet\n",
+                       skb->data, vlan_len);
                return -ENOMEM;
        }
 
-       descr->buf_addr = buf;
+       descr->buf_addr = buf[0];
        descr->buf_size = vlan_len;
        descr->skb = skb; /* not used */
        descr->data_status = 0;
@@ -904,99 +691,64 @@ gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
        card->tx_chain.head = card->tx_chain.head->next;
        descr->next_descr_addr = descr->next->bus_addr;
        descr = descr->next;
-       if (gelic_net_get_descr_status(descr) !=
-                       GELIC_NET_DESCR_NOT_IN_USE) {
-               printk("ERROR descr()\n"); /* XXX will be removed */
-       }
-       buf = dma_map_single(&card->dev->core, hdr + GELIC_NET_VLAN_POS,
-                               skb->len - GELIC_NET_VLAN_POS,
-                               PCI_DMA_BIDIRECTIONAL);
-
-       if (buf == DMA_ERROR_CODE) {
-               printk("could not iommu-map packet (%p, %i). "
-                         "Dropping packet\n", hdr + GELIC_NET_VLAN_POS,
-                         skb->len - GELIC_NET_VLAN_POS);
+       if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE)
+               dev_err(ctodev(card),"descr is not free!\n"); /* XXX will be removed */
+
+       buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS,
+                            skb->len - GELIC_NET_VLAN_POS,
+                            DMA_BIDIRECTIONAL);
+
+       if (!buf[1]) {
+               dev_err(ctodev(card),
+                       "dma map 2 failed (%p, %i). Dropping packet\n",
+                       skb->data + GELIC_NET_VLAN_POS,
+                       skb->len - GELIC_NET_VLAN_POS);
+               dma_unmap_single(ctodev(card), buf[0], vlan_len,
+                                DMA_BIDIRECTIONAL);
                return -ENOMEM;
        }
 
-       descr->buf_addr = buf;
+       descr->buf_addr = buf[1];
        descr->buf_size = skb->len - GELIC_NET_VLAN_POS;
        descr->skb = skb;
        descr->data_status = 0;
-       descr->next_descr_addr= 0;
+       descr->next_descr_addr= 0; /* terminate hw descr */
        gelic_net_set_txdescr_cmdstat(descr,skb, 0);
 
        return 0;
 }
 
-static int
-gelic_net_prepare_tx_descr(struct gelic_net_card *card,
-                           struct gelic_net_descr *descr,
-                           struct sk_buff *skb)
-{
-       dma_addr_t buf = dma_map_single(&card->dev->core, skb->data,
-                                        skb->len, PCI_DMA_BIDIRECTIONAL);
-
-       if (buf == DMA_ERROR_CODE) {
-               printk("could not iommu-map packet (%p, %i). "
-                         "Dropping packet\n", skb->data, skb->len);
-               return -ENOMEM;
-       }
-
-       descr->buf_addr = buf;
-       descr->buf_size = skb->len;
-       descr->skb = skb;
-       descr->data_status = 0;
-
-       return 0;
-}
-
-static void
-gelic_net_set_frame_end(struct gelic_net_card *card,
-               struct gelic_net_descr *descr, struct sk_buff *skb)
-{
-       descr->next_descr_addr= 0;
-       gelic_net_set_txdescr_cmdstat(descr,skb, 0);
-       wmb();
-       if (descr->prev) {
-               descr->prev->next_descr_addr = descr->bus_addr;
-       }
-}
-
 /**
  * gelic_net_kick_txdma - enables TX DMA processing
  * @card: card structure
  * @descr: descriptor address to enable TX processing at
  *
  */
-static void
-gelic_net_kick_txdma(struct gelic_net_card *card,
-                      struct gelic_net_descr *descr)
+static int gelic_net_kick_txdma(struct gelic_net_card *card,
+                               struct gelic_net_descr *descr)
 {
-       uint64_t status = -1;
+       int status = -ENXIO;
        int count = 10;
 
-       if (dmac_status) {
-               return ;
-       }
+       if (card->tx_dma_progress)
+               return 0;
 
        if (gelic_net_get_descr_status(descr) == GELIC_NET_DESCR_CARDOWNED) {
-               /* kick */
-               dmac_status = 1;
-
-               while(count--) {
-                       status = lv1_net_start_tx_dma(card->dev->did.bus_id,
-                                       card->dev->did.dev_id,
-                                       (uint64_t)descr->bus_addr, 0);
-                       if (!status) {
+               card->tx_dma_progress = 1;
+               /* sometimes we need retry here */
+               while (count--) {
+                       status = lv1_net_start_tx_dma(bus_id(card),
+                                                     dev_id(card),
+                                                     descr->bus_addr, 0);
+                       if (!status)
                                break;
-                       }
-               }
-               if (!count) {
-                       printk("lv1_net_start_txdma failed, status=%ld %016lx\n",\
-                               status, gelic_irq_status);
                }
+               if (!count)
+                       dev_info(ctodev(card), "lv1_net_start_txdma failed," \
+                               "status=%d %#lx\n",
+                                status, card->irq_status);
        }
+       return status;
 }
 
 /**
@@ -1006,53 +758,45 @@ gelic_net_kick_txdma(struct gelic_net_card *card,
  *
  * returns 0 on success, <0 on failure
  */
-static int
-gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
+static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
        struct gelic_net_descr *descr = NULL;
        int result;
        unsigned long flags;
 
-       spin_lock_irqsave(&card->intmask_lock, flags);
+       spin_lock_irqsave(&card->tx_dma_lock, flags);
 
        gelic_net_release_tx_chain(card, 0);
-       if (skb == NULL){
+       if (!skb)
                goto kick;
-       }
-       descr = gelic_net_get_next_tx_descr(card); /* get tx_chain.head */
+       descr = gelic_net_get_next_tx_descr(card);
        if (!descr) {
                netif_stop_queue(netdev);
-               spin_unlock_irqrestore(&card->intmask_lock, flags);
-               return 1;
+               spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+               return NETDEV_TX_BUSY;
        }
-  if (!ps3_gelic_param) {
-       result = gelic_net_prepare_tx_descr(card, descr, skb);
-  } else {
        result = gelic_net_prepare_tx_descr_v(card, descr, skb);
-  }
+
        if (result)
                goto error;
 
        card->tx_chain.head = card->tx_chain.head->next;
-  if (!ps3_gelic_param) {
-       gelic_net_set_frame_end(card, descr, skb);
-  } else {
-       if (descr->prev) {
+
+       if (descr->prev)
                descr->prev->next_descr_addr = descr->bus_addr;
-       }
-  }
 kick:
        wmb();
-       gelic_net_kick_txdma(card, card->tx_chain.tail);
+       if (gelic_net_kick_txdma(card, card->tx_chain.tail))
+               goto error;
 
        netdev->trans_start = jiffies;
-       spin_unlock_irqrestore(&card->intmask_lock, flags);
+       spin_unlock_irqrestore(&card->tx_dma_lock, flags);
        return NETDEV_TX_OK;
 
 error:
        card->netdev_stats.tx_dropped++;
-       spin_unlock_irqrestore(&card->intmask_lock, flags);
+       spin_unlock_irqrestore(&card->tx_dma_lock, flags);
        return NETDEV_TX_LOCKED;
 }
 
@@ -1061,71 +805,58 @@ error:
  * @descr: descriptor to process
  * @card: card structure
  *
- * returns 1 on success, 0 if no packet was passed to the stack
- *
  * iommu-unmaps the skb, fills out skb structure and passes the data to the
  * stack. The descriptor state is not changed.
  */
-static int
-gelic_net_pass_skb_up(struct gelic_net_descr *descr,
-                      struct gelic_net_card *card)
+static void gelic_net_pass_skb_up(struct gelic_net_descr *descr,
+                                struct gelic_net_card *card)
 {
        struct sk_buff *skb;
        struct net_device *netdev;
-       uint32_t data_status, data_error;
+       u32 data_status, data_error;
 
        data_status = descr->data_status;
        data_error = descr->data_error;
-
        netdev = card->netdev;
-       /* check for errors in the data_error flag */
-       if ((data_error & GELIC_NET_DATA_ERROR_MASK))
-               DPRINTK("error in received descriptor found, "
-                      "data_status=x%08x, data_error=x%08x\n",
-                      data_status, data_error);
-       /* prepare skb, unmap descriptor */
+       /* unmap skb buffer */
        skb = descr->skb;
-       dma_unmap_single(&card->dev->core, descr->buf_addr, GELIC_NET_MAX_MTU,
-                        PCI_DMA_BIDIRECTIONAL);
-
-       /* the cases we'll throw away the packet immediately */
-       if (data_error & GELIC_NET_DATA_ERROR_FLG) {
-               DPRINTK("ERROR DESTROY:%x\n", data_error);
-               return 0;
-       }
+       dma_unmap_single(ctodev(card), descr->buf_addr, GELIC_NET_MAX_MTU,
+                        DMA_BIDIRECTIONAL);
 
        skb->dev = netdev;
-       skb_put(skb, descr->valid_size);
+       skb_put(skb, descr->valid_size? descr->valid_size : descr->result_size);
+       if (!descr->valid_size)
+               dev_info(ctodev(card), "buffer full %x %x %x\n",
+                        descr->result_size, descr->buf_size, descr->dmac_cmd_status);
+
        descr->skb = NULL;
-       /* the card seems to add 2 bytes of junk in front
-        * of the ethernet frame */
-#define GELIC_NET_MISALIGN             2
-       skb_pull(skb, GELIC_NET_MISALIGN);
+       /*
+        * the card put 2 bytes vlan tag in front
+        * of the ethernet frame
+        */
+       skb_pull(skb, 2);
        skb->protocol = eth_type_trans(skb, netdev);
 
        /* checksum offload */
        if (card->rx_csum) {
-               if ( (data_status & GELIC_NET_DATA_STATUS_CHK_MASK) &&
-                    (!(data_error & GELIC_NET_DATA_ERROR_CHK_MASK)) )
+               if ((data_status & GELIC_NET_DATA_STATUS_CHK_MASK) &&
+                   (!(data_error & GELIC_NET_DATA_ERROR_CHK_MASK)))
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                else
                        skb->ip_summed = CHECKSUM_NONE;
-       } else {
+       } else
                skb->ip_summed = CHECKSUM_NONE;
-       }
-
-       /* pass skb up to stack */
-       netif_receive_skb(skb);
 
        /* update netdevice statistics */
        card->netdev_stats.rx_packets++;
        card->netdev_stats.rx_bytes += skb->len;
 
-       return 1;
+       /* pass skb up to stack */
+       netif_receive_skb(skb);
 }
 
 /**
- * gelic_net_decode_descr - processes an rx descriptor
+ * gelic_net_decode_one_descr - processes an rx descriptor
  * @card: card structure
  *
  * returns 1 if a packet has been sent to the stack, otherwise 0
@@ -1133,66 +864,71 @@ gelic_net_pass_skb_up(struct gelic_net_descr *descr,
  * processes an rx descriptor by iommu-unmapping the data buffer and passing
  * the packet up to the stack
  */
-static int
-gelic_net_decode_one_descr(struct gelic_net_card *card)
+static int gelic_net_decode_one_descr(struct gelic_net_card *card)
 {
        enum gelic_net_descr_status status;
-       struct gelic_net_descr *descr;
        struct gelic_net_descr_chain *chain = &card->rx_chain;
-       int result = 0;
-       int kick = 0;
-       uint32_t cmd_status;
-
-       descr = chain->tail;
-       cmd_status = chain->tail->dmac_cmd_status;
-       rmb();
-       status = cmd_status >> GELIC_NET_DESCR_IND_PROC_SHIFT;
+       struct gelic_net_descr *descr = chain->tail;
+#ifndef GELIC_RING_CHAIN
+       int dmac_chain_ended;
+#endif
+
+       status = gelic_net_get_descr_status(descr);
+#ifndef GELIC_RING_CHAIN
+       /* is this descriptor terminated with next_descr == NULL? */
+       dmac_chain_ended = descr->dmac_cmd_status & GELIC_NET_DMAC_CMDSTAT_RXDCEIS;
+#endif
+
        if (status == GELIC_NET_DESCR_CARDOWNED) {
-               goto no_decode;
+               return 0;
        }
        if (status == GELIC_NET_DESCR_NOT_IN_USE) {
-               printk("err: decode_one_descr\n");
-               goto no_decode;
+               dev_dbg(ctodev(card), "dormant descr? %p\n", descr);
+               return 0;
        }
 
-       if ( (status == GELIC_NET_DESCR_RESPONSE_ERROR) ||
-            (status == GELIC_NET_DESCR_PROTECTION_ERROR) ||
-            (status == GELIC_NET_DESCR_FORCE_END) ) {
-               printk("%s: dropping RX descriptor with state %d\n",
-                      card->netdev->name, status);
+       if ((status == GELIC_NET_DESCR_RESPONSE_ERROR) ||
+           (status == GELIC_NET_DESCR_PROTECTION_ERROR) ||
+           (status == GELIC_NET_DESCR_FORCE_END)) {
+               dev_info(ctodev(card), "dropping RX descriptor with state %x\n",
+                        status);
                card->netdev_stats.rx_dropped++;
                goto refill;
        }
 
-       if ( (status != GELIC_NET_DESCR_COMPLETE) &&
-            (status != GELIC_NET_DESCR_FRAME_END) ) {
-               printk("%s: RX descriptor with state %d\n",
-                      card->netdev->name, status);
+       if ((status != GELIC_NET_DESCR_COMPLETE) &&
+           (status != GELIC_NET_DESCR_FRAME_END)) {
+               dev_dbg(ctodev(card), "RX descriptor with state %x\n",
+                       status);
                goto refill;
        }
 
        /* ok, we've got a packet in descr */
-       result = gelic_net_pass_skb_up(descr, card); /* 1: skb_up sccess */
-       if (cmd_status & GELIC_NET_DMAC_CMDSTAT_CHAIN_END) {
-               kick = 1;
-       }
+       gelic_net_pass_skb_up(descr, card); /* 1: skb_up sccess */
+
 refill:
+#ifndef GELIC_RING_CHAIN
        descr->next_descr_addr = 0; /* unlink the descr */
-       wmb();
-       gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);
+#endif
        /* change the descriptor state: */
-       gelic_net_prepare_rx_descr(card, descr); /* refill one desc */
+       gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE);
+
+       /* refill one desc
+        * FIXME: this can fail, but for now, just leave this
+        * descriptor without skb
+        */
+       gelic_net_prepare_rx_descr(card, descr);
        chain->head = descr;
        chain->tail = descr->next;
        descr->prev->next_descr_addr = descr->bus_addr;
-       if(kick) {
-               wmb();
+
+#ifndef GELIC_RING_CHAIN
+       if (dmac_chain_ended) {
                gelic_net_enable_rxdmac(card);
+               dev_dbg(ctodev(card), "reenable rx dma\n");
        }
-       return result;
-
-no_decode:
-       return 0;
+#endif
+       return 1;
 }
 
 /**
@@ -1204,8 +940,7 @@ no_decode:
  * if the quota is exceeded, but the driver has still packets.
  *
  */
-static int
-gelic_net_poll(struct net_device *netdev, int *budget)
+static int gelic_net_poll(struct net_device *netdev, int *budget)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
        int packets_to_do, packets_done = 0;
@@ -1225,18 +960,12 @@ gelic_net_poll(struct net_device *netdev, int *budget)
        }
        netdev->quota -= packets_done;
        *budget -= packets_done;
-       if (no_more_packets == 1) {
+       if (no_more_packets) {
                netif_rx_complete(netdev);
-
-               /* one more check */
-               while (1) {
-                       if (!gelic_net_decode_one_descr(card) ) break;
-               };
-
+               gelic_net_rx_irq_on(card);
                return 0;
-       }else {
+       } else
                return 1;
-       }
 }
 
 /**
@@ -1245,13 +974,11 @@ gelic_net_poll(struct net_device *netdev, int *budget)
  *
  * returns the interface statistics residing in the gelic_net_card struct
  */
-static struct net_device_stats *
-gelic_net_get_stats(struct net_device *netdev)
+static struct net_device_stats * gelic_net_get_stats(struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
-       struct net_device_stats *stats = &card->netdev_stats;
 
-       return stats;
+       return &card->netdev_stats;
 }
 
 /**
@@ -1261,13 +988,12 @@ gelic_net_get_stats(struct net_device *netdev)
  *
  * returns 0 on success, <0 on failure
  */
-static int
-gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
+static int gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
 {
        /* no need to re-alloc skbs or so -- the max mtu is about 2.3k
         * and mtu is outbound only anyway */
-       if ( (new_mtu < GELIC_NET_MIN_MTU ) ||
-               (new_mtu > GELIC_NET_MAX_MTU) ) {
+       if ((new_mtu < GELIC_NET_MIN_MTU) ||
+           (new_mtu > GELIC_NET_MAX_MTU)) {
                return -EINVAL;
        }
        netdev->mtu = new_mtu;
@@ -1277,38 +1003,33 @@ gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
 /**
  * gelic_net_interrupt - event handler for gelic_net
  */
-static irqreturn_t
-gelic_net_interrupt(int irq, void *ptr)
+static irqreturn_t gelic_net_interrupt(int irq, void *ptr)
 {
+       unsigned long flags;
        struct net_device *netdev = ptr;
        struct gelic_net_card *card = netdev_priv(netdev);
-       uint32_t status0, status1, status2;
-       unsigned long flags;
-       uint64_t status;
+       u64 status;
 
-       status = gelic_irq_status;
-       rmb();
-       status0 = (uint32_t)(status >> 32);
-       status1 = (uint32_t)(status & 0xffffffff);
-       status2 = 0;
+       status = card->irq_status;
 
-       if (!status0 && !status1 && !status2) {
+       if (!status)
                return IRQ_NONE;
-       }
 
-       if(status1 & (1 << GELIC_NET_GDADCEINT) )  {
-               netif_rx_schedule(netdev);
-       }else
-       if (status0 & (1 << GELIC_NET_GRFANMINT) ) {
+       if (status & GELIC_NET_RXINT) {
+               gelic_net_rx_irq_off(card);
                netif_rx_schedule(netdev);
        }
 
-       if (status0 & (1 << GELIC_NET_GDTDCEINT) ) {
-               spin_lock_irqsave(&card->intmask_lock, flags);
-               dmac_status = 0;
-               spin_unlock_irqrestore(&card->intmask_lock, flags);
+       if (status & GELIC_NET_TXINT) {
+               spin_lock_irqsave(&card->tx_dma_lock, flags);
+               card->tx_dma_progress = 0;
+               spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+               /* start pending DMA */
                gelic_net_xmit(NULL, netdev);
        }
+#ifdef CONFIG_GELIC_WIRELESS
+       gelicw_interrupt(netdev, status);
+#endif
        return IRQ_HANDLED;
 }
 
@@ -1319,8 +1040,7 @@ gelic_net_interrupt(int irq, void *ptr)
  *
  * see Documentation/networking/netconsole.txt
  */
-static void
-gelic_net_poll_controller(struct net_device *netdev)
+static void gelic_net_poll_controller(struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
 
@@ -1334,27 +1054,26 @@ gelic_net_poll_controller(struct net_device *netdev)
  * gelic_net_open_device - open device and map dma region
  * @card: card structure
  */
-static int
-gelic_net_open_device(struct gelic_net_card *card)
+static int gelic_net_open_device(struct gelic_net_card *card)
 {
-       unsigned long result;
-       int ret;
+       int result;
 
        result = ps3_sb_event_receive_port_setup(PS3_BINDING_CPU_ANY,
                &card->dev->did, card->dev->interrupt_id, &card->netdev->irq);
 
        if (result) {
-               printk("%s:%d: gelic_net_open_device failed (%ld)\n",
-                       __func__, __LINE__, result);
-               ret = -EPERM;
+               dev_info(ctodev(card),
+                        "%s:%d: gelic_net_open_device failed (%d)\n",
+                        __func__, __LINE__, result);
+               result = -EPERM;
                goto fail_alloc_irq;
        }
 
-       ret = request_irq(card->netdev->irq, gelic_net_interrupt, IRQF_DISABLED,
+       result = request_irq(card->netdev->irq, gelic_net_interrupt, IRQF_DISABLED,
                "gelic network", card->netdev);
 
-       if (ret) {
-               printk("%s:%d: request_irq failed (%ld)\n",
+       if (result) {
+               dev_info(ctodev(card), "%s:%d: request_irq failed (%d)\n",
                        __func__, __LINE__, result);
                goto fail_request_irq;
        }
@@ -1366,7 +1085,7 @@ fail_request_irq:
                card->dev->interrupt_id, card->netdev->irq);
        card->netdev->irq = NO_IRQ;
 fail_alloc_irq:
-       return ret;
+       return result;
 }
 
 
@@ -1379,38 +1098,46 @@ fail_alloc_irq:
  * gelic_net_open allocates all the descriptors and memory needed for
  * operation, sets up multicast list and enables interrupts
  */
-int
-gelic_net_open(struct net_device *netdev)
+static int gelic_net_open(struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
 
-       printk(" -> %s:%d\n", __func__, __LINE__);
+       dev_dbg(ctodev(card), " -> %s:%d\n", __func__, __LINE__);
 
        gelic_net_open_device(card);
 
        if (gelic_net_init_chain(card, &card->tx_chain,
-                       card->descr, tx_descriptors))
+                       card->descr, GELIC_NET_TX_DESCRIPTORS))
                goto alloc_tx_failed;
        if (gelic_net_init_chain(card, &card->rx_chain,
-                       card->descr + tx_descriptors, rx_descriptors))
+                                card->descr + GELIC_NET_RX_DESCRIPTORS,
+                                GELIC_NET_RX_DESCRIPTORS))
                goto alloc_rx_failed;
 
        /* head of chain */
        card->tx_top = card->tx_chain.head;
        card->rx_top = card->rx_chain.head;
-
+       dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n",
+               card->rx_top, card->tx_top, sizeof(struct gelic_net_descr),
+               GELIC_NET_RX_DESCRIPTORS);
        /* allocate rx skbs */
        if (gelic_net_alloc_rx_skbs(card))
                goto alloc_skbs_failed;
 
-       dmac_status = 0;
+       card->tx_dma_progress = 0;
        card->ghiintmask = GELIC_NET_RXINT | GELIC_NET_TXINT;
+#ifdef CONFIG_GELIC_WIRELESS
+       card->ghiintmask |= GELICW_DEVICE_CMD_COMP | GELICW_DEVICE_EVENT_RECV;
+#endif
        gelic_net_set_irq_mask(card, card->ghiintmask);
        gelic_net_enable_rxdmac(card);
 
        netif_start_queue(netdev);
        netif_carrier_on(netdev);
        netif_poll_enable(netdev);
+#ifdef CONFIG_GELIC_WIRELESS
+       gelicw_up(netdev);
+#endif
 
        return 0;
 
@@ -1423,22 +1150,23 @@ alloc_tx_failed:
 }
 
 #ifdef GELIC_NET_ETHTOOL
-static void
-gelic_net_get_drvinfo (struct net_device *netdev, struct ethtool_drvinfo *info)
+static void gelic_net_get_drvinfo (struct net_device *netdev,
+                                  struct ethtool_drvinfo *info)
 {
        strncpy(info->driver, GELIC_NET_DRV_NAME, sizeof(info->driver) - 1);
        strncpy(info->version, GELIC_NET_DRV_VERSION, sizeof(info->version) - 1);
 }
 
-static int
-gelic_net_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+static int gelic_net_get_settings(struct net_device *netdev,
+                                 struct ethtool_cmd *cmd)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
-       uint64_t status, v1, v2;
+       int status;
+       u64 v1, v2;
        int speed, duplex;
 
        speed = duplex = -1;
-       status = lv1_net_control(card->dev->did.bus_id, card->dev->did.dev_id,
+       status = lv1_net_control(bus_id(card), dev_id(card),
                        GELIC_NET_GET_ETH_PORT_STATUS, GELIC_NET_PORT, 0, 0,
                        &v1, &v2);
        if (status) {
@@ -1471,28 +1199,33 @@ gelic_net_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
        return 0;
 }
 
-static uint32_t
-gelic_net_get_link(struct net_device *netdev)
+static u32 gelic_net_get_link(struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
-       uint64_t status, v1, v2;
+       int status;
+       u64 v1, v2;
        int link;
 
-       status = lv1_net_control(card->dev->did.bus_id, card->dev->did.dev_id,
+       status = lv1_net_control(bus_id(card), dev_id(card),
                        GELIC_NET_GET_ETH_PORT_STATUS, GELIC_NET_PORT, 0, 0,
                        &v1, &v2);
-       if (status) {
+       if (status)
                return 0; /* link down */
-       }
+
        if (v1 & GELIC_NET_LINK_UP)
                link = 1;
        else
                link = 0;
+#ifdef CONFIG_GELIC_WIRELESS
+       /* (v1 & GELIC_NET_LINK_UP) is always 0 in wireless mode */
+       if (gelicw_is_associated(netdev)) {
+               link = 1;
+       }
+#endif
        return link;
 }
 
-static int
-gelic_net_nway_reset(struct net_device *netdev)
+static int gelic_net_nway_reset(struct net_device *netdev)
 {
        if (netif_running(netdev)) {
                gelic_net_stop(netdev);
@@ -1501,14 +1234,12 @@ gelic_net_nway_reset(struct net_device *netdev)
        return 0;
 }
 
-static uint32_t
-gelic_net_get_tx_csum(struct net_device *netdev)
+static u32 gelic_net_get_tx_csum(struct net_device *netdev)
 {
        return (netdev->features & NETIF_F_IP_CSUM) != 0;
 }
 
-static int
-gelic_net_set_tx_csum(struct net_device *netdev, uint32_t data)
+static int gelic_net_set_tx_csum(struct net_device *netdev, u32 data)
 {
        if (data)
                netdev->features |= NETIF_F_IP_CSUM;
@@ -1518,16 +1249,14 @@ gelic_net_set_tx_csum(struct net_device *netdev, uint32_t data)
        return 0;
 }
 
-static uint32_t
-gelic_net_get_rx_csum(struct net_device *netdev)
+static u32 gelic_net_get_rx_csum(struct net_device *netdev)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
 
        return card->rx_csum;
 }
 
-static int
-gelic_net_set_rx_csum(struct net_device *netdev, uint32_t data)
+static int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
 {
        struct gelic_net_card *card = netdev_priv(netdev);
 
@@ -1547,67 +1276,20 @@ static struct ethtool_ops gelic_net_ethtool_ops = {
 };
 #endif
 
-static int
-gelic_net_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
-{
-       struct gelic_net_card *card = netdev_priv(netdev);
-       void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data;
-       int mode, res = 0;
-
-       switch(cmd) {
-       case GELIC_NET_GET_MODE:
-               DPRINTK("GELIC_NET_GET_MODE:\n");
-               mode = card->vlan_index;
-               if (copy_to_user(addr, &mode, sizeof(mode)) ) {
-                       printk("error copy_to_user\n");
-               }
-               res = 0;
-               break;
-       case GELIC_NET_SET_MODE:
-               if (card->vlan_index == -1) {
-                       res = -EOPNOTSUPP; /* vlan mode only */
-                       break;
-               }
-               if (copy_from_user(&mode, addr, sizeof(mode)) ) {
-                       printk("error copy_from_user\n");
-               }
-               DPRINTK("GELIC_NET_SET_MODE:%x --> %x \n",
-                               card->vlan_index, mode);
-               if (mode > GELIC_NET_VLAN_MAX -1 || mode < -1)
-                       mode = GELIC_NET_VLAN_WIRED - 1;
-
-               if (card->vlan_index != mode) {
-                       card->vlan_index = mode;
-                       if (netif_running(netdev)) {
-                               gelic_net_stop(netdev);
-                               gelic_net_open(netdev);
-                       }
-               }
-               res = 0;
-               break;
-       default:
-               res = -EOPNOTSUPP;
-               break;
-       }
-
-       return res;
-}
-
 /**
  * gelic_net_tx_timeout_task - task scheduled by the watchdog timeout
  * function (to be called not under interrupt status)
- * @data: data, is interface device structure
+ * @work: work is context of tx timout task
  *
  * called as task when tx hangs, resets interface (if interface is up)
  */
-static void
-gelic_net_tx_timeout_task(struct work_struct *work)
+static void gelic_net_tx_timeout_task(struct work_struct *work)
 {
        struct gelic_net_card *card =
                container_of(work, struct gelic_net_card, tx_timeout_task);
        struct net_device *netdev = card->netdev;
 
-       printk("Timed out. Restarting... \n");
+       dev_info(ctodev(card), "%s:Timed out. Restarting... \n", __func__);
 
        if (!(netdev->flags & IFF_UP))
                goto out;
@@ -1628,8 +1310,7 @@ out:
  *
  * called, if tx hangs. Schedules a task that resets the interface
  */
-static void
-gelic_net_tx_timeout(struct net_device *netdev)
+static void gelic_net_tx_timeout(struct net_device *netdev)
 {
        struct gelic_net_card *card;
 
@@ -1647,8 +1328,7 @@ gelic_net_tx_timeout(struct net_device *netdev)
  *
  * fills out function pointers in the net_device structure
  */
-static void
-gelic_net_setup_netdev_ops(struct net_device *netdev)
+static void gelic_net_setup_netdev_ops(struct net_device *netdev)
 {
        netdev->open = &gelic_net_open;
        netdev->stop = &gelic_net_stop;
@@ -1665,7 +1345,6 @@ gelic_net_setup_netdev_ops(struct net_device *netdev)
 #ifdef GELIC_NET_ETHTOOL
        netdev->ethtool_ops = &gelic_net_ethtool_ops;
 #endif
-       netdev->do_ioctl = &gelic_net_ioctl;
 }
 
 /**
@@ -1676,18 +1355,17 @@ gelic_net_setup_netdev_ops(struct net_device *netdev)
  *
  * gelic_net_setup_netdev initializes the net_device structure
  **/
-static int
-gelic_net_setup_netdev(struct gelic_net_card *card)
+static int gelic_net_setup_netdev(struct gelic_net_card *card)
 {
-       int i, result;
        struct net_device *netdev = card->netdev;
        struct sockaddr addr;
-       uint8_t *mac;
-       uint64_t status, v1, v2;
+       unsigned int i;
+       int status;
+       u64 v1, v2;
 
        SET_MODULE_OWNER(netdev);
        SET_NETDEV_DEV(netdev, &card->dev->core);
-       spin_lock_init(&card->intmask_lock);
+       spin_lock_init(&card->tx_dma_lock);
 
        card->rx_csum = GELIC_NET_RX_CSUM_DEFAULT;
 
@@ -1695,53 +1373,59 @@ gelic_net_setup_netdev(struct gelic_net_card *card)
 
        netdev->features = NETIF_F_IP_CSUM;
 
-       status = lv1_net_control(card->dev->did.bus_id, card->dev->did.dev_id,
-                               GELIC_NET_GET_MAC_ADDRESS,
-                               0, 0, 0, &v1, &v2);
-       if (status || !v1) {
-               printk("lv1_net_control GET_MAC_ADDR not supported, status=%ld\n",
-                       status);
+       status = lv1_net_control(bus_id(card), dev_id(card),
+                                GELIC_NET_GET_MAC_ADDRESS,
+                                0, 0, 0, &v1, &v2);
+       if (status || !is_valid_ether_addr((u8*)&v1)) {
+               dev_info(ctodev(card),
+                        "%s:lv1_net_control GET_MAC_ADDR failed %d\n",
+                        __func__,status);
                return -EINVAL;
        }
        v1 <<= 16;
-       mac = (uint8_t *)&v1;
-       memcpy(addr.sa_data, mac, ETH_ALEN);
+       memcpy(addr.sa_data, &v1, ETH_ALEN);
        memcpy(netdev->dev_addr, addr.sa_data, ETH_ALEN);
-
-       result = register_netdev(netdev);
-       if (result) {
-                       printk("Couldn't register net_device: %i\n", result);
-               return result;
-       }
-
-       printk("%s: %s\n", netdev->name, GELIC_NET_DRV_NAME);
-       printk("%s: Ethernet Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
-               netdev->name,
-               netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
-               netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
+       dev_info(ctodev(card), "MAC addr %02x:%02x:%02x:%02x:%02x:%02x\n",
+                netdev->dev_addr[0], netdev->dev_addr[1],
+                netdev->dev_addr[2], netdev->dev_addr[3],
+                netdev->dev_addr[4], netdev->dev_addr[5]);
 
        card->vlan_index = -1;  /* no vlan */
-       for (i = 0; i < GELIC_NET_VLAN_MAX ;i++) {
-               status = lv1_net_control(card->dev->did.bus_id,
-                                       card->dev->did.dev_id,
+       for (i = 0; i < GELIC_NET_VLAN_MAX; i++) {
+               status = lv1_net_control(bus_id(card), dev_id(card),
                                        GELIC_NET_GET_VLAN_ID,
-                                       i + 1, /* GELIC_NET_VLAN_X */
+                                       i + 1, /* index; one based */
                                        0, 0, &v1, &v2);
                if (status == GELIC_NET_VLAN_NO_ENTRY) {
-                       DPRINTK("GELIC_VLAN_ID no entry:%ld, VLAN disabled\n",
+                       dev_dbg(ctodev(card),
+                               "GELIC_VLAN_ID no entry:%d, VLAN disabled\n",
                                status);
                        card->vlan_id[i] = 0;
                } else if (status) {
-                       printk("GELIC_NET_VLAN_ID faild, status=%ld\n", status);
+                       dev_dbg(ctodev(card),
+                               "%s:GELIC_NET_VLAN_ID faild, status=%d\n",
+                               __func__, status);
                        card->vlan_id[i] = 0;
                } else {
-                       card->vlan_id[i] = (uint32_t)v1;
-                       DPRINTK("vlan_id:%d, %lx\n", i, v1);
+                       card->vlan_id[i] = (u32)v1;
+                       dev_dbg(ctodev(card), "vlan_id:%d, %lx\n", i, v1);
                }
        }
-       if (card->vlan_id[GELIC_NET_VLAN_WIRED - 1]) {
+       if (card->vlan_id[GELIC_NET_VLAN_WIRED - 1])
                card->vlan_index = GELIC_NET_VLAN_WIRED - 1;
+#ifdef CONFIG_GELIC_WIRELESS
+       card->w.card = card;
+       /* init wireless extension */
+       /* No wireless vlan_index:-1 */
+       gelicw_setup_netdev(netdev, card->vlan_index);
+#endif
+       status = register_netdev(netdev);
+       if (status) {
+               dev_err(ctodev(card), "%s:Couldn't register net_device: %d\n",
+                       __func__, status);
+               return status;
        }
+
        return 0;
 }
 
@@ -1752,16 +1436,26 @@ gelic_net_setup_netdev(struct gelic_net_card *card)
  *
  * the card and net_device structures are linked to each other
  */
-static struct gelic_net_card *
-gelic_net_alloc_card(void)
+static struct gelic_net_card * gelic_net_alloc_card(void)
 {
        struct net_device *netdev;
        struct gelic_net_card *card;
        size_t alloc_size;
 
        alloc_size = sizeof (*card) +
-               sizeof (struct gelic_net_descr) * rx_descriptors +
-               sizeof (struct gelic_net_descr) * tx_descriptors;
+               sizeof (struct gelic_net_descr) * GELIC_NET_RX_DESCRIPTORS +
+               sizeof (struct gelic_net_descr) * GELIC_NET_TX_DESCRIPTORS;
+       /*
+        * we assume private data is allocated 32 bytes (or more) aligned
+        * so that gelic_net_descr should be 32 bytes aligned.
+        * Current alloc_etherdev() does do it because NETDEV_ALIGN
+        * is 32.
+        * check this assumption here.
+        */
+       BUILD_BUG_ON(NETDEV_ALIGN < 32);
+       BUILD_BUG_ON(offsetof(struct gelic_net_card, irq_status) % 8);
+       BUILD_BUG_ON(offsetof(struct gelic_net_card, descr) % 32);
+
        netdev = alloc_etherdev(alloc_size);
        if (!netdev)
                return NULL;
@@ -1780,61 +1474,101 @@ gelic_net_alloc_card(void)
  */
 static int ps3_gelic_driver_probe (struct ps3_system_bus_device *dev)
 {
-       struct gelic_net_card *card;
-       int error = -EIO;
-       uint64_t status;
-       uint64_t lpar;
+       struct gelic_net_card * card;
+       int result;
 
        card = gelic_net_alloc_card();
+
        if (!card) {
-               printk("Couldn't allocate net_device structure, aborting.\n");
-               return -ENOMEM;
+               dev_info(&dev->core, "gelic_net_alloc_card failed\n");
+               result = -ENOMEM;
+               goto fail_alloc_card;
        }
-       gcard = card;
+
+       ps3_system_bus_set_driver_data(dev, card);
        card->dev = dev;
 
-       /* setup status indicator */
-       lpar = ps3_mm_phys_to_lpar(__pa(&gelic_irq_status));
-       status = lv1_net_set_interrupt_status_indicator(
-                                               card->dev->did.bus_id,
-                                               card->dev->did.dev_id,
-                                               lpar, 0);
-       if (status) {
-               printk("lv1_net_set_interrupt_status_indicator failed, status=%ld\n",
-                       status);
-               goto error;
+       result = ps3_open_hv_device(dev);
+
+       if (result) {
+               dev_dbg(&dev->core, "ps3_open_hv_device failed\n");
+               goto fail_open;
        }
 
-       error = gelic_net_setup_netdev(card);
-       if (error) {
-               printk("gelic_net_setup_netdev() failed: error = %d\n", error);
-               goto error;
+       result = ps3_dma_region_create(dev->d_region);
+
+       if (result) {
+               dev_dbg(&dev->core, "ps3_dma_region_create failed(%d)\n",
+                       result);
+               BUG_ON("check region type");
+               goto fail_dma_region;
        }
+
+       result = lv1_net_set_interrupt_status_indicator(bus_id(card),
+                                                       dev_id(card),
+               ps3_mm_phys_to_lpar(__pa(&card->irq_status)),
+               0);
+
+       if (result) {
+               dev_dbg(&dev->core,
+                       "lv1_net_set_interrupt_status_indicator failed: %s\n",
+                       ps3_result(result));
+               result = -EIO;
+               goto fail_status_indicator;
+       }
+
+       result = gelic_net_setup_netdev(card);
+
+       if (result) {
+               dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+                       "(%d)\n", __func__, __LINE__, result);
+               goto fail_setup_netdev;
+       }
+
        return 0;
 
-error:
+fail_setup_netdev:
+       lv1_net_set_interrupt_status_indicator(bus_id(card),
+                                              bus_id(card),
+                                              0 , 0);
+fail_status_indicator:
+       ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+       ps3_close_hv_device(dev);
+fail_open:
+       ps3_system_bus_set_driver_data(dev, NULL);
        free_netdev(card->netdev);
-       return error;
+fail_alloc_card:
+       return result;
 }
 
 /**
  * ps3_gelic_driver_remove - remove a device from the control of this driver
  */
 
-static int
-ps3_gelic_driver_remove (struct ps3_system_bus_device *dev)
+static int ps3_gelic_driver_remove (struct ps3_system_bus_device *dev)
 {
-       struct net_device *netdev;
-       struct gelic_net_card *card;
+       struct gelic_net_card * card;
 
-       card = gcard;
-       netdev = card->netdev;
+       card = ps3_system_bus_get_driver_data(dev);
 
+#ifdef CONFIG_GELIC_WIRELESS
+       gelicw_remove(card->netdev);
+#endif
        wait_event(card->waitq,
                   atomic_read(&card->tx_timeout_task_counter) == 0);
 
-       unregister_netdev(netdev);
-       free_netdev(netdev);
+       unregister_netdev(card->netdev);
+       free_netdev(card->netdev);
+
+       lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card),
+                                              0 , 0);
+
+       ps3_system_bus_set_driver_data(dev, NULL);
+
+       ps3_dma_region_free(dev->d_region);
+
+       ps3_close_hv_device(dev);
 
        return 0;
 }
@@ -1843,22 +1577,22 @@ static struct ps3_system_bus_driver ps3_gelic_driver = {
        .match_id = PS3_MATCH_ID_GELIC,
        .probe = ps3_gelic_driver_probe,
        .remove = ps3_gelic_driver_remove,
+       .shutdown = ps3_gelic_driver_remove,
        .core = {
                .owner = THIS_MODULE,
                .name = "ps3_gelic_driver",
        },
 };
 
-static int __init
-ps3_gelic_driver_init (void)
+static int __init ps3_gelic_driver_init (void)
 {
        return firmware_has_feature(FW_FEATURE_PS3_LV1)
-               ? ps3_system_bus_driver_register(&ps3_gelic_driver)
+               ? ps3_system_bus_driver_register(&ps3_gelic_driver,
+                                                PS3_IOBUS_SB)
                : -ENODEV;
 }
 
-static void __exit
-ps3_gelic_driver_exit (void)
+static void __exit ps3_gelic_driver_exit (void)
 {
        ps3_system_bus_driver_unregister(&ps3_gelic_driver);
 }
@@ -1866,17 +1600,4 @@ ps3_gelic_driver_exit (void)
 module_init (ps3_gelic_driver_init);
 module_exit (ps3_gelic_driver_exit);
 
-#ifdef CONFIG_GELIC_NET
-static int __init early_param_gelic_net(char *p)
-{
-       if (strstr(p, "n")) {
-               ps3_gelic_param = 0;    /* gelic_vlan off */
-               printk("ps3_gelic_param:vlan off\n");
-       } else {
-               ps3_gelic_param = 1;    /* gelic_vlan on */
-       }
-       return 0;
 
-}
-early_param("gelic_vlan", early_param_gelic_net);
-#endif
diff --git a/drivers/net/gelic_net.h b/drivers/net/gelic_net.h
new file mode 100644 (file)
index 0000000..e3fb535
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ *  PS3 Platfom gelic network driver.
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corporation.
+ *
+ *  this file is based on: spider_net.h
+ *
+ * Network device driver for Cell Processor-Based Blade
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Authors : Utz Bacher <utz.bacher@de.ibm.com>
+ *           Jens Osterkamp <Jens.Osterkamp@de.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _GELIC_NET_H
+#define _GELIC_NET_H
+
+#include <linux/wireless.h>
+#include <net/ieee80211.h>
+
+#define GELIC_NET_DRV_NAME "Gelic Network Driver"
+#define GELIC_NET_DRV_VERSION "1.0"
+
+#define GELIC_NET_ETHTOOL               /* use ethtool */
+
+/* ioctl */
+#define GELIC_NET_GET_MODE              (SIOCDEVPRIVATE + 0)
+#define GELIC_NET_SET_MODE              (SIOCDEVPRIVATE + 1)
+
+/* descriptors */
+#define GELIC_NET_RX_DESCRIPTORS        128 /* num of descriptors */
+#define GELIC_NET_TX_DESCRIPTORS        128 /* num of descriptors */
+
+#define GELIC_NET_MAX_MTU               2308
+#define GELIC_NET_MIN_MTU               64
+#define GELIC_NET_RXBUF_ALIGN           128
+#define GELIC_NET_RX_CSUM_DEFAULT       1 /* hw chksum */
+#define GELIC_NET_WATCHDOG_TIMEOUT      5*HZ
+#define GELIC_NET_NAPI_WEIGHT           (GELIC_NET_RX_DESCRIPTORS)
+#define GELIC_NET_BROADCAST_ADDR        0xffffffffffffL
+#define GELIC_NET_VLAN_POS              (VLAN_ETH_ALEN * 2)
+#define GELIC_NET_VLAN_MAX              4
+#define GELIC_NET_MC_COUNT_MAX          32 /* multicast address list */
+
+enum gelic_net_int0_status {
+       GELIC_NET_GDTDCEINT  = 24,
+       GELIC_NET_GRFANMINT  = 28,
+};
+
+/* GHIINT1STS bits */
+enum gelic_net_int1_status {
+       GELIC_NET_GDADCEINT = 14,
+};
+
+/* interrupt mask */
+#define GELIC_NET_TXINT                   (1L << (GELIC_NET_GDTDCEINT + 32))
+
+#define GELIC_NET_RXINT0                  (1L << (GELIC_NET_GRFANMINT + 32))
+#define GELIC_NET_RXINT1                  (1L << GELIC_NET_GDADCEINT)
+#define GELIC_NET_RXINT                   (GELIC_NET_RXINT0 | GELIC_NET_RXINT1)
+
+ /* RX descriptor data_status bits */
+#define GELIC_NET_RXDMADU              0x80000000 /* destination MAC addr unknown */
+#define GELIC_NET_RXLSTFBF             0x40000000 /* last frame buffer            */
+#define GELIC_NET_RXIPCHK              0x20000000 /* IP checksum performed        */
+#define GELIC_NET_RXTCPCHK             0x10000000 /* TCP/UDP checksup performed   */
+#define GELIC_NET_RXIPSPKT             0x08000000 /* IPsec packet   */
+#define GELIC_NET_RXIPSAHPRT           0x04000000 /* IPsec AH protocol performed */
+#define GELIC_NET_RXIPSESPPRT          0x02000000 /* IPsec ESP protocol performed */
+#define GELIC_NET_RXSESPAH             0x01000000 /* IPsec ESP protocol auth performed */
+#define GELIC_NET_RXWTPKT              0x00C00000 /*
+                                                   * wakeup trigger packet
+                                                   * 01: Magic Packet (TM)
+                                                   * 10: ARP packet
+                                                   * 11: Multicast MAC addr
+                                                   */
+#define GELIC_NET_RXVLNPKT             0x00200000 /* VLAN packet */
+/* bit 20..16 reserved */
+#define GELIC_NET_RXRECNUM             0x0000ff00 /* reception receipt number */
+/* bit 7..0 reserved */
+
+#define GELIC_NET_TXDESC_TAIL          0
+#define GELIC_NET_DATA_STATUS_CHK_MASK (GELIC_NET_RXIPCHK | GELIC_NET_RXTCPCHK)
+
+/* RX descriptor data_error bits */
+/* bit 31 reserved */
+#define GELIC_NET_RXALNERR             0x40000000 /* alignement error 10/100M */
+#define GELIC_NET_RXOVERERR            0x20000000 /* oversize error */
+#define GELIC_NET_RXRNTERR             0x10000000 /* Runt error */
+#define GELIC_NET_RXIPCHKERR           0x08000000 /* IP checksum  error */
+#define GELIC_NET_RXTCPCHKERR          0x04000000 /* TCP/UDP checksum  error */
+#define GELIC_NET_RXUMCHSP             0x02000000 /* unmatched sp on sp */
+#define GELIC_NET_RXUMCHSPI            0x01000000 /* unmatched SPI on SAD */
+#define GELIC_NET_RXUMCHSAD            0x00800000 /* unmatched SAD */
+#define GELIC_NET_RXIPSAHERR           0x00400000 /* auth error on AH protocol
+                                                   * processing */
+#define GELIC_NET_RXIPSESPAHERR                0x00200000 /* auth error on ESP protocol
+                                                   * processing */
+#define GELIC_NET_RXDRPPKT             0x00100000 /* drop packet */
+#define GELIC_NET_RXIPFMTERR           0x00080000 /* IP packet format error */
+/* bit 18 reserved */
+#define GELIC_NET_RXDATAERR            0x00020000 /* IP packet format error */
+#define GELIC_NET_RXCALERR             0x00010000 /* cariier extension length
+                                                   * error */
+#define GELIC_NET_RXCREXERR            0x00008000 /* carrier extention error */
+#define GELIC_NET_RXMLTCST             0x00004000 /* multicast address frame */
+/* bit 13..0 reserved */
+//#define GELIC_NET_IP         29 /* last frame buffer */
+//#define GELIC_NET_RXIPCHKERR              27
+//#define GELIC_NET_RXTCPCHKERR             26
+//#define GELIC_NET_DATA_ERROR_CHK_MASK     (1 << GELIC_NET_RXIPCHKERR | 1 << GELIC_NET_RXTCPCHKERR)
+
+/* ignore ipsec ans multicast */
+/*#define GELIC_NET_DATA_ERROR_MASK         0xfdefbfff */
+/* ignore unmatched sp on sp, drop_packet, multicast address frame*/
+/*#define GELIC_NET_DATA_ERROR_FLG          0x7def8000*/
+#define GELIC_NET_DATA_ERROR_CHK_MASK  (GELIC_NET_RXIPCHKERR | GELIC_NET_RXTCPCHKERR)
+
+
+/* tx descriptor command and status */
+#define GELIC_NET_DMAC_CMDSTAT_NOCS       0xa0080000 /* middle of frame */
+#define GELIC_NET_DMAC_CMDSTAT_TCPCS      0xa00a0000
+#define GELIC_NET_DMAC_CMDSTAT_UDPCS      0xa00b0000
+#define GELIC_NET_DMAC_CMDSTAT_END_FRAME  0x00040000 /* end of frame */
+
+#define GELIC_NET_DMAC_CMDSTAT_RXDCEIS   0x00000002 /* descriptor chain end
+                                                     * interrupt status */
+
+#define GELIC_NET_DMAC_CMDSTAT_CHAIN_END  0x00000002 /* RXDCEIS:DMA stopped */
+
+#define GELIC_NET_DESCR_IND_PROC_SHIFT    28
+#define GELIC_NET_DESCR_IND_PROC_MASKO    0x0fffffff
+
+
+enum gelic_net_descr_status {
+       GELIC_NET_DESCR_COMPLETE            = 0x00, /* used in rx and tx */
+       GELIC_NET_DESCR_RESPONSE_ERROR      = 0x01, /* used in rx and tx */
+       GELIC_NET_DESCR_PROTECTION_ERROR    = 0x02, /* used in rx and tx */
+       GELIC_NET_DESCR_FRAME_END           = 0x04, /* used in rx */
+       GELIC_NET_DESCR_FORCE_END           = 0x05, /* used in rx and tx */
+       GELIC_NET_DESCR_CARDOWNED           = 0x0a, /* used in rx and tx */
+       GELIC_NET_DESCR_NOT_IN_USE                  /* any other value */
+};
+#define GELIC_NET_DMAC_CMDSTAT_NOT_IN_USE 0xb0000000
+
+struct gelicw_bss {
+       u8 bssid[ETH_ALEN];
+       u8 channel;
+       u8 mode;
+       u8 essid_len;
+       u8 essid[IW_ESSID_MAX_SIZE + 1]; /* null terminated for debug msg */
+
+       u16 capability;
+       u16 beacon_interval;
+
+       u8 rates_len;
+       u8 rates[MAX_RATES_LENGTH];
+       u8 rates_ex_len;
+       u8 rates_ex[MAX_RATES_EX_LENGTH];
+       u8 rssi;
+
+       /* scan results have sec_info instead of rsn_ie or wpa_ie */
+       u16 sec_info;
+};
+
+/* max scan list */
+#define MAX_SCAN_BSS                   16
+
+struct gelic_wireless {
+       struct gelic_net_card *card;
+       struct completion cmd_done, rssi_done;
+       struct work_struct work_event, work_start_done;
+       struct delayed_work work_rssi, work_scan_all, work_scan_essid;
+       struct delayed_work work_common, work_encode;
+       struct delayed_work work_start, work_stop, work_roam;
+       wait_queue_head_t waitq_cmd, waitq_scan;
+
+       u64 cmd_tag, cmd_id;
+       u8 cmd_send_flg;
+
+       struct iw_public_data wireless_data;
+       u8 *data_buf; /* data buffer for lv1_net_control */
+
+       u8 wireless; /* wireless support */
+       u8 state;
+       u8 scan_all; /* essid scan or all scan */
+       u8 essid_search; /* essid background scan */
+       u8 is_assoc;
+
+       u16 ch_info; /* supoprted channels */
+       u8 wireless_mode; /* 11b/g */
+       u8 channel; /* current ch */
+       u8 iw_mode; /* INFRA or Ad-hoc */
+       u8 rssi;
+       u8 essid_len;
+       u8 essid[IW_ESSID_MAX_SIZE + 1]; /* null terminated for debug msg */
+       u8 nick[IW_ESSID_MAX_SIZE + 1];
+       u8 bssid[ETH_ALEN];
+
+       u8 key_index;
+       u8 key[WEP_KEYS][IW_ENCODING_TOKEN_MAX]; /* 4 * 64byte */
+       u8 key_len[WEP_KEYS];
+       u8 key_alg; /* key algorithm  */
+       u8 auth_mode; /* authenticaton mode */
+
+       u8 bss_index; /* current bss in bss_list */
+       u8 num_bss_list;
+       u8 bss_key_alg; /* key alg of bss */
+       u8 wap_bssid[ETH_ALEN];
+       unsigned long last_scan; /* last scan time */
+       struct gelicw_bss current_bss;
+       struct gelicw_bss bss_list[MAX_SCAN_BSS];
+};
+
+#define GELIC_NET_DESCR_SIZE              32
+struct gelic_net_descr {
+       /* as defined by the hardware */
+       u32 buf_addr;
+       u32 buf_size;
+       u32 next_descr_addr;
+       u32 dmac_cmd_status;
+       u32 result_size;
+       u32 valid_size; /* all zeroes for tx */
+       u32 data_status;
+       u32 data_error; /* all zeroes for tx */
+
+       /* used in the driver */
+       struct sk_buff *skb;
+       dma_addr_t bus_addr;
+       struct gelic_net_descr *next;
+       struct gelic_net_descr *prev;
+       struct vlan_ethhdr vlan;
+} __attribute__((aligned(32)));
+
+struct gelic_net_descr_chain {
+       /* we walk from tail to head */
+       struct gelic_net_descr *head;
+       struct gelic_net_descr *tail;
+};
+
+struct gelic_net_card {
+       struct net_device *netdev;
+       /*
+        * hypervisor requires irq_status should be
+        * 8 bytes aligned, but u64 member is
+        * always disposed in that manner
+        */
+       u64 irq_status;
+       u64 ghiintmask;
+
+       struct ps3_system_bus_device *dev;
+       u32 vlan_id[GELIC_NET_VLAN_MAX];
+       int vlan_index;
+
+       struct gelic_net_descr_chain tx_chain;
+       struct gelic_net_descr_chain rx_chain;
+       spinlock_t chain_lock;
+
+       struct net_device_stats netdev_stats;
+       int rx_csum;
+       spinlock_t tx_dma_lock;
+       int tx_dma_progress;
+
+       struct work_struct tx_timeout_task;
+       atomic_t tx_timeout_task_counter;
+       wait_queue_head_t waitq;
+
+       struct gelic_net_descr *tx_top, *rx_top;
+#ifdef CONFIG_GELIC_WIRELESS
+       struct gelic_wireless w;
+#endif
+       struct gelic_net_descr descr[0];
+};
+
+/* for lv1_net_control */
+#define GELIC_NET_GET_MAC_ADDRESS               0x0000000000000001
+#define GELIC_NET_GET_ETH_PORT_STATUS           0x0000000000000002
+#define GELIC_NET_SET_NEGOTIATION_MODE          0x0000000000000003
+#define GELIC_NET_GET_VLAN_ID                   0x0000000000000004
+
+#define GELIC_NET_LINK_UP                       0x0000000000000001
+#define GELIC_NET_FULL_DUPLEX                   0x0000000000000002
+#define GELIC_NET_AUTO_NEG                      0x0000000000000004
+#define GELIC_NET_SPEED_10                      0x0000000000000010
+#define GELIC_NET_SPEED_100                     0x0000000000000020
+#define GELIC_NET_SPEED_1000                    0x0000000000000040
+
+#define GELIC_NET_VLAN_ALL                      0x0000000000000001
+#define GELIC_NET_VLAN_WIRED                    0x0000000000000002
+#define GELIC_NET_VLAN_WIRELESS                 0x0000000000000003
+#define GELIC_NET_VLAN_PSP                      0x0000000000000004
+#define GELIC_NET_VLAN_PORT0                    0x0000000000000010
+#define GELIC_NET_VLAN_PORT1                    0x0000000000000011
+#define GELIC_NET_VLAN_PORT2                    0x0000000000000012
+#define GELIC_NET_VLAN_DAEMON_CLIENT_BSS        0x0000000000000013
+#define GELIC_NET_VLAN_LIBERO_CLIENT_BSS        0x0000000000000014
+#define GELIC_NET_VLAN_NO_ENTRY                 -6
+
+#define GELIC_NET_PORT                          2 /* for port status */
+
+/* wireless */
+#define GELICW_WIRELESS_NOT_EXIST      0
+#define GELICW_WIRELESS_SUPPORTED      1
+#define GELICW_WIRELESS_ON             2
+#define GELICW_WIRELESS_SHUTDOWN       3
+/* state */
+#define GELICW_STATE_DOWN              0
+#define GELICW_STATE_UP                        1
+#define GELICW_STATE_SCANNING          2
+#define GELICW_STATE_SCAN_DONE         3
+#define GELICW_STATE_ASSOCIATED                4
+
+/* cmd_send_flg */
+#define GELICW_CMD_SEND_NONE           0x00
+#define GELICW_CMD_SEND_COMMON         0x01
+#define GELICW_CMD_SEND_ENCODE         0x02
+#define GELICW_CMD_SEND_SCAN           0x04
+#define GELICW_CMD_SEND_ALL            (GELICW_CMD_SEND_COMMON \
+                                       | GELICW_CMD_SEND_ENCODE \
+                                       | GELICW_CMD_SEND_SCAN)
+
+#define GELICW_SCAN_INTERVAL           (HZ)
+
+#ifdef DEBUG
+#define CH_INFO_FAIL 0x0600 /* debug */
+#else
+#define CH_INFO_FAIL 0
+#endif
+
+
+/* net_control command */
+#define GELICW_SET_PORT                        3 /* control Ether port */
+#define GELICW_GET_INFO                        6 /* get supported channels */
+#define GELICW_SET_CMD                 9 /* set configuration */
+#define GELICW_GET_RES                 10 /* get command response */
+#define GELICW_GET_EVENT               11 /* get event from device */
+/* net_control command data buffer */
+#define GELICW_DATA_BUF_SIZE           0x1000
+
+/* GELICW_SET_CMD params */
+#define GELICW_CMD_START               1
+#define GELICW_CMD_STOP                        2
+#define GELICW_CMD_SCAN                        3
+#define GELICW_CMD_GET_SCAN            4
+#define GELICW_CMD_SET_CONFIG          5
+#define GELICW_CMD_GET_CONFIG          6
+#define GELICW_CMD_SET_WEP             7
+#define GELICW_CMD_GET_WEP             8
+#define GELICW_CMD_SET_WPA             9
+#define GELICW_CMD_GET_WPA             10
+#define GELICW_CMD_GET_RSSI            11
+
+/* GELICW_SET_PORT params */
+#define GELICW_ETHER_PORT              2
+#define GELICW_PORT_DOWN               0 /* Ether port off */
+#define GELICW_PORT_UP                 4 /* Ether port on (auto neg) */
+
+/* interrupt status bit */
+#define GELICW_DEVICE_CMD_COMP         (1UL << 31)
+#define GELICW_DEVICE_EVENT_RECV       (1UL << 30)
+
+/* GELICW_GET_EVENT ID */
+#define GELICW_EVENT_UNKNOWN           0x00
+#define GELICW_EVENT_DEVICE_READY      0x01
+#define GELICW_EVENT_SCAN_COMPLETED    0x02
+#define GELICW_EVENT_DEAUTH            0x04
+#define GELICW_EVENT_BEACON_LOST       0x08
+#define GELICW_EVENT_CONNECTED         0x10
+#define GELICW_EVENT_WPA_CONNECTED     0x20
+#define GELICW_EVENT_WPA_ERROR         0x40
+#define GELICW_EVENT_NO_ENTRY          (-6)
+
+#define MAX_IW_PRIV_SIZE               32
+
+/* structure of data buffer for lv1_net_contol */
+/* wep_config: sec */
+#define GELICW_WEP_SEC_NONE            0
+#define GELICW_WEP_SEC_40BIT           1
+#define GELICW_WEP_SEC_104BIT          2
+struct wep_config {
+       u16 sec;
+       u8  key[4][16];
+} __attribute__ ((packed));
+
+/* wpa_config: sec */
+#define GELICW_WPA_SEC_NONE            0
+#define GELICW_WPA_SEC_TKIP            1
+#define GELICW_WPA_SEC_AES             2
+/* wpa_config: psk_type */
+#define GELICW_PSK_PASSPHRASE          0
+#define GELICW_PSK_64HEX               1
+struct wpa_config {
+       u16 sec;
+       u16 psk_type;
+       u8  psk_material[64]; /* key */
+} __attribute__ ((packed));
+
+/* common_config: bss_type */
+#define GELICW_BSS_INFRA               0
+#define GELICW_BSS_ADHOC               1
+/* common_config: auth_method */
+#define GELICW_AUTH_OPEN               0
+#define GELICW_AUTH_SHARED             1
+/* common_config: op_mode */
+#define GELICW_OP_MODE_11BG            0
+#define GELICW_OP_MODE_11B             1
+#define GELICW_OP_MODE_11G             2
+struct common_config {
+       u16 scan_index; /* index of scan_desc list */
+       u16 bss_type;
+       u16 auth_method;
+       u16 op_mode;
+} __attribute__ ((packed));
+
+/* scan_descriptor: security */
+#define GELICW_SEC_TYPE_NONE           0x0000
+#define GELICW_SEC_TYPE_WEP            0x0100
+#define GELICW_SEC_TYPE_WEP40          0x0101
+#define GELICW_SEC_TYPE_WEP104         0x0102
+#define GELICW_SEC_TYPE_TKIP           0x0201
+#define GELICW_SEC_TYPE_AES            0x0202
+#define GELICW_SEC_TYPE_WEP_MASK       0xFF00
+struct scan_desc {
+       u16 size;
+       u16 rssi;
+       u16 channel;
+       u16 beacon_period;
+       u16 capability;
+       u16 security;
+       u64 bssid;
+       u8  essid[32];
+       u8  rate[16];
+       u8  ext_rate[16];
+       u32 reserved1;
+       u32 reserved2;
+       u32 reserved3;
+       u32 reserved4;
+} __attribute__ ((packed));
+
+/* rssi_descriptor */
+struct rssi_desc {
+       u16 rssi; /* max rssi = 100 */
+} __attribute__ ((packed));
+
+extern unsigned long p_to_lp(long pa);
+extern int gelicw_setup_netdev(struct net_device *netdev, int wi);
+extern void gelicw_up(struct net_device *netdev);
+extern int gelicw_down(struct net_device *netdev);
+extern void gelicw_remove(struct net_device *netdev);
+extern void gelicw_interrupt(struct net_device *netdev, u64 status);
+extern int gelicw_is_associated(struct net_device *netdev);
+
+#endif //_GELIC_NET_H
diff --git a/drivers/net/gelic_wireless.c b/drivers/net/gelic_wireless.c
new file mode 100644 (file)
index 0000000..36f3e88
--- /dev/null
@@ -0,0 +1,2191 @@
+/*
+ * gelic_wireless.c: wireless extension for gelic_net
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+//#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/completion.h>
+#include <linux/ctype.h>
+#include <asm/ps3.h>
+#include <asm/lv1call.h>
+
+#include "gelic_net.h"
+
+MODULE_AUTHOR("SCE Inc.");
+MODULE_DESCRIPTION("Gelic Wireless Extension");
+MODULE_LICENSE("GPL");
+
+static struct iw_handler_def gelicw_handler_def;
+
+/*
+ * Data tables
+ */
+static const u32 freq_list[] = {
+       2412, 2417, 2422, 2427, 2432,
+       2437, 2442, 2447, 2452, 2457,
+       2462, 2467, 2472, 2484 };
+
+static const u32 bitrate_list[] = {
+        1000000,  2000000,  5500000, 11000000, /* 11b */
+        6000000,  9000000, 12000000, 18000000,
+       24000000, 36000000, 48000000, 54000000
+};
+#define GELICW_NUM_11B_BITRATES 4 /* 802.11b: 1 ~ 11 Mb/s */
+
+static inline struct device * ntodev(struct net_device *netdev)
+{
+       return &((struct gelic_net_card *)netdev_priv(netdev))->dev->core;
+}
+
+static inline struct device * wtodev(struct gelic_wireless *w)
+{
+       return &w->card->dev->core;
+}
+static inline struct gelic_wireless *gelicw_priv(struct net_device *netdev)
+{
+       struct gelic_net_card *card = netdev_priv(netdev);
+       return &card->w;
+}
+static inline unsigned int bus_id(struct gelic_wireless *w)
+{
+       return w->card->dev->did.bus_id;
+}
+static inline unsigned int dev_id(struct gelic_wireless *w)
+{
+       return w->card->dev->did.dev_id;
+}
+
+/* control wired or wireless */
+static void gelicw_vlan_mode(struct net_device *netdev, int mode)
+{
+       struct gelic_net_card *card = netdev_priv(netdev);
+
+       if ((mode < GELIC_NET_VLAN_WIRED)
+               || (mode > GELIC_NET_VLAN_WIRELESS))
+               return;
+
+       card->vlan_index = mode - 1;
+}
+
+/* wireless_send_event */
+static void notify_assoc_event(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       union iwreq_data wrqu;
+
+       cancel_delayed_work(&w->work_scan_all);
+       cancel_delayed_work(&w->work_scan_essid);
+
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       if (w->state < GELICW_STATE_ASSOCIATED) {
+               dev_dbg(ntodev(netdev), "notify disassociated\n");
+               w->is_assoc = 0;
+               memset(w->bssid, 0, ETH_ALEN);
+       } else {
+               dev_dbg(ntodev(netdev), "notify associated\n");
+               w->channel = w->current_bss.channel;
+               w->is_assoc = 1;
+               memcpy(w->bssid, w->current_bss.bssid, ETH_ALEN);
+       }
+       memcpy(wrqu.ap_addr.sa_data, w->bssid, ETH_ALEN);
+       wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL);
+}
+
+/* association/disassociation */
+static void gelicw_assoc(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (w->state == GELICW_STATE_ASSOCIATED)
+               return;
+       schedule_delayed_work(&w->work_start, 0);
+}
+
+static int gelicw_disassoc(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       memset(w->bssid, 0, ETH_ALEN); /* clear current bssid */
+       if (w->state < GELICW_STATE_ASSOCIATED)
+               return 0;
+
+       schedule_delayed_work(&w->work_stop, 0);
+       w->state = GELICW_STATE_SCAN_DONE;
+       /* notify disassociation */
+       notify_assoc_event(netdev);
+
+       return 0;
+}
+
+static void gelicw_reassoc(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (w->cmd_send_flg != GELICW_CMD_SEND_ALL)
+               return;
+
+       if (!gelicw_disassoc(netdev))
+               gelicw_assoc(netdev);
+}
+
+
+/*
+ * lv1_net_control command
+ */
+/* control Ether port */
+static int gelicw_cmd_set_port(struct net_device *netdev, int mode)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 tag, val;
+       int status;
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_PORT, GELICW_ETHER_PORT, mode, 0,
+                       &tag, &val);
+       if (status)
+               dev_dbg(ntodev(netdev), "GELICW_SET_PORT failed:%d\n", status);
+       return status;
+}
+
+/* check support channels */
+static int gelicw_cmd_get_ch_info(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 ch_info, val;
+       int status;
+
+       if (w->state < GELICW_STATE_UP)
+               return -EIO;
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_INFO, 0, 0 , 0,
+                       &ch_info, &val);
+       if (status) {
+               dev_dbg(ntodev(netdev), "GELICW_GET_INFO failed:%d\n", status);
+               w->ch_info = CH_INFO_FAIL;
+       } else {
+               dev_dbg(ntodev(netdev), "ch_info:%lx val:%lx\n", ch_info, val);
+               w->ch_info = ch_info >> 48; /* MSB 16bit shows supported channnels */
+       }
+       return status;
+}
+
+
+/*
+ * lv1_net_control GELICW_SET_CMD command
+ * queued using schedule_work()
+ */
+/* association */
+static void gelicw_cmd_start(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 val;
+       int status;
+
+       if (w->state < GELICW_STATE_SCAN_DONE)
+               return;
+
+       dev_dbg(ntodev(netdev), "GELICW_CMD_START\n");
+       w->cmd_id = GELICW_CMD_START;
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_CMD, w->cmd_id, 0, 0,
+                       &w->cmd_tag, &val);
+       if (status) {
+               w->cmd_tag = 0;
+               dev_dbg(ntodev(netdev), "GELICW_CMD_START failed:%d\n", status);
+       }
+}
+
+/* association done */
+static void gelicw_cmd_start_done(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 res, val;
+       int status;
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_RES, w->cmd_tag, 0, 0,
+                       &res, &val);
+       w->cmd_tag = 0;
+       wake_up_interruptible(&w->waitq_cmd);
+
+       if (status || res)
+               dev_dbg(ntodev(netdev), "GELICW_CMD_START res:%d,%ld\n", status, res);
+}
+
+/* disassociation */
+static void gelicw_cmd_stop(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 res, val;
+       int status;
+
+       if (w->state < GELICW_STATE_SCAN_DONE)
+               return;
+
+       dev_dbg(ntodev(netdev), "GELICW_CMD_STOP\n");
+       init_completion(&w->cmd_done);
+       w->cmd_id = GELICW_CMD_STOP;
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_CMD, w->cmd_id, 0, 0,
+                       &w->cmd_tag, &val);
+       if (status) {
+               w->cmd_tag = 0;
+               dev_dbg(ntodev(netdev), "GELICW_CMD_STOP failed:%d\n", status);
+               return;
+       }
+       wait_for_completion_interruptible(&w->cmd_done);
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_RES, w->cmd_tag, 0, 0,
+                       &res, &val);
+       w->cmd_tag = 0;
+       if (status || res) {
+               dev_dbg(ntodev(netdev), "GELICW_CMD_STOP res:%d,%ld\n", status, res);
+               return;
+       }
+}
+
+/* get rssi */
+static void gelicw_cmd_rssi(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 lpar, res, val;
+       int status;
+       struct rssi_desc *rssi;
+
+       if (w->state < GELICW_STATE_ASSOCIATED) {
+               w->rssi = 0;
+               complete(&w->rssi_done);
+               return;
+       }
+
+       lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+       init_completion(&w->cmd_done);
+       w->cmd_id = GELICW_CMD_GET_RSSI;
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_CMD, w->cmd_id, 0, 0,
+                       &w->cmd_tag, &val);
+       if (status) {
+               w->cmd_tag = 0;
+               w->rssi = 0;
+               dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI failed:%d\n", status);
+       } else {
+               wait_for_completion_interruptible(&w->cmd_done);
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_RES, w->cmd_tag, lpar, sizeof(*rssi),
+                               &res, &val);
+               w->cmd_tag = 0;
+               if (status || res) {
+                       w->rssi = 0;
+                       dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI res:%d,%ld\n", status, res);
+               }
+               rssi = (struct rssi_desc *)w->data_buf;
+               w->rssi = rssi->rssi;
+               dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI:%d\n", rssi->rssi);
+       }
+
+       complete(&w->rssi_done);
+}
+
+/* set common configuration */
+static int gelicw_cmd_common(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 lpar, res, val;
+       int status;
+       struct common_config *config;
+
+       if (w->state < GELICW_STATE_SCAN_DONE)
+               return -EIO;
+
+       lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+       config = (struct common_config *)w->data_buf;
+       config->scan_index = w->bss_index;
+       config->bss_type = (w->iw_mode == IW_MODE_ADHOC) ? \
+                               GELICW_BSS_ADHOC : GELICW_BSS_INFRA;
+       config->auth_method = (w->auth_mode == IW_AUTH_ALG_SHARED_KEY) ? \
+                               GELICW_AUTH_SHARED : GELICW_AUTH_OPEN;
+       switch (w->wireless_mode) {
+       case IEEE_B:
+               config->op_mode = GELICW_OP_MODE_11B;
+               break;
+       case IEEE_G:
+               config->op_mode = GELICW_OP_MODE_11G;
+               break;
+       case IEEE_B | IEEE_G:
+       default:
+               /* default 11bg mode */
+               config->op_mode = GELICW_OP_MODE_11BG;
+               break;
+       }
+
+       dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG: index:%d type:%d auth:%d mode:%d\n",\
+               config->scan_index, config->bss_type,\
+               config->auth_method,config->op_mode);
+       init_completion(&w->cmd_done);
+       w->cmd_id = GELICW_CMD_SET_CONFIG;
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_CMD, w->cmd_id,
+                       lpar, sizeof(struct common_config),
+                       &w->cmd_tag, &val);
+       if (status) {
+               w->cmd_tag = 0;
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG failed:%d\n", status);
+               return status;
+       }
+       wait_for_completion_interruptible(&w->cmd_done);
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_RES, w->cmd_tag, 0, 0,
+                       &res, &val);
+       w->cmd_tag = 0;
+       if (status || res) {
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG res:%d,%ld\n", status, res);
+               return -EFAULT;
+       }
+
+       w->cmd_send_flg |= GELICW_CMD_SEND_COMMON;
+
+       return 0;
+}
+
+#define h2i(c)    (isdigit(c) ? c - '0' : toupper(c) - 'A' + 10)
+/* send WEP/WPA configuration */
+static int gelicw_cmd_encode(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 res, val, lpar;
+       int status;
+       struct wep_config *config;
+       struct wpa_config *wpa_config;
+
+       u8 *key, key_len;
+
+       if (w->state < GELICW_STATE_SCAN_DONE)
+               return -EIO;
+
+       lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+
+       if (w->key_alg == IW_ENCODE_ALG_WEP
+               || w->key_alg == IW_ENCODE_ALG_NONE) {
+               /* WEP */
+               config = (struct wep_config *)w->data_buf;
+               memset(config, 0, sizeof(struct wep_config));
+
+               /* check key len */
+               key_len = w->key_len[w->key_index];
+               key = w->key[w->key_index];
+               if (w->key_alg == IW_ENCODE_ALG_NONE)
+                       config->sec = GELICW_WEP_SEC_NONE;
+               else
+                       config->sec = (key_len == 5) ? GELICW_WEP_SEC_40BIT :
+                                                       GELICW_WEP_SEC_104BIT;
+               /* copy key */
+               memcpy(config->key[w->key_index], key, key_len);
+
+               /* send wep config */
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP\n");
+               init_completion(&w->cmd_done);
+               w->cmd_id = GELICW_CMD_SET_WEP;
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_CMD, w->cmd_id,
+                       lpar, sizeof(struct wep_config),
+                       &w->cmd_tag, &val);
+               if (status) {
+                       w->cmd_tag = 0;
+                       dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP failed:%d\n", status);
+                       goto err;
+               }
+               wait_for_completion_interruptible(&w->cmd_done);
+
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_GET_RES, w->cmd_tag, 0, 0,
+                               &res, &val);
+               w->cmd_tag = 0;
+               if (status || res) {
+                       dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP res:%d,%ld\n", status, res);
+                       status = -EFAULT;
+                       goto err;
+               }
+       } else {
+               /* WPA */
+               wpa_config = (struct wpa_config *)w->data_buf;
+               memset(wpa_config, 0, sizeof(struct wpa_config));
+
+               switch (w->key_alg) {
+               case IW_ENCODE_ALG_TKIP:
+                       wpa_config->sec = GELICW_WPA_SEC_TKIP;
+                       break;
+               default:
+               case IW_ENCODE_ALG_CCMP:
+                       wpa_config->sec = GELICW_WPA_SEC_AES;
+                       break;
+               }
+               /* check key len */
+               key = w->key[w->key_index];
+               key_len = w->key_len[w->key_index];
+
+               if (key_len > 64) key_len = 64;
+               if (key_len != 64) {
+                       /* if key_len isn't 64byte ,it should be passphrase */
+                       /* pass phrase */
+                       memcpy(wpa_config->psk_material, key, key_len);
+                       wpa_config->psk_type = GELICW_PSK_PASSPHRASE;
+               } else {
+                       int i;
+                       /* 64 hex */
+                       for (i = 0; i < 32; i++)
+                               wpa_config->psk_material[i] =
+                                               h2i(key[ 2 * i]) * 16
+                                               + h2i(key[2 * i + 1]);
+                       wpa_config->psk_type = GELICW_PSK_64HEX;
+               }
+
+               /* send wpa config */
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA:type:%d\n", wpa_config->psk_type);
+               init_completion(&w->cmd_done);
+               w->cmd_id = GELICW_CMD_SET_WPA;
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_SET_CMD, w->cmd_id,
+                               lpar, sizeof(struct wpa_config),
+                               &w->cmd_tag, &val);
+               if (status) {
+                       w->cmd_tag = 0;
+                       dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA failed:%d\n", status);
+                       goto err;
+               }
+               wait_for_completion_interruptible(&w->cmd_done);
+
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_GET_RES, w->cmd_tag, 0, 0, &res, &val);
+               w->cmd_tag = 0;
+               if (status || res) {
+                       dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA res:%d,%ld\n", status, res);
+                       status = -EFAULT;
+                       goto err;
+               }
+       }
+
+       w->cmd_send_flg |= GELICW_CMD_SEND_ENCODE;
+       /* (re)associate */
+       gelicw_reassoc(netdev);
+
+       return 0;
+err:
+       gelicw_disassoc(netdev);
+       return status;
+}
+
+static int gelicw_is_ap_11b(struct gelicw_bss *list)
+{
+       if (list->rates_len + list->rates_ex_len == GELICW_NUM_11B_BITRATES)
+               return 1;
+       else
+               return 0;
+}
+
+/* get scan results */
+static int gelicw_cmd_get_scan(struct gelic_wireless *w)
+{
+       u64 lpar, res, val;
+       int status;
+       struct scan_desc *desc;
+       int i, j;
+       u8 *p;
+       struct gelicw_bss *bss;
+
+       lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+
+       /* get scan */
+       dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN\n");
+       init_completion(&w->cmd_done);
+       w->cmd_id = GELICW_CMD_GET_SCAN;
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_SET_CMD, w->cmd_id, 0, 0,
+                       &w->cmd_tag, &val);
+       if (status) {
+               w->cmd_tag = 0;
+               dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN failed:%d\n", status);
+               return status;
+       }
+       wait_for_completion_interruptible(&w->cmd_done);
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_RES, w->cmd_tag, lpar, GELICW_DATA_BUF_SIZE,
+                       &res, &val);
+       w->cmd_tag = 0;
+       if (status || res) {
+               dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN res:%d,%ld\n", status, res);
+               return -EFAULT;
+       }
+
+       desc = (struct scan_desc *)w->data_buf;
+       for (i = 0; i < val/ sizeof(struct scan_desc) ; i++) {
+               bss = &w->bss_list[i];
+               bss->rates_len = 0;
+               for (j = 0; j < MAX_RATES_LENGTH; j++)
+                       if (desc[i].rate[j])
+                               bss->rates[bss->rates_len++] = desc[i].rate[j];
+               bss->rates_ex_len = 0;
+               for (j = 0; j < MAX_RATES_EX_LENGTH; j++)
+                       if (desc[i].ext_rate[j])
+                               bss->rates_ex[bss->rates_ex_len++]
+                                               = desc[i].ext_rate[j];
+
+               if (desc[i].capability & 0x3) {
+                       if (desc[i].capability & 0x1)
+                               bss->mode = IW_MODE_INFRA;
+                       else
+                               bss->mode = IW_MODE_ADHOC;
+               }
+               bss->channel = desc[i].channel;
+               bss->essid_len = strnlen(desc[i].essid, IW_ESSID_MAX_SIZE);
+               bss->rssi = (u8)desc[i].rssi;
+               bss->capability = desc[i].capability;
+               bss->beacon_interval = desc[i].beacon_period;
+               memcpy(bss->essid, desc[i].essid, bss->essid_len);
+               p = (u8 *)&desc[i].bssid;
+               memcpy(bss->bssid, &p[2], ETH_ALEN);/* bssid:64bit in desc */
+               bss->sec_info = desc[i].security;
+       }
+       w->num_bss_list = i;
+
+       if (!i)
+               return -1; /* no ap found */
+       else
+               return 0; /* ap found */
+}
+
+/* search bssid in bss list */
+static int gelicw_search_bss_list(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       int i;
+       int check_bss = 0, check_11g = 0, found_bss, found_11g;
+
+       if (!w->num_bss_list)
+               return -1;      /* no bss list */
+
+       if (memcmp(off, w->wap_bssid, ETH_ALEN))
+               check_bss = 1;
+
+       /* wireless op_mode seems not working with CMD_SET_CONFIG */
+       if (w->wireless_mode == IEEE_G)
+               check_11g = 1;
+
+       if (!check_bss && !check_11g)
+               return 0;       /* no check bssid, wmode */
+
+       for (i = 0; i < w->num_bss_list; i++) {
+               found_bss = found_11g = 1;
+               if (check_bss &&
+                   memcmp(w->bss_list[i].bssid, w->wap_bssid, ETH_ALEN))
+                       found_bss = 0; /* not found */
+
+               if (check_11g &&
+                   gelicw_is_ap_11b(&w->bss_list[i]))
+                       found_11g = 0; /* not found */
+
+               if (found_bss && found_11g)
+                       break;
+       }
+
+       if (i == w->num_bss_list)
+               return -1; /* not found */
+       else
+               return i;
+}
+
+/* scan done */
+static void gelicw_scan_complete(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       int res;
+       int bss_index;
+
+       /* get scan results */
+       res = gelicw_cmd_get_scan(w);
+       if (w->is_assoc)
+               w->state = GELICW_STATE_ASSOCIATED;
+       else
+               w->state = GELICW_STATE_SCAN_DONE;
+
+       if (res) {
+               /* No AP found */
+               if (!w->scan_all) {
+                       /* no specified AP */
+                       gelicw_disassoc(netdev);
+                       /* rescan */
+                       if (w->essid_search && w->essid_len)
+                               schedule_delayed_work(&w->work_scan_essid,
+                                                       GELICW_SCAN_INTERVAL);
+                       return;
+               }
+       }
+
+       if (w->scan_all) {
+               /* all params should be set again after scan */
+               w->cmd_send_flg = 0;
+               return;
+       }
+
+       bss_index = gelicw_search_bss_list(netdev);
+       if (bss_index < 0) {
+               /* no wap_bssid in bss_list */
+               if (w->essid_search && w->essid_len)
+                       schedule_delayed_work(&w->work_scan_essid,
+                                               GELICW_SCAN_INTERVAL);
+               return;
+       }
+       w->bss_index = (u8)bss_index;
+       w->current_bss = w->bss_list[w->bss_index];
+
+       /* essid search complete */
+       w->essid_search = 0;
+       w->cmd_send_flg |= GELICW_CMD_SEND_SCAN;
+
+       /* (re)connect to AP */
+       if (w->is_assoc) {
+               /* notify disassociation */
+               w->state = GELICW_STATE_SCAN_DONE;
+               notify_assoc_event(netdev);
+       }
+       schedule_delayed_work(&w->work_common, 0);
+       schedule_delayed_work(&w->work_encode, 0);
+}
+
+/* start scan */
+static int gelicw_cmd_set_scan(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 res, val, lpar;
+       int status;
+       u8 *p;
+
+       if (w->state < GELICW_STATE_UP) {
+               w->scan_all = 0;
+               return -EIO;
+       }
+       if (!w->scan_all && !w->essid_len)
+               return -EINVAL; /* unsupported essid ANY */
+
+       /* set device wired to wireless when essid is set */
+       if (!w->scan_all && w->wireless < GELICW_WIRELESS_ON) {
+               gelicw_vlan_mode(netdev, GELIC_NET_VLAN_WIRELESS);
+               gelicw_cmd_set_port(netdev, GELICW_PORT_DOWN);
+               w->wireless = GELICW_WIRELESS_ON;
+       }
+
+       p = (u8 *)w->data_buf;
+       lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+
+       /* avoid frequent scanning */
+       if (!w->essid_search /* background scan off */
+         && w->scan_all
+         && (jiffies - w->last_scan) < (5 * HZ)) {
+               return 0;
+       }
+
+       w->bss_key_alg = IW_ENCODE_ALG_NONE;
+
+       init_completion(&w->cmd_done);
+       w->state = GELICW_STATE_SCANNING;
+       w->cmd_id = GELICW_CMD_SCAN;
+
+       if (w->scan_all) {
+               /* scan all ch */
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN all\n");
+               w->last_scan = jiffies; /* last scan time */
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_SET_CMD, w->cmd_id, 0, 0,
+                               &w->cmd_tag, &val);
+       } else {
+               /* scan essid */
+               memset(p, 0, 32);
+               memcpy(p, w->essid, w->essid_len);
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN essid\n");
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_SET_CMD, w->cmd_id, lpar, 32,
+                               &w->cmd_tag, &val);
+       }
+
+       if (status) {
+               w->cmd_tag = 0;
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN failed:%d\n", status);
+               return status;
+       }
+       wait_for_completion_interruptible(&w->cmd_done);
+
+       status = lv1_net_control(bus_id(w), dev_id(w),
+                       GELICW_GET_RES, w->cmd_tag, 0, 0,
+                       &res, &val);
+       w->cmd_tag = 0;
+       if (status || res) {
+               dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN res:%d,%ld\n", status, res);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static void gelicw_send_common_config(struct net_device *netdev,
+                                       u8 *cur, u8 mode)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (*cur != mode) {
+               *cur = mode;
+               if (w->state < GELICW_STATE_SCAN_DONE)
+                       return;
+
+               if (!(w->cmd_send_flg & GELICW_CMD_SEND_SCAN)
+                   && w->essid_len)
+                       /* scan essid and set other params */
+                       schedule_delayed_work(&w->work_scan_essid, 0);
+               else {
+                       schedule_delayed_work(&w->work_common, 0);
+                       if (w->cmd_send_flg
+                           & GELICW_CMD_SEND_ENCODE)
+                               /* (re)send encode key */
+                               schedule_delayed_work(&w->work_encode, 0);
+               }
+       }
+}
+
+
+/*
+ * work queue
+ */
+static void gelicw_work_rssi(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_rssi.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag) {
+               schedule_delayed_work(&w->work_rssi, HZ / 5);
+               return;
+       }
+
+       gelicw_cmd_rssi(netdev);
+}
+
+static void gelicw_work_scan_all(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_scan_all.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag || w->state == GELICW_STATE_SCANNING) {
+               schedule_delayed_work(&w->work_scan_all, HZ / 5);
+               return;
+       }
+
+       w->scan_all = 1;
+       gelicw_cmd_set_scan(netdev);
+}
+
+static void gelicw_work_scan_essid(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_scan_essid.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag || w->scan_all || w->state == GELICW_STATE_SCANNING) {
+               schedule_delayed_work(&w->work_scan_essid, HZ / 5);
+               return;
+       }
+       w->bss_index = 0;
+       w->scan_all = 0;
+       gelicw_cmd_set_scan(netdev);
+}
+
+static void gelicw_work_common(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_common.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag) {
+               schedule_delayed_work(&w->work_common, HZ / 5);
+               return;
+       }
+       gelicw_cmd_common(netdev);
+}
+
+static void gelicw_work_encode(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_encode.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag) {
+               schedule_delayed_work(&w->work_encode, HZ / 5);
+               return;
+       }
+       gelicw_cmd_encode(netdev);
+}
+
+static void gelicw_work_start(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_start.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag) {
+               schedule_delayed_work(&w->work_start, HZ / 5);
+               return;
+       }
+       gelicw_cmd_start(netdev);
+}
+
+static void gelicw_work_start_done(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_start_done);
+       struct net_device *netdev = w->card->netdev;
+
+       gelicw_cmd_start_done(netdev);
+}
+
+static void gelicw_work_stop(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_stop.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag) {
+               schedule_delayed_work(&w->work_stop, HZ / 5);
+               return;
+       }
+       gelicw_cmd_stop(netdev);
+}
+
+static void gelicw_work_roam(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_roam.work);
+       struct net_device *netdev = w->card->netdev;
+
+       if (w->cmd_tag || w->scan_all || w->state == GELICW_STATE_SCANNING) {
+               schedule_delayed_work(&w->work_roam, HZ / 5);
+               return;
+       }
+       gelicw_cmd_stop(netdev);
+       w->bss_index = 0;
+       w->scan_all = 0;
+       gelicw_cmd_set_scan(netdev);
+}
+
+/*
+ * Event handler
+ */
+#define GELICW_EVENT_LOOP_MAX 16
+static void gelicw_event(struct work_struct *work)
+{
+       struct gelic_wireless *w =
+               container_of(work, struct gelic_wireless, work_event);
+       struct net_device *netdev = w->card->netdev;
+       u64 event_type, val;
+       int i, status;
+
+       for (i = 0; i < GELICW_EVENT_LOOP_MAX; i++) {
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_GET_EVENT, 0, 0 , 0,
+                               &event_type, &val);
+               if (status == GELICW_EVENT_NO_ENTRY) {
+                       /* got all events */
+                       break;
+               } else if (status){
+                       dev_dbg(ntodev(netdev), "GELICW_GET_EVENT failed:%d\n", status);
+                       return;
+               }
+               switch(event_type) {
+               case GELICW_EVENT_DEVICE_READY:
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_DEVICE_READY\n");
+                       break;
+               case GELICW_EVENT_SCAN_COMPLETED:
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_SCAN_COMPLETED\n");
+                       gelicw_scan_complete(netdev);
+                       break;
+               case GELICW_EVENT_BEACON_LOST:
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_BEACON_LOST\n");
+                       w->state = GELICW_STATE_SCAN_DONE;
+                       notify_assoc_event(netdev);
+                       /* roaming */
+                       w->essid_search = 1;
+                       schedule_delayed_work(&w->work_roam, 0);
+                       break;
+               case GELICW_EVENT_CONNECTED:
+               {
+                       u16 ap_sec;
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_CONNECTED\n");
+                       /* this event ocuured with any key_alg */
+                       ap_sec = w->current_bss.sec_info;
+                       if (w->key_alg == IW_ENCODE_ALG_NONE) {
+                               /* no encryption */
+                               if (ap_sec == 0) {
+                                       w->state = GELICW_STATE_ASSOCIATED;
+                                       notify_assoc_event(netdev);
+                               }
+                       } else if (w->key_alg == IW_ENCODE_ALG_WEP){
+                               if ((ap_sec & GELICW_SEC_TYPE_WEP_MASK)
+                                   == GELICW_SEC_TYPE_WEP) {
+                                       /* wep */
+                                       w->state = GELICW_STATE_ASSOCIATED;
+                                       notify_assoc_event(netdev);
+                               }
+                       }
+                       break;
+               }
+               case GELICW_EVENT_WPA_CONNECTED:
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_WPA_CONNECTED\n");
+                       w->state = GELICW_STATE_ASSOCIATED;
+                       notify_assoc_event(netdev);
+                       break;
+               case GELICW_EVENT_WPA_ERROR:
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_WPA_ERROR\n");
+                       break;
+               default:
+                       dev_dbg(ntodev(netdev), "  GELICW_EVENT_UNKNOWN\n");
+                       break;
+               }
+       }
+}
+
+static void gelicw_clear_event(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u64 event_type, val;
+       int i, status;
+
+       for (i = 0; i < GELICW_EVENT_LOOP_MAX; i++) {
+               status = lv1_net_control(bus_id(w), dev_id(w),
+                               GELICW_GET_EVENT, 0, 0 , 0,
+                               &event_type, &val);
+               if (status) {
+                       /* got all events */
+                       return;
+               }
+               switch(event_type) {
+               case GELICW_EVENT_SCAN_COMPLETED:
+                       w->state = GELICW_STATE_SCAN_DONE;
+                       wake_up_interruptible(&w->waitq_scan);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+/*
+ * gelic_net support function
+ */
+static void gelicw_clear_params(struct gelic_wireless *w)
+{
+       int i;
+
+       /* clear status */
+       w->state = GELICW_STATE_DOWN;
+       w->cmd_send_flg = 0;
+       w->scan_all = 0;
+       w->is_assoc = 0;
+       w->essid_search = 0;
+       w->cmd_tag = 0;
+       w->cmd_id = 0;
+       w->last_scan = 0;
+
+       /* default mode and settings */
+       w->essid_len = 0;
+       w->essid[0] = '\0';
+       w->nick[0] = '\0';
+       w->iw_mode = IW_MODE_INFRA;
+       w->auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+       w->wireless_mode = IEEE_B | IEEE_G;
+       w->bss_index = 0;
+       memset(w->bssid, 0, ETH_ALEN);
+       memset(w->wap_bssid, 0, ETH_ALEN);
+
+       /* init key */
+       w->key_index = 0;
+       for (i = 0; i < WEP_KEYS; i++) {
+               w->key[i][0] = '\0';
+               w->key_len[i] = 0;
+       }
+       w->key_alg = IW_ENCODE_ALG_NONE;
+       w->bss_key_alg = IW_ENCODE_ALG_NONE;
+}
+
+int gelicw_setup_netdev(struct net_device *netdev, int wi)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (wi < 0) {
+               /* PS3 low model has no wireless */
+               dev_dbg(ntodev(netdev), "No wireless dvice in this system\n");
+               w->wireless = 0;
+               return 0;
+       }
+       w->data_buf = kmalloc(GELICW_DATA_BUF_SIZE, GFP_KERNEL);
+       if (!w->data_buf) {
+               w->wireless = 0;
+               dev_info(ntodev(netdev), "%s:kmalloc failed\n", __func__);
+               return -ENOMEM;
+       }
+       w->wireless = GELICW_WIRELESS_SUPPORTED; /* wireless support */
+
+       w->ch_info = 0;
+       w->channel = 0;
+       netdev->wireless_data = &w->wireless_data;
+       netdev->wireless_handlers = &gelicw_handler_def;
+       INIT_WORK(&w->work_event, gelicw_event);
+       INIT_WORK(&w->work_start_done, gelicw_work_start_done);
+       INIT_DELAYED_WORK(&w->work_rssi, gelicw_work_rssi);
+       INIT_DELAYED_WORK(&w->work_scan_all, gelicw_work_scan_all);
+       INIT_DELAYED_WORK(&w->work_scan_essid, gelicw_work_scan_essid);
+       INIT_DELAYED_WORK(&w->work_common, gelicw_work_common);
+       INIT_DELAYED_WORK(&w->work_encode, gelicw_work_encode);
+       INIT_DELAYED_WORK(&w->work_start, gelicw_work_start);
+       INIT_DELAYED_WORK(&w->work_stop, gelicw_work_stop);
+       INIT_DELAYED_WORK(&w->work_roam, gelicw_work_roam);
+       init_waitqueue_head(&w->waitq_cmd);
+       init_waitqueue_head(&w->waitq_scan);
+
+       gelicw_clear_params(w);
+
+       return 0;
+}
+
+void gelicw_up(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (!w->wireless)
+               return;
+
+       dev_dbg(ntodev(netdev), "gelicw_up\n");
+       if (w->state < GELICW_STATE_UP)
+               w->state = GELICW_STATE_UP;
+
+       /* start essid scanning */
+       if (w->essid_len)
+               schedule_delayed_work(&w->work_scan_essid, 0);
+}
+
+int gelicw_down(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (!w->wireless || w->state == GELICW_STATE_DOWN)
+               return 0;
+
+       dev_dbg(ntodev(netdev), "gelicw_down\n");
+       w->wireless = GELICW_WIRELESS_SHUTDOWN;
+       flush_scheduled_work();
+
+       /* check cmd_tag of CMD_START */
+       if (w->cmd_id == GELICW_CMD_START) {
+               wait_event_interruptible(w->waitq_cmd, !w->cmd_tag);
+       }
+       /* wait scan done */
+       if (w->state == GELICW_STATE_SCANNING) {
+               wait_event_interruptible(w->waitq_scan,
+                                       w->state != GELICW_STATE_SCANNING);
+               gelicw_cmd_get_scan(w);
+       }
+
+       gelicw_cmd_stop(netdev);
+       if (w->is_assoc) {
+               w->state = GELICW_STATE_DOWN;
+               notify_assoc_event(netdev);
+       }
+       gelicw_clear_params(w);
+
+       /* set device wireless to wired */
+       gelicw_vlan_mode(netdev, GELIC_NET_VLAN_WIRED);
+       gelicw_cmd_set_port(netdev, GELICW_PORT_UP);
+       w->wireless = GELICW_WIRELESS_SUPPORTED;
+
+       return 0;
+}
+
+void gelicw_remove(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (!w->wireless)
+               return;
+
+       dev_dbg(ntodev(netdev), "gelicw_remove\n");
+       gelicw_down(netdev);
+       w->wireless = 0;
+       netdev->wireless_handlers = NULL;
+       kfree(w->data_buf);
+}
+
+void gelicw_interrupt(struct net_device *netdev, u64 status)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (!w->wireless) {
+               return;
+       }
+
+       if (status & GELICW_DEVICE_CMD_COMP) {
+               dev_dbg(ntodev(netdev), "GELICW_DEVICE_CMD_COMP\n");
+               if (w->cmd_id == GELICW_CMD_START) {
+                       schedule_work(&w->work_start_done);
+               } else {
+                       complete(&w->cmd_done);
+               }
+       }
+       if (status & GELICW_DEVICE_EVENT_RECV) {
+               dev_dbg(ntodev(netdev), "GELICW_DEVICE_EVENT_RECV\n");
+               if (w->wireless == GELICW_WIRELESS_SHUTDOWN)
+                       gelicw_clear_event(netdev);
+               else
+                       schedule_work(&w->work_event);
+       }
+}
+
+int gelicw_is_associated(struct net_device *netdev)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       if (!w->wireless)
+               return 0;
+
+       return w->is_assoc;
+}
+
+
+/*
+ * Wireless externsions
+ */
+static int gelicw_get_name(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx: get_name\n");
+       if (w->state < GELICW_STATE_UP) {
+               strcpy(wrqu->name, "radio off");
+               return 0;
+       }
+
+       if (w->wireless_mode == IEEE_B ||
+               (w->is_assoc && gelicw_is_ap_11b(&w->current_bss)))
+               strcpy(wrqu->name, "IEEE 802.11b");
+       else {
+               switch (w->wireless_mode) {
+               case IEEE_G:
+                       strcpy(wrqu->name, "IEEE 802.11g");
+                       break;
+               case IEEE_B | IEEE_G:
+               default:
+                       strcpy(wrqu->name, "IEEE 802.11bg");
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int gelicw_set_freq(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_freq *fwrq = &wrqu->freq;
+       int ch;
+
+       dev_dbg(ntodev(netdev), "wx: set_freq e:%d m:%d\n", fwrq->e, fwrq->m);
+       if (w->is_assoc || w->state < GELICW_STATE_UP)
+               return 0;
+
+       /* this setting has no effect for INFRA mode */
+       if (fwrq->e == 1) {
+               u32 f = fwrq->m / 100000;
+               int i;
+               for (i = 0; i < ARRAY_SIZE(freq_list); i++)
+                       if (freq_list[i] == f)
+                               break;
+               if (i == ARRAY_SIZE(freq_list))
+                       return -EINVAL;
+               fwrq->m = i + 1; /* ch number */
+               fwrq->e = 0;
+       }
+       if (fwrq->e > 0)
+               return -EINVAL;
+
+       ch = fwrq->m;
+       if (ch < 1)
+               w->channel = 0; /* auto */
+       else if (ch > ARRAY_SIZE(freq_list))
+               return -EINVAL;
+       else {
+               /* check supported channnel */
+               if (!w->ch_info)
+                       gelicw_cmd_get_ch_info(netdev);
+               if (w->ch_info & (1 << (ch - 1)))
+                       w->channel = ch;
+               else
+                       return -EINVAL;
+       }
+       dev_dbg(ntodev(netdev), " set cnannel: %d\n", w->channel);
+
+       return 0;
+}
+
+static int gelicw_get_freq(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx: get_freq:%d\n", w->channel);
+       if (w->channel == 0)
+               wrqu->freq.m = 0;
+       else
+               wrqu->freq.m = freq_list[w->channel - 1] * 100000;
+       wrqu->freq.e = 1;
+
+       return 0;
+}
+
+static int gelicw_set_mode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       int mode = wrqu->mode;
+       u8 iw_mode = IW_MODE_INFRA;
+
+       dev_dbg(ntodev(netdev), "wx: set_mode:%x\n",mode);
+       switch (mode) {
+       case IW_MODE_ADHOC:
+               dev_dbg(ntodev(netdev), "IW_MODE_ADHOC\n");
+               iw_mode = mode;
+               return -EOPNOTSUPP; /* adhoc not supported */
+       case IW_MODE_INFRA:
+       default:
+               dev_dbg(ntodev(netdev), "IW_MODE_INFRA\n");
+               iw_mode = IW_MODE_INFRA;
+               break;
+       }
+
+       /* send common config */
+       gelicw_send_common_config(netdev, &w->iw_mode, iw_mode);
+
+       return 0;
+}
+
+static int gelicw_get_mode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx: get_mode\n");
+       wrqu->mode = w->iw_mode;
+
+       return 0;
+}
+
+static inline int gelicw_qual2level(int qual)
+{
+       return (qual * 4 - 820)/10; /* FIXME: dummy */
+}
+
+static int gelicw_get_range(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_range *range = (struct iw_range *)extra;
+       int num_ch, i;
+
+       dev_dbg(ntodev(netdev), "wx: get_range\n");
+       wrqu->data.length = sizeof(*range);
+       memset(range, 0, sizeof(*range));
+
+       /* wireless extension */
+       range->we_version_compiled = WIRELESS_EXT;
+       range->we_version_source = 19;
+
+       /* supported bitrates */
+       if (w->wireless_mode == IEEE_B)
+               range->num_bitrates = GELICW_NUM_11B_BITRATES;
+       else
+               range->num_bitrates = ARRAY_SIZE(bitrate_list);
+       range->throughput = bitrate_list[range->num_bitrates -1] / 2; /* half */
+       for (i = 0; i < range->num_bitrates; i++)
+               range->bitrate[i] = bitrate_list[i];
+
+       range->max_qual.qual = 100; /* relative value */
+       range->max_qual.level = 0;
+       range->avg_qual.qual = 50;
+       range->avg_qual.level = 0;
+       range->sensitivity = 0;
+
+       /* encryption capabilities */
+       range->encoding_size[0] = 5;    /* 40bit WEP */
+       range->encoding_size[1] = 13;   /* 104bit WEP */
+       range->encoding_size[2] = 64;   /* WPA-PSK */
+       range->num_encoding_sizes = 3;
+       range->max_encoding_tokens = WEP_KEYS;
+       range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+                         IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+       /* freq */
+       if (!w->ch_info)
+               gelicw_cmd_get_ch_info(netdev); /* get supported freq */
+
+       num_ch = 0;
+       for (i = 0; i < ARRAY_SIZE(freq_list); i++)
+               if (w->ch_info & (1 << i)) {
+                       range->freq[num_ch].i = i + 1;
+                       range->freq[num_ch].m = freq_list[i] * 100000;
+                       range->freq[num_ch].e = 1;
+                       if (++num_ch == IW_MAX_FREQUENCIES)
+                               break;
+               }
+
+       range->num_channels = num_ch;
+       range->num_frequency = num_ch;
+
+       /* event capabilities */
+       range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+                               IW_EVENT_CAPA_MASK(SIOCGIWAP));
+       range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+       return 0;
+}
+
+static int gelicw_set_wap(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       static const u8 any[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       dev_dbg(ntodev(netdev), "wx: set_wap\n");
+       if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+               return -EINVAL;
+
+       if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN)
+           || !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+               if (!memcmp(off, w->wap_bssid, ETH_ALEN))
+                       return 0; /* ap off, no change */
+               else {
+                       memset(w->wap_bssid, 0, ETH_ALEN);
+                       /* start scan */
+               }
+       } else if (!memcmp(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+               /* no change */
+               return 0;
+       } else if (!memcmp(w->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)){
+               /* current bss */
+               memcpy(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+               return 0;
+       } else
+               memcpy(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+       /* start scan */
+       if (w->essid_len && w->state >= GELICW_STATE_SCAN_DONE) {
+               gelicw_disassoc(netdev);
+               /* scan essid */
+               cancel_delayed_work(&w->work_scan_all);
+               cancel_delayed_work(&w->work_scan_essid);
+               w->essid_search = 1;
+               schedule_delayed_work(&w->work_scan_essid, 0);
+       }
+
+       return 0;
+}
+
+static int gelicw_get_wap(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx: get_wap\n");
+       wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+       memcpy(wrqu->ap_addr.sa_data, w->bssid, ETH_ALEN);
+
+       return 0;
+}
+
+static int gelicw_set_scan(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx: set_scan\n");
+       if (w->state < GELICW_STATE_UP)
+               return -EIO;
+
+       /* cancel scan */
+       cancel_delayed_work(&w->work_scan_all);
+       cancel_delayed_work(&w->work_scan_essid);
+
+       schedule_delayed_work(&w->work_scan_all, 0);
+
+       return 0;
+}
+
+#define MAX_CUSTOM_LEN 64
+static char *gelicw_translate_scan(struct net_device *netdev,
+                               char *start, char *stop,
+                               struct gelicw_bss *list)
+{
+       char custom[MAX_CUSTOM_LEN];
+       struct iw_event iwe;
+       int i;
+       char *p, *current_val;
+
+       /* BSSID */
+       iwe.cmd = SIOCGIWAP;
+       iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+       memcpy(iwe.u.ap_addr.sa_data, list->bssid, ETH_ALEN);
+       start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
+
+       /* ESSID */
+       iwe.cmd = SIOCGIWESSID;
+       iwe.u.data.flags = 1;
+       iwe.u.data.length = list->essid_len;
+       start = iwe_stream_add_point(start, stop, &iwe, list->essid);
+
+       /* protocol name */
+       iwe.cmd = SIOCGIWNAME;
+       if (gelicw_is_ap_11b(list))
+               snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b");
+       else
+               snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg");
+       start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
+
+       /* MODE */
+       iwe.cmd = SIOCGIWMODE;
+       iwe.u.mode = list->mode;
+       start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
+
+       /* FREQ */
+       iwe.cmd = SIOCGIWFREQ;
+       iwe.u.freq.m = list->channel;
+       iwe.u.freq.e = 0;
+       iwe.u.freq.i = 0;
+       start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
+
+       /* ENCODE */
+       iwe.cmd = SIOCGIWENCODE;
+       if (list->capability & WLAN_CAPABILITY_PRIVACY)
+               iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+       else
+               iwe.u.data.flags = IW_ENCODE_DISABLED;
+       iwe.u.data.length = 0;
+       start = iwe_stream_add_point(start, stop, &iwe, list->essid);
+
+       /* QUAL */
+       iwe.cmd = IWEVQUAL;
+       iwe.u.qual.updated  = IW_QUAL_QUAL_UPDATED |
+                       IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+       iwe.u.qual.qual = list->rssi;
+       iwe.u.qual.level = gelicw_qual2level(list->rssi);
+       start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
+
+       /* RATE */
+       current_val = start + IW_EV_LCP_LEN;
+       iwe.cmd = SIOCGIWRATE;
+       iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+       for (i = 0; i < list->rates_len; i++) {
+               iwe.u.bitrate.value = ((list->rates[i] & 0x7f) * 500000);
+               current_val = iwe_stream_add_value(start, current_val, stop,
+                                       &iwe, IW_EV_PARAM_LEN);
+       }
+       for (i = 0; i < list->rates_ex_len; i++) {
+               iwe.u.bitrate.value = ((list->rates_ex[i] & 0x7f) * 500000);
+               current_val = iwe_stream_add_value(start, current_val, stop,
+                                       &iwe, IW_EV_PARAM_LEN);
+       }
+       if ((current_val - start) > IW_EV_LCP_LEN)
+               start = current_val;
+
+       /* Extra */
+       /* BEACON */
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       p = custom;
+       p += snprintf(p, MAX_CUSTOM_LEN, "bcn_int=%d", list->beacon_interval);
+       iwe.u.data.length = p - custom;
+       start = iwe_stream_add_point(start, stop, &iwe, custom);
+
+       /* AP security */
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       p = custom;
+       p += snprintf(p, MAX_CUSTOM_LEN, "ap_sec=%04X", list->sec_info);
+       iwe.u.data.length = p - custom;
+       start = iwe_stream_add_point(start, stop, &iwe, custom);
+
+       return start;
+}
+
+static int gelicw_get_scan(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       int i;
+       char *ev = extra;
+       char *stop = ev + wrqu->data.length;
+
+       dev_dbg(ntodev(netdev), "wx: get_scan \n");
+       switch (w->state) {
+       case GELICW_STATE_DOWN:
+       case GELICW_STATE_UP:
+               return 0; /* no scan results */
+       case GELICW_STATE_SCANNING:
+               return -EAGAIN; /* now scanning */
+       case GELICW_STATE_SCAN_DONE:
+               if (!w->scan_all) /* essid scan */
+                       return -EAGAIN;
+               break;
+       default:
+               break;
+       }
+
+       w->scan_all = 0;
+       for (i = 0; i < w->num_bss_list; i++)
+               ev = gelicw_translate_scan(netdev, ev, stop, &w->bss_list[i]);
+       wrqu->data.length = ev - extra;
+       wrqu->data.flags = 0;
+
+       /* start background scan */
+       if (w->essid_search)
+               schedule_delayed_work(&w->work_scan_essid,
+                                       GELICW_SCAN_INTERVAL);
+
+       return 0;
+}
+
+static int gelicw_set_essid(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u16 length = 0;
+
+       dev_dbg(ntodev(netdev), "wx:set_essid\n");
+       /* cancel scan */
+       w->essid_search = 0;
+       cancel_delayed_work(&w->work_scan_all);
+       cancel_delayed_work(&w->work_scan_essid);
+
+       if (wrqu->essid.flags && wrqu->essid.length)
+               length = wrqu->essid.length;
+
+       if (length == 0) {
+               /* essid ANY scan not supported */
+               dev_dbg(ntodev(netdev), "ESSID ANY\n");
+               w->essid_len = 0; /* clear essid */
+               w->essid[0] = '\0';
+               return 0;
+       } else {
+               /* check essid */
+               if (length > IW_ESSID_MAX_SIZE)
+                       return -EINVAL;
+               if (w->essid_len == length
+                   && !strncmp(w->essid, extra, length)) {
+                       /* same essid */
+                       if (w->is_assoc)
+                               return 0;
+               } else {
+                       /* set new essid */
+                       w->essid_len = length;
+                       memcpy(w->essid, extra, length);
+               }
+       }
+       /* start essid scan */
+       w->essid_search = 1;
+       schedule_delayed_work(&w->work_scan_essid, 0);
+
+       return 0;
+}
+
+static int gelicw_get_essid(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx:get_essid\n");
+       if (w->essid_len) {
+               memcpy(extra, w->essid, w->essid_len);
+               wrqu->essid.length = w->essid_len;
+               wrqu->essid.flags = 1;
+       } else {
+               wrqu->essid.length = 0;
+               wrqu->essid.flags = 0;
+       }
+
+       return 0;
+}
+
+static int gelicw_set_nick(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       u32 len = wrqu->data.length;
+
+       dev_dbg(ntodev(netdev), "wx:set_nick\n");
+       if (len > IW_ESSID_MAX_SIZE)
+               return -EINVAL;
+
+       memset(w->nick, 0, sizeof(w->nick));
+       memcpy(w->nick, extra, len);
+
+       return 0;
+}
+
+static int gelicw_get_nick(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx:get_nick\n");
+       wrqu->data.length = strlen(w->nick);
+       memcpy(extra, w->nick, wrqu->data.length);
+       wrqu->data.flags = 1;
+
+       return 0;
+}
+
+static int gelicw_set_rate(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       dev_dbg(ntodev(netdev), "wx:set_rate:%d\n", wrqu->bitrate.value);
+       if (wrqu->bitrate.value == -1)
+               return 0;       /* auto rate only */
+
+       return -EOPNOTSUPP;
+}
+
+static int gelicw_get_rate(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx:get_rate\n");
+
+       if (w->wireless_mode == IEEE_B ||
+               (w->is_assoc && gelicw_is_ap_11b(&w->current_bss)))
+               wrqu->bitrate.value = bitrate_list[GELICW_NUM_11B_BITRATES -1];
+       else
+               wrqu->bitrate.value = bitrate_list[ARRAY_SIZE(bitrate_list) -1];
+
+       wrqu->bitrate.fixed = 0;
+
+       return 0;
+}
+
+static int gelicw_set_encode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_point *enc = &wrqu->encoding;
+       int i, index, key_index;
+
+       dev_dbg(ntodev(netdev), "wx:set_encode: flags:%x\n", enc->flags );
+       index = enc->flags & IW_ENCODE_INDEX;
+       if (index < 0 || index > WEP_KEYS)
+               return -EINVAL;
+       index--;
+
+       if (enc->length > IW_ENCODING_TOKEN_MAX)
+               return -EINVAL;
+
+       if (index != -1)
+               w->key_index = index;
+       key_index = w->key_index;
+
+       if (enc->flags & IW_ENCODE_DISABLED) {
+               /* disable encryption */
+               if (index == -1) {
+                       /* disable all */
+                       w->key_alg = IW_ENCODE_ALG_NONE;
+                       for (i = 0; i < WEP_KEYS; i++)
+                               w->key_len[i] = 0;
+               } else
+                       w->key_len[key_index] = 0;
+       } else if (enc->flags & IW_ENCODE_NOKEY) {
+               /* key not changed */
+               if (w->key_alg == IW_ENCODE_ALG_NONE)
+                       w->key_alg = IW_ENCODE_ALG_WEP; /* default wep */
+       } else {
+               /* enable encryption */
+               w->key_len[key_index] = enc->length;
+               if (w->key_alg == IW_ENCODE_ALG_NONE)
+                       w->key_alg = IW_ENCODE_ALG_WEP; /* default wep */
+               memcpy(w->key[key_index], extra, w->key_len[key_index]);
+       }
+       dev_dbg(ntodev(netdev), "key %d len:%d alg:%x\n",\
+               key_index, w->key_len[key_index], w->key_alg);
+
+       if (w->state >= GELICW_STATE_SCAN_DONE
+               && w->cmd_send_flg == 0 && w->essid_len)
+               /* scan essid and set other params */
+               schedule_delayed_work(&w->work_scan_essid, 0);
+       else
+               schedule_delayed_work(&w->work_encode, 0);
+
+       return 0;
+}
+
+static int gelicw_get_encode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_point *enc = &wrqu->encoding;
+       int index, key_index;
+
+       dev_dbg(ntodev(netdev), "wx:get_encode\n");
+       index = enc->flags & IW_ENCODE_INDEX;
+       if (index < 0 || index > WEP_KEYS)
+               return -EINVAL;
+
+       index--;
+       key_index = (index == -1 ? w->key_index : index);
+       enc->flags = key_index + 1;
+
+       if (w->key_alg == IW_ENCODE_ALG_NONE || !w->key_len[key_index]) {
+               /* no encryption */
+               enc->flags |= IW_ENCODE_DISABLED;
+               enc->length = 0;
+       } else {
+               enc->flags |= IW_ENCODE_NOKEY;
+               enc->length = w->key_len[key_index];
+               memset(extra, 0, w->key_len[key_index]);
+       }
+
+       return 0;
+}
+
+static int gelicw_set_auth(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_param *param = &wrqu->param;
+       int value = param->value;
+       int ret = 0;
+
+       dev_dbg(ntodev(netdev), "wx:set_auth:%x\n", param->flags & IW_AUTH_INDEX);
+       switch(param->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_WPA_VERSION:
+       case IW_AUTH_CIPHER_PAIRWISE:
+       case IW_AUTH_CIPHER_GROUP:
+       case IW_AUTH_KEY_MGMT:
+       case IW_AUTH_TKIP_COUNTERMEASURES:
+       case IW_AUTH_DROP_UNENCRYPTED:
+       case IW_AUTH_WPA_ENABLED:
+       case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+       case IW_AUTH_ROAMING_CONTROL:
+       case IW_AUTH_PRIVACY_INVOKED:
+               /* ignore */
+               dev_dbg(ntodev(netdev), "IW_AUTH(%x)\n", param->flags & IW_AUTH_INDEX);
+               break;
+       case IW_AUTH_80211_AUTH_ALG:
+               dev_dbg(ntodev(netdev), "IW_AUTH_80211_AUTH_ALG:\n");
+               if (value & IW_AUTH_ALG_SHARED_KEY)
+                       w->auth_mode = IW_AUTH_ALG_SHARED_KEY;
+               else if (value & IW_AUTH_ALG_OPEN_SYSTEM)
+                       w->auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+               else
+                       ret = -EINVAL;
+               break;
+       default:
+               dev_dbg(ntodev(netdev), "IW_AUTH_UNKNOWN flags:%x\n", param->flags);
+               ret = -EOPNOTSUPP;
+       }
+
+       return ret;
+}
+
+static int gelicw_get_auth(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_param *param = &wrqu->param;
+
+       dev_dbg(ntodev(netdev), "wx:get_auth\n");
+       switch(param->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_80211_AUTH_ALG:
+               param->value = w->auth_mode;
+               break;
+       case IW_AUTH_WPA_ENABLED:
+               if ((w->key_alg & IW_ENCODE_ALG_TKIP)
+                       || (w->key_alg & IW_ENCODE_ALG_CCMP))
+                       param->value = 1;
+               else
+                       param->value = 0;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int gelicw_set_encodeext(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_point *enc = &wrqu->encoding;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+       int i, index, key_index;
+
+       dev_dbg(ntodev(netdev), "wx:set_encodeext\n");
+       index = enc->flags & IW_ENCODE_INDEX;
+       if (index < 0 || index > WEP_KEYS)
+               return -EINVAL;
+
+       index--;
+       if (ext->key_len > IW_ENCODING_TOKEN_MAX)
+               return -EINVAL;
+
+       if (index != -1)
+               w->key_index = index;
+       key_index = w->key_index;
+
+       if (enc->flags & IW_ENCODE_DISABLED) {
+               /* disable encryption */
+               if (index == -1) {
+                       /* disable all */
+                       w->key_alg = IW_ENCODE_ALG_NONE;
+                       for (i = 0; i < WEP_KEYS; i++)
+                               w->key_len[i] = 0;
+               } else
+                       w->key_len[key_index] = 0;
+       } else if (enc->flags & IW_ENCODE_NOKEY) {
+               /* key not changed */
+               w->key_alg = ext->alg;
+       } else {
+               w->key_len[key_index] = ext->key_len;
+               w->key_alg = ext->alg;
+               if (w->key_alg != IW_ENCODE_ALG_NONE && w->key_len[key_index])
+                       memcpy(w->key[key_index], ext->key, w->key_len[key_index]);
+       }
+       dev_dbg(ntodev(netdev), "key %d len:%d alg:%x\n",\
+               key_index, w->key_len[key_index], w->key_alg);
+
+       if (w->state >= GELICW_STATE_SCAN_DONE
+               && w->cmd_send_flg == 0 && w->essid_len)
+               /* scan essid and set other params */
+               schedule_delayed_work(&w->work_scan_essid, 0);
+       else
+               schedule_delayed_work(&w->work_encode, 0);
+
+       return 0;
+}
+
+static int gelicw_get_encodeext(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       struct iw_point *enc = &wrqu->encoding;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+       int index, key_index, key_len;
+
+       dev_dbg(ntodev(netdev), "wx:get_encodeext\n");
+       key_len = enc->length - sizeof(*ext);
+       if (key_len < 0)
+               return -EINVAL;
+
+       index = enc->flags & IW_ENCODE_INDEX;
+       if (index < 0 || index > WEP_KEYS)
+               return -EINVAL;
+
+       index--;
+       key_index = (index == -1 ? w->key_index : index);
+
+       memset(ext, 0, sizeof(*ext));
+       enc->flags = key_index + 1;
+
+       if (w->key_alg == IW_ENCODE_ALG_NONE || !w->key_len[key_index]) {
+               /* no encryption */
+               enc->flags |= IW_ENCODE_DISABLED;
+               ext->alg = IW_ENCODE_ALG_NONE;
+               ext->key_len = 0;
+       } else {
+               enc->flags |= IW_ENCODE_NOKEY;
+               ext->alg = w->key_alg;
+               ext->key_len = w->key_len[key_index];
+       }
+
+       return 0;
+}
+
+/*
+ * wireless stats
+ */
+static struct iw_statistics *gelicw_get_wireless_stats(struct net_device *netdev)
+{
+       static struct iw_statistics wstats;
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx:wireless_stats\n");
+       if (w->state < GELICW_STATE_ASSOCIATED) {
+               wstats.qual.updated  = IW_QUAL_QUAL_UPDATED |
+                               IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+               wstats.qual.qual = 0;
+               wstats.qual.level = 0;
+               return &wstats;
+       }
+       init_completion(&w->rssi_done);
+       schedule_delayed_work(&w->work_rssi, 0);
+
+       wait_for_completion_interruptible(&w->rssi_done);
+       wstats.qual.updated  = IW_QUAL_QUAL_UPDATED |
+                       IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+       wstats.qual.qual = w->rssi;
+       wstats.qual.level = gelicw_qual2level(w->rssi);
+
+       return &wstats;
+}
+
+/*
+ * private handler
+ */
+static int gelicw_priv_set_wireless_mode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       int mode = *(int *)extra;
+
+       dev_dbg(ntodev(netdev), "wx:priv_set_wmode\n");
+       switch (mode) {
+       case IEEE_B: /* 0x02 */
+       case IEEE_G: /* 0x04 */
+       case IEEE_B | IEEE_G:
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* send common config */
+       gelicw_send_common_config(netdev, &w->wireless_mode, (u8)mode);
+
+       return 0;
+}
+
+static int gelicw_priv_get_wireless_mode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+
+       dev_dbg(ntodev(netdev), "wx:priv_get_wmode\n");
+       switch (w->wireless_mode) {
+       case IEEE_B:
+               strncpy(extra, "802.11b (2)", MAX_IW_PRIV_SIZE);
+               break;
+       case IEEE_G:
+               strncpy(extra, "802.11g (4)", MAX_IW_PRIV_SIZE);
+               break;
+       case IEEE_B | IEEE_G:
+       default:
+               strncpy(extra, "802.11bg (6)", MAX_IW_PRIV_SIZE);
+               break;
+       }
+       wrqu->data.length = strlen(extra);
+
+       return 0;
+}
+
+static int gelicw_priv_set_alg_mode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       int mode = *(int *)extra;
+
+       dev_dbg(ntodev(netdev), "wx:priv_set_alg\n");
+       switch (mode) {
+       case IW_ENCODE_ALG_NONE:
+       case IW_ENCODE_ALG_WEP:
+       case IW_ENCODE_ALG_TKIP:
+       case IW_ENCODE_ALG_CCMP:
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* send common config */
+       gelicw_send_common_config(netdev, &w->key_alg, (u8)mode);
+
+       return 0;
+}
+
+static int gelicw_priv_get_alg_mode(struct net_device *netdev,
+                          struct iw_request_info *info,
+                          union iwreq_data *wrqu, char *extra)
+{
+       struct gelic_wireless *w = gelicw_priv(netdev);
+       char *p;
+
+       dev_dbg(ntodev(netdev), "wx:priv_get_alg\n");
+       switch (w->key_alg) {
+       case IW_ENCODE_ALG_NONE:
+               strncpy(extra, "OFF", MAX_IW_PRIV_SIZE);
+               break;
+       case IW_ENCODE_ALG_WEP:
+               strncpy(extra, "WEP", MAX_IW_PRIV_SIZE);
+               break;
+       case IW_ENCODE_ALG_TKIP:
+               strncpy(extra, "TKIP", MAX_IW_PRIV_SIZE);
+               break;
+       case IW_ENCODE_ALG_CCMP:
+               strncpy(extra, "AES-CCMP", MAX_IW_PRIV_SIZE);
+               break;
+       default:
+               break;
+       }
+       p = extra + strlen(extra);
+
+       if (w->key_alg == IW_ENCODE_ALG_TKIP
+               || w->key_alg == IW_ENCODE_ALG_CCMP) {
+               if (w->key_len[w->key_index] == 64) /* current key index */
+                       strncpy(p, " hex", MAX_IW_PRIV_SIZE);
+               else
+                       strncpy(p, " passphrase", MAX_IW_PRIV_SIZE);
+       }
+       wrqu->data.length = strlen(extra);
+
+       return 0;
+}
+
+
+/*
+ * Wireless handlers
+ */
+static const iw_handler gelicw_handler[] =
+{
+       [IW_IOCTL_IDX(SIOCGIWNAME)]      = (iw_handler) gelicw_get_name,
+       [IW_IOCTL_IDX(SIOCSIWFREQ)]      = (iw_handler) gelicw_set_freq,
+       [IW_IOCTL_IDX(SIOCGIWFREQ)]      = (iw_handler) gelicw_get_freq,
+       [IW_IOCTL_IDX(SIOCSIWMODE)]      = (iw_handler) gelicw_set_mode,
+       [IW_IOCTL_IDX(SIOCGIWMODE)]      = (iw_handler) gelicw_get_mode,
+       [IW_IOCTL_IDX(SIOCGIWRANGE)]     = (iw_handler) gelicw_get_range,
+       [IW_IOCTL_IDX(SIOCSIWAP)]        = (iw_handler) gelicw_set_wap,
+       [IW_IOCTL_IDX(SIOCGIWAP)]        = (iw_handler) gelicw_get_wap,
+       [IW_IOCTL_IDX(SIOCSIWSCAN)]      = (iw_handler) gelicw_set_scan,
+       [IW_IOCTL_IDX(SIOCGIWSCAN)]      = (iw_handler) gelicw_get_scan,
+       [IW_IOCTL_IDX(SIOCSIWESSID)]     = (iw_handler) gelicw_set_essid,
+       [IW_IOCTL_IDX(SIOCGIWESSID)]     = (iw_handler) gelicw_get_essid,
+       [IW_IOCTL_IDX(SIOCSIWNICKN)]     = (iw_handler) gelicw_set_nick,
+       [IW_IOCTL_IDX(SIOCGIWNICKN)]     = (iw_handler) gelicw_get_nick,
+       [IW_IOCTL_IDX(SIOCSIWRATE)]      = (iw_handler) gelicw_set_rate,
+       [IW_IOCTL_IDX(SIOCGIWRATE)]      = (iw_handler) gelicw_get_rate,
+       [IW_IOCTL_IDX(SIOCSIWENCODE)]    = (iw_handler) gelicw_set_encode,
+       [IW_IOCTL_IDX(SIOCGIWENCODE)]    = (iw_handler) gelicw_get_encode,
+       [IW_IOCTL_IDX(SIOCSIWAUTH)]      = (iw_handler) gelicw_set_auth,
+       [IW_IOCTL_IDX(SIOCGIWAUTH)]      = (iw_handler) gelicw_get_auth,
+       [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = (iw_handler) gelicw_set_encodeext,
+       [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = (iw_handler) gelicw_get_encodeext,
+};
+
+/*
+ * Private wireless handlers
+ */
+enum {
+       GELICW_PRIV_SET_WIRELESS_MODE = SIOCIWFIRSTPRIV,
+       GELICW_PRIV_GET_WIRELESS_MODE,
+       GELICW_PRIV_SET_AUTH,
+       GELICW_PRIV_GET_AUTH,
+       GELICW_PRIV_START,
+       GELICW_PRIV_STOP,
+};
+
+static struct iw_priv_args gelicw_private_args[] = {
+       {
+        .cmd = GELICW_PRIV_SET_WIRELESS_MODE,
+        .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        .name = "set_wmode"
+       },
+       {
+        .cmd = GELICW_PRIV_GET_WIRELESS_MODE,
+        .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_IW_PRIV_SIZE,
+        .name = "get_wmode"
+       },
+       {
+        .cmd = GELICW_PRIV_SET_AUTH,
+        .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        .name = "set_alg"
+       },
+       {
+        .cmd = GELICW_PRIV_GET_AUTH,
+        .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_IW_PRIV_SIZE,
+        .name = "get_alg"
+       },
+};
+
+static const iw_handler gelicw_private_handler[] =
+{
+       gelicw_priv_set_wireless_mode,
+       gelicw_priv_get_wireless_mode,
+       gelicw_priv_set_alg_mode,
+       gelicw_priv_get_alg_mode,
+};
+
+static struct iw_handler_def gelicw_handler_def =
+{
+       .num_standard   = ARRAY_SIZE(gelicw_handler),
+       .num_private    = ARRAY_SIZE(gelicw_private_handler),
+       .num_private_args = ARRAY_SIZE(gelicw_private_args),
+       .standard       = (iw_handler *)gelicw_handler,
+       .private        = (iw_handler *)gelicw_private_handler,
+       .private_args   = (struct iw_priv_args *)gelicw_private_args,
+       .get_wireless_stats = gelicw_get_wireless_stats
+};
+
+EXPORT_SYMBOL_GPL(gelicw_setup_netdev);
+EXPORT_SYMBOL_GPL(gelicw_up);
+EXPORT_SYMBOL_GPL(gelicw_down);
+EXPORT_SYMBOL_GPL(gelicw_remove);
+EXPORT_SYMBOL_GPL(gelicw_interrupt);
+EXPORT_SYMBOL_GPL(gelicw_is_associated);
+
index e251d1c..07ed0af 100644 (file)
@@ -1,3 +1,5 @@
 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
+obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o
index 1a56d39..d54b403 100644 (file)
@@ -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
  */
-
+#undef DEBUG
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/kernel.h>
 #include <linux/ioctl.h>
 
 #include <asm/firmware.h>
-#include <asm/lv1call.h>
 #include <asm/ps3av.h>
 #include <asm/ps3.h>
 
@@ -55,6 +53,7 @@ static struct ps3av {
        u32 audio_port;
        int ps3av_mode;
        int ps3av_mode_old;
+       void (*flip_ctl)(int on);
 } ps3av;
 
 static struct ps3_vuart_port_device ps3av_dev = {
@@ -176,7 +175,7 @@ static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr)
                else
                        printk(KERN_ERR
                               "%s: failed event packet, cid:%08x size:%d\n",
-                              __FUNCTION__, hdr->cid, hdr->size);
+                              __func__, hdr->cid, hdr->size);
                return 1;       /* receive event packet */
        }
        return 0;
@@ -198,7 +197,7 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,
        if (res < 0) {
                dev_dbg(&ps3av_dev.core,
                        "%s: ps3av_vuart_write() failed (result=%d)\n",
-                       __FUNCTION__, res);
+                       __func__, res);
                return res;
        }
 
@@ -211,7 +210,7 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,
                if (res != PS3AV_HDR_SIZE) {
                        dev_dbg(&ps3av_dev.core,
                                "%s: ps3av_vuart_read() failed (result=%d)\n",
-                               __FUNCTION__, res);
+                               __func__, res);
                        return res;
                }
 
@@ -221,7 +220,7 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,
                if (res < 0) {
                        dev_dbg(&ps3av_dev.core,
                                "%s: ps3av_vuart_read() failed (result=%d)\n",
-                               __FUNCTION__, res);
+                               __func__, res);
                        return res;
                }
                res += PS3AV_HDR_SIZE;  /* total len */
@@ -231,7 +230,7 @@ static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,
 
        if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) {
                dev_dbg(&ps3av_dev.core, "%s: reply err (result=%x)\n",
-                       __FUNCTION__, recv_buf->cid);
+                       __func__, recv_buf->cid);
                return -EINVAL;
        }
 
@@ -293,7 +292,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size,
        if (res < 0) {
                printk(KERN_ERR
                       "%s: ps3av_send_cmd_pkt() failed (result=%d)\n",
-                      __FUNCTION__, res);
+                      __func__, res);
                goto err;
        }
 
@@ -302,7 +301,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size,
                                         usr_buf_size);
        if (res < 0) {
                printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n",
-                      __FUNCTION__, res);
+                      __func__, res);
                goto err;
        }
 
@@ -311,7 +310,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size,
 
       err:
        mutex_unlock(&ps3av.mutex);
-       printk(KERN_ERR "%s: failed cid:%x res:%d\n", __FUNCTION__, cid, res);
+       printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res);
        return res;
 }
 
@@ -522,7 +521,7 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id)
        if (res == PS3AV_STATUS_NO_SYNC_HEAD)
                printk(KERN_WARNING
                       "%s: Command failed. Please try your request again. \n",
-                      __FUNCTION__);
+                      __func__);
        else if (res)
                dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n");
 
@@ -715,8 +714,7 @@ 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", __FUNCTION__,
-                       id);
+               dev_dbg(&ps3av_dev.core, "%s: error id :%d\n", __func__, id);
                return -EINVAL;
        }
 
@@ -725,8 +723,7 @@ int ps3av_set_video_mode(u32 id, int boot)
        if ((id & PS3AV_MODE_MASK) == 0) {
                id = ps3av_auto_videomode(&ps3av.av_hw_conf, boot);
                if (id < 1) {
-                       printk(KERN_ERR "%s: invalid id :%d\n", __FUNCTION__,
-                              id);
+                       printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);
                        return -EINVAL;
                }
                id |= option;
@@ -744,6 +741,13 @@ int ps3av_set_video_mode(u32 id, int boot)
 
 EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
 
+int ps3av_get_auto_mode(int boot)
+{
+       return ps3av_auto_videomode(&ps3av.av_hw_conf, boot);
+}
+
+EXPORT_SYMBOL_GPL(ps3av_get_auto_mode);
+
 int ps3av_set_mode(u32 id, int boot)
 {
        int res;
@@ -779,7 +783,7 @@ int ps3av_get_scanmode(int id)
        id = id & PS3AV_MODE_MASK;
        size = ARRAY_SIZE(video_mode_table);
        if (id > size - 1 || id < 0) {
-               printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
+               printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);
                return -EINVAL;
        }
        return video_mode_table[id].interlace;
@@ -794,7 +798,7 @@ int ps3av_get_refresh_rate(int id)
        id = id & PS3AV_MODE_MASK;
        size = ARRAY_SIZE(video_mode_table);
        if (id > size - 1 || id < 0) {
-               printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
+               printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);
                return -EINVAL;
        }
        return video_mode_table[id].freq;
@@ -810,7 +814,7 @@ int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres)
        id = id & PS3AV_MODE_MASK;
        size = ARRAY_SIZE(video_mode_table);
        if (id > size - 1 || id < 0) {
-               printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
+               printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);
                return -EINVAL;
        }
        *xres = video_mode_table[id].x;
@@ -837,47 +841,22 @@ 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))
 {
-       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",
-                              __FUNCTION__, status);
-                       ps3av.open_count--;
-               }
-       }
+       ps3av.flip_ctl = flip_ctl;
        mutex_unlock(&ps3av.mutex);
-
-       return status;
 }
+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", __FUNCTION__);
-               status = -1;
-       } else if (!--ps3av.open_count) {
-               status = lv1_gpu_close();
-               if (status)
-                       printk(KERN_WARNING "%s: lv1_gpu_close failed %d\n",
-                              __FUNCTION__, status);
-       }
+       if (ps3av.flip_ctl)
+               ps3av.flip_ctl(on);
        mutex_unlock(&ps3av.mutex);
-
-       return status;
 }
 
-EXPORT_SYMBOL_GPL(ps3av_dev_close);
-
 static int ps3av_probe(struct ps3_vuart_port_device *dev)
 {
        int res;
@@ -919,7 +898,7 @@ static int ps3av_probe(struct ps3_vuart_port_device *dev)
        /* init avsetting modules */
        res = ps3av_cmd_init();
        if (res < 0)
-               printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __FUNCTION__,
+               printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __func__,
                       res);
 
        ps3av_get_hw_conf(&ps3av);
@@ -971,7 +950,7 @@ static int ps3av_module_init(void)
        if (error) {
                printk(KERN_ERR
                       "%s: ps3_vuart_port_driver_register failed %d\n",
-                      __FUNCTION__, error);
+                      __func__, error);
                return error;
        }
 
@@ -979,7 +958,7 @@ static int ps3av_module_init(void)
        if (error)
                printk(KERN_ERR
                       "%s: ps3_vuart_port_device_register failed %d\n",
-                      __FUNCTION__, error);
+                      __func__, error);
 
        return error;
 }
index bc70e81..063c867 100644 (file)
@@ -854,7 +854,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),
@@ -868,7 +868,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;
 }
 
diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c
new file mode 100644 (file)
index 0000000..a8a292a
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+#define DEBUG
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+
+/**
+ *     ps3stor_interrupt - common interrupt routine for storage drivers
+ *     @irq: IRQ number
+ *     @data: Pointer to a struct ps3_storage_device
+ */
+irqreturn_t ps3stor_interrupt(int irq, void *data)
+{
+       struct ps3_storage_device *dev = data;
+
+       dev->lv1_res = lv1_storage_get_async_status(dev->sbd.did.dev_id,
+                                                   &dev->lv1_tag,
+                                                   &dev->lv1_status);
+       /*
+        * lv1_status = -1 may mean that ATAPI transport completed OK, but
+        * ATAPI command itself resulted CHECK CONDITION
+        * so, upper layer should issue REQUEST_SENSE to check the sense data
+        */
+
+       if (dev->lv1_tag != dev->tag)
+               dev_err(&dev->sbd.core,
+                       "%s:%u: tag mismatch, got %lx, expected %lx\n",
+                       __func__, __LINE__, dev->lv1_tag, dev->tag);
+       if (dev->lv1_res)
+               dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n",
+                       __func__, __LINE__, dev->lv1_res, dev->lv1_status);
+       else
+               complete(&dev->irq_done);
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(ps3stor_interrupt);
+
+
+/**
+ *     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 idx = ffs(dev->accessible_regions)-1;
+       unsigned int region_id = dev->regions[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->irq_done);
+       res = write ? lv1_storage_write(dev->sbd.did.dev_id, region_id,
+                                       start_sector, sectors, 0, lpar,
+                                       &dev->tag)
+                   : lv1_storage_read(dev->sbd.did.dev_id, region_id,
+                                      start_sector, sectors, 0, lpar,
+                                      &dev->tag);
+       if (res) {
+               dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
+                       __LINE__, op, res);
+               return -1;
+       }
+
+       wait_for_completion(&dev->irq_done);
+       if (dev->lv1_status) {
+               dev_err(&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_probe_access - Probe for accessibility of regions
+ *     @dev: Pointer to a struct ps3_storage_device
+ *
+ *     Returns the index of the first accessible region, or an error code
+ */
+int ps3stor_probe_access(struct ps3_storage_device *dev)
+{
+       int res, error;
+       unsigned int irq, i;
+       unsigned long n;
+       void *buf;
+       dma_addr_t dma;
+       u64 lpar;
+
+       error = ps3_open_hv_device(&dev->sbd);
+       if (error) {
+               dev_err(&dev->sbd.core, "%s:%u: open device %u:%u failed %d\n",
+                       __func__, __LINE__, dev->sbd.did.bus_id,
+                       dev->sbd.did.dev_id, error);
+               return error;
+       }
+
+       error = ps3_sb_event_receive_port_setup(PS3_BINDING_CPU_ANY,
+                                               &dev->sbd.did,
+                                               dev->sbd.interrupt_id, &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(irq, ps3stor_interrupt, IRQF_DISABLED,
+                           "ps3stor-probe", dev);
+       if (error) {
+               dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n",
+                       __func__, __LINE__, error);
+               goto fail_event_receive_port_destroy;
+       }
+
+       /* PAGE_SIZE >= 4 KiB buffer for fail safe of large sector devices */
+       buf = (void *)__get_free_page(GFP_KERNEL);
+       if (!buf) {
+               dev_err(&dev->sbd.core, "%s:%u: no memory while probing",
+                       __func__, dev->sbd.did.dev_id);
+               error = -ENOMEM;
+               goto fail_free_irq;
+       };
+
+       ps3_dma_region_init(&dev->dma_region, &dev->sbd.did, PS3_DMA_4K,
+                           PS3_DMA_OTHER, buf, PAGE_SIZE, PS3_IOBUS_SB);
+       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_buf;
+       }
+
+       lpar = ps3_mm_phys_to_lpar(__pa(buf));
+
+       dma = dma_map_single(&dev->sbd.core, buf, PAGE_SIZE, DMA_FROM_DEVICE);
+       if (!dma) {
+               dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n",
+                       __func__, __LINE__);
+               error = -ENODEV;
+               goto fail_free_dma;
+       }
+
+       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);
+
+               init_completion(&dev->irq_done);
+               res = lv1_storage_read(dev->sbd.did.dev_id, dev->regions[i].id,
+                                      0, /* start sector */
+                                      1, /* sector count */
+                                      0, /* flags */
+                                      lpar, &dev->tag);
+               if (res) {
+                       dev_dbg(&dev->sbd.core,
+                               "%s:%u: read failed %d, region %u is not accessible\n",
+                               __func__, __LINE__, res, i);
+                       continue;
+               }
+
+               wait_for_completion(&dev->irq_done);
+
+               if (dev->lv1_res || dev->lv1_status) {
+                       dev_dbg(&dev->sbd.core,
+                               "%s:%u: read failed, region %u is not accessible\n",
+                               __func__, __LINE__, i);
+                       continue;
+               }
+
+               if (dev->lv1_tag != dev->tag) {
+                       dev_err(&dev->sbd.core,
+                               "%s:%u: tag mismatch, got %lx, expected %lx\n",
+                               __func__, __LINE__, dev->lv1_tag, dev->tag);
+                       break;
+               }
+
+               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;
+       }
+       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_dbg(&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);
+
+       dma_unmap_single(&dev->sbd.core, dma, PAGE_SIZE, DMA_FROM_DEVICE);
+fail_free_dma:
+       ps3_dma_region_free(&dev->dma_region);
+fail_free_buf:
+       free_page((unsigned long)buf);
+fail_free_irq:
+       free_irq(irq, dev);
+fail_event_receive_port_destroy:
+       ps3_sb_event_receive_port_destroy(&dev->sbd.did, dev->sbd.interrupt_id,
+                                         irq);
+fail_close_device:
+       ps3_close_hv_device(&dev->sbd);
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(ps3stor_probe_access);
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 Storage Bus Library");
+MODULE_AUTHOR("Sony Corporation");
diff --git a/drivers/ps3/sys-manager-core.c b/drivers/ps3/sys-manager-core.c
new file mode 100644 (file)
index 0000000..6e37c67
--- /dev/null
@@ -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
+ */
+#define DEBUG
+#include <linux/kernel.h>
+#include <asm/ps3.h>
+
+/**
+ * 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;
+}
index 59d45c9..78197e1 100644 (file)
@@ -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,
 };
 
@@ -185,6 +207,16 @@ enum ps3_sys_manager_cmd {
 };
 
 /**
+ * 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.
  *
  */
@@ -216,12 +248,7 @@ 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,
        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;
@@ -249,12 +282,7 @@ static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_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_vuart_port_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);
 }
 
@@ -326,12 +363,7 @@ static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *d
 static int ps3_sys_manager_send_response(struct ps3_vuart_port_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;
@@ -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,24 @@ 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;
+               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;
+               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",
@@ -425,6 +476,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,6 +500,7 @@ 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)
@@ -464,12 +517,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 +536,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,9 +557,9 @@ fail_id:
 }
 
 /**
- * ps3_sys_manager_work - Asyncronous read handler.
+ * ps3_sys_manager_work - Asynchronous read handler.
  *
- * Signaled when a complete message arrives at the vuart port.
+ * Signaled when PS3_SM_RX_MSG_LEN_MIN bytes arrive at the vuart port.
  */
 
 static void ps3_sys_manager_work(struct work_struct *work)
@@ -504,35 +567,29 @@ 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);
+       ps3_vuart_read_async(dev, ps3_sys_manager_work, PS3_SM_RX_MSG_LEN_MIN);
 }
 
-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_vuart_port_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 +600,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_vuart_port_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);
 
@@ -575,24 +639,37 @@ void ps3_sys_manager_power_off(void)
 static int ps3_sys_manager_probe(struct ps3_vuart_port_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);
+               PS3_SM_RX_MSG_LEN_MIN);
        BUG_ON(result);
 
        return result;
 }
 
-static void ps3_sys_manager_remove(struct ps3_vuart_port_device *dev)
+static int ps3_sys_manager_remove(struct ps3_vuart_port_device *dev)
 {
+       dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
+       return 0;
+}
+
+static void ps3_sys_manager_shutdown(struct ps3_vuart_port_device *dev)
+{
+       dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
 }
 
 static struct ps3_vuart_port_driver ps3_sys_manager = {
@@ -602,7 +679,7 @@ static struct ps3_vuart_port_driver ps3_sys_manager = {
        },
        .probe = ps3_sys_manager_probe,
        .remove = ps3_sys_manager_remove,
-       .shutdown = ps3_sys_manager_remove,
+       .shutdown = ps3_sys_manager_shutdown,
 };
 
 static int __init ps3_sys_manager_init(void)
@@ -614,3 +691,4 @@ static int __init ps3_sys_manager_init(void)
 }
 
 module_init(ps3_sys_manager_init);
+/* Module remove not supported. */
index a42a0f5..e10c777 100644 (file)
@@ -437,6 +437,7 @@ void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev,
 
        dev->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.
@@ -519,6 +520,7 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ps3_vuart_write);
 
 /**
  * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list.
@@ -638,6 +640,7 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf,
        spin_unlock_irqrestore(&dev->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)
@@ -671,11 +674,13 @@ int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(ps3_vuart_read_async);
 
 void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev)
 {
        dev->priv->rx_list.work.trigger = 0;
 }
+EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async);
 
 /**
  * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
@@ -1061,6 +1066,7 @@ static int ps3_vuart_remove(struct device *_dev)
        if(!dev->core.driver) {
                dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
                        __LINE__);
+               up(&vuart_bus_priv.probe_mutex);
                return 0;
        }
 
@@ -1085,7 +1091,6 @@ static int ps3_vuart_remove(struct device *_dev)
        dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
 
        up(&vuart_bus_priv.probe_mutex);
-
        return 0;
 }
 
@@ -1114,6 +1119,7 @@ static void ps3_vuart_shutdown(struct device *_dev)
        if(!dev->core.driver) {
                dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
                        __LINE__);
+               up(&vuart_bus_priv.probe_mutex);
                return;
        }
 
@@ -1138,7 +1144,6 @@ static void ps3_vuart_shutdown(struct device *_dev)
        dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
 
        up(&vuart_bus_priv.probe_mutex);
-
        return;
 }
 
index 79ecf4e..2ea8ec2 100644 (file)
@@ -132,6 +132,7 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/
 obj-$(CONFIG_SCSI_IBMVSCSIS)   += ibmvscsi/
 obj-$(CONFIG_SCSI_HPTIOP)      += hptiop.o
 obj-$(CONFIG_SCSI_STEX)                += stex.o
+obj-$(CONFIG_PS3_ROM)          += ps3rom.o
 
 obj-$(CONFIG_ARM)              += arm/
 
diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c
new file mode 100644 (file)
index 0000000..922fc3b
--- /dev/null
@@ -0,0 +1,931 @@
+/*
+ * PS3 ROM Storage Driver
+ *
+ * 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.
+ */
+
+#undef DEBUG
+
+#include <linux/cdrom.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/highmem.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3stor.h>
+
+
+#define DEVICE_NAME                    "ps3rom"
+
+#define BOUNCE_SIZE                    (64*1024)
+
+#define PS3ROM_MAX_SECTORS             (BOUNCE_SIZE / CD_FRAMESIZE)
+
+#define LV1_STORAGE_SEND_ATAPI_COMMAND (1)
+
+
+struct ps3rom_private {
+       spinlock_t lock;
+       struct task_struct *thread;
+       struct Scsi_Host *host;
+       struct scsi_cmnd *cmd;
+       void (*scsi_done)(struct scsi_cmnd *);
+};
+#define ps3rom_priv(dev)       ((dev)->sbd.core.driver_data)
+
+struct lv1_atapi_cmnd_block {
+       u8      pkt[32];        /* packet command block           */
+       u32     pktlen;         /* should be 12 for ATAPI 8020    */
+       u32     blocks;
+       u32     block_size;
+       u32     proto;          /* transfer mode                  */
+       u32     in_out;         /* transfer direction             */
+       u64     buffer;         /* parameter except command block */
+       u32     arglen;         /* length above                   */
+};
+
+/*
+ * to position parameter
+ */
+enum {
+       NOT_AVAIL          = -1,
+       USE_SRB_10         = -2,
+       USE_SRB_6          = -3,
+       USE_CDDA_FRAME_RAW = -4
+};
+
+enum lv1_atapi_proto {
+       NA_PROTO = -1,
+       NON_DATA_PROTO     = 0,
+       PIO_DATA_IN_PROTO  = 1,
+       PIO_DATA_OUT_PROTO = 2,
+       DMA_PROTO = 3
+};
+
+enum lv1_atapi_in_out {
+       DIR_NA = -1,
+       DIR_WRITE = 0, /* memory -> device */
+       DIR_READ = 1 /* device -> memory */
+};
+
+
+#ifdef DEBUG
+static const char *scsi_command(unsigned char cmd)
+{
+       switch (cmd) {
+       case TEST_UNIT_READY:           return "TEST_UNIT_READY/GPCMD_TEST_UNIT_READY";
+       case REZERO_UNIT:               return "REZERO_UNIT";
+       case REQUEST_SENSE:             return "REQUEST_SENSE/GPCMD_REQUEST_SENSE";
+       case FORMAT_UNIT:               return "FORMAT_UNIT/GPCMD_FORMAT_UNIT";
+       case READ_BLOCK_LIMITS:         return "READ_BLOCK_LIMITS";
+       case REASSIGN_BLOCKS:           return "REASSIGN_BLOCKS/INITIALIZE_ELEMENT_STATUS";
+       case READ_6:                    return "READ_6";
+       case WRITE_6:                   return "WRITE_6/MI_REPORT_TARGET_PGS";
+       case SEEK_6:                    return "SEEK_6";
+       case READ_REVERSE:              return "READ_REVERSE";
+       case WRITE_FILEMARKS:           return "WRITE_FILEMARKS/SAI_READ_CAPACITY_16";
+       case SPACE:                     return "SPACE";
+       case INQUIRY:                   return "INQUIRY/GPCMD_INQUIRY";
+       case RECOVER_BUFFERED_DATA:     return "RECOVER_BUFFERED_DATA";
+       case MODE_SELECT:               return "MODE_SELECT";
+       case RESERVE:                   return "RESERVE";
+       case RELEASE:                   return "RELEASE";
+       case COPY:                      return "COPY";
+       case ERASE:                     return "ERASE";
+       case MODE_SENSE:                return "MODE_SENSE";
+       case START_STOP:                return "START_STOP/GPCMD_START_STOP_UNIT";
+       case RECEIVE_DIAGNOSTIC:        return "RECEIVE_DIAGNOSTIC";
+       case SEND_DIAGNOSTIC:           return "SEND_DIAGNOSTIC";
+       case ALLOW_MEDIUM_REMOVAL:      return "ALLOW_MEDIUM_REMOVAL/GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL";
+       case SET_WINDOW:                return "SET_WINDOW";
+       case READ_CAPACITY:             return "READ_CAPACITY/GPCMD_READ_CDVD_CAPACITY";
+       case READ_10:                   return "READ_10/GPCMD_READ_10";
+       case WRITE_10:                  return "WRITE_10/GPCMD_WRITE_10";
+       case SEEK_10:                   return "SEEK_10/POSITION_TO_ELEMENT/GPCMD_SEEK";
+       case WRITE_VERIFY:              return "WRITE_VERIFY/GPCMD_WRITE_AND_VERIFY_10";
+       case VERIFY:                    return "VERIFY/GPCMD_VERIFY_10";
+       case SEARCH_HIGH:               return "SEARCH_HIGH";
+       case SEARCH_EQUAL:              return "SEARCH_EQUAL";
+       case SEARCH_LOW:                return "SEARCH_LOW";
+       case SET_LIMITS:                return "SET_LIMITS";
+       case PRE_FETCH:                 return "PRE_FETCH/READ_POSITION";
+       case SYNCHRONIZE_CACHE:         return "SYNCHRONIZE_CACHE/GPCMD_FLUSH_CACHE";
+       case LOCK_UNLOCK_CACHE:         return "LOCK_UNLOCK_CACHE";
+       case READ_DEFECT_DATA:          return "READ_DEFECT_DATA";
+       case MEDIUM_SCAN:               return "MEDIUM_SCAN";
+       case COMPARE:                   return "COMPARE";
+       case COPY_VERIFY:               return "COPY_VERIFY";
+       case WRITE_BUFFER:              return "WRITE_BUFFER";
+       case READ_BUFFER:               return "READ_BUFFER";
+       case UPDATE_BLOCK:              return "UPDATE_BLOCK";
+       case READ_LONG:                 return "READ_LONG";
+       case WRITE_LONG:                return "WRITE_LONG";
+       case CHANGE_DEFINITION:         return "CHANGE_DEFINITION";
+       case WRITE_SAME:                return "WRITE_SAME";
+       case READ_TOC:                  return "READ_TOC/GPCMD_READ_TOC_PMA_ATIP";
+       case LOG_SELECT:                return "LOG_SELECT";
+       case LOG_SENSE:                 return "LOG_SENSE";
+       case MODE_SELECT_10:            return "MODE_SELECT_10/GPCMD_MODE_SELECT_10";
+       case RESERVE_10:                return "RESERVE_10";
+       case RELEASE_10:                return "RELEASE_10";
+       case MODE_SENSE_10:             return "MODE_SENSE_10/GPCMD_MODE_SENSE_10";
+       case PERSISTENT_RESERVE_IN:     return "PERSISTENT_RESERVE_IN";
+       case PERSISTENT_RESERVE_OUT:    return "PERSISTENT_RESERVE_OUT";
+       case REPORT_LUNS:               return "REPORT_LUNS";
+       case MAINTENANCE_IN:            return "MAINTENANCE_IN/GPCMD_SEND_KEY";
+       case MOVE_MEDIUM:               return "MOVE_MEDIUM";
+       case EXCHANGE_MEDIUM:           return "EXCHANGE_MEDIUM/GPCMD_LOAD_UNLOAD";
+       case READ_12:                   return "READ_12/GPCMD_READ_12";
+       case WRITE_12:                  return "WRITE_12";
+       case WRITE_VERIFY_12:           return "WRITE_VERIFY_12";
+       case SEARCH_HIGH_12:            return "SEARCH_HIGH_12";
+       case SEARCH_EQUAL_12:           return "SEARCH_EQUAL_12";
+       case SEARCH_LOW_12:             return "SEARCH_LOW_12";
+       case READ_ELEMENT_STATUS:       return "READ_ELEMENT_STATUS";
+       case SEND_VOLUME_TAG:           return "SEND_VOLUME_TAG/GPCMD_SET_STREAMING";
+       case WRITE_LONG_2:              return "WRITE_LONG_2";
+       case READ_16:                   return "READ_16";
+       case WRITE_16:                  return "WRITE_16";
+       case VERIFY_16:                 return "VERIFY_16";
+       case SERVICE_ACTION_IN:         return "SERVICE_ACTION_IN";
+       case ATA_16:                    return "ATA_16";
+       case ATA_12:                    return "ATA_12/GPCMD_BLANK";
+       case GPCMD_CLOSE_TRACK:         return "GPCMD_CLOSE_TRACK";
+       case GPCMD_GET_CONFIGURATION:   return "GPCMD_GET_CONFIGURATION";
+       case GPCMD_GET_EVENT_STATUS_NOTIFICATION:       return "GPCMD_GET_EVENT_STATUS_NOTIFICATION";
+       case GPCMD_GET_PERFORMANCE:     return "GPCMD_GET_PERFORMANCE";
+       case GPCMD_MECHANISM_STATUS:    return "GPCMD_MECHANISM_STATUS";
+       case GPCMD_PAUSE_RESUME:        return "GPCMD_PAUSE_RESUME";
+       case GPCMD_PLAY_AUDIO_10:       return "GPCMD_PLAY_AUDIO_10";
+       case GPCMD_PLAY_AUDIO_MSF:      return "GPCMD_PLAY_AUDIO_MSF";
+       case GPCMD_PLAY_AUDIO_TI:       return "GPCMD_PLAY_AUDIO_TI/GPCMD_PLAYAUDIO_TI";
+       case GPCMD_PLAY_CD:             return "GPCMD_PLAY_CD";
+       case GPCMD_READ_BUFFER_CAPACITY:        return "GPCMD_READ_BUFFER_CAPACITY";
+       case GPCMD_READ_CD:             return "GPCMD_READ_CD";
+       case GPCMD_READ_CD_MSF:         return "GPCMD_READ_CD_MSF";
+       case GPCMD_READ_DISC_INFO:      return "GPCMD_READ_DISC_INFO";
+       case GPCMD_READ_DVD_STRUCTURE:  return "GPCMD_READ_DVD_STRUCTURE";
+       case GPCMD_READ_FORMAT_CAPACITIES:      return "GPCMD_READ_FORMAT_CAPACITIES";
+       case GPCMD_READ_HEADER:         return "GPCMD_READ_HEADER";
+       case GPCMD_READ_TRACK_RZONE_INFO:       return "GPCMD_READ_TRACK_RZONE_INFO";
+       case GPCMD_READ_SUBCHANNEL:     return "GPCMD_READ_SUBCHANNEL";
+       case GPCMD_REPAIR_RZONE_TRACK:  return "GPCMD_REPAIR_RZONE_TRACK";
+       case GPCMD_REPORT_KEY:          return "GPCMD_REPORT_KEY";
+       case GPCMD_RESERVE_RZONE_TRACK: return "GPCMD_RESERVE_RZONE_TRACK";
+       case GPCMD_SEND_CUE_SHEET:      return "GPCMD_SEND_CUE_SHEET";
+       case GPCMD_SCAN:                return "GPCMD_SCAN";
+       case GPCMD_SEND_DVD_STRUCTURE:  return "GPCMD_SEND_DVD_STRUCTURE";
+       case GPCMD_SEND_EVENT:          return "GPCMD_SEND_EVENT";
+       case GPCMD_SEND_OPC:            return "GPCMD_SEND_OPC";
+       case GPCMD_SET_READ_AHEAD:      return "GPCMD_SET_READ_AHEAD";
+       case GPCMD_STOP_PLAY_SCAN:      return "GPCMD_STOP_PLAY_SCAN";
+       case GPCMD_SET_SPEED:           return "GPCMD_SET_SPEED";
+       case GPCMD_GET_MEDIA_STATUS:    return "GPCMD_GET_MEDIA_STATUS";
+
+       default:
+           return "***UNKNOWN***";
+       }
+}
+#else /* !DEBUG */
+static inline const char *scsi_command(unsigned char cmd) { return NULL; }
+#endif /* DEBUG */
+
+
+static int ps3rom_slave_alloc(struct scsi_device *scsi_dev)
+{
+       struct ps3_storage_device *dev;
+
+       dev = (struct ps3_storage_device *)scsi_dev->host->hostdata[0];
+
+       dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__,
+               __LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel);
+
+       scsi_dev->hostdata = dev;
+       return 0;
+}
+
+static int ps3rom_slave_configure(struct scsi_device *scsi_dev)
+{
+       struct ps3_storage_device *dev = scsi_dev->hostdata;
+
+       dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__,
+               __LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel);
+
+       /*
+        * ATAPI SFF8020 devices use MODE_SENSE_10,
+        * so we can prohibit MODE_SENSE_6
+        */
+       scsi_dev->use_10_for_ms = 1;
+
+       return 0;
+}
+
+static void ps3rom_slave_destroy(struct scsi_device *scsi_dev)
+{
+}
+
+static int ps3rom_queuecommand(struct scsi_cmnd *cmd,
+                              void (*done)(struct scsi_cmnd *))
+{
+       struct ps3_storage_device *dev = cmd->device->hostdata;
+       struct ps3rom_private *priv = ps3rom_priv(dev);
+
+       dev_dbg(&dev->sbd.core, "%s:%u: command 0x%02x (%s)\n", __func__,
+               __LINE__, cmd->cmnd[0], scsi_command(cmd->cmnd[0]));
+
+       spin_lock_irq(&priv->lock);
+       if (priv->cmd) {
+               /* no more than one can be processed */
+               dev_err(&dev->sbd.core, "%s:%u: more than 1 command queued\n",
+                       __func__, __LINE__);
+               spin_unlock_irq(&priv->lock);
+               return SCSI_MLQUEUE_HOST_BUSY;
+       }
+
+       // FIXME Prevalidate commands?
+       priv->cmd = cmd;
+       priv->scsi_done = done;
+       spin_unlock_irq(&priv->lock);
+       wake_up_process(priv->thread);
+       return 0;
+
+
+       return -1;
+}
+
+/*
+ * copy data from device into scatter/gather buffer
+ */
+static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf,
+                               int buflen)
+{
+       int k, req_len, act_len, len, active;
+       void *kaddr;
+       struct scatterlist *sgpnt;
+
+       if (!cmd->request_bufflen)
+               return 0;
+
+       if (!cmd->request_buffer)
+               return DID_ERROR << 16;
+
+       if (cmd->sc_data_direction != DMA_BIDIRECTIONAL &&
+           cmd->sc_data_direction != DMA_FROM_DEVICE)
+               return DID_ERROR << 16;
+
+       if (!cmd->use_sg) {
+               req_len = cmd->request_bufflen;
+               act_len = min(req_len, buflen);
+               memcpy(cmd->request_buffer, buf, act_len);
+               cmd->resid = req_len - act_len;
+               return 0;
+       }
+
+       sgpnt = cmd->request_buffer;
+       active = 1;
+       for (k = 0, req_len = 0, act_len = 0; k < cmd->use_sg; ++k, ++sgpnt) {
+               if (active) {
+                       kaddr = kmap_atomic(sgpnt->page, KM_USER0);
+                       if (!kaddr)
+                               return DID_ERROR << 16;
+                       len = sgpnt->length;
+                       if ((req_len + len) > buflen) {
+                               active = 0;
+                               len = buflen - req_len;
+                       }
+                       memcpy(kaddr + sgpnt->offset, buf + req_len, len);
+                       kunmap_atomic(kaddr, KM_USER0);
+                       act_len += len;
+               }
+               req_len += sgpnt->length;
+       }
+       cmd->resid = req_len - act_len;
+       return 0;
+}
+
+/*
+ * copy data from scatter/gather into device's buffer
+ */
+static int fetch_to_dev_buffer(struct scsi_cmnd *cmd, void *buf, int buflen)
+{
+       int k, req_len, len, fin;
+       void *kaddr;
+       struct scatterlist *sgpnt;
+
+       if (!cmd->request_bufflen)
+               return 0;
+
+       if (!cmd->request_buffer)
+               return -1;
+
+       if (cmd->sc_data_direction != DMA_BIDIRECTIONAL &&
+           cmd->sc_data_direction != DMA_TO_DEVICE)
+               return -1;
+
+       if (!cmd->use_sg) {
+               req_len = cmd->request_bufflen;
+               len = min(req_len, buflen);
+               memcpy(buf, cmd->request_buffer, len);
+               return len;
+       }
+
+       sgpnt = cmd->request_buffer;
+       for (k = 0, req_len = 0, fin = 0; k < cmd->use_sg; ++k, ++sgpnt) {
+               kaddr = kmap_atomic(sgpnt->page, KM_USER0);
+               if (!kaddr)
+                       return -1;
+               len = sgpnt->length;
+               if ((req_len + len) > buflen) {
+                       len = buflen - req_len;
+                       fin = 1;
+               }
+               memcpy(buf + req_len, kaddr + sgpnt->offset, len);
+               kunmap_atomic(kaddr, KM_USER0);
+               if (fin)
+                       return req_len + len;
+               req_len += sgpnt->length;
+       }
+       return req_len;
+}
+
+static int decode_lv1_status(u64 status, unsigned char *sense_key,
+                            unsigned char *asc, unsigned char *ascq)
+{
+       if (((status >> 24) & 0xff) != SAM_STAT_CHECK_CONDITION)
+               return -1;
+
+       *sense_key = (status >> 16) & 0xff;
+       *asc       = (status >>  8) & 0xff;
+       *ascq      =  status        & 0xff;
+       return 0;
+}
+
+static inline unsigned int srb6_lba(const struct scsi_cmnd *cmd)
+{
+       BUG_ON(cmd->cmnd[1] & 0xe0);    // FIXME lun == 0
+       return cmd->cmnd[1] << 16 | cmd->cmnd[2] << 8 | cmd->cmnd[3];
+}
+
+static inline unsigned int srb6_len(const struct scsi_cmnd *cmd)
+{
+       return cmd->cmnd[4];
+}
+
+static inline unsigned int srb10_lba(const struct scsi_cmnd *cmd)
+{
+       return cmd->cmnd[2] << 24 | cmd->cmnd[3] << 16 | cmd->cmnd[4] << 8 |
+              cmd->cmnd[5];
+}
+
+static inline unsigned int srb10_len(const struct scsi_cmnd *cmd)
+{
+       return cmd->cmnd[7] << 8 | cmd->cmnd[8];
+}
+
+static inline unsigned int cdda_raw_len(const struct scsi_cmnd *cmd)
+{
+       unsigned int nframes;
+
+       nframes = cmd->cmnd[6] << 16 | cmd->cmnd[7] <<  8 | cmd->cmnd[8];
+       return nframes * CD_FRAMESIZE_RAW;
+}
+
+static u64 ps3rom_send_atapi_command(struct ps3_storage_device *dev,
+                                    struct lv1_atapi_cmnd_block *cmd)
+{
+       int res;
+       u64 lpar;
+
+       dev_dbg(&dev->sbd.core, "%s:%u: send ATAPI command 0x%02x (%s)\n",
+               __func__, __LINE__, cmd->pkt[0], scsi_command(cmd->pkt[0]));
+
+       init_completion(&dev->irq_done);
+
+       lpar = ps3_mm_phys_to_lpar(__pa(cmd));
+       res = lv1_storage_send_device_command(dev->sbd.did.dev_id,
+                                             LV1_STORAGE_SEND_ATAPI_COMMAND,
+                                             lpar, sizeof(*cmd), cmd->buffer,
+                                             cmd->arglen, &dev->tag);
+       if (res) {
+               dev_err(&dev->sbd.core,
+                       "%s:%u: send_device_command failed %d\n", __func__,
+                       __LINE__, res);
+               return -1;
+       }
+
+       wait_for_completion(&dev->irq_done);
+       if (dev->lv1_status)
+               dev_dbg(&dev->sbd.core, "%s:%u: ATAPI command failed 0x%lx\n",
+                       __func__, __LINE__, dev->lv1_status);
+       else
+               dev_dbg(&dev->sbd.core, "%s:%u: ATAPI command completed\n",
+                       __func__, __LINE__);
+
+       return dev->lv1_status;
+}
+
+static void ps3rom_atapi_request(struct ps3_storage_device *dev,
+                                struct scsi_cmnd *cmd, unsigned int len,
+                                int proto, int in_out, int auto_sense)
+{
+       struct lv1_atapi_cmnd_block atapi_cmnd;
+       unsigned char *cmnd = cmd->cmnd;
+       u64 status;
+       unsigned char sense_key, asc, ascq;
+
+       if (len > dev->bounce_size) {
+               static int printed;
+               if (!printed++)
+                       dev_err(&dev->sbd.core,
+                               "%s:%u: data size too large %u > %lu\n",
+                              __func__, __LINE__, len, dev->bounce_size);
+               cmd->result = DID_ERROR << 16;
+               memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+               cmd->sense_buffer[0] = 0x70;
+               cmd->sense_buffer[2] = ILLEGAL_REQUEST;
+               return;
+       }
+
+       memset(&atapi_cmnd, 0, sizeof(struct lv1_atapi_cmnd_block));
+       memcpy(&atapi_cmnd.pkt, cmnd, 12);
+       atapi_cmnd.pktlen = 12;
+       atapi_cmnd.proto = proto;
+       if (in_out != DIR_NA)
+               atapi_cmnd.in_out = in_out;
+
+       if (atapi_cmnd.in_out == DIR_WRITE) {
+               // FIXME check error
+               fetch_to_dev_buffer(cmd, dev->bounce_buf, len);
+       }
+
+       atapi_cmnd.block_size = 1; /* transfer size is block_size * blocks */
+
+       atapi_cmnd.blocks = atapi_cmnd.arglen = len;
+       atapi_cmnd.buffer = dev->bounce_lpar;
+
+       status = ps3rom_send_atapi_command(dev, &atapi_cmnd);
+       if (status == -1) {
+               cmd->result = DID_ERROR << 16; /* FIXME: is better other error code ? */
+               return;
+       }
+
+       if (!status) {
+               /* OK, completed */
+               if (atapi_cmnd.in_out == DIR_READ) {
+                       // FIXME check error
+                       fill_from_dev_buffer(cmd, dev->bounce_buf, len);
+               }
+               cmd->result = DID_OK << 16;
+               return;
+       }
+
+       /* error */
+       if (!auto_sense) {
+               cmd->result = (DID_ERROR << 16) | (CHECK_CONDITION << 1);
+               dev_err(&dev->sbd.core, "%s:%u: end error without autosense\n",
+                      __func__, __LINE__);
+               return;
+       }
+
+       if (!decode_lv1_status(status, &sense_key, &asc, &ascq)) {
+               /* lv1 may have issued autosense ... */
+               cmd->sense_buffer[0]  = 0x70;
+               cmd->sense_buffer[2]  = sense_key;
+               cmd->sense_buffer[7]  = 16 - 6;
+               cmd->sense_buffer[12] = asc;
+               cmd->sense_buffer[13] = ascq;
+               cmd->result = SAM_STAT_CHECK_CONDITION;
+               return;
+       }
+
+       /* do auto sense by ourselves */
+       memset(&atapi_cmnd, 0, sizeof(struct lv1_atapi_cmnd_block));
+       atapi_cmnd.pkt[0] = REQUEST_SENSE;
+       atapi_cmnd.pkt[4] = 18;
+       atapi_cmnd.pktlen = 12;
+       atapi_cmnd.arglen = atapi_cmnd.blocks = atapi_cmnd.pkt[4];
+       atapi_cmnd.block_size = 1;
+       atapi_cmnd.proto = DMA_PROTO;
+       atapi_cmnd.in_out = DIR_READ;
+       atapi_cmnd.buffer = dev->bounce_lpar;
+
+       /* issue REQUEST_SENSE command */
+       status = ps3rom_send_atapi_command(dev, &atapi_cmnd);
+       if (status == -1) {
+               cmd->result = DID_ERROR << 16; /* FIXME: is better other error code ? */
+               return;
+       }
+
+       /* scsi spec says request sense should never get error */
+       if (status) {
+               decode_lv1_status(status, &sense_key, &asc, &ascq);
+               dev_err(&dev->sbd.core,
+                       "%s:%u: auto REQUEST_SENSE error %#x %#x %#x\n",
+                       __func__, __LINE__, sense_key, asc, ascq);
+       }
+
+       memcpy(cmd->sense_buffer, dev->bounce_buf,
+              min_t(size_t, atapi_cmnd.pkt[4], SCSI_SENSE_BUFFERSIZE));
+       cmd->result = SAM_STAT_CHECK_CONDITION;
+}
+
+static void ps3rom_read_request(struct ps3_storage_device *dev,
+                               struct scsi_cmnd *cmd, u32 start_sector,
+                               u32 sectors)
+{
+       u64 status;
+
+       status = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
+                                           start_sector, sectors, 0);
+       if (status == -1) {
+               cmd->result = DID_ERROR << 16; /* FIXME: other error code? */
+               return;
+       }
+
+       if (status) {
+               memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+               decode_lv1_status(dev->lv1_status, &cmd->sense_buffer[2],
+                                 &cmd->sense_buffer[12],
+                                 &cmd->sense_buffer[13]);
+               cmd->sense_buffer[7] = 16 - 6;  // FIXME hardcoded numbers?
+               cmd->result = SAM_STAT_CHECK_CONDITION;
+               return;
+       }
+
+       // FIXME check error
+       fill_from_dev_buffer(cmd, dev->bounce_buf, sectors * CD_FRAMESIZE);
+
+       cmd->result = DID_OK << 16;
+}
+
+static void ps3rom_write_request(struct ps3_storage_device *dev,
+                                struct scsi_cmnd *cmd, u32 start_sector,
+                                u32 sectors)
+{
+       u64 status;
+
+       // FIXME check error
+       fetch_to_dev_buffer(cmd, dev->bounce_buf, sectors * CD_FRAMESIZE);
+
+       status = ps3stor_read_write_sectors(dev, dev->bounce_lpar,
+                                           start_sector, sectors, 1);
+       if (status == -1) {
+               cmd->result = DID_ERROR << 16; /* FIXME: other error code? */
+               return;
+       }
+
+       if (status) {
+               memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+               decode_lv1_status(dev->lv1_status, &cmd->sense_buffer[2],
+                                 &cmd->sense_buffer[12],
+                                 &cmd->sense_buffer[13]);
+               cmd->sense_buffer[7] = 16 - 6;  // FIXME hardcoded numbers?
+               cmd->result = SAM_STAT_CHECK_CONDITION;
+               return;
+       }
+
+       cmd->result = DID_OK << 16;
+}
+
+static void ps3rom_request(struct ps3_storage_device *dev,
+                          struct scsi_cmnd *cmd)
+{
+       unsigned char opcode = cmd->cmnd[0];
+       struct ps3rom_private *priv = ps3rom_priv(dev);
+
+       dev_dbg(&dev->sbd.core, "%s:%u: command 0x%02x (%s)\n", __func__,
+               __LINE__, opcode, scsi_command(opcode));
+
+       switch (opcode) {
+       case INQUIRY:
+               ps3rom_atapi_request(dev, cmd, srb6_len(cmd),
+                                    PIO_DATA_IN_PROTO, DIR_READ, 1);
+               break;
+
+       case REQUEST_SENSE:
+               ps3rom_atapi_request(dev, cmd, srb6_len(cmd),
+                                    PIO_DATA_IN_PROTO, DIR_READ, 0);
+               break;
+
+       case ALLOW_MEDIUM_REMOVAL:
+       case START_STOP:
+       case TEST_UNIT_READY:
+               ps3rom_atapi_request(dev, cmd, 0, NON_DATA_PROTO, DIR_NA, 1);
+               break;
+
+       case READ_CAPACITY:
+               ps3rom_atapi_request(dev, cmd, 8, PIO_DATA_IN_PROTO, DIR_READ,
+                                    1);
+               break;
+
+       case MODE_SENSE_10:
+       case READ_TOC:
+       case GPCMD_GET_CONFIGURATION:
+       case GPCMD_READ_DISC_INFO:
+               ps3rom_atapi_request(dev, cmd, srb10_len(cmd),
+                                    PIO_DATA_IN_PROTO, DIR_READ, 1);
+               break;
+
+       case READ_6:
+               ps3rom_read_request(dev, cmd, srb6_lba(cmd), srb6_len(cmd));
+               break;
+
+       case READ_10:
+               ps3rom_read_request(dev, cmd, srb10_lba(cmd), srb10_len(cmd));
+               break;
+
+       case WRITE_6:
+               ps3rom_write_request(dev, cmd, srb6_lba(cmd), srb6_len(cmd));
+               break;
+
+       case WRITE_10:
+               ps3rom_write_request(dev, cmd, srb10_lba(cmd), srb10_len(cmd));
+               break;
+
+       case GPCMD_READ_CD:
+               ps3rom_atapi_request(dev, cmd, cdda_raw_len(cmd), DMA_PROTO,
+                                    DIR_READ, 1);
+               break;
+
+       default:
+               dev_err(&dev->sbd.core, "%s:%u: illegal request 0x%02x (%s)\n",
+                       __func__, __LINE__, opcode, scsi_command(opcode));
+               cmd->result = DID_ERROR << 16;
+               memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+               cmd->sense_buffer[0] = 0x70;
+               cmd->sense_buffer[2] = ILLEGAL_REQUEST;
+       }
+
+       spin_lock_irq(&priv->lock);
+       priv->cmd = NULL;
+       priv->scsi_done(cmd);
+       spin_unlock_irq(&priv->lock);
+}
+
+static int ps3rom_thread(void *data)
+{
+       struct ps3_storage_device *dev = data;
+       struct ps3rom_private *priv = ps3rom_priv(dev);
+       struct scsi_cmnd *cmd;
+
+       dev_dbg(&dev->sbd.core, "%s thread init\n", __func__);
+
+       current->flags |= PF_NOFREEZE;
+
+       while (!kthread_should_stop()) {
+               spin_lock_irq(&priv->lock);
+               set_current_state(TASK_INTERRUPTIBLE);
+               cmd = priv->cmd;
+               spin_unlock_irq(&priv->lock);
+               if (!cmd) {
+                       schedule();
+                       continue;
+               }
+               ps3rom_request(dev, cmd);
+       }
+
+       dev_dbg(&dev->sbd.core, "%s thread exit\n", __func__);
+       return 0;
+}
+
+
+static struct scsi_host_template ps3rom_host_template = {
+       .name =                 DEVICE_NAME,
+       .slave_alloc =          ps3rom_slave_alloc,
+       .slave_configure =      ps3rom_slave_configure,
+       .slave_destroy =        ps3rom_slave_destroy,
+       .queuecommand =         ps3rom_queuecommand,
+       .can_queue =            1,
+       .this_id =              7,
+       .sg_tablesize =         SG_ALL,
+       .cmd_per_lun =          1,
+       .emulated =             1,              /* only sg driver uses this */
+       .max_sectors =          PS3ROM_MAX_SECTORS,
+       .use_clustering =       ENABLE_CLUSTERING,
+       .module =               THIS_MODULE,
+};
+
+
+static int __devinit ps3rom_probe(struct ps3_system_bus_device *_dev)
+{
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3rom_private *priv;
+       int res, error;
+       struct Scsi_Host *host;
+       struct task_struct *task;
+
+       /* special case: CD-ROM is assumed always accessible */
+       dev->accessible_regions = 1;
+
+       if (dev->blk_size != CD_FRAMESIZE) {
+               dev_err(&dev->sbd.core,
+                       "%s:%u: cannot handle block size %lu\n", __func__,
+                       __LINE__, dev->blk_size);
+               return -EINVAL;
+       }
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       ps3rom_priv(dev) = priv;
+       spin_lock_init(&priv->lock);
+
+       dev->bounce_size = BOUNCE_SIZE;
+       dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA);
+       if (!dev->bounce_buf) {
+               error = -ENOMEM;
+               goto fail_free_priv;
+       }
+
+       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_free_bounce;
+       }
+
+       error = ps3_sb_event_receive_port_setup(PS3_BINDING_CPU_ANY,
+                                               &dev->sbd.did,
+                                               dev->sbd.interrupt_id,
+                                               &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, ps3stor_interrupt, IRQF_DISABLED,
+                           DEVICE_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;
+       }
+
+       dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf));
+
+       dev->sbd.d_region = &dev->dma_region;
+       ps3_dma_region_init(&dev->dma_region, &dev->sbd.did, PS3_DMA_4K,
+                           PS3_DMA_OTHER, dev->bounce_buf,
+                           dev->bounce_size, PS3_IOBUS_SB);
+       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_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;
+       }
+
+       host = scsi_host_alloc(&ps3rom_host_template,
+                              sizeof(struct ps3_system_bus_device *));
+       if (!host) {
+               dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed\n",
+                       __func__, __LINE__);
+               goto fail_unmap_dma;
+       }
+
+       priv->host = host;
+       host->hostdata[0] = (unsigned long)dev;
+
+       /* One device/LUN per SCSI bus */
+       host->max_id = 1;
+       host->max_lun = 1;
+
+       error = scsi_add_host(host, &dev->sbd.core);
+       if (error) {
+               dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed %d\n",
+                       __func__, __LINE__, error);
+               error = -ENODEV;
+               goto fail_host_put;
+       }
+
+       task = kthread_run(ps3rom_thread, dev, DEVICE_NAME);
+       if (IS_ERR(task)) {
+               error = PTR_ERR(task);
+               goto fail_remove_host;
+       }
+       priv->thread = task;
+
+       scsi_scan_host(host);
+       return 0;
+
+fail_remove_host:
+       scsi_remove_host(host);
+fail_host_put:
+       scsi_host_put(host);
+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.did, dev->sbd.interrupt_id,
+                                         dev->irq);
+fail_close_device:
+       ps3_close_hv_device(&dev->sbd);
+fail_free_bounce:
+       kfree(dev->bounce_buf);
+fail_free_priv:
+       kfree(priv);
+       return error;
+}
+
+static int ps3rom_remove(struct ps3_system_bus_device *_dev)
+{
+       struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
+       struct ps3rom_private *priv = ps3rom_priv(dev);
+       int error;
+
+       dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
+
+       if (priv->host) {
+               scsi_remove_host(priv->host);
+               scsi_host_put(priv->host);
+       }
+
+       if (priv->thread)
+               kthread_stop(priv->thread);
+
+       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.did,
+                                                 dev->sbd.interrupt_id,
+                                                 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);
+
+       kfree(dev->bounce_buf);
+       kfree(priv);
+       return 0;
+}
+
+
+static struct ps3_system_bus_driver ps3rom = {
+       .match_id       = PS3_MATCH_ID_STOR_ROM,
+       .core.name      = DEVICE_NAME,
+       .probe          = ps3rom_probe,
+       .remove         = ps3rom_remove
+};
+
+
+static int __init ps3rom_init(void)
+{
+       return ps3_system_bus_driver_register(&ps3rom, PS3_IOBUS_SB);
+}
+
+static void __exit ps3rom_exit(void)
+{
+       return ps3_system_bus_driver_unregister(&ps3rom);
+}
+
+module_init(ps3rom_init);
+module_exit(ps3rom_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PS3 ROM Storage Driver");
+MODULE_AUTHOR("Sony Corporation");
index c7458f7..56b1d22 100644 (file)
@@ -938,7 +938,7 @@ MODULE_LICENSE ("GPL");
 
 #ifdef CONFIG_PPC_PS3
 #include "ehci-ps3.c"
-#define        PS3_SYSTEM_BUS_DRIVER   ps3_ehci_sb_driver
+#define        PS3_SYSTEM_BUS_DRIVER   ps3_ehci_driver
 #endif
 
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
@@ -973,8 +973,8 @@ static int __init ehci_hcd_init(void)
 
 #ifdef PS3_SYSTEM_BUS_DRIVER
        if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
-               retval = ps3_system_bus_driver_register(
-                               &PS3_SYSTEM_BUS_DRIVER);
+               retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER,
+                                                       PS3_IOBUS_SB);
                if (retval < 0) {
 #ifdef PLATFORM_DRIVER
                        platform_driver_unregister(&PLATFORM_DRIVER);
index 29dcd27..b9a3a95 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <asm/ps3.h>
+#include <asm/lv1call.h>
 
 static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
 {
@@ -80,7 +81,7 @@ static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg(
 #endif
 
 
-static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
+static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
 {
        int result;
        struct usb_hcd *hcd;
@@ -92,13 +93,30 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
                goto fail_start;
        }
 
+       result = ps3_open_hv_device(dev);
+
+       if (result) {
+               dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
+                       __func__, __LINE__);
+               goto fail_open;
+       }
+
+       result = ps3_dma_region_create(dev->d_region);
+
+       if (result) {
+               dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+                       "(%d)\n", __func__, __LINE__, result);
+               BUG_ON("check region type");
+               goto fail_dma_region;
+       }
+
        result = ps3_mmio_region_create(dev->m_region);
 
        if (result) {
                dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
                        __func__, __LINE__);
                result = -EPERM;
-               goto fail_mmio;
+               goto fail_mmio_region;
        }
 
        dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
@@ -127,6 +145,11 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
 
        hcd->rsrc_start = dev->m_region->lpar_addr;
        hcd->rsrc_len = dev->m_region->len;
+
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+               dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+                       __func__, __LINE__);
+
        hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
 
        if (!hcd->regs) {
@@ -160,34 +183,61 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
 fail_add_hcd:
        iounmap(hcd->regs);
 fail_ioremap:
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
        usb_put_hcd(hcd);
 fail_create_hcd:
        ps3_io_irq_destroy(virq);
 fail_irq:
        ps3_free_mmio_region(dev->m_region);
-fail_mmio:
+fail_mmio_region:
+       ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+       ps3_close_hv_device(dev);
+fail_open:
 fail_start:
        return result;
 }
 
-static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev)
+static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
 {
+       unsigned int tmp;
        struct usb_hcd *hcd =
                (struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
 
-       usb_put_hcd(hcd);
+       BUG_ON(!hcd);
+
+       dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+       dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+       tmp = hcd->irq;
+
+       usb_remove_hcd(hcd);
+
        ps3_system_bus_set_driver_data(dev, NULL);
 
+       BUG_ON(!hcd->regs);
+       iounmap(hcd->regs);
+
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       usb_put_hcd(hcd);
+
+       ps3_io_irq_destroy(tmp);
+       ps3_free_mmio_region(dev->m_region);
+
+       ps3_dma_region_free(dev->d_region);
+       ps3_close_hv_device(dev);
+
        return 0;
 }
 
 MODULE_ALIAS("ps3-ehci");
 
-static struct ps3_system_bus_driver ps3_ehci_sb_driver = {
+static struct ps3_system_bus_driver ps3_ehci_driver = {
        .match_id = PS3_MATCH_ID_EHCI,
        .core = {
                .name = "ps3-ehci-driver",
        },
-       .probe = ps3_ehci_sb_probe,
-       .remove = ps3_ehci_sb_remove,
+       .probe = ps3_ehci_probe,
+       .remove = ps3_ehci_remove,
+       .shutdown = ps3_ehci_remove,
 };
index 93c5e02..61e8171 100644 (file)
@@ -971,7 +971,7 @@ MODULE_LICENSE ("GPL");
 
 #ifdef CONFIG_PPC_PS3
 #include "ohci-ps3.c"
-#define PS3_SYSTEM_BUS_DRIVER  ps3_ohci_sb_driver
+#define PS3_SYSTEM_BUS_DRIVER  ps3_ohci_driver
 #endif
 
 #if    !defined(PCI_DRIVER) &&         \
@@ -995,8 +995,8 @@ static int __init ohci_hcd_mod_init(void)
 
 #ifdef PS3_SYSTEM_BUS_DRIVER
        if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
-               retval = ps3_system_bus_driver_register(
-                               &PS3_SYSTEM_BUS_DRIVER);
+               retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER,
+                                                       PS3_IOBUS_SB);
                if (retval < 0)
                        goto error_ps3;
        }
index 93a6eb0..9f9d066 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <asm/ps3.h>
+#include <asm/lv1call.h>
 
 static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
 {
@@ -83,7 +84,7 @@ static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg(
        const struct device *_dev, const char *fmt, ...) {return 0;}
 #endif
 
-static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
+static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
 {
        int result;
        struct usb_hcd *hcd;
@@ -92,16 +93,35 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
 
        if (usb_disabled()) {
                result = -ENODEV;
+               BUG();
                goto fail_start;
        }
 
+       result = ps3_open_hv_device(dev);
+
+       if (result) {
+               dev_dbg(&dev->core, "%s:%d: lv1_open_device failed: %s\n",
+                       __func__, __LINE__, ps3_result(result));
+               result = -EPERM;
+               goto fail_open;
+       }
+
+       result = ps3_dma_region_create(dev->d_region);
+
+       if (result) {
+               dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+                       "(%d)\n", __func__, __LINE__, result);
+               BUG_ON("check region type");
+               goto fail_dma_region;
+       }
+
        result = ps3_mmio_region_create(dev->m_region);
 
        if (result) {
                dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
                        __func__, __LINE__);
                result = -EPERM;
-               goto fail_mmio;
+               goto fail_mmio_region;
        }
 
        dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
@@ -130,6 +150,11 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
 
        hcd->rsrc_start = dev->m_region->lpar_addr;
        hcd->rsrc_len = dev->m_region->len;
+
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+               dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+                       __func__, __LINE__);
+
        hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
 
        if (!hcd->regs) {
@@ -163,34 +188,61 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
 fail_add_hcd:
        iounmap(hcd->regs);
 fail_ioremap:
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
        usb_put_hcd(hcd);
 fail_create_hcd:
        ps3_io_irq_destroy(virq);
 fail_irq:
        ps3_free_mmio_region(dev->m_region);
-fail_mmio:
+fail_mmio_region:
+       ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+       ps3_close_hv_device(dev);
+fail_open:
 fail_start:
        return result;
 }
 
-static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev)
+static int ps3_ohci_remove (struct ps3_system_bus_device *dev)
 {
+       unsigned int tmp;
        struct usb_hcd *hcd =
                (struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
 
-       usb_put_hcd(hcd);
+       BUG_ON(!hcd);
+
+       dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+       dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+       tmp = hcd->irq;
+
+       usb_remove_hcd(hcd);
+
        ps3_system_bus_set_driver_data(dev, NULL);
 
+       BUG_ON(!hcd->regs);
+       iounmap(hcd->regs);
+
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       usb_put_hcd(hcd);
+
+       ps3_io_irq_destroy(tmp);
+       ps3_free_mmio_region(dev->m_region);
+
+       ps3_dma_region_free(dev->d_region);
+       ps3_close_hv_device(dev);
+
        return 0;
 }
 
 MODULE_ALIAS("ps3-ohci");
 
-static struct ps3_system_bus_driver ps3_ohci_sb_driver = {
+static struct ps3_system_bus_driver ps3_ohci_driver = {
        .match_id = PS3_MATCH_ID_OHCI,
        .core = {
                .name = "ps3-ohci-driver",
        },
-       .probe = ps3_ohci_sb_probe,
-       .remove = ps3_ohci_sb_remove,
+       .probe = ps3_ohci_probe,
+       .remove = ps3_ohci_remove,
+       .shutdown = ps3_ohci_remove,
 };
index a59e3a2..829253e 100644 (file)
@@ -90,6 +90,43 @@ config FB_CFB_IMAGEBLIT
          blitting. This is used by drivers that don't provide their own
          (accelerated) version.
 
+config FB_SYS_FILLRECT
+       tristate
+       depends on FB
+       default n
+       ---help---
+         Include the sys_fillrect function for generic software rectangle
+         filling. This is used by drivers that don't provide their own
+         (accelerated) version and the framebuffer is in system RAM.
+
+config FB_SYS_COPYAREA
+       tristate
+       depends on FB
+       default n
+       ---help---
+         Include the sys_copyarea function for generic software area copying.
+         This is used by drivers that don't provide their own (accelerated)
+         version and the framebuffer is in system RAM.
+
+config FB_SYS_IMAGEBLIT
+       tristate
+       depends on FB
+       default n
+       ---help---
+         Include the sys_imageblit function for generic software image
+         blitting. This is used by drivers that don't provide their own
+         (accelerated) version and the framebuffer is in system RAM.
+
+config FB_SYS_FOPS
+       tristate
+       depends on FB
+       default n
+
+config FB_DEFERRED_IO
+       bool
+       depends on FB
+       default y
+
 config FB_SVGALIB
        tristate
        depends on FB
@@ -1615,11 +1652,12 @@ config FB_IBM_GXT4500
          adaptor, found on some IBM System P (pSeries) machines.
 
 config FB_PS3
-       bool "PS3 GPU framebuffer driver"
+       tristate "PS3 GPU framebuffer driver"
        depends on (FB = y) && PS3_PS3AV
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select FB_SYS_FOPS
        ---help---
          Include support for the virtual frame buffer in the PS3 platform.
 
index ea69992..30ef9c2 100644 (file)
@@ -18,9 +18,14 @@ obj-$(CONFIG_BOOTSPLASH)       += bootsplash/
 obj-$(CONFIG_FB_CFB_FILLRECT)  += cfbfillrect.o
 obj-$(CONFIG_FB_CFB_COPYAREA)  += cfbcopyarea.o
 obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
+obj-$(CONFIG_FB_SYS_FILLRECT)  += sysfillrect.o
+obj-$(CONFIG_FB_SYS_COPYAREA)  += syscopyarea.o
+obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o
+obj-$(CONFIG_FB_SYS_FOPS)      += fb_sys_fops.o
 obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
+obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p.o
index 30a8369..64dace3 100644 (file)
@@ -439,14 +439,11 @@ static int arcfb_ioctl(struct fb_info *info,
  * the fb. it's inefficient for them to do anything less than 64*8
  * writes since we update the lcd in each write() anyway.
  */
-static ssize_t arcfb_write(struct file *file, const char __user *buf, size_t count,
-                               loff_t *ppos)
+static ssize_t arcfb_write(struct fb_info *info, const char __user *buf,
+                          size_t count, loff_t *ppos)
 {
        /* modded from epson 1355 */
 
-       struct inode *inode;
-       int fbidx;
-       struct fb_info *info;
        unsigned long p;
        int err=-EINVAL;
        unsigned int fbmemlength,x,y,w,h, bitppos, startpos, endpos, bitcount;
@@ -454,13 +451,6 @@ static ssize_t arcfb_write(struct file *file, const char __user *buf, size_t cou
        unsigned int xres;
 
        p = *ppos;
-       inode = file->f_path.dentry->d_inode;
-       fbidx = iminor(inode);
-       info = registered_fb[fbidx];
-
-       if (!info || !info->screen_base)
-               return -ENODEV;
-
        par = info->par;
        xres = info->var.xres;
        fbmemlength = (xres * info->var.yres)/8;
index 4ea1718..46c0d3d 100644 (file)
@@ -201,7 +201,7 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
 static void fbcon_modechanged(struct fb_info *info);
 static void fbcon_set_all_vcs(struct fb_info *info);
 static void fbcon_start(void);
-static void fbcon_exit(void);
+void fbcon_exit(void);
 static struct class_device *fbcon_class_device;
 
 #ifdef CONFIG_MAC
@@ -463,6 +463,8 @@ static void fbcon_del_cursor_timer(struct fb_info *info)
 {
        struct fbcon_ops *ops = info->fbcon_par;
 
+       printk("%s:%d:\n", __func__, __LINE__);
+
        if (info->queue.func == fb_flashcursor &&
            ops->flags & FBCON_FLAGS_CURSOR_TIMER) {
                del_timer_sync(&ops->cursor_timer);
@@ -3055,6 +3057,43 @@ static void fbcon_new_modelist(struct fb_info *info)
        }
 }
 
+static void fbcon_get_requirement(struct fb_info *info,
+                                 struct fb_blit_caps *caps)
+{
+       struct vc_data *vc;
+       struct display *p;
+       int charcnt;
+
+       if (caps->flags) {
+               int i;
+
+               for (i = first_fb_vc; i <= last_fb_vc; i++) {
+                       vc = vc_cons[i].d;
+                       if (vc && vc->vc_mode == KD_TEXT) {
+                               p = &fb_display[i];
+                               caps->x |= 1 << (vc->vc_font.width - 1);
+                               caps->y |= 1 << (vc->vc_font.height - 1);
+                               charcnt = (p->userfont) ?
+                                       FNTCHARCNT(p->fontdata) : 256;
+                               if (caps->len < charcnt)
+                                       caps->len = charcnt;
+                       }
+               }
+       } else {
+               vc = vc_cons[fg_console].d;
+
+               if (vc && vc->vc_mode == KD_TEXT) {
+                       p = &fb_display[fg_console];
+                       caps->x |= 1 << (vc->vc_font.width - 1);
+                       caps->y |= 1 << (vc->vc_font.height - 1);
+                       charcnt = (p->userfont) ?
+                               FNTCHARCNT(p->fontdata) : 256;
+                       if (caps->len < charcnt)
+                               caps->len = charcnt;
+               }
+       }
+}
+
 static int fbcon_event_notify(struct notifier_block *self, 
                              unsigned long action, void *data)
 {
@@ -3062,6 +3101,7 @@ static int fbcon_event_notify(struct notifier_block *self,
        struct fb_info *info = event->info;
        struct fb_videomode *mode;
        struct fb_con2fbmap *con2fb;
+       struct fb_blit_caps *caps;
        int ret = 0;
 
        /*
@@ -3110,6 +3150,10 @@ static int fbcon_event_notify(struct notifier_block *self,
        case FB_EVENT_NEW_MODELIST:
                fbcon_new_modelist(info);
                break;
+       case FB_EVENT_GET_REQ:
+               caps = event->data;
+               fbcon_get_requirement(info, caps);
+               break;
        }
 
 done:
@@ -3268,7 +3312,7 @@ static void fbcon_start(void)
        }
 }
 
-static void fbcon_exit(void)
+void fbcon_exit(void)
 {
        struct fb_info *info;
        int i, j, mapped;
@@ -3320,6 +3364,7 @@ static void fbcon_exit(void)
 
        fbcon_has_exited = 1;
 }
+EXPORT_SYMBOL_GPL(fbcon_exit);
 
 static int __init fb_console_init(void)
 {
index 29e07c1..3889060 100644 (file)
@@ -403,17 +403,10 @@ static inline unsigned long copy_to_user16(void *to, const void *from,
 
 
 static ssize_t
-epson1355fb_read(struct file *file, char *buf, size_t count, loff_t * ppos)
+epson1355fb_read(struct fb_info *info, char *buf, size_t count, loff_t * ppos)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
-       int fbidx = iminor(inode);
-       struct fb_info *info = registered_fb[fbidx];
        unsigned long p = *ppos;
 
-       /* from fbmem.c except for our own copy_*_user */
-       if (!info || !info->screen_base)
-               return -ENODEV;
-
        if (p >= info->fix.smem_len)
                return 0;
        if (count >= info->fix.smem_len)
@@ -434,20 +427,13 @@ epson1355fb_read(struct file *file, char *buf, size_t count, loff_t * ppos)
 }
 
 static ssize_t
-epson1355fb_write(struct file *file, const char *buf,
+epson1355fb_write(struct fb_info *info, const char *buf,
                  size_t count, loff_t * ppos)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
-       int fbidx = iminor(inode);
-       struct fb_info *info = registered_fb[fbidx];
        unsigned long p = *ppos;
        int err;
 
        /* from fbmem.c except for our own copy_*_user */
-       if (!info || !info->screen_base)
-               return -ENODEV;
-
-       /* from fbmem.c except for our own copy_*_user */
        if (p > info->fix.smem_len)
                return -ENOSPC;
        if (count >= info->fix.smem_len)
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
new file mode 100644 (file)
index 0000000..da7d277
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *  linux/drivers/video/fb_defio.c
+ *
+ *  Copyright (C) 2006 Jaya Kumar
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+
+/* to support deferred IO */
+#include <linux/rmap.h>
+#include <linux/pagemap.h>
+
+/* this is to find and return the vmalloc-ed fb pages */
+static struct page* fb_deferred_io_nopage(struct vm_area_struct *vma,
+                                       unsigned long vaddr, int *type)
+{
+       unsigned long offset;
+       struct page *page;
+       struct fb_info *info = vma->vm_private_data;
+
+       offset = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+       if (offset >= info->fix.smem_len)
+               return NOPAGE_SIGBUS;
+
+       page = vmalloc_to_page(info->screen_base + offset);
+       if (!page)
+               return NOPAGE_OOM;
+
+       get_page(page);
+       if (type)
+               *type = VM_FAULT_MINOR;
+       return page;
+}
+
+int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       struct fb_info *info = file->private_data;
+
+       /* Kill off the delayed work */
+       cancel_rearming_delayed_work(&info->deferred_work);
+
+       /* Run it immediately */
+       return schedule_delayed_work(&info->deferred_work, 0);
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
+
+/* vm_ops->page_mkwrite handler */
+int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
+                                       struct page *page)
+{
+       struct fb_info *info = vma->vm_private_data;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+
+       /* this is a callback we get when userspace first tries to
+       write to the page. we schedule a workqueue. that workqueue
+       will eventually mkclean the touched pages and execute the
+       deferred framebuffer IO. then if userspace touches a page
+       again, we repeat the same scheme */
+
+       /* protect against the workqueue changing the page list */
+       mutex_lock(&fbdefio->lock);
+       list_add(&page->lru, &fbdefio->pagelist);
+       mutex_unlock(&fbdefio->lock);
+
+       /* come back after delay to process the deferred IO */
+       schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+       return 0;
+}
+
+static struct vm_operations_struct fb_deferred_io_vm_ops = {
+       .nopage         = fb_deferred_io_nopage,
+       .page_mkwrite   = fb_deferred_io_mkwrite,
+};
+
+static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+       vma->vm_ops = &fb_deferred_io_vm_ops;
+       vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND );
+       vma->vm_private_data = info;
+       return 0;
+}
+
+/* workqueue callback */
+static void fb_deferred_io_work(struct work_struct *work)
+{
+       struct fb_info *info = container_of(work, struct fb_info,
+                                               deferred_work.work);
+       struct list_head *node, *next;
+       struct page *cur;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+
+       /* here we mkclean the pages, then do all deferred IO */
+       mutex_lock(&fbdefio->lock);
+       list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+               lock_page(cur);
+               page_mkclean(cur);
+               unlock_page(cur);
+       }
+
+       /* driver's callback with pagelist */
+       fbdefio->deferred_io(info, &fbdefio->pagelist);
+
+       /* clear the list */
+       list_for_each_safe(node, next, &fbdefio->pagelist) {
+               list_del(node);
+       }
+       mutex_unlock(&fbdefio->lock);
+}
+
+void fb_deferred_io_init(struct fb_info *info)
+{
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+
+       BUG_ON(!fbdefio);
+       mutex_init(&fbdefio->lock);
+       info->fbops->fb_mmap = fb_deferred_io_mmap;
+       INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
+       INIT_LIST_HEAD(&fbdefio->pagelist);
+       if (fbdefio->delay == 0) /* set a default of 1 s */
+               fbdefio->delay = HZ;
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_init);
+
+void fb_deferred_io_cleanup(struct fb_info *info)
+{
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+
+       BUG_ON(!fbdefio);
+       cancel_delayed_work(&info->deferred_work);
+       flush_scheduled_work();
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/fb_sys_fops.c b/drivers/video/fb_sys_fops.c
new file mode 100644 (file)
index 0000000..cf2538d
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * linux/drivers/video/fb_sys_read.c - Generic file operations where
+ * framebuffer is in system RAM
+ *
+ * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count,
+                   loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *src;
+       int err = 0;
+       unsigned long total_size;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return -EPERM;
+
+       total_size = info->screen_size;
+
+       if (total_size == 0)
+               total_size = info->fix.smem_len;
+
+       if (p >= total_size)
+               return 0;
+
+       if (count >= total_size)
+               count = total_size;
+
+       if (count + p > total_size)
+               count = total_size - p;
+
+       src = (void __force *)(info->screen_base + p);
+
+       if (info->fbops->fb_sync)
+               info->fbops->fb_sync(info);
+
+       if (copy_to_user(buf, src, count))
+               err = -EFAULT;
+
+       if  (!err)
+               *ppos += count;
+
+       return (err) ? err : count;
+}
+EXPORT_SYMBOL_GPL(fb_sys_read);
+
+ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
+                    size_t count, loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *dst;
+       int err = 0;
+       unsigned long total_size;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return -EPERM;
+
+       total_size = info->screen_size;
+
+       if (total_size == 0)
+               total_size = info->fix.smem_len;
+
+       if (p > total_size)
+               return -EFBIG;
+
+       if (count > total_size) {
+               err = -EFBIG;
+               count = total_size;
+       }
+
+       if (count + p > total_size) {
+               if (!err)
+                       err = -ENOSPC;
+
+               count = total_size - p;
+       }
+
+       dst = (void __force *) (info->screen_base + p);
+
+       if (info->fbops->fb_sync)
+               info->fbops->fb_sync(info);
+
+       if (copy_from_user(dst, buf, count))
+               err = -EFAULT;
+
+       if  (!err)
+               *ppos += count;
+
+       return (err) ? err : count;
+}
+EXPORT_SYMBOL_GPL(fb_sys_write);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Generic file read (fb in system RAM)");
+MODULE_LICENSE("GPL");
index 2822526..76461a5 100644 (file)
@@ -586,7 +586,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                return -EPERM;
 
        if (info->fbops->fb_read)
-               return info->fbops->fb_read(file, buf, count, ppos);
+               return info->fbops->fb_read(info, buf, count, ppos);
        
        total_size = info->screen_size;
 
@@ -661,7 +661,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
                return -EPERM;
 
        if (info->fbops->fb_write)
-               return info->fbops->fb_write(file, buf, count, ppos);
+               return info->fbops->fb_write(info, buf, count, ppos);
        
        total_size = info->screen_size;
 
@@ -771,6 +771,29 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
         return 0;
 }
 
+static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
+                        u32 activate)
+{
+       struct fb_event event;
+       struct fb_blit_caps caps, fbcaps;
+       int err = 0;
+
+       memset(&caps, 0, sizeof(caps));
+       memset(&fbcaps, 0, sizeof(fbcaps));
+       caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
+       event.info = info;
+       event.data = &caps;
+       fb_notifier_call_chain(FB_EVENT_GET_REQ, &event);
+       info->fbops->fb_get_caps(info, &fbcaps, var);
+
+       if (((fbcaps.x ^ caps.x) & caps.x) ||
+           ((fbcaps.y ^ caps.y) & caps.y) ||
+           (fbcaps.len < caps.len))
+               err = -EINVAL;
+
+       return err;
+}
+
 int
 fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 {
@@ -801,6 +824,8 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 
        if ((var->activate & FB_ACTIVATE_FORCE) ||
            memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {
+               u32 activate = var->activate;
+
                if (!info->fbops->fb_check_var) {
                        *var = info->var;
                        return 0;
@@ -813,7 +838,15 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
                        struct fb_videomode mode;
                        int err = 0;
 
+                       if (info->fbops->fb_get_caps) {
+                               err = fb_check_caps(info, var, activate);
+
+                               if (err)
+                                       goto done;
+                       }
+
                        info->var = *var;
+
                        if (info->fbops->fb_set_par)
                                info->fbops->fb_set_par(info);
 
@@ -829,7 +862,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 
                        if (!err && (flags & FBINFO_MISC_USEREVENT)) {
                                struct fb_event event;
-                               int evnt = (var->activate & FB_ACTIVATE_ALL) ?
+                               int evnt = (activate & FB_ACTIVATE_ALL) ?
                                        FB_EVENT_MODE_CHANGE_ALL :
                                        FB_EVENT_MODE_CHANGE;
 
@@ -839,6 +872,8 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
                        }
                }
        }
+
+ done:
        return 0;
 }
 
@@ -1198,6 +1233,10 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
 #elif defined(__arm__) || defined(__sh__) || defined(__m32r__)
        vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+#elif defined(__avr32__)
+       vma->vm_page_prot = __pgprot((pgprot_val(vma->vm_page_prot)
+                                     & ~_PAGE_CACHABLE)
+                                    | (_PAGE_BUFFER | _PAGE_DIRTY));
 #elif defined(__ia64__)
        if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start))
                vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
@@ -1266,6 +1305,9 @@ static const struct file_operations fb_fops = {
 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
        .get_unmapped_area = get_fb_unmapped_area,
 #endif
+#ifdef CONFIG_FB_DEFERRED_IO
+       .fsync =        fb_deferred_io_fsync,
+#endif
 };
 
 struct class *fb_class;
@@ -1316,6 +1358,12 @@ register_framebuffer(struct fb_info *fb_info)
        }       
        fb_info->pixmap.offset = 0;
 
+       if (!fb_info->pixmap.blit_x)
+               fb_info->pixmap.blit_x = ~(u32)0;
+
+       if (!fb_info->pixmap.blit_y)
+               fb_info->pixmap.blit_y = ~(u32)0;
+
        if (!fb_info->modelist.prev || !fb_info->modelist.next)
                INIT_LIST_HEAD(&fb_info->modelist);
 
index b40cbd6..bf4305b 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/platform_device.h>
 #include <linux/console.h>
 #include <linux/ioctl.h>
 #include <linux/notifier.h>
 #include <asm/ps3fb.h>
 #include <asm/ps3.h>
 
+
+#define DEVICE_NAME            "ps3fb"
+
 #ifdef PS3FB_DEBUG
-#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ##args)
+#define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args)
 #else
 #define DPRINTK(fmt, args...)
 #endif
@@ -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)
 {
@@ -396,7 +396,7 @@ static int ps3fb_sync(u32 frame)
 
        if (frame > ps3fb.num_frames - 1) {
                printk(KERN_WARNING "%s: invalid frame number (%u)\n",
-                      __FUNCTION__, frame);
+                      __func__, frame);
                return -EINVAL;
        }
        offset = xres * yres * BPP * frame;
@@ -409,23 +409,26 @@ static int ps3fb_sync(u32 frame)
                                           (xres << 16) | yres,
                                           xres * BPP); /* line_length */
        if (status)
-               printk(KERN_ERR "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
-                      __FUNCTION__, status);
+               printk(KERN_ERR
+                      "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
+                      __func__, status);
 #ifdef HEAD_A
        status = lv1_gpu_context_attribute(ps3fb.context_handle,
                                           L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
                                           0, offset, 0, 0);
        if (status)
-               printk(KERN_ERR "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
-                      __FUNCTION__, status);
+               printk(KERN_ERR
+                      "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
+                      __func__, status);
 #endif
 #ifdef HEAD_B
        status = lv1_gpu_context_attribute(ps3fb.context_handle,
                                           L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP,
                                           1, offset, 0, 0);
        if (status)
-               printk(KERN_ERR "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
-                      __FUNCTION__, status);
+               printk(KERN_ERR
+                      "%s: lv1_gpu_context_attribute FLIP failed: %d\n",
+                      __func__, status);
 #endif
        return 0;
 }
@@ -634,7 +637,7 @@ static int ps3fb_blank(int blank, struct fb_info *info)
 {
        int retval;
 
-       DPRINTK("%s: blank:%d\n", __FUNCTION__, blank);
+       DPRINTK("%s: blank:%d\n", __func__, blank);
        switch (blank) {
        case FB_BLANK_POWERDOWN:
        case FB_BLANK_HSYNC_SUSPEND:
@@ -732,6 +735,11 @@ static int ps3fb_ioctl(struct fb_info *info, unsigned int cmd,
                        if (copy_from_user(&val, argp, sizeof(val)))
                                break;
 
+                       if (!(val & PS3AV_MODE_MASK)) {
+                               u32 id = ps3av_get_auto_mode(0);
+                               if (id > 0)
+                                       val = (val & ~PS3AV_MODE_MASK) | id;
+                       }
                        DPRINTK("PS3FB_IOCTL_SETMODE:%x\n", val);
                        retval = -EINVAL;
                        old_mode = ps3fb_mode;
@@ -825,7 +833,7 @@ static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr)
        status = lv1_gpu_context_intr(ps3fb.context_handle, &v1);
        if (status) {
                printk(KERN_ERR "%s: lv1_gpu_context_intr failed: %d\n",
-                      __FUNCTION__, status);
+                      __func__, status);
                return IRQ_NONE;
        }
 
@@ -843,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;
 
@@ -884,24 +864,23 @@ static int ps3fb_vsync_settings(struct gpu_driver_info *dinfo, void *dev)
                dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000);
 
        if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) {
-               printk(KERN_ERR "%s: version_driver err:%x\n", __FUNCTION__,
+               printk(KERN_ERR "%s: version_driver err:%x\n", __func__,
                       dinfo->version_driver);
                return -EINVAL;
        }
 
-       ps3fb.dev = dev;
        error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet,
                                   &ps3fb.irq_no);
        if (error) {
-               printk(KERN_ERR "%s: ps3_alloc_irq failed %d\n", __FUNCTION__,
+               printk(KERN_ERR "%s: ps3_alloc_irq failed %d\n", __func__,
                       error);
                return error;
        }
 
        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", __FUNCTION__,
+               printk(KERN_ERR "%s: request_irq failed %d\n", __func__,
                       error);
                ps3_irq_plug_destroy(ps3fb.irq_no);
                return error;
@@ -920,7 +899,7 @@ static int ps3fb_xdr_settings(u64 xdr_lpar)
                                       xdr_lpar, ps3fb_videomemory.size, 0);
        if (status) {
                printk(KERN_ERR "%s: lv1_gpu_context_iomap failed: %d\n",
-                      __FUNCTION__, status);
+                      __func__, status);
                return -ENXIO;
        }
        DPRINTK("video:%p xdr_ea:%p ioif:%lx lpar:%lx phys:%lx size:%lx\n",
@@ -932,8 +911,9 @@ static int ps3fb_xdr_settings(u64 xdr_lpar)
                                           xdr_lpar, ps3fb_videomemory.size,
                                           GPU_IOIF, 0);
        if (status) {
-               printk(KERN_ERR "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n",
-                      __FUNCTION__, status);
+               printk(KERN_ERR
+                      "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n",
+                      __func__, status);
                return -ENXIO;
        }
        return 0;
@@ -942,12 +922,14 @@ static int ps3fb_xdr_settings(u64 xdr_lpar)
 static struct fb_ops ps3fb_ops = {
        .fb_open        = ps3fb_open,
        .fb_release     = ps3fb_release,
+       .fb_read        = fb_sys_read,
+       .fb_write       = fb_sys_write,
        .fb_check_var   = ps3fb_check_var,
        .fb_set_par     = ps3fb_set_par,
        .fb_setcolreg   = ps3fb_setcolreg,
-       .fb_fillrect    = cfb_fillrect,
-       .fb_copyarea    = cfb_copyarea,
-       .fb_imageblit   = cfb_imageblit,
+       .fb_fillrect    = sys_fillrect,
+       .fb_copyarea    = sys_copyarea,
+       .fb_imageblit   = sys_imageblit,
        .fb_mmap        = ps3fb_mmap,
        .fb_blank       = ps3fb_blank,
        .fb_ioctl       = ps3fb_ioctl,
@@ -955,16 +937,49 @@ 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;
+}
+
+//EXPORT_SYMBOL_GPL(ps3fb_set_sync);
+
+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;
@@ -975,12 +990,38 @@ static int __init ps3fb_probe(struct platform_device *dev)
        unsigned long offset;
        struct task_struct *task;
 
+       printk(" -> %s:%u\n", __func__, __LINE__);
+
+       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);
        if (status) {
                printk(KERN_ERR "%s: lv1_gpu_memory_allocate failed: %d\n",
-                      __FUNCTION__, status);
+                      __func__, status);
                goto err;
        }
        DPRINTK("ddr:lpar:0x%lx\n", ddr_lpar);
@@ -991,14 +1032,14 @@ static int __init ps3fb_probe(struct platform_device *dev)
                                          &lpar_reports, &lpar_reports_size);
        if (status) {
                printk(KERN_ERR "%s: lv1_gpu_context_attribute failed: %d\n",
-                      __FUNCTION__, status);
+                      __func__, status);
                goto err_gpu_memory_free;
        }
 
        /* vsync interrupt */
        ps3fb.dinfo = ioremap(lpar_driver_info, 128 * 1024);
        if (!ps3fb.dinfo) {
-               printk(KERN_ERR "%s: ioremap failed\n", __FUNCTION__);
+               printk(KERN_ERR "%s: ioremap failed\n", __func__);
                goto err_gpu_context_free;
        }
 
@@ -1018,7 +1059,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;
 
@@ -1050,13 +1091,13 @@ 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;
@@ -1064,6 +1105,7 @@ static int __init ps3fb_probe(struct platform_device *dev)
 
        ps3fb.task = task;
 
+       printk(" <- %s:%u\n", __func__, __LINE__);
        return 0;
 
 err_unregister_framebuffer:
@@ -1073,7 +1115,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);
@@ -1085,18 +1127,33 @@ err:
        return retval;
 }
 
-static void ps3fb_shutdown(struct platform_device *dev)
+static int __devexit ps3fb_remove(struct ps3_system_bus_device *dev)
 {
-       ps3fb_flip_ctl(0);      /* 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);
+       BUG();
+       return 0;
 }
 
-void ps3fb_cleanup(void)
+extern void fbcon_exit(void);
+extern void vt_console_stop(void);
+
+static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
 {
        int status;
+       struct fb_info *info = dev->core.driver_data;
+
+       printk(" -> %s:%d\n", __func__, __LINE__);
+
+       vt_console_stop();
+       fbcon_exit();
+
+       ps3fb_flip_ctl(0); /* flip off */
+       ps3fb.dinfo->irq.mask = 0;
+
+       if (info) {
+               unregister_framebuffer(info);
+               fb_dealloc_cmap(&info->cmap);
+               framebuffer_release(info);
+       }
 
        if (ps3fb.task) {
                struct task_struct *task = ps3fb.task;
@@ -1104,7 +1161,7 @@ void ps3fb_cleanup(void)
                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);
@@ -1117,132 +1174,66 @@ void ps3fb_cleanup(void)
        if (status)
                DPRINTK("lv1_gpu_memory_free failed: %d\n", status);
 
-       ps3av_dev_close();
-}
-
-EXPORT_SYMBOL_GPL(ps3fb_cleanup);
+       ps3_close_hv_device(dev);
+       printk(" <- %s:%d\n", __func__, __LINE__);
 
-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 ps3_system_bus_driver ps3fb_driver = {
+       .match_id       = PS3_MATCH_ID_GFX,
+       .core.name      = DEVICE_NAME,
+       .core.owner     = THIS_MODULE,
+       .probe          = ps3fb_probe,
+       .remove         = __devexit_p(ps3fb_remove),
+       .shutdown       = ps3fb_shutdown,
 };
 
-static struct platform_device ps3fb_device = {
-       .name   = "ps3fb",
-       .id     = 0,
-       .dev    = { .release = ps3fb_platform_release }
-};
-
-int ps3fb_set_sync(void)
+#ifndef MODULE
+static int __init ps3fb_setup(void)
 {
-       int status;
+       char *options, *this_opt;
 
-#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",
-                      __FUNCTION__, 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 (fb_get_options(DEVICE_NAME, &options))
+               return -ENXIO;
 
-       if (status) {
-               printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE failed: %d\n",
-                      __FUNCTION__, status);
-               return -1;
+       if (!options || !*options)
+               return 0;
+
+       while ((this_opt = strsep(&options, ",")) != NULL) {
+               if (!*this_opt)
+                       continue;
+               if (!strncmp(this_opt, "mode:", 5))
+                       ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0);
+               else
+                       mode_option = this_opt;
        }
-#endif
        return 0;
 }
-
-EXPORT_SYMBOL_GPL(ps3fb_set_sync);
+#else
+#define ps3fb_setup()  0
+#endif /* MODULE */
 
 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", __FUNCTION__);
-               goto err;
-       }
-
-       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;
-
-       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;
-
-       error = platform_driver_register(&ps3fb_driver);
-       if (!error) {
-               error = platform_device_register(&ps3fb_device);
-               if (error)
-                       platform_driver_unregister(&ps3fb_driver);
-       }
-
-       ps3fb_set_sync();
-
-       return error;
+       if (!ps3fb_videomemory.address ||  ps3fb_setup())
+               return -ENXIO;
 
-err:
-       return -ENXIO;
+       return ps3_system_bus_driver_register(&ps3fb_driver, 0);
 }
 
-module_init(ps3fb_init);
-
-#ifdef MODULE
 static void __exit ps3fb_exit(void)
 {
-       platform_device_unregister(&ps3fb_device);
-       platform_driver_unregister(&ps3fb_driver);
+       printk(" -> %s:%d\n", __func__, __LINE__);
+       ps3_system_bus_driver_unregister(&ps3fb_driver);
+       printk(" <- %s:%d\n", __func__, __LINE__);
 }
 
-module_exit(ps3fb_exit);
+module_init(ps3fb_init);
+#if 0
+module_exit(ps3fb_exit);       /* FIXME: need to fix fbcon to support remove */
+#endif
 
 MODULE_LICENSE("GPL");
-#endif                         /* MODULE */
+MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver");
+MODULE_AUTHOR("Sony Computer Entertainment Inc.");
index a93618b..df2909a 100644 (file)
@@ -214,7 +214,7 @@ static int pvr2_init_cable(void);
 static int pvr2_get_param(const struct pvr2_params *p, const char *s,
                             int val, int size);
 #ifdef CONFIG_SH_DMA
-static ssize_t pvr2fb_write(struct file *file, const char *buf,
+static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
                            size_t count, loff_t *ppos);
 #endif
 
@@ -674,7 +674,7 @@ static int pvr2_init_cable(void)
 }
 
 #ifdef CONFIG_SH_DMA
-static ssize_t pvr2fb_write(struct file *file, const char *buf,
+static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
                            size_t count, loff_t *ppos)
 {
        unsigned long dst, start, end, len;
diff --git a/drivers/video/syscopyarea.c b/drivers/video/syscopyarea.c
new file mode 100644 (file)
index 0000000..e348893
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ *  Generic Bit Block Transfer for frame buffers located in system RAM with
+ *  packed pixels of any depth.
+ *
+ *  Based almost entirely from cfbcopyarea.c (which is based almost entirely
+ *  on Geert Uytterhoeven's copyarea routine)
+ *
+ *      Copyright (C)  2007 Antonino Daplas <adaplas@pol.net>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+#include <asm/types.h>
+#include <asm/io.h>
+
+    /*
+     *  Compose two values, using a bitmask as decision value
+     *  This is equivalent to (a & mask) | (b & ~mask)
+     */
+
+static inline unsigned long
+comp(unsigned long a, unsigned long b, unsigned long mask)
+{
+    return ((a ^ b) & mask) ^ b;
+}
+
+    /*
+     *  Generic bitwise copy algorithm
+     */
+
+static void
+bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
+       int src_idx, int bits, unsigned n)
+{
+       unsigned long first, last;
+       int const shift = dst_idx-src_idx;
+       int left, right;
+
+       first = FB_SHIFT_HIGH(~0UL, dst_idx);
+       last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
+
+       if (!shift) {
+               /* Same alignment for source and dest */
+               if (dst_idx+n <= bits) {
+                       /* Single word */
+                       if (last)
+                               first &= last;
+                       *dst = comp(*src, *dst, first);
+               } else {
+                       /* Multiple destination words */
+                       /* Leading bits */
+                       if (first != ~0UL) {
+                               *dst = comp(*src, *dst, first);
+                               dst++;
+                               src++;
+                               n -= bits - dst_idx;
+                       }
+
+                       /* Main chunk */
+                       n /= bits;
+                       while (n >= 8) {
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               *dst++ = *src++;
+                               n -= 8;
+                       }
+                       while (n--)
+                               *dst++ = *src++;
+
+                       /* Trailing bits */
+                       if (last)
+                               *dst = comp(*src, *dst, last);
+               }
+       } else {
+               unsigned long d0, d1;
+               int m;
+
+               /* Different alignment for source and dest */
+               right = shift & (bits - 1);
+               left = -shift & (bits - 1);
+
+               if (dst_idx+n <= bits) {
+                       /* Single destination word */
+                       if (last)
+                               first &= last;
+                       if (shift > 0) {
+                               /* Single source word */
+                               *dst = comp(*src >> right, *dst, first);
+                       } else if (src_idx+n <= bits) {
+                               /* Single source word */
+                               *dst = comp(*src << left, *dst, first);
+                       } else {
+                               /* 2 source words */
+                               d0 = *src++;
+                               d1 = *src;
+                               *dst = comp(d0 << left | d1 >> right, *dst,
+                                           first);
+                       }
+               } else {
+                       /* Multiple destination words */
+                       /** We must always remember the last value read,
+                           because in case SRC and DST overlap bitwise (e.g.
+                           when moving just one pixel in 1bpp), we always
+                           collect one full long for DST and that might
+                           overlap with the current long from SRC. We store
+                           this value in 'd0'. */
+                       d0 = *src++;
+                       /* Leading bits */
+                       if (shift > 0) {
+                               /* Single source word */
+                               *dst = comp(d0 >> right, *dst, first);
+                               dst++;
+                               n -= bits - dst_idx;
+                       } else {
+                               /* 2 source words */
+                               d1 = *src++;
+                               *dst = comp(d0 << left | *dst >> right, *dst, first);
+                               d0 = d1;
+                               dst++;
+                               n -= bits - dst_idx;
+                       }
+
+                       /* Main chunk */
+                       m = n % bits;
+                       n /= bits;
+                       while (n >= 4) {
+                               d1 = *src++;
+                               *dst++ = d0 << left | d1 >> right;
+                               d0 = d1;
+                               d1 = *src++;
+                               *dst++ = d0 << left | d1 >> right;
+                               d0 = d1;
+                               d1 = *src++;
+                               *dst++ = d0 << left | d1 >> right;
+                               d0 = d1;
+                               d1 = *src++;
+                               *dst++ = d0 << left | d1 >> right;
+                               d0 = d1;
+                               n -= 4;
+                       }
+                       while (n--) {
+                               d1 = *src++;
+                               *dst++ = d0 << left | d1 >> right;
+                               d0 = d1;
+                       }
+
+                       /* Trailing bits */
+                       if (last) {
+                               if (m <= right) {
+                                       /* Single source word */
+                                       *dst = comp(d0 << left, *dst, last);
+                               } else {
+                                       /* 2 source words */
+                                       d1 = *src;
+                                       *dst = comp(d0 << left | d1 >> right,
+                                                   *dst, last);
+                               }
+                       }
+               }
+       }
+}
+
+    /*
+     *  Generic bitwise copy algorithm, operating backward
+     */
+
+static void
+bitcpy_rev(unsigned long *dst, int dst_idx, const unsigned long *src,
+          int src_idx, int bits, unsigned n)
+{
+       unsigned long first, last;
+       int shift;
+
+       dst += (n-1)/bits;
+       src += (n-1)/bits;
+       if ((n-1) % bits) {
+               dst_idx += (n-1) % bits;
+               dst += dst_idx >> (ffs(bits) - 1);
+               dst_idx &= bits - 1;
+               src_idx += (n-1) % bits;
+               src += src_idx >> (ffs(bits) - 1);
+               src_idx &= bits - 1;
+       }
+
+       shift = dst_idx-src_idx;
+
+       first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
+       last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
+
+       if (!shift) {
+               /* Same alignment for source and dest */
+               if ((unsigned long)dst_idx+1 >= n) {
+                       /* Single word */
+                       if (last)
+                               first &= last;
+                       *dst = comp(*src, *dst, first);
+               } else {
+                       /* Multiple destination words */
+
+                       /* Leading bits */
+                       if (first != ~0UL) {
+                               *dst = comp(*src, *dst, first);
+                               dst--;
+                               src--;
+                               n -= dst_idx+1;
+                       }
+
+                       /* Main chunk */
+                       n /= bits;
+                       while (n >= 8) {
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               *dst-- = *src--;
+                               n -= 8;
+                       }
+                       while (n--)
+                               *dst-- = *src--;
+                       /* Trailing bits */
+                       if (last)
+                               *dst = comp(*src, *dst, last);
+               }
+       } else {
+               /* Different alignment for source and dest */
+
+               int const left = -shift & (bits-1);
+               int const right = shift & (bits-1);
+
+               if ((unsigned long)dst_idx+1 >= n) {
+                       /* Single destination word */
+                       if (last)
+                               first &= last;
+                       if (shift < 0) {
+                               /* Single source word */
+                               *dst = comp(*src << left, *dst, first);
+                       } else if (1+(unsigned long)src_idx >= n) {
+                               /* Single source word */
+                               *dst = comp(*src >> right, *dst, first);
+                       } else {
+                               /* 2 source words */
+                               *dst = comp(*src >> right | *(src-1) << left,
+                                           *dst, first);
+                       }
+               } else {
+                       /* Multiple destination words */
+                       /** We must always remember the last value read,
+                           because in case SRC and DST overlap bitwise (e.g.
+                           when moving just one pixel in 1bpp), we always
+                           collect one full long for DST and that might
+                           overlap with the current long from SRC. We store
+                           this value in 'd0'. */
+                       unsigned long d0, d1;
+                       int m;
+
+                       d0 = *src--;
+                       /* Leading bits */
+                       if (shift < 0) {
+                               /* Single source word */
+                               *dst = comp(d0 << left, *dst, first);
+                       } else {
+                               /* 2 source words */
+                               d1 = *src--;
+                               *dst = comp(d0 >> right | d1 << left, *dst,
+                                           first);
+                               d0 = d1;
+                       }
+                       dst--;
+                       n -= dst_idx+1;
+
+                       /* Main chunk */
+                       m = n % bits;
+                       n /= bits;
+                       while (n >= 4) {
+                               d1 = *src--;
+                               *dst-- = d0 >> right | d1 << left;
+                               d0 = d1;
+                               d1 = *src--;
+                               *dst-- = d0 >> right | d1 << left;
+                               d0 = d1;
+                               d1 = *src--;
+                               *dst-- = d0 >> right | d1 << left;
+                               d0 = d1;
+                               d1 = *src--;
+                               *dst-- = d0 >> right | d1 << left;
+                               d0 = d1;
+                               n -= 4;
+                       }
+                       while (n--) {
+                               d1 = *src--;
+                               *dst-- = d0 >> right | d1 << left;
+                               d0 = d1;
+                       }
+
+                       /* Trailing bits */
+                       if (last) {
+                               if (m <= left) {
+                                       /* Single source word */
+                                       *dst = comp(d0 >> right, *dst, last);
+                               } else {
+                                       /* 2 source words */
+                                       d1 = *src;
+                                       *dst = comp(d0 >> right | d1 << left,
+                                                   *dst, last);
+                               }
+                       }
+               }
+       }
+}
+
+void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+       u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
+       u32 height = area->height, width = area->width;
+       unsigned long const bits_per_line = p->fix.line_length*8u;
+       unsigned long *dst = NULL, *src = NULL;
+       int bits = BITS_PER_LONG, bytes = bits >> 3;
+       int dst_idx = 0, src_idx = 0, rev_copy = 0;
+
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       /* if the beginning of the target area might overlap with the end of
+       the source area, be have to copy the area reverse. */
+       if ((dy == sy && dx > sx) || (dy > sy)) {
+               dy += height;
+               sy += height;
+               rev_copy = 1;
+       }
+
+       /* split the base of the framebuffer into a long-aligned address and
+          the index of the first bit */
+       dst = src = (unsigned long *)((unsigned long)p->screen_base &
+                                     ~(bytes-1));
+       dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
+       /* add offset of source and target area */
+       dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
+       src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
+
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+
+       if (rev_copy) {
+               while (height--) {
+                       dst_idx -= bits_per_line;
+                       src_idx -= bits_per_line;
+                       dst += dst_idx >> (ffs(bits) - 1);
+                       dst_idx &= (bytes - 1);
+                       src += src_idx >> (ffs(bits) - 1);
+                       src_idx &= (bytes - 1);
+                       bitcpy_rev(dst, dst_idx, src, src_idx, bits,
+                               width*p->var.bits_per_pixel);
+               }
+       } else {
+               while (height--) {
+                       dst += dst_idx >> (ffs(bits) - 1);
+                       dst_idx &= (bytes - 1);
+                       src += src_idx >> (ffs(bits) - 1);
+                       src_idx &= (bytes - 1);
+                       bitcpy(dst, dst_idx, src, src_idx, bits,
+                               width*p->var.bits_per_pixel);
+                       dst_idx += bits_per_line;
+                       src_idx += bits_per_line;
+               }
+       }
+}
+
+EXPORT_SYMBOL(sys_copyarea);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/sysfillrect.c b/drivers/video/sysfillrect.c
new file mode 100644 (file)
index 0000000..10de707
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ *  Generic fillrect for frame buffers in system RAM with packed pixels of
+ *  any depth.
+ *
+ *  Based almost entirely from cfbfillrect.c (which is based almost entirely
+ *  on Geert Uytterhoeven's fillrect routine)
+ *
+ *      Copyright (C)  2007 Antonino Daplas <adaplas@pol.net>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+
+    /*
+     *  Compose two values, using a bitmask as decision value
+     *  This is equivalent to (a & mask) | (b & ~mask)
+     */
+
+static inline unsigned long
+comp(unsigned long a, unsigned long b, unsigned long mask)
+{
+    return ((a ^ b) & mask) ^ b;
+}
+
+    /*
+     *  Create a pattern with the given pixel's color
+     */
+
+#if BITS_PER_LONG == 64
+static inline unsigned long
+pixel_to_pat( u32 bpp, u32 pixel)
+{
+       switch (bpp) {
+       case 1:
+               return 0xfffffffffffffffful*pixel;
+       case 2:
+               return 0x5555555555555555ul*pixel;
+       case 4:
+               return 0x1111111111111111ul*pixel;
+       case 8:
+               return 0x0101010101010101ul*pixel;
+       case 12:
+               return 0x0001001001001001ul*pixel;
+       case 16:
+               return 0x0001000100010001ul*pixel;
+       case 24:
+               return 0x0000000001000001ul*pixel;
+       case 32:
+               return 0x0000000100000001ul*pixel;
+       default:
+               panic("pixel_to_pat(): unsupported pixelformat\n");
+    }
+}
+#else
+static inline unsigned long
+pixel_to_pat( u32 bpp, u32 pixel)
+{
+       switch (bpp) {
+       case 1:
+               return 0xfffffffful*pixel;
+       case 2:
+               return 0x55555555ul*pixel;
+       case 4:
+               return 0x11111111ul*pixel;
+       case 8:
+               return 0x01010101ul*pixel;
+       case 12:
+               return 0x00001001ul*pixel;
+       case 16:
+               return 0x00010001ul*pixel;
+       case 24:
+               return 0x00000001ul*pixel;
+       case 32:
+               return 0x00000001ul*pixel;
+       default:
+               panic("pixel_to_pat(): unsupported pixelformat\n");
+    }
+}
+#endif
+
+    /*
+     *  Aligned pattern fill using 32/64-bit memory accesses
+     */
+
+static void
+bitfill_aligned(unsigned long *dst, int dst_idx, unsigned long pat,
+               unsigned n, int bits)
+{
+       unsigned long first, last;
+
+       if (!n)
+               return;
+
+       first = FB_SHIFT_HIGH(~0UL, dst_idx);
+       last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
+
+       if (dst_idx+n <= bits) {
+               /* Single word */
+               if (last)
+                       first &= last;
+               *dst = comp(pat, *dst, first);
+       } else {
+               /* Multiple destination words */
+
+               /* Leading bits */
+               if (first!= ~0UL) {
+                       *dst = comp(pat, *dst, first);
+                       dst++;
+                       n -= bits - dst_idx;
+               }
+
+               /* Main chunk */
+               n /= bits;
+               while (n >= 8) {
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       *dst++ = pat;
+                       n -= 8;
+               }
+               while (n--)
+                       *dst++ = pat;
+               /* Trailing bits */
+               if (last)
+                       *dst = comp(pat, *dst, last);
+       }
+}
+
+
+    /*
+     *  Unaligned generic pattern fill using 32/64-bit memory accesses
+     *  The pattern must have been expanded to a full 32/64-bit value
+     *  Left/right are the appropriate shifts to convert to the pattern to be
+     *  used for the next 32/64-bit word
+     */
+
+static void
+bitfill_unaligned(unsigned long *dst, int dst_idx, unsigned long pat,
+                 int left, int right, unsigned n, int bits)
+{
+       unsigned long first, last;
+
+       if (!n)
+               return;
+
+       first = FB_SHIFT_HIGH(~0UL, dst_idx);
+       last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
+
+       if (dst_idx+n <= bits) {
+               /* Single word */
+               if (last)
+                       first &= last;
+               *dst = comp(pat, *dst, first);
+       } else {
+               /* Multiple destination words */
+               /* Leading bits */
+               if (first) {
+                       *dst = comp(pat, *dst, first);
+                       dst++;
+                       pat = pat << left | pat >> right;
+                       n -= bits - dst_idx;
+               }
+
+               /* Main chunk */
+               n /= bits;
+               while (n >= 4) {
+                       *dst++ = pat;
+                       pat = pat << left | pat >> right;
+                       *dst++ = pat;
+                       pat = pat << left | pat >> right;
+                       *dst++ = pat;
+                       pat = pat << left | pat >> right;
+                       *dst++ = pat;
+                       pat = pat << left | pat >> right;
+                       n -= 4;
+               }
+               while (n--) {
+                       *dst++ = pat;
+                       pat = pat << left | pat >> right;
+               }
+
+               /* Trailing bits */
+               if (last)
+                       *dst = comp(pat, *dst, first);
+       }
+}
+
+    /*
+     *  Aligned pattern invert using 32/64-bit memory accesses
+     */
+static void
+bitfill_aligned_rev(unsigned long *dst, int dst_idx, unsigned long pat,
+                   unsigned n, int bits)
+{
+       unsigned long val = pat;
+       unsigned long first, last;
+
+       if (!n)
+               return;
+
+       first = FB_SHIFT_HIGH(~0UL, dst_idx);
+       last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
+
+       if (dst_idx+n <= bits) {
+               /* Single word */
+               if (last)
+                       first &= last;
+               *dst = comp(*dst ^ val, *dst, first);
+       } else {
+               /* Multiple destination words */
+               /* Leading bits */
+               if (first!=0UL) {
+                       *dst = comp(*dst ^ val, *dst, first);
+                       dst++;
+                       n -= bits - dst_idx;
+               }
+
+               /* Main chunk */
+               n /= bits;
+               while (n >= 8) {
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       *dst++ ^= val;
+                       n -= 8;
+               }
+               while (n--)
+                       *dst++ ^= val;
+               /* Trailing bits */
+               if (last)
+                       *dst = comp(*dst ^ val, *dst, last);
+       }
+}
+
+
+    /*
+     *  Unaligned generic pattern invert using 32/64-bit memory accesses
+     *  The pattern must have been expanded to a full 32/64-bit value
+     *  Left/right are the appropriate shifts to convert to the pattern to be
+     *  used for the next 32/64-bit word
+     */
+
+static void
+bitfill_unaligned_rev(unsigned long *dst, int dst_idx, unsigned long pat,
+                       int left, int right, unsigned n, int bits)
+{
+       unsigned long first, last;
+
+       if (!n)
+               return;
+
+       first = FB_SHIFT_HIGH(~0UL, dst_idx);
+       last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
+
+       if (dst_idx+n <= bits) {
+               /* Single word */
+               if (last)
+                       first &= last;
+               *dst = comp(*dst ^ pat, *dst, first);
+       } else {
+               /* Multiple destination words */
+
+               /* Leading bits */
+               if (first != 0UL) {
+                       *dst = comp(*dst ^ pat, *dst, first);
+                       dst++;
+                       pat = pat << left | pat >> right;
+                       n -= bits - dst_idx;
+               }
+
+               /* Main chunk */
+               n /= bits;
+               while (n >= 4) {
+                       *dst++ ^= pat;
+                       pat = pat << left | pat >> right;
+                       *dst++ ^= pat;
+                       pat = pat << left | pat >> right;
+                       *dst++ ^= pat;
+                       pat = pat << left | pat >> right;
+                       *dst++ ^= pat;
+                       pat = pat << left | pat >> right;
+                       n -= 4;
+               }
+               while (n--) {
+                       *dst ^= pat;
+                       pat = pat << left | pat >> right;
+               }
+
+               /* Trailing bits */
+               if (last)
+                       *dst = comp(*dst ^ pat, *dst, last);
+       }
+}
+
+void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+       unsigned long pat, fg;
+       unsigned long width = rect->width, height = rect->height;
+       int bits = BITS_PER_LONG, bytes = bits >> 3;
+       u32 bpp = p->var.bits_per_pixel;
+       unsigned long *dst;
+       int dst_idx, left;
+
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+           p->fix.visual == FB_VISUAL_DIRECTCOLOR )
+               fg = ((u32 *) (p->pseudo_palette))[rect->color];
+       else
+               fg = rect->color;
+
+       pat = pixel_to_pat( bpp, fg);
+
+       dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
+       dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
+       dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
+       /* FIXME For now we support 1-32 bpp only */
+       left = bits % bpp;
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+       if (!left) {
+               void (*fill_op32)(unsigned long *dst, int dst_idx,
+                                 unsigned long pat, unsigned n, int bits) =
+                       NULL;
+
+               switch (rect->rop) {
+               case ROP_XOR:
+                       fill_op32 = bitfill_aligned_rev;
+                       break;
+               case ROP_COPY:
+                       fill_op32 = bitfill_aligned;
+                       break;
+               default:
+                       printk( KERN_ERR "cfb_fillrect(): unknown rop, "
+                               "defaulting to ROP_COPY\n");
+                       fill_op32 = bitfill_aligned;
+                       break;
+               }
+               while (height--) {
+                       dst += dst_idx >> (ffs(bits) - 1);
+                       dst_idx &= (bits - 1);
+                       fill_op32(dst, dst_idx, pat, width*bpp, bits);
+                       dst_idx += p->fix.line_length*8;
+               }
+       } else {
+               int right;
+               int r;
+               int rot = (left-dst_idx) % bpp;
+               void (*fill_op)(unsigned long *dst, int dst_idx,
+                               unsigned long pat, int left, int right,
+                               unsigned n, int bits) = NULL;
+
+               /* rotate pattern to correct start position */
+               pat = pat << rot | pat >> (bpp-rot);
+
+               right = bpp-left;
+               switch (rect->rop) {
+               case ROP_XOR:
+                       fill_op = bitfill_unaligned_rev;
+                       break;
+               case ROP_COPY:
+                       fill_op = bitfill_unaligned;
+                       break;
+               default:
+                       printk(KERN_ERR "cfb_fillrect(): unknown rop, "
+                               "defaulting to ROP_COPY\n");
+                       fill_op = bitfill_unaligned;
+                       break;
+               }
+               while (height--) {
+                       dst += dst_idx >> (ffs(bits) - 1);
+                       dst_idx &= (bits - 1);
+                       fill_op(dst, dst_idx, pat, left, right,
+                               width*bpp, bits);
+                       r = (p->fix.line_length*8) % bpp;
+                       pat = pat << (bpp-r) | pat >> r;
+                       dst_idx += p->fix.line_length*8;
+               }
+       }
+}
+
+EXPORT_SYMBOL(sys_fillrect);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/sysimgblt.c b/drivers/video/sysimgblt.c
new file mode 100644 (file)
index 0000000..bd7e7e9
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ *  Generic 1-bit or 8-bit source to 1-32 bit destination expansion
+ *  for frame buffer located in system RAM with packed pixels of any depth.
+ *
+ *  Based almost entirely on cfbimgblt.c
+ *
+ *      Copyright (C)  April 2007 Antonino Daplas <adaplas@pol.net>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <asm/types.h>
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt,__FUNCTION__,## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+static const u32 cfb_tab8[] = {
+#if defined(__BIG_ENDIAN)
+    0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
+    0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
+    0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
+    0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+    0x00000000,0xff000000,0x00ff0000,0xffff0000,
+    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
+    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
+    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+
+static const u32 cfb_tab16[] = {
+#if defined(__BIG_ENDIAN)
+    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+
+static const u32 cfb_tab32[] = {
+       0x00000000, 0xffffffff
+};
+
+static void color_imageblit(const struct fb_image *image, struct fb_info *p,
+                           void *dst1, u32 start_index, u32 pitch_index)
+{
+       /* Draw the penguin */
+       u32 *dst, *dst2;
+       u32 color = 0, val, shift;
+       int i, n, bpp = p->var.bits_per_pixel;
+       u32 null_bits = 32 - bpp;
+       u32 *palette = (u32 *) p->pseudo_palette;
+       const u8 *src = image->data;
+
+       dst2 = dst1;
+       for (i = image->height; i--; ) {
+               n = image->width;
+               dst = dst1;
+               shift = 0;
+               val = 0;
+
+               if (start_index) {
+                       u32 start_mask = ~(FB_SHIFT_HIGH(~(u32)0,
+                                                        start_index));
+                       val = *dst & start_mask;
+                       shift = start_index;
+               }
+               while (n--) {
+                       if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+                           p->fix.visual == FB_VISUAL_DIRECTCOLOR )
+                               color = palette[*src];
+                       else
+                               color = *src;
+                       color <<= FB_LEFT_POS(bpp);
+                       val |= FB_SHIFT_HIGH(color, shift);
+                       if (shift >= null_bits) {
+                               *dst++ = val;
+
+                               val = (shift == null_bits) ? 0 :
+                                       FB_SHIFT_LOW(color, 32 - shift);
+                       }
+                       shift += bpp;
+                       shift &= (32 - 1);
+                       src++;
+               }
+               if (shift) {
+                       u32 end_mask = FB_SHIFT_HIGH(~(u32)0, shift);
+
+                       *dst &= end_mask;
+                       *dst |= val;
+               }
+               dst1 += p->fix.line_length;
+               if (pitch_index) {
+                       dst2 += p->fix.line_length;
+                       dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
+
+                       start_index += pitch_index;
+                       start_index &= 32 - 1;
+               }
+       }
+}
+
+static void slow_imageblit(const struct fb_image *image, struct fb_info *p,
+                                 void *dst1, u32 fgcolor, u32 bgcolor,
+                                 u32 start_index, u32 pitch_index)
+{
+       u32 shift, color = 0, bpp = p->var.bits_per_pixel;
+       u32 *dst, *dst2;
+       u32 val, pitch = p->fix.line_length;
+       u32 null_bits = 32 - bpp;
+       u32 spitch = (image->width+7)/8;
+       const u8 *src = image->data, *s;
+       u32 i, j, l;
+
+       dst2 = dst1;
+       fgcolor <<= FB_LEFT_POS(bpp);
+       bgcolor <<= FB_LEFT_POS(bpp);
+
+       for (i = image->height; i--; ) {
+               shift = val = 0;
+               l = 8;
+               j = image->width;
+               dst = dst1;
+               s = src;
+
+               /* write leading bits */
+               if (start_index) {
+                       u32 start_mask = ~(FB_SHIFT_HIGH(~(u32)0,start_index));
+                       val = *dst & start_mask;
+                       shift = start_index;
+               }
+
+               while (j--) {
+                       l--;
+                       color = (*s & (1 << l)) ? fgcolor : bgcolor;
+                       val |= FB_SHIFT_HIGH(color, shift);
+
+                       /* Did the bitshift spill bits to the next long? */
+                       if (shift >= null_bits) {
+                               *dst++ = val;
+                               val = (shift == null_bits) ? 0 :
+                                       FB_SHIFT_LOW(color,32 - shift);
+                       }
+                       shift += bpp;
+                       shift &= (32 - 1);
+                       if (!l) { l = 8; s++; };
+               }
+
+               /* write trailing bits */
+               if (shift) {
+                       u32 end_mask = FB_SHIFT_HIGH(~(u32)0, shift);
+
+                       *dst &= end_mask;
+                       *dst |= val;
+               }
+
+               dst1 += pitch;
+               src += spitch;
+               if (pitch_index) {
+                       dst2 += pitch;
+                       dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1));
+                       start_index += pitch_index;
+                       start_index &= 32 - 1;
+               }
+
+       }
+}
+
+/*
+ * fast_imageblit - optimized monochrome color expansion
+ *
+ * Only if:  bits_per_pixel == 8, 16, or 32
+ *           image->width is divisible by pixel/dword (ppw);
+ *           fix->line_legth is divisible by 4;
+ *           beginning and end of a scanline is dword aligned
+ */
+static void fast_imageblit(const struct fb_image *image, struct fb_info *p,
+                                 void *dst1, u32 fgcolor, u32 bgcolor)
+{
+       u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel;
+       u32 ppw = 32/bpp, spitch = (image->width + 7)/8;
+       u32 bit_mask, end_mask, eorx, shift;
+       const char *s = image->data, *src;
+       u32 *dst;
+       const u32 *tab = NULL;
+       int i, j, k;
+
+       switch (bpp) {
+       case 8:
+               tab = cfb_tab8;
+               break;
+       case 16:
+               tab = cfb_tab16;
+               break;
+       case 32:
+       default:
+               tab = cfb_tab32;
+               break;
+       }
+
+       for (i = ppw-1; i--; ) {
+               fgx <<= bpp;
+               bgx <<= bpp;
+               fgx |= fgcolor;
+               bgx |= bgcolor;
+       }
+
+       bit_mask = (1 << ppw) - 1;
+       eorx = fgx ^ bgx;
+       k = image->width/ppw;
+
+       for (i = image->height; i--; ) {
+               dst = dst1;
+               shift = 8;
+               src = s;
+
+               for (j = k; j--; ) {
+                       shift -= ppw;
+                       end_mask = tab[(*src >> shift) & bit_mask];
+                       *dst++ = (end_mask & eorx) ^ bgx;
+                       if (!shift) {
+                               shift = 8;
+                               src++;
+                       }
+               }
+               dst1 += p->fix.line_length;
+               s += spitch;
+       }
+}
+
+void sys_imageblit(struct fb_info *p, const struct fb_image *image)
+{
+       u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
+       u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
+       u32 width = image->width;
+       u32 dx = image->dx, dy = image->dy;
+       void *dst1;
+
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
+       start_index = bitstart & (32 - 1);
+       pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
+
+       bitstart /= 8;
+       bitstart &= ~(bpl - 1);
+       dst1 = (void __force *)p->screen_base + bitstart;
+
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+
+       if (image->depth == 1) {
+               if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+                   p->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+                       fgcolor = ((u32*)(p->pseudo_palette))[image->fg_color];
+                       bgcolor = ((u32*)(p->pseudo_palette))[image->bg_color];
+               } else {
+                       fgcolor = image->fg_color;
+                       bgcolor = image->bg_color;
+               }
+
+               if (32 % bpp == 0 && !start_index && !pitch_index &&
+                   ((width & (32/bpp-1)) == 0) &&
+                   bpp >= 8 && bpp <= 32)
+                       fast_imageblit(image, p, dst1, fgcolor, bgcolor);
+               else
+                       slow_imageblit(image, p, dst1, fgcolor, bgcolor,
+                                       start_index, pitch_index);
+       } else
+               color_imageblit(image, p, dst1, start_index, pitch_index);
+}
+
+EXPORT_SYMBOL(sys_imageblit);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("1-bit/8-bit to 1-32 bit color expansion (sys-to-sys)");
+MODULE_LICENSE("GPL");
+
index f733bee..81713ac 100644 (file)
@@ -238,6 +238,7 @@ LV1_CALL(destruct_virtual_address_space,                1, 0,  10 )
 LV1_CALL(configure_irq_state_bitmap,                    3, 0,  11 )
 LV1_CALL(connect_irq_plug_ext,                          5, 0,  12 )
 LV1_CALL(release_memory,                                1, 0,  13 )
+LV1_CALL(put_iopte,                                     5, 0,  15 )
 LV1_CALL(disconnect_irq_plug_ext,                       3, 0,  17 )
 LV1_CALL(construct_event_receive_port,                  0, 1,  18 )
 LV1_CALL(destruct_event_receive_port,                   1, 0,  19 )
@@ -268,6 +269,8 @@ LV1_CALL(remove_repository_node,                        4, 0,  93 )
 LV1_CALL(read_htab_entries,                             2, 5,  95 )
 LV1_CALL(set_dabr,                                      2, 0,  96 )
 LV1_CALL(get_total_execution_time,                      2, 1, 103 )
+LV1_CALL(allocate_io_segment,                           3, 1, 116 )
+LV1_CALL(release_io_segment,                            2, 0, 117 )
 LV1_CALL(construct_io_irq_outlet,                       1, 1, 120 )
 LV1_CALL(destruct_io_irq_outlet,                        1, 0, 121 )
 LV1_CALL(map_htab,                                      1, 1, 122 )
index f809622..c37421e 100644 (file)
@@ -59,7 +59,6 @@ struct ps3_device_id {
        unsigned int dev_id;
 };
 
-
 /* dma routines */
 
 enum ps3_dma_page_size {
@@ -74,28 +73,57 @@ enum ps3_dma_region_type {
        PS3_DMA_INTERNAL = 2,
 };
 
+enum ps3_iobus_type {
+       PS3_IOBUS_IOC0 = 1,
+       PS3_IOBUS_SB
+};
+
+struct ps3_dma_region_ops;
+
 /**
+ * struct ps3_dma_region_ops - dma region operations
  * struct ps3_dma_region - A per device dma state variables structure
  * @did: The HV device id.
  * @page_size: The ioc pagesize.
  * @region_type: The HV region type.
  * @bus_addr: The 'translated' bus address of the region.
  * @len: The length in bytes of the region.
+ * @offset: The offset from the start of memory of the region.
+ * @ioid: The IOID of the device who owns this region
  * @chunk_list: Opaque variable used by the ioc page manager.
  */
 
 struct ps3_dma_region {
+       /* device variables (set by ps3_dma_region_init) */
+       const struct ps3_dma_region_ops *region_ops;
        struct ps3_device_id did;
+       unsigned char ioid;
        enum ps3_dma_page_size page_size;
        enum ps3_dma_region_type region_type;
-       unsigned long bus_addr;
        unsigned long len;
+       unsigned long offset;
+       //unsigned long iopte_flag;
+
+       /* driver variables  (set by ps3_dma_region_create) */
+       unsigned long bus_addr;
        struct {
                spinlock_t lock;
                struct list_head head;
        } chunk_list;
 };
 
+struct ps3_dma_region_ops {
+       int (*create)(struct ps3_dma_region *);
+       int (*free)(struct ps3_dma_region *);
+       int (*map)(struct ps3_dma_region *,
+                  unsigned long virt_addr,
+                  unsigned long len,
+                  unsigned long * bus_addr,
+                  u64 iopte_pp);
+       int (*unmap)(struct ps3_dma_region *,
+                    unsigned long bus_addr,
+                    unsigned long len);
+};
 /**
  * struct ps3_dma_region_init - Helper to initialize structure variables
  *
@@ -103,18 +131,16 @@ struct ps3_dma_region {
  * ps3_system_bus_device_register.
  */
 
-static inline void ps3_dma_region_init(struct ps3_dma_region *r,
-       const struct ps3_device_id* did, enum ps3_dma_page_size page_size,
-       enum ps3_dma_region_type region_type)
-{
-       r->did = *did;
-       r->page_size = page_size;
-       r->region_type = region_type;
-}
+void ps3_dma_region_init(struct ps3_dma_region *r,
+        const struct ps3_device_id *did,
+        enum ps3_dma_page_size page_size,
+        enum ps3_dma_region_type region_type, void *addr,
+        unsigned long len, enum ps3_iobus_type iobus_type);
 int ps3_dma_region_create(struct ps3_dma_region *r);
 int ps3_dma_region_free(struct ps3_dma_region *r);
 int ps3_dma_map(struct ps3_dma_region *r, unsigned long virt_addr,
-       unsigned long len, unsigned long *bus_addr);
+       unsigned long len, unsigned long *bus_addr,
+       u64 iopte_pp);
 int ps3_dma_unmap(struct ps3_dma_region *r, unsigned long bus_addr,
        unsigned long len);
 
@@ -125,6 +151,7 @@ enum ps3_mmio_page_size {
        PS3_MMIO_64K = 16U
 };
 
+struct ps3_mmio_region_ops;
 /**
  * struct ps3_mmio_region - a per device mmio state variables structure
  *
@@ -132,6 +159,7 @@ enum ps3_mmio_page_size {
  */
 
 struct ps3_mmio_region {
+       const struct ps3_mmio_region_ops * mmio_ops;
        struct ps3_device_id did;
        unsigned long bus_addr;
        unsigned long len;
@@ -139,6 +167,10 @@ struct ps3_mmio_region {
        unsigned long lpar_addr;
 };
 
+struct ps3_mmio_region_ops {
+       int (*create)(struct ps3_mmio_region *);
+       int (*free)(struct ps3_mmio_region *);
+};
 /**
  * struct ps3_mmio_region_init - Helper to initialize structure variables
  *
@@ -146,15 +178,10 @@ struct ps3_mmio_region {
  * ps3_system_bus_device_register.
  */
 
-static inline void ps3_mmio_region_init(struct ps3_mmio_region *r,
+void ps3_mmio_region_init(struct ps3_mmio_region *r,
        const struct ps3_device_id* did, unsigned long bus_addr,
-       unsigned long len, enum ps3_mmio_page_size page_size)
-{
-       r->did = *did;
-       r->bus_addr = bus_addr;
-       r->len = len;
-       r->page_size = page_size;
-}
+       unsigned long len, enum ps3_mmio_page_size page_size,
+                         enum ps3_iobus_type iobus_type);
 int ps3_mmio_region_create(struct ps3_mmio_region *r);
 int ps3_free_mmio_region(struct ps3_mmio_region *r);
 unsigned long ps3_mm_phys_to_lpar(unsigned long phys_addr);
@@ -294,6 +321,11 @@ enum ps3_match_id {
        PS3_MATCH_ID_GELIC,
        PS3_MATCH_ID_AV_SETTINGS,
        PS3_MATCH_ID_SYSTEM_MANAGER,
+       PS3_MATCH_ID_STOR_DISK,
+       PS3_MATCH_ID_STOR_ROM,
+       PS3_MATCH_ID_STOR_FLASH,
+       PS3_MATCH_ID_SOUND,
+       PS3_MATCH_ID_GFX,
 };
 
 /**
@@ -310,6 +342,18 @@ struct ps3_system_bus_device {
        struct device core;
 };
 
+static inline void ps3_system_bus_device_init(
+       struct ps3_system_bus_device *dev, enum ps3_match_id match_id,
+       struct ps3_dma_region * d_region, struct ps3_mmio_region * m_region)
+{
+       dev->match_id = match_id;
+       dev->m_region = m_region;
+       dev->d_region = d_region;
+};
+
+int ps3_open_hv_device(struct ps3_system_bus_device *dev);
+int ps3_close_hv_device(struct ps3_system_bus_device *dev);
+
 /**
  * struct ps3_system_bus_driver - a driver for a device on the system bus
  */
@@ -319,12 +363,15 @@ struct ps3_system_bus_driver {
        struct device_driver core;
        int (*probe)(struct ps3_system_bus_device *);
        int (*remove)(struct ps3_system_bus_device *);
+       int (*shutdown)(struct ps3_system_bus_device *);
 /*     int (*suspend)(struct ps3_system_bus_device *, pm_message_t); */
 /*     int (*resume)(struct ps3_system_bus_device *); */
 };
 
-int ps3_system_bus_device_register(struct ps3_system_bus_device *dev);
-int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv);
+int ps3_system_bus_device_register(struct ps3_system_bus_device *dev,
+                                  enum ps3_iobus_type iobus_type);
+int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv,
+                                  enum ps3_iobus_type iobus_type);
 void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv);
 static inline struct ps3_system_bus_driver *to_ps3_system_bus_driver(
        struct device_driver *_drv)
@@ -377,8 +424,15 @@ int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev);
 
 /* system manager */
 
-void ps3_sys_manager_restart(void);
+struct ps3_sys_manager_ops {
+       struct ps3_vuart_port_device *dev;
+       void (*power_off)(struct ps3_vuart_port_device *dev);
+       void (*restart)(struct ps3_vuart_port_device *dev);
+};
+
+void ps3_sys_manager_register_ops(const struct ps3_sys_manager_ops *ops);
 void ps3_sys_manager_power_off(void);
+void ps3_sys_manager_restart(void);
 
 struct ps3_prealloc {
     const char *name;
@@ -388,6 +442,7 @@ struct ps3_prealloc {
 };
 
 extern struct ps3_prealloc ps3fb_videomemory;
-extern struct ps3_prealloc ps3_stor_bounce_buffer;
+extern struct ps3_prealloc ps3flash_bounce_buffer;
+
 
 #endif
index 1366fc5..cb5fc9a 100644 (file)
@@ -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_
 
@@ -706,6 +709,7 @@ extern int ps3av_vuart_read(struct ps3_vuart_port_device *dev, void *buf,
 
 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);
 extern int ps3av_set_mode(u32, int);
 extern int ps3av_get_mode(void);
 extern int ps3av_get_scanmode(int);
@@ -715,5 +719,7 @@ 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 (*func)(int on));
+extern void ps3av_flip_ctl(int on);
 
 #endif /* _ASM_POWERPC_PS3AV_H_ */
index ad81cf4..05f3430 100644 (file)
@@ -43,14 +43,6 @@ struct ps3fb_ioctl_res {
 
 #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_ */
diff --git a/include/asm-powerpc/ps3stor.h b/include/asm-powerpc/ps3stor.h
new file mode 100644 (file)
index 0000000..3edf0cb
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * PS3 Storage Bus
+ *
+ * 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 <linux/irqreturn.h>
+
+#include <asm/ps3.h>
+
+
+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;
+       int lv1_res;
+       u64 lv1_tag;
+       u64 lv1_status;
+       struct completion irq_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_probe_access(struct ps3_storage_device *dev);
+extern irqreturn_t ps3stor_interrupt(int irq, void *data);
+extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
+                                     u64 start_sector, u64 sectors,
+                                     int write);
+
+#endif /* _ASM_POWERPC_PS3STOR_H_ */
index 21a4f85..7153c3a 100644 (file)
@@ -4,6 +4,8 @@
 #include <asm/types.h>
 #include <linux/i2c.h>
 
+struct dentry;
+
 /* Definitions of frame buffers                                                */
 
 #define FB_MAJOR               29
@@ -525,12 +527,20 @@ struct fb_cursor_user {
 #define FB_EVENT_MODE_CHANGE_ALL       0x0B
 /*     A software display blank change occured */
 #define FB_EVENT_CONBLANK               0x0C
+/*      Get drawing requirements        */
+#define FB_EVENT_GET_REQ                0x0D
 
 struct fb_event {
        struct fb_info *info;
        void *data;
 };
 
+struct fb_blit_caps {
+       u32 x;
+       u32 y;
+       u32 len;
+       u32 flags;
+};
 
 extern int fb_register_client(struct notifier_block *nb);
 extern int fb_unregister_client(struct notifier_block *nb);
@@ -556,11 +566,25 @@ struct fb_pixmap {
        u32 scan_align;         /* alignment per scanline               */
        u32 access_align;       /* alignment per read/write (bits)      */
        u32 flags;              /* see FB_PIXMAP_*                      */
+       u32 blit_x;             /* supported bit block dimensions (1-32)*/
+       u32 blit_y;             /* Format: blit_x = 1 << (width - 1)    */
+                               /*         blit_y = 1 << (height - 1)   */
+                               /* if 0, will be set to 0xffffffff (all)*/
        /* access methods */
        void (*writeio)(struct fb_info *info, void __iomem *dst, void *src, unsigned int size);
        void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size);
 };
 
+#ifdef CONFIG_FB_DEFERRED_IO
+struct fb_deferred_io {
+       /* delay between mkwrite and deferred handler */
+       unsigned long delay;
+       struct mutex lock; /* mutex that protects the page list */
+       struct list_head pagelist; /* list of touched pages */
+       /* callback */
+       void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
+};
+#endif
 
 /*
  * Frame buffer operations
@@ -579,8 +603,10 @@ struct fb_ops {
        /* For framebuffers with strange non linear layouts or that do not
         * work with normal memory mapped access
         */
-       ssize_t (*fb_read)(struct file *file, char __user *buf, size_t count, loff_t *ppos);
-       ssize_t (*fb_write)(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
+       ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
+                          size_t count, loff_t *ppos);
+       ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
+                           size_t count, loff_t *ppos);
 
        /* checks var and eventually tweaks it to something supported,
         * DO NOT MODIFY PAR */
@@ -634,10 +660,13 @@ struct fb_ops {
 
        /* restore saved state */
        void (*fb_restore_state)(struct fb_info *info);
+
+       /* get capability given var */
+       void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
+                           struct fb_var_screeninfo *var);
 };
 
 #ifdef CONFIG_FB_TILEBLITTING
-
 #define FB_TILE_CURSOR_NONE        0
 #define FB_TILE_CURSOR_UNDERLINE   1
 #define FB_TILE_CURSOR_LOWER_THIRD 2
@@ -709,6 +738,8 @@ struct fb_tile_ops {
        /* cursor */
        void (*fb_tilecursor)(struct fb_info *info,
                              struct fb_tilecursor *cursor);
+       /* get maximum length of the tile map */
+       int (*fb_get_tilemax)(struct fb_info *info);
 };
 #endif /* CONFIG_FB_TILEBLITTING */
 
@@ -778,6 +809,10 @@ struct fb_info {
        struct mutex bl_curve_mutex;    
        u8 bl_curve[FB_BACKLIGHT_LEVELS];
 #endif
+#ifdef CONFIG_FB_DEFERRED_IO
+       struct delayed_work deferred_work;
+       struct fb_deferred_io *fbdefio;
+#endif
 
        struct fb_ops *fbops;
        struct device *device;          /* This is the parent */
@@ -841,7 +876,7 @@ struct fb_info {
 #define fb_writeq sbus_writeq
 #define fb_memset sbus_memset_io
 
-#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || (defined(__sh__) && !defined(__SH5__)) || defined(__powerpc__)
+#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || (defined(__sh__) && !defined(__SH5__)) || defined(__powerpc__) || defined(__avr32__)
 
 #define fb_readb __raw_readb
 #define fb_readw __raw_readw
@@ -887,6 +922,16 @@ extern int fb_blank(struct fb_info *info, int blank);
 extern void cfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect); 
 extern void cfb_copyarea(struct fb_info *info, const struct fb_copyarea *area); 
 extern void cfb_imageblit(struct fb_info *info, const struct fb_image *image);
+/*
+ * Drawing operations where framebuffer is in system RAM
+ */
+extern void sys_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
+extern void sys_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+extern void sys_imageblit(struct fb_info *info, const struct fb_image *image);
+extern ssize_t fb_sys_read(struct fb_info *info, char __user *buf,
+                          size_t count, loff_t *ppos);
+extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
+                           size_t count, loff_t *ppos);
 
 /* drivers/video/fbmem.c */
 extern int register_framebuffer(struct fb_info *fb_info);
@@ -921,6 +966,12 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
        }
 }
 
+/* drivers/video/fb_defio.c */
+extern void fb_deferred_io_init(struct fb_info *info);
+extern void fb_deferred_io_cleanup(struct fb_info *info);
+extern int fb_deferred_io_fsync(struct file *file, struct dentry *dentry,
+                               int datasync);
+
 /* drivers/video/fbsysfs.c */
 extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev);
 extern void framebuffer_release(struct fb_info *info);
index 41456c1..4d8d37e 100644 (file)
@@ -251,6 +251,12 @@ struct hci_cp_set_event_flt {
        __u8     condition[0];
 } __attribute__ ((packed));
 
+struct hci_cp_set_event_flt_conn {
+       __u8 flt_type;
+       __u8 cond_type;
+       __u8 condition;
+} __attribute__ ((packed));
+
 /* Filter types */
 #define HCI_FLT_CLEAR_ALL      0x00
 #define HCI_FLT_INQ_RESULT     0x01
index b82146e..482a314 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -504,6 +504,7 @@ int page_mkclean(struct page *page)
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(page_mkclean);
 
 /**
  * page_set_anon_rmap - setup new anonymous rmap
index 4917919..187ba62 100644 (file)
@@ -46,6 +46,9 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#if defined(CONFIG_PPC_PS3)
+#include <asm/firmware.h>
+#endif
 
 #ifndef CONFIG_BT_HCI_CORE_DEBUG
 #undef  BT_DBG
@@ -239,6 +242,22 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
                hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, sizeof(cp), &cp);
        }
 
+#if defined(CONFIG_PPC_PS3)
+       /*
+        * The PS3 built-in bluetooth host controler doesn't support the
+        * HCI_FLT_CLEAR_ALL command properly.  Use the HCI_FLT_CONN_SETUP
+        * command to clear the event filter.
+        */
+       if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
+               struct hci_cp_set_event_flt_conn cp;
+               cp.flt_type = HCI_FLT_CONN_SETUP;
+               cp.cond_type = 0; /* all devices */
+               cp.condition = 1; /* auto accept is off */
+               hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, sizeof(cp),
+                       &cp);
+       }
+#endif
+
        /* Page timeout ~20 secs */
        param = __cpu_to_le16(0x8000);
        hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_PG_TIMEOUT, 2, &param);