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