f8a9b02d70e40b5b0c9bb14b2c0689857b2f4e62
[10.03/openwrt.git] / target / linux / rb532 / files / drivers / block / rb500 / bdev.c
1 /* CF-mips driver
2    This is a block driver for the direct (mmaped) interface to the CF-slot,
3    found in Routerboard.com's RB532 board
4    See SDK provided from routerboard.com.
5    
6    Module adapted By P.Christeas <p_christeas@yahoo.com>, 2005-6.
7    Cleaned up and adapted to platform_device by Felix Fietkau <nbd@openwrt.org>
8
9    This work is redistributed under the terms of the GNU General Public License.
10 */
11
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <linux/time.h>
16 #include <linux/wait.h>
17 #include <linux/fs.h>
18 #include <linux/genhd.h>
19 #include <linux/blkdev.h>
20 #include <linux/blkpg.h>
21 #include <linux/hdreg.h>
22 #include <linux/platform_device.h>
23
24 #include <asm/uaccess.h>
25 #include <asm/io.h>
26 #include <asm/gpio.h>
27
28 #include <asm/rc32434/rb.h>
29
30 #ifdef DEBUG
31 #define DEBUGP printk
32 #define DLEVEL 1
33 #else
34 #define DEBUGP(format, args...)
35 #define DLEVEL 0
36 #endif
37
38 #define CF_MIPS_MAJOR 13
39 #define MAJOR_NR        CF_MIPS_MAJOR
40 #define CF_MAX_PART     16              /* max 15 partitions */
41
42 #include "ata.h"
43
44 //extern struct block_device_operations cf_bdops;
45
46 // static struct hd_struct cf_parts[CF_MAX_PART];
47 // static int cf_part_sizes[CF_MAX_PART];
48 // static int cf_hsect_sizes[CF_MAX_PART];
49 // static int cf_max_sectors[CF_MAX_PART];
50 // static int cf_blksize_sizes[CF_MAX_PART];
51
52 // static spinlock_t lock = SPIN_LOCK_UNLOCKED;
53
54 // volatile int cf_busy = 0;
55
56 static struct request *active_req = NULL;
57
58 static int cf_open (struct inode *, struct file *);
59 static int cf_release (struct inode *, struct file *);
60 static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long);
61
62 static void cf_request(request_queue_t * q);
63 static int cf_transfer(const struct request *req);
64
65 /*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
66 long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/
67 // int (*direct_access) (struct block_device *, sector_t, unsigned long *);
68 // int (*media_changed) (struct gendisk *);
69 // int (*revalidate_disk) (struct gendisk *);
70
71 static struct block_device_operations cf_bdops = {
72       .owner = THIS_MODULE,
73       .open = cf_open,
74       .release = cf_release,
75       .ioctl = cf_ioctl,
76       .media_changed = NULL,
77       .unlocked_ioctl = NULL,
78       .revalidate_disk = NULL,
79       .compat_ioctl = NULL,
80       .direct_access = NULL
81 };
82
83
84 int cf_mips_probe(struct platform_device *pdev)
85 {
86         struct gendisk* cf_gendisk=NULL;
87         struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
88         struct cf_mips_dev *dev;
89         struct resource *r;
90         int reg_result;
91
92         reg_result = register_blkdev(MAJOR_NR, "cf-mips");
93         if (reg_result < 0) {
94                 printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR);
95                 return reg_result;
96         }
97
98         dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL);
99         if (!dev)
100                 goto out_err;
101         memset(dev, 0, sizeof(struct cf_mips_dev));
102         cdev->dev = dev;
103         
104         dev->pin = cdev->gpio_pin;
105         dev->irq = platform_get_irq_byname(pdev, "cf_irq");
106         r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase");
107         dev->base = (void *) r->start;
108         
109         if (cf_init(dev)) goto out_err;
110         printk("init done");
111         
112         spin_lock_init(&dev->lock);
113         dev->queue = blk_init_queue(cf_request,&dev->lock);
114         if (!dev->queue){
115                 printk(KERN_ERR "cf-mips: no mem for queue\n");
116                 goto out_err;
117         }
118         blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD);
119
120         /* For memory devices, it is always better to avoid crossing segments
121         inside the same request. */
122 /*      if (dev->dtype==0x848A){
123                 printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1);
124                 blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1);
125         }*/
126
127         dev->gd = alloc_disk(CF_MAX_PART);
128         cf_gendisk = dev->gd;
129         cdev->gd = dev->gd;
130         if (!cf_gendisk) goto out_err; /* Last of these goto's */
131         
132         cf_gendisk->major = MAJOR_NR;
133         cf_gendisk->first_minor = 0;
134         cf_gendisk->queue=dev->queue;
135         BUG_ON(cf_gendisk->minors != CF_MAX_PART);
136         strcpy(cf_gendisk->disk_name,"cfa");
137         cf_gendisk->fops = &cf_bdops;
138         cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */
139         cf_gendisk->private_data=dev;
140         
141         set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL);
142         
143         /* Let the disk go live */
144         add_disk(cf_gendisk);
145 #if 0
146         result = cf_init();
147         
148         /* default cfg for all partitions */
149         memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART);
150         memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART);
151         for (i = 0; i < CF_MAX_PART; ++i) {
152                 cf_hsect_sizes[i] = CF_SECT_SIZE;
153                 cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD;
154                 cf_blksize_sizes[i] = BLOCK_SIZE;
155         }
156
157         /* setup info for whole disk (partition 0) */
158         cf_part_sizes[0] = cf_sectors / 2;
159         cf_parts[0].nr_sects = cf_sectors;
160
161         blk_size[MAJOR_NR] = cf_part_sizes;
162         blksize_size[MAJOR_NR] = cf_blksize_sizes;
163         max_sectors[MAJOR_NR] = cf_max_sectors;
164         hardsect_size[MAJOR_NR] = cf_hsect_sizes;
165         read_ahead[MAJOR_NR] = 8;       /* (4kB) */
166
167         blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
168
169         add_gendisk(&cf_gendisk);
170 #endif
171 //      printk(KERN_INFO "cf-mips partition check: \n");
172 //      register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART,
173 //                    &cf_bdops, dev->sectors);
174         return 0;
175
176 out_err:
177         if (dev->queue){
178                 blk_cleanup_queue(dev->queue);
179         }
180         if (reg_result) {
181                 unregister_blkdev(MAJOR_NR, "cf-mips");
182                 return reg_result;
183         }
184         if (dev){
185                 cf_cleanup(dev);
186                 kfree(dev);
187         }
188         return 1;
189 }
190
191 static int
192 cf_mips_remove(struct platform_device *pdev)
193 {
194         struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data;
195         struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev;
196         
197         unregister_blkdev(MAJOR_NR, "cf-mips");
198         blk_cleanup_queue(dev->queue);
199
200         del_gendisk(dev->gd);
201         cf_cleanup(dev);
202         return 0;
203 }
204
205
206 static struct platform_driver cf_driver = {
207         .driver.name = "rb500-cf",
208         .probe = cf_mips_probe,
209         .remove = cf_mips_remove,
210 };
211
212 static int __init cf_mips_init(void)
213 {
214         printk(KERN_INFO "cf-mips module loaded\n");
215         return platform_driver_register(&cf_driver);
216 }
217
218 static void cf_mips_cleanup(void)
219 {
220         platform_driver_unregister(&cf_driver);
221         printk(KERN_INFO "cf-mips module removed\n");
222 }
223
224 module_init(cf_mips_init);
225 module_exit(cf_mips_cleanup);
226
227 MODULE_LICENSE("GPL");
228 MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR);
229
230
231 static int cf_open(struct inode *inode, struct file *filp)
232 {
233         struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
234         int minor = MINOR(inode->i_rdev);
235         
236         if (minor >= CF_MAX_PART)
237                 return -ENODEV;
238         //DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor);
239         spin_lock(&dev->lock);
240         dev->users++;
241         spin_unlock(&dev->lock);
242         filp->private_data=dev;
243         
244         /* dirty workaround to set CFRDY GPIO as an input when some other
245            program sets it as an output */
246         gpio_set_value(dev->pin, 0);
247         return 0;               /* success */
248 }
249
250 static int cf_release(struct inode *inode, struct file *filp)
251 {
252         int minor = MINOR(inode->i_rdev);
253         struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
254         spin_lock(&dev->lock);
255         dev->users--;
256         spin_unlock(&dev->lock);
257         return 0;
258 }
259
260 static int cf_ioctl(struct inode *inode, struct file *filp,
261          unsigned int cmd, unsigned long arg)
262 {
263         unsigned minor = MINOR(inode->i_rdev);
264         struct cf_mips_dev  *dev=inode->i_bdev->bd_disk->private_data;
265
266         DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd);
267         switch (cmd) {
268         case BLKRRPART: /* re-read partition table */
269                 if (!capable(CAP_SYS_ADMIN))
270                         return -EACCES;
271                 printk(KERN_INFO "cf-mips partition check: \n");
272                 register_disk(dev->gd);
273                 return 0;
274
275         case HDIO_GETGEO:
276                 {
277                         struct hd_geometry geo;
278                         geo.cylinders = dev->cyl;
279                         geo.heads = dev->head;
280                         geo.sectors = dev->spt;
281                         geo.start = (*dev->gd->part)[minor].start_sect;
282                         if (copy_to_user((void *) arg, &geo, sizeof (geo)))
283                                 return -EFAULT;
284                 }
285                 return 0;
286         }
287
288         return -EINVAL;         /* unknown command */
289 }
290
291 static void cf_request(request_queue_t * q)
292 {
293         struct cf_mips_dev* dev;
294         
295         struct request * req;
296         int status;
297
298         /* We could have q->queuedata = dev , but haven't yet. */
299         if (active_req)
300                 return;
301
302         while ((req=elv_next_request(q))!=NULL){
303                 dev=req->rq_disk->private_data;
304                 status=cf_transfer(req);
305                 if (status==CF_TRANS_IN_PROGRESS){
306                         active_req=req;
307                         return;
308                 }
309                 end_request(req,status);
310         }
311 }
312
313 static int cf_transfer(const struct request *req)
314 {
315         struct cf_mips_dev* dev=req->rq_disk->private_data;
316
317         if (!blk_fs_request(req)){      
318                 if (printk_ratelimit())
319                         printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]);
320                 return CF_TRANS_FAILED;
321         }
322         
323         return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req));
324 }
325
326 void cf_async_trans_done(struct cf_mips_dev * dev,int result)
327 {
328         struct request *req;
329         
330         spin_lock(&dev->lock);
331         req=active_req;
332         active_req=NULL;
333         end_request(req,result);
334         spin_unlock(&dev->lock);
335
336         spin_lock(&dev->lock);
337         cf_request(dev->queue);
338         spin_unlock(&dev->lock);
339 }
340