Add basic 2.6.24 support for rb532, korina napi code has to be adapted to work
[openwrt.git] / target / linux / rb532 / files-2.6.24 / drivers / block / rb500 / bdev.c
diff --git a/target/linux/rb532/files-2.6.24/drivers/block/rb500/bdev.c b/target/linux/rb532/files-2.6.24/drivers/block/rb500/bdev.c
new file mode 100644 (file)
index 0000000..f8a9b02
--- /dev/null
@@ -0,0 +1,340 @@
+/* CF-mips driver
+   This is a block driver for the direct (mmaped) interface to the CF-slot,
+   found in Routerboard.com's RB532 board
+   See SDK provided from routerboard.com.
+   
+   Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
+   Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
+
+   This work is redistributed under the terms of the GNU General Public License.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+#include <linux/hdreg.h>
+#include <linux/platform_device.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+#include <asm/rc32434/rb.h>
+
+#ifdef DEBUG
+#define DEBUGP printk
+#define DLEVEL 1
+#else
+#define DEBUGP(format, args...)
+#define DLEVEL 0
+#endif
+
+#define CF_MIPS_MAJOR 13
+#define MAJOR_NR       CF_MIPS_MAJOR
+#define CF_MAX_PART    16              /* max 15 partitions */
+
+#include "ata.h"
+
+//extern struct block_device_operations cf_bdops;
+
+// static struct hd_struct cf_parts[CF_MAX_PART];
+// static int cf_part_sizes[CF_MAX_PART];
+// static int cf_hsect_sizes[CF_MAX_PART];
+// static int cf_max_sectors[CF_MAX_PART];
+// static int cf_blksize_sizes[CF_MAX_PART];
+
+// static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+// volatile int cf_busy = 0;
+
+static struct request *active_req = NULL;
+
+static int cf_open (struct inode *, struct file *);
+static int cf_release (struct inode *, struct file *);
+static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long);
+
+static void cf_request(request_queue_t * q);
+static int cf_transfer(const struct request *req);
+
+/*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
+long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/
+// int (*direct_access) (struct block_device *, sector_t, unsigned long *);
+// int (*media_changed) (struct gendisk *);
+// int (*revalidate_disk) (struct gendisk *);
+
+static struct block_device_operations cf_bdops = {
+      .owner = THIS_MODULE,
+      .open = cf_open,
+      .release = cf_release,
+      .ioctl = cf_ioctl,
+      .media_changed = NULL,
+      .unlocked_ioctl = NULL,
+      .revalidate_disk = NULL,
+      .compat_ioctl = NULL,
+      .direct_access = NULL
+};
+
+
+int cf_mips_probe(struct platform_device *pdev)
+{
+       struct gendisk* cf_gendisk=NULL;
+       struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
+       struct cf_mips_dev *dev;
+       struct resource *r;
+       int reg_result;
+
+       reg_result = register_blkdev(MAJOR_NR, "cf-mips");
+       if (reg_result < 0) {
+               printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR);
+               return reg_result;
+       }
+
+       dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL);
+       if (!dev)
+               goto out_err;
+       memset(dev, 0, sizeof(struct cf_mips_dev));
+       cdev->dev = dev;
+       
+       dev->pin = cdev->gpio_pin;
+       dev->irq = platform_get_irq_byname(pdev, "cf_irq");
+       r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase");
+       dev->base = (void *) r->start;
+       
+       if (cf_init(dev)) goto out_err;
+       printk("init done");
+       
+       spin_lock_init(&dev->lock);
+       dev->queue = blk_init_queue(cf_request,&dev->lock);
+       if (!dev->queue){
+               printk(KERN_ERR "cf-mips: no mem for queue\n");
+               goto out_err;
+       }
+       blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD);
+
+       /* For memory devices, it is always better to avoid crossing segments
+       inside the same request. */
+/*     if (dev->dtype==0x848A){
+               printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1);
+               blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1);
+       }*/
+
+       dev->gd = alloc_disk(CF_MAX_PART);
+       cf_gendisk = dev->gd;
+       cdev->gd = dev->gd;
+       if (!cf_gendisk) goto out_err; /* Last of these goto's */
+       
+       cf_gendisk->major = MAJOR_NR;
+       cf_gendisk->first_minor = 0;
+       cf_gendisk->queue=dev->queue;
+       BUG_ON(cf_gendisk->minors != CF_MAX_PART);
+       strcpy(cf_gendisk->disk_name,"cfa");
+       cf_gendisk->fops = &cf_bdops;
+       cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */
+       cf_gendisk->private_data=dev;
+       
+       set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL);
+       
+       /* Let the disk go live */
+       add_disk(cf_gendisk);
+#if 0
+       result = cf_init();
+       
+       /* default cfg for all partitions */
+       memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART);
+       memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART);
+       for (i = 0; i < CF_MAX_PART; ++i) {
+               cf_hsect_sizes[i] = CF_SECT_SIZE;
+               cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD;
+               cf_blksize_sizes[i] = BLOCK_SIZE;
+       }
+
+       /* setup info for whole disk (partition 0) */
+       cf_part_sizes[0] = cf_sectors / 2;
+       cf_parts[0].nr_sects = cf_sectors;
+
+       blk_size[MAJOR_NR] = cf_part_sizes;
+       blksize_size[MAJOR_NR] = cf_blksize_sizes;
+       max_sectors[MAJOR_NR] = cf_max_sectors;
+       hardsect_size[MAJOR_NR] = cf_hsect_sizes;
+       read_ahead[MAJOR_NR] = 8;       /* (4kB) */
+
+       blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+
+       add_gendisk(&cf_gendisk);
+#endif
+//     printk(KERN_INFO "cf-mips partition check: \n");
+//     register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART,
+//                   &cf_bdops, dev->sectors);
+       return 0;
+
+out_err:
+       if (dev->queue){
+               blk_cleanup_queue(dev->queue);
+       }
+       if (reg_result) {
+               unregister_blkdev(MAJOR_NR, "cf-mips");
+               return reg_result;
+       }
+       if (dev){
+               cf_cleanup(dev);
+               kfree(dev);
+       }
+       return 1;
+}
+
+static int
+cf_mips_remove(struct platform_device *pdev)
+{
+       struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
+       struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev;
+       
+       unregister_blkdev(MAJOR_NR, "cf-mips");
+       blk_cleanup_queue(dev->queue);
+
+       del_gendisk(dev->gd);
+       cf_cleanup(dev);
+       return 0;
+}
+
+
+static struct platform_driver cf_driver = {
+       .driver.name = "rb500-cf",
+       .probe = cf_mips_probe,
+       .remove = cf_mips_remove,
+};
+
+static int __init cf_mips_init(void)
+{
+       printk(KERN_INFO "cf-mips module loaded\n");
+       return platform_driver_register(&cf_driver);
+}
+
+static void cf_mips_cleanup(void)
+{
+       platform_driver_unregister(&cf_driver);
+       printk(KERN_INFO "cf-mips module removed\n");
+}
+
+module_init(cf_mips_init);
+module_exit(cf_mips_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR);
+
+
+static int cf_open(struct inode *inode, struct file *filp)
+{
+       struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
+       int minor = MINOR(inode->i_rdev);
+       
+       if (minor >= CF_MAX_PART)
+               return -ENODEV;
+       //DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor);
+       spin_lock(&dev->lock);
+       dev->users++;
+       spin_unlock(&dev->lock);
+       filp->private_data=dev;
+       
+       /* dirty workaround to set CFRDY GPIO as an input when some other
+          program sets it as an output */
+       gpio_set_value(dev->pin, 0);
+       return 0;               /* success */
+}
+
+static int cf_release(struct inode *inode, struct file *filp)
+{
+       int minor = MINOR(inode->i_rdev);
+       struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
+       spin_lock(&dev->lock);
+       dev->users--;
+       spin_unlock(&dev->lock);
+       return 0;
+}
+
+static int cf_ioctl(struct inode *inode, struct file *filp,
+        unsigned int cmd, unsigned long arg)
+{
+       unsigned minor = MINOR(inode->i_rdev);
+       struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
+
+       DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd);
+       switch (cmd) {
+       case BLKRRPART: /* re-read partition table */
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EACCES;
+               printk(KERN_INFO "cf-mips partition check: \n");
+               register_disk(dev->gd);
+               return 0;
+
+       case HDIO_GETGEO:
+               {
+                       struct hd_geometry geo;
+                       geo.cylinders = dev->cyl;
+                       geo.heads = dev->head;
+                       geo.sectors = dev->spt;
+                       geo.start = (*dev->gd->part)[minor].start_sect;
+                       if (copy_to_user((void *) arg, &geo, sizeof (geo)))
+                               return -EFAULT;
+               }
+               return 0;
+       }
+
+       return -EINVAL;         /* unknown command */
+}
+
+static void cf_request(request_queue_t * q)
+{
+       struct cf_mips_dev* dev;
+       
+       struct request * req;
+       int status;
+
+       /* We could have q->queuedata = dev , but haven't yet. */
+       if (active_req)
+               return;
+
+       while ((req=elv_next_request(q))!=NULL){
+               dev=req->rq_disk->private_data;
+               status=cf_transfer(req);
+               if (status==CF_TRANS_IN_PROGRESS){
+                       active_req=req;
+                       return;
+               }
+               end_request(req,status);
+       }
+}
+
+static int cf_transfer(const struct request *req)
+{
+       struct cf_mips_dev* dev=req->rq_disk->private_data;
+
+       if (!blk_fs_request(req)){      
+               if (printk_ratelimit())
+                       printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]);
+               return CF_TRANS_FAILED;
+       }
+       
+       return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req));
+}
+
+void cf_async_trans_done(struct cf_mips_dev * dev,int result)
+{
+       struct request *req;
+       
+       spin_lock(&dev->lock);
+       req=active_req;
+       active_req=NULL;
+       end_request(req,result);
+       spin_unlock(&dev->lock);
+
+       spin_lock(&dev->lock);
+       cf_request(dev->queue);
+       spin_unlock(&dev->lock);
+}
+
This page took 0.040909 seconds and 4 git commands to generate.