--- /dev/null
+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);
#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];
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)
{
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)
{
int i, ret;
+ for (i = 0; i < MAX_NUMNODES; i++)
+ INIT_LIST_HEAD(&spu_list[i]);
+
if (!spu_management_ops)
return 0;
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) {
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);
{
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;
}
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,
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,
{
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)
{
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,
{
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)
{
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,
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,
};
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,
};
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)
{
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,
#include <asm/prom.h>
#include <asm/semaphore.h>
#include <asm/spu.h>
+#include <asm/spu_priv1.h>
#include <asm/uaccess.h>
#include "spufs.h"
ei->i_gang = NULL;
ei->i_ctx = NULL;
- ei->i_openers = 0;
return &ei->vfs_inode;
}
{
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,
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;
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)
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.
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
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
#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"
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);
+ 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",
return result;
fail:
-#if defined(DEBUG)
+#ifdef DEBUG
memset(dev, 0xad, sizeof(struct ps3_system_bus_device)
+ sizeof(struct ps3_dma_region));
#endif
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);
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",
return result;
fail:
-#if defined(DEBUG)
+#ifdef DEBUG
memset(p, 0xad, sizeof(struct ohci_layout));
#endif
kfree(p);
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);
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",
return result;
fail:
-#if defined(DEBUG)
+#ifdef DEBUG
memset(p, 0xad, sizeof(struct ohci_layout));
#endif
kfree(p);
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);
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",
return result;
fail:
-#if defined(DEBUG)
+#ifdef DEBUG
memset(p, 0xad, sizeof(struct ehci_layout));
#endif
kfree(p);
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);
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",
return result;
fail:
-#if defined(DEBUG)
+#ifdef DEBUG
memset(p, 0xad, sizeof(struct ehci_layout));
#endif
kfree(p);
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;
//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;
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)
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.
goto fail_set;
}
+ ps3_chip_mask(*virq);
+
return result;
fail_set:
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)
{
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));
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)
{
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;
}
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);
{
int result;
+ ps3_chip_mask(virq);
result = lv1_deconfigure_virtual_uart_irq();
if (result) {
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;
}
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);
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;
}
* 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>
#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 {
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;
}
}
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;
}
/*============================================================================*/
/**
- * 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__)
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);
}
/**
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);
{
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;
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.
* 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;
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));
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",
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.
* 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,
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.
* 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);
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;
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.
* 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;
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",
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;
}
* @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);
}
/*============================================================================*/
/* 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;
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,
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
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)
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)
#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)
#endif
prealloc_ps3fb_videomemory();
- prealloc_ps3_stor_bounce_buffer();
+ prealloc_ps3flash_bounce_buffer();
ppc_md.power_save = ps3_power_save;
#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__);
}
--- /dev/null
+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
.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)
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;
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);
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)
{
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,
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;
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;
}
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,
};
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;
}
* 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;
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",
* 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);
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",
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)
{
}
}
-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);
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",
#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)
#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
};
* 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);
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;
}
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);
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
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
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
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
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;
}
{
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;
}
--- /dev/null
+/*
+ * 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");
+
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
--- /dev/null
+/*
+ * 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");
#ifdef CONFIG_VT_CONSOLE
+void vt_console_stop(void)
+{
+ printable = 0;
+}
+EXPORT_SYMBOL_GPL(vt_console_stop);
+
/*
* Console on virtual terminal
*
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
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
/*
* 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;
}
* 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();
* @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;
}
}
*
* 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;
}
* 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) &
/* 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;
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;
}
/**
*
* 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;
}
/**
*
* 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;
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;
*
* 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;
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);
- }
}
/**
* 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);
}
/**
* 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);
}
/**
*
* 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);
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;
- }
- }
+
}
/**
* 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
* 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;
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;
}
/**
*
* 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;
}
* @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
* 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;
}
/**
* 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;
}
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;
- }
}
/**
*
* 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;
}
/**
*
* 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;
/**
* 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;
}
*
* 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);
* 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;
}
card->dev->interrupt_id, card->netdev->irq);
card->netdev->irq = NO_IRQ;
fail_alloc_irq:
- return ret;
+ return result;
}
* 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;
}
#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) {
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);
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;
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);
};
#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;
*
* 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;
*
* 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;
#ifdef GELIC_NET_ETHTOOL
netdev->ethtool_ops = &gelic_net_ethtool_ops;
#endif
- netdev->do_ioctl = &gelic_net_ioctl;
}
/**
*
* 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;
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;
}
*
* 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;
*/
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;
}
.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);
}
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+
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
/*
- * 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>
u32 audio_port;
int ps3av_mode;
int ps3av_mode_old;
+ void (*flip_ctl)(int on);
} ps3av;
static struct ps3_vuart_port_device ps3av_dev = {
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;
if (res < 0) {
dev_dbg(&ps3av_dev.core,
"%s: ps3av_vuart_write() failed (result=%d)\n",
- __FUNCTION__, res);
+ __func__, res);
return res;
}
if (res != PS3AV_HDR_SIZE) {
dev_dbg(&ps3av_dev.core,
"%s: ps3av_vuart_read() failed (result=%d)\n",
- __FUNCTION__, res);
+ __func__, res);
return res;
}
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 */
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;
}
if (res < 0) {
printk(KERN_ERR
"%s: ps3av_send_cmd_pkt() failed (result=%d)\n",
- __FUNCTION__, res);
+ __func__, res);
goto err;
}
usr_buf_size);
if (res < 0) {
printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n",
- __FUNCTION__, res);
+ __func__, res);
goto err;
}
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;
}
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");
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;
}
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;
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;
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;
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;
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;
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;
/* 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);
if (error) {
printk(KERN_ERR
"%s: ps3_vuart_port_driver_register failed %d\n",
- __FUNCTION__, error);
+ __func__, error);
return error;
}
if (error)
printk(KERN_ERR
"%s: ps3_vuart_port_device_register failed %d\n",
- __FUNCTION__, error);
+ __func__, error);
return error;
}
{
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),
res);
out:
- ps3fb_flip_ctl(1); /* flip on */
+ ps3av_flip_ctl(1); /* flip on */
return res;
}
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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;
+}
/**
* 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.
*
* @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 {
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 {
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,
};
};
/**
+ * ps3_sm_force_power_off - Poweroff helper.
+ *
+ * A global variable used to force a poweroff when the power button has
+ * been pressed irrespective of how init handles the ctrl_alt_del signal.
+ *
+ */
+
+static unsigned int ps3_sm_force_power_off;
+
+/**
* ps3_sys_manager_write - Helper to write a two part message to the vuart.
*
*/
static int ps3_sys_manager_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];
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;
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;
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;
* 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);
}
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];
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;
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",
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",
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;
/**
* 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)
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:
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);
}
/**
- * 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)
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);
}
/**
- * 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);
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 = {
},
.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)
}
module_init(ps3_sys_manager_init);
+/* Module remove not supported. */
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.
return 0;
}
+EXPORT_SYMBOL_GPL(ps3_vuart_write);
/**
* ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list.
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)
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
if(!dev->core.driver) {
dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
__LINE__);
+ up(&vuart_bus_priv.probe_mutex);
return 0;
}
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
up(&vuart_bus_priv.probe_mutex);
-
return 0;
}
if(!dev->core.driver) {
dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
__LINE__);
+ up(&vuart_bus_priv.probe_mutex);
return;
}
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
up(&vuart_bus_priv.probe_mutex);
-
return;
}
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/
--- /dev/null
+/*
+ * 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");
#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) && \
#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);
*/
#include <asm/ps3.h>
+#include <asm/lv1call.h>
static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
{
#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;
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__,
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) {
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,
};
#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) && \
#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;
}
*/
#include <asm/ps3.h>
+#include <asm/lv1call.h>
static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
{
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;
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__,
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) {
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,
};
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
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.
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
* 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;
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;
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
{
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);
}
}
+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)
{
struct fb_info *info = event->info;
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
+ struct fb_blit_caps *caps;
int ret = 0;
/*
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:
}
}
-static void fbcon_exit(void)
+void fbcon_exit(void)
{
struct fb_info *info;
int i, j, mapped;
fbcon_has_exited = 1;
}
+EXPORT_SYMBOL_GPL(fbcon_exit);
static int __init fb_console_init(void)
{
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)
}
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)
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
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;
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;
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 = ∩︀
+ 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)
{
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;
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);
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;
}
}
}
+
+ done:
return 0;
}
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);
#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;
}
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);
#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
struct ps3fb_priv {
unsigned int irq_no;
- void *dev;
u64 context_handle, memory_handle;
void *xdr_ea;
{ 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") */
#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)
{
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;
(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;
}
{
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:
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;
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;
}
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;
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;
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",
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;
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,
};
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;
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);
&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;
}
* 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;
if (retval < 0)
goto err_fb_dealloc;
- platform_set_drvdata(dev, info);
+ dev->core.driver_data = info;
printk(KERN_INFO
"fb%d: PS3 frame buffer device, using %ld KiB of video memory\n",
info->node, ps3fb_videomemory.size >> 10);
- task = kthread_run(ps3fbd, info, "ps3fbd");
+ task = kthread_run(ps3fbd, info, DEVICE_NAME);
if (IS_ERR(task)) {
retval = PTR_ERR(task);
goto err_unregister_framebuffer;
ps3fb.task = task;
+ printk(" <- %s:%u\n", __func__, __LINE__);
return 0;
err_unregister_framebuffer:
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);
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;
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);
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.");
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
}
#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;
--- /dev/null
+/*
+ * 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");
+
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
+
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 )
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 )
unsigned int dev_id;
};
-
/* dma routines */
enum ps3_dma_page_size {
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
*
* 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);
PS3_MMIO_64K = 16U
};
+struct ps3_mmio_region_ops;
/**
* struct ps3_mmio_region - a per device mmio state variables structure
*
*/
struct ps3_mmio_region {
+ const struct ps3_mmio_region_ops * mmio_ops;
struct ps3_device_id did;
unsigned long bus_addr;
unsigned long len;
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
*
* 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);
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,
};
/**
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
*/
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)
/* 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;
};
extern struct ps3_prealloc ps3fb_videomemory;
-extern struct ps3_prealloc ps3_stor_bounce_buffer;
+extern struct ps3_prealloc ps3flash_bounce_buffer;
+
#endif
/*
- * 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_
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);
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_ */
#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_ */
--- /dev/null
+/*
+ * 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_ */
#include <asm/types.h>
#include <linux/i2c.h>
+struct dentry;
+
/* Definitions of frame buffers */
#define FB_MAJOR 29
#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);
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
/* 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 */
/* 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
/* 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 */
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 */
#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
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);
}
}
+/* 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);
__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
return ret;
}
+EXPORT_SYMBOL_GPL(page_mkclean);
/**
* page_set_anon_rmap - setup new anonymous rmap
#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
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, ¶m);