Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / raid / ida / ida_disk.c
1 /*-
2  * Copyright (c) 1999,2000 Jonathan Lemon
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/ida/ida_disk.c,v 1.12.2.6 2001/11/27 20:21:02 ps Exp $
27  */
28
29 /*
30  * Disk driver for Compaq SMART RAID adapters.
31  */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/malloc.h>
36 #include <sys/kernel.h>
37
38 #include <sys/buf.h>
39 #include <sys/bus.h>
40 #include <sys/conf.h>
41 #include <sys/cons.h>
42 #include <sys/devicestat.h>
43 #include <sys/disk.h>
44
45 #if NPCI > 0
46 #include <machine/bus_memio.h>
47 #endif
48 #include <machine/bus_pio.h>
49 #include <machine/bus.h>
50 #include <machine/clock.h>
51 #include <sys/rman.h>
52
53 #include <vm/vm.h>
54 #include <vm/pmap.h>
55 #include <machine/md_var.h>
56
57 #include <dev/ida/idareg.h>
58 #include <dev/ida/idavar.h>
59
60 /* prototypes */
61 static int idad_probe(device_t dev);
62 static int idad_attach(device_t dev);
63 static int idad_detach(device_t dev);
64
65 static  d_open_t        idad_open;
66 static  d_close_t       idad_close;
67 static  d_strategy_t    idad_strategy;
68 static  d_dump_t        idad_dump;
69
70 #define IDAD_BDEV_MAJOR 29
71 #define IDAD_CDEV_MAJOR 109
72
73 static struct cdevsw id_cdevsw = {
74         /* open */      idad_open,
75         /* close */     idad_close,
76         /* read */      physread,
77         /* write */     physwrite,
78         /* ioctl */     noioctl,
79         /* poll */      nopoll,
80         /* mmap */      nommap,
81         /* strategy */  idad_strategy,
82         /* name */      "idad",
83         /* maj */       IDAD_CDEV_MAJOR,
84         /* dump */      idad_dump,
85         /* psize */     nopsize,
86         /* flags */     D_DISK,
87         /* bmaj */      IDAD_BDEV_MAJOR
88 };
89
90 static devclass_t       idad_devclass;
91 static struct cdevsw    idaddisk_cdevsw;
92 static int              disks_registered = 0;
93
94 static device_method_t idad_methods[] = {
95         DEVMETHOD(device_probe,         idad_probe),
96         DEVMETHOD(device_attach,        idad_attach),
97         DEVMETHOD(device_detach,        idad_detach),
98         { 0, 0 }
99 };
100
101 static driver_t idad_driver = {
102         "idad",
103         idad_methods,
104         sizeof(struct idad_softc)
105 };
106
107 DRIVER_MODULE(idad, ida, idad_driver, idad_devclass, 0, 0);
108
109 static __inline struct idad_softc *
110 idad_getsoftc(dev_t dev)
111 {
112
113         return ((struct idad_softc *)dev->si_drv1);
114 }
115
116 static int
117 idad_open(dev_t dev, int flags, int fmt, struct proc *p)
118 {
119         struct idad_softc *drv;
120         struct disklabel *label;
121
122         drv = idad_getsoftc(dev);
123         if (drv == NULL)
124                 return (ENXIO);
125
126         label = &drv->disk.d_label;
127         bzero(label, sizeof(*label));
128         label->d_type = DTYPE_SCSI;
129         label->d_secsize = drv->secsize;
130         label->d_nsectors = drv->sectors;
131         label->d_ntracks = drv->heads;
132         label->d_ncylinders = drv->cylinders;
133         label->d_secpercyl = drv->sectors * drv->heads;
134         label->d_secperunit = drv->secperunit;
135
136         return (0);
137 }
138
139 static int
140 idad_close(dev_t dev, int flags, int fmt, struct proc *p)
141 {
142         struct idad_softc *drv;
143
144         drv = idad_getsoftc(dev);
145         if (drv == NULL)
146                 return (ENXIO);
147         return (0);
148 }
149
150 /*
151  * Read/write routine for a buffer.  Finds the proper unit, range checks
152  * arguments, and schedules the transfer.  Does not wait for the transfer
153  * to complete.  Multi-page transfers are supported.  All I/O requests must
154  * be a multiple of a sector in length.
155  */
156 static void
157 idad_strategy(struct buf *bp)
158 {
159         struct idad_softc *drv;
160         int s;
161
162         drv = idad_getsoftc(bp->b_dev);
163         if (drv == NULL) {
164                 bp->b_error = EINVAL;
165                 goto bad;
166         }
167
168         /*
169          * software write protect check
170          */
171         if (drv->flags & DRV_WRITEPROT && (bp->b_flags & B_READ) == 0) {
172                 bp->b_error = EROFS;
173                 goto bad;
174         }
175
176         /*
177          * If it's a null transfer, return immediately
178          */
179         if (bp->b_bcount == 0)
180                 goto done;
181
182         bp->b_driver1 = drv;
183         s = splbio();
184         devstat_start_transaction(&drv->stats);
185         ida_submit_buf(drv->controller, bp);
186         splx(s);
187         return;
188
189 bad:
190         bp->b_flags |= B_ERROR;
191
192 done:
193         /*
194          * Correctly set the buf to indicate a completed transfer
195          */
196         bp->b_resid = bp->b_bcount;
197         biodone(bp);
198         return;
199 }
200
201 static int
202 idad_dump(dev_t dev)
203 {
204         struct idad_softc *drv;
205         u_int count, blkno, secsize;
206         long blkcnt;
207         int i, error, dumppages;
208         caddr_t va;
209         vm_offset_t addr, a;
210
211         if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize)))
212                 return (error);
213
214         drv = idad_getsoftc(dev);
215         if (drv == NULL)
216                 return (ENXIO);
217
218         addr = 0;
219         blkcnt = howmany(PAGE_SIZE, secsize);
220
221         while (count > 0) {
222                 va = NULL;
223
224                 dumppages = imin(count / blkcnt, MAXDUMPPGS); 
225
226                 for (i = 0; i < dumppages; i++) {
227                         a = addr + (i * PAGE_SIZE);
228                         if (is_physical_memory(a))
229                                 va = pmap_kenter_temporary(trunc_page(a), i);
230                         else
231                                 va = pmap_kenter_temporary(trunc_page(0), i);
232                 }
233
234                 error = ida_command(drv->controller, CMD_WRITE, va,
235                     PAGE_SIZE * dumppages, drv->drive, blkno, DMA_DATA_OUT);
236                 if (error)
237                         return (error);
238
239                 if (dumpstatus(addr, (off_t)count * DEV_BSIZE) < 0)
240                         return (EINTR);
241
242                 blkno += blkcnt * dumppages;
243                 count -= blkcnt * dumppages;
244                 addr += PAGE_SIZE * dumppages;
245         }
246         return (0);
247 }
248
249 void
250 idad_intr(struct buf *bp)
251 {
252         struct idad_softc *drv = (struct idad_softc *)bp->b_driver1;
253
254         if (bp->b_flags & B_ERROR)
255                 bp->b_error = EIO;
256         else
257                 bp->b_resid = 0;
258
259         devstat_end_transaction_buf(&drv->stats, bp);
260         biodone(bp);
261 }
262
263 static int
264 idad_probe(device_t dev)
265 {
266
267         device_set_desc(dev, "Compaq Logical Drive");
268         return (0);
269 }
270
271 static int
272 idad_attach(device_t dev)
273 {
274         struct ida_drive_info dinfo;
275         struct idad_softc *drv;
276         device_t parent;
277         dev_t dsk;
278         int error;
279
280         drv = (struct idad_softc *)device_get_softc(dev);
281         parent = device_get_parent(dev);
282         drv->controller = (struct ida_softc *)device_get_softc(parent);
283         drv->unit = device_get_unit(dev);
284         drv->drive = drv->controller->num_drives;
285         drv->controller->num_drives++;
286
287         error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO,
288             &dinfo, sizeof(dinfo), drv->drive, 0, DMA_DATA_IN);
289         if (error) {
290                 device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n");
291                 return (ENXIO);
292         }
293
294         drv->cylinders = dinfo.ncylinders;
295         drv->heads = dinfo.nheads;
296         drv->sectors = dinfo.nsectors;
297         drv->secsize = dinfo.secsize == 0 ? 512 : dinfo.secsize;
298         drv->secperunit = dinfo.secperunit;
299
300         /* XXX
301          * other initialization
302          */
303         device_printf(dev, "%uMB (%u sectors), blocksize=%d\n",
304             drv->secperunit / ((1024 * 1024) / drv->secsize),
305             drv->secperunit, drv->secsize);
306
307         devstat_add_entry(&drv->stats, "idad", drv->unit, drv->secsize,
308             DEVSTAT_NO_ORDERED_TAGS,
309             DEVSTAT_TYPE_STORARRAY| DEVSTAT_TYPE_IF_OTHER,
310             DEVSTAT_PRIORITY_ARRAY);
311
312         dsk = disk_create(drv->unit, &drv->disk, 0,
313             &id_cdevsw, &idaddisk_cdevsw);
314
315         dsk->si_drv1 = drv;
316         dsk->si_iosize_max = DFLTPHYS;          /* XXX guess? */
317         disks_registered++;
318
319         return (0);
320 }
321
322 static int
323 idad_detach(device_t dev)
324 {
325         struct idad_softc *drv;
326
327         drv = (struct idad_softc *)device_get_softc(dev);
328         devstat_remove_entry(&drv->stats);
329
330         if (--disks_registered == 0)
331                 cdevsw_remove(&idaddisk_cdevsw);
332         return (0);
333 }