1114902Sscottl/*-
2119418Sobrien * Written by: David Jeffery
3114902Sscottl * Copyright (c) 2002 Adaptec Inc.
4114902Sscottl * All rights reserved.
5114902Sscottl *
6114902Sscottl * Redistribution and use in source and binary forms, with or without
7114902Sscottl * modification, are permitted provided that the following conditions
8114902Sscottl * are met:
9114902Sscottl * 1. Redistributions of source code must retain the above copyright
10114902Sscottl *    notice, this list of conditions and the following disclaimer.
11114902Sscottl * 2. Redistributions in binary form must reproduce the above copyright
12114902Sscottl *    notice, this list of conditions and the following disclaimer in the
13114902Sscottl *    documentation and/or other materials provided with the distribution.
14114902Sscottl *
15114902Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16114902Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17114902Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18114902Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19114902Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20114902Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21114902Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22114902Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23114902Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24114902Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25114902Sscottl * SUCH DAMAGE.
26114902Sscottl */
27114902Sscottl
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD$");
30114902Sscottl
31152919Sscottl#include <dev/ips/ipsreg.h>
32114902Sscottl#include <dev/ips/ips.h>
33114902Sscottl#include <dev/ips/ips_disk.h>
34114902Sscottl#include <sys/stat.h>
35114902Sscottl
36114902Sscottlstatic int ipsd_probe(device_t dev);
37114902Sscottlstatic int ipsd_attach(device_t dev);
38114902Sscottlstatic int ipsd_detach(device_t dev);
39114902Sscottl
40141062Sscottlstatic int ipsd_dump(void *arg, void *virtual, vm_offset_t physical,
41141062Sscottl		     off_t offset, size_t length);
42141062Sscottlstatic void ipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs,
43141062Sscottl			     int error);
44141062Sscottlstatic void ipsd_dump_block_complete(ips_command_t *command);
45141062Sscottl
46114902Sscottlstatic disk_open_t ipsd_open;
47114902Sscottlstatic disk_close_t ipsd_close;
48114902Sscottlstatic disk_strategy_t ipsd_strategy;
49114902Sscottl
50114902Sscottlstatic device_method_t ipsd_methods[] = {
51114902Sscottl	DEVMETHOD(device_probe,		ipsd_probe),
52114902Sscottl	DEVMETHOD(device_attach,	ipsd_attach),
53114902Sscottl	DEVMETHOD(device_detach,	ipsd_detach),
54114902Sscottl	{ 0, 0 }
55114902Sscottl};
56114902Sscottl
57114902Sscottlstatic driver_t ipsd_driver = {
58114902Sscottl	"ipsd",
59114902Sscottl	ipsd_methods,
60114902Sscottl	sizeof(ipsdisk_softc_t)
61114902Sscottl};
62114902Sscottl
63114902Sscottlstatic devclass_t ipsd_devclass;
64114902SscottlDRIVER_MODULE(ipsd, ips, ipsd_driver, ipsd_devclass, 0, 0);
65114902Sscottl
66114902Sscottl/* handle opening of disk device.  It must set up all
67114902Sscottl   information about the geometry and size of the disk */
68114902Sscottlstatic int ipsd_open(struct disk *dp)
69114902Sscottl{
70114902Sscottl	ipsdisk_softc_t *dsc = dp->d_drv1;
71114902Sscottl
72114902Sscottl	dsc->state |= IPS_DEV_OPEN;
73114902Sscottl	DEVICE_PRINTF(2, dsc->dev, "I'm open\n");
74114902Sscottl       	return 0;
75114902Sscottl}
76114902Sscottl
77114902Sscottlstatic int ipsd_close(struct disk *dp)
78114902Sscottl{
79114902Sscottl	ipsdisk_softc_t *dsc = dp->d_drv1;
80114902Sscottl	dsc->state &= ~IPS_DEV_OPEN;
81114902Sscottl	DEVICE_PRINTF(2, dsc->dev, "I'm closed for the day\n");
82114902Sscottl        return 0;
83114902Sscottl}
84114902Sscottl
85114902Sscottl/* ipsd_finish is called to clean up and return a completed IO request */
86114902Sscottlvoid ipsd_finish(struct bio *iobuf)
87114902Sscottl{
88126364Sscottl	ipsdisk_softc_t *dsc;
89126364Sscottl	dsc = iobuf->bio_disk->d_drv1;
90126364Sscottl
91114902Sscottl	if (iobuf->bio_flags & BIO_ERROR) {
92114902Sscottl		ipsdisk_softc_t *dsc;
93114902Sscottl		dsc = iobuf->bio_disk->d_drv1;
94114902Sscottl		device_printf(dsc->dev, "iobuf error %d\n", iobuf->bio_error);
95114902Sscottl	} else
96114902Sscottl		iobuf->bio_resid = 0;
97114902Sscottl
98114902Sscottl	biodone(iobuf);
99126364Sscottl	ips_start_io_request(dsc->sc);
100114902Sscottl}
101114902Sscottl
102114902Sscottl
103114902Sscottlstatic void ipsd_strategy(struct bio *iobuf)
104114902Sscottl{
105114902Sscottl	ipsdisk_softc_t *dsc;
106114902Sscottl
107114902Sscottl	dsc = iobuf->bio_disk->d_drv1;
108114902Sscottl	DEVICE_PRINTF(8,dsc->dev,"in strategy\n");
109116931Speter	iobuf->bio_driver1 = (void *)(uintptr_t)dsc->sc->drives[dsc->disk_number].drivenum;
110126364Sscottl	mtx_lock(&dsc->sc->queue_mtx);
111140923Sscottl	bioq_insert_tail(&dsc->sc->queue, iobuf);
112140923Sscottl	ips_start_io_request(dsc->sc);
113126364Sscottl	mtx_unlock(&dsc->sc->queue_mtx);
114114902Sscottl}
115114902Sscottl
116114902Sscottlstatic int ipsd_probe(device_t dev)
117114902Sscottl{
118114902Sscottl	DEVICE_PRINTF(2,dev, "in probe\n");
119114902Sscottl	device_set_desc(dev, "Logical Drive");
120114902Sscottl	return 0;
121114902Sscottl}
122114902Sscottl
123114902Sscottlstatic int ipsd_attach(device_t dev)
124114902Sscottl{
125114902Sscottl	device_t adapter;
126114902Sscottl	ipsdisk_softc_t *dsc;
127114902Sscottl	u_int totalsectors;
128114902Sscottl
129114902Sscottl	DEVICE_PRINTF(2,dev, "in attach\n");
130114902Sscottl
131114902Sscottl	dsc = (ipsdisk_softc_t *)device_get_softc(dev);
132114902Sscottl	bzero(dsc, sizeof(ipsdisk_softc_t));
133114902Sscottl	adapter = device_get_parent(dev);
134114902Sscottl	dsc->dev = dev;
135114902Sscottl	dsc->sc = device_get_softc(adapter);
136114902Sscottl	dsc->unit = device_get_unit(dev);
137116931Speter	dsc->disk_number = (uintptr_t) device_get_ivars(dev);
138125975Sphk	dsc->ipsd_disk = disk_alloc();
139125975Sphk	dsc->ipsd_disk->d_drv1 = dsc;
140125975Sphk	dsc->ipsd_disk->d_name = "ipsd";
141125975Sphk	dsc->ipsd_disk->d_maxsize = IPS_MAX_IO_SIZE;
142125975Sphk	dsc->ipsd_disk->d_open = ipsd_open;
143125975Sphk	dsc->ipsd_disk->d_close = ipsd_close;
144125975Sphk	dsc->ipsd_disk->d_strategy = ipsd_strategy;
145141062Sscottl	dsc->ipsd_disk->d_dump = ipsd_dump;
146114902Sscottl
147114902Sscottl	totalsectors = dsc->sc->drives[dsc->disk_number].sector_count;
148114902Sscottl   	if ((totalsectors > 0x400000) &&
149114902Sscottl       			((dsc->sc->adapter_info.miscflags & 0x8) == 0)) {
150125975Sphk      		dsc->ipsd_disk->d_fwheads = IPS_NORM_HEADS;
151125975Sphk      		dsc->ipsd_disk->d_fwsectors = IPS_NORM_SECTORS;
152114902Sscottl   	} else {
153125975Sphk      		dsc->ipsd_disk->d_fwheads = IPS_COMP_HEADS;
154125975Sphk      		dsc->ipsd_disk->d_fwsectors = IPS_COMP_SECTORS;
155114902Sscottl   	}
156125975Sphk	dsc->ipsd_disk->d_sectorsize = IPS_BLKSIZE;
157125975Sphk	dsc->ipsd_disk->d_mediasize = (off_t)totalsectors * IPS_BLKSIZE;
158125975Sphk	dsc->ipsd_disk->d_unit = dsc->unit;
159140923Sscottl	dsc->ipsd_disk->d_flags = 0;
160125975Sphk	disk_create(dsc->ipsd_disk, DISK_VERSION);
161114902Sscottl
162114902Sscottl	device_printf(dev, "Logical Drive  (%dMB)\n",
163114902Sscottl		      dsc->sc->drives[dsc->disk_number].sector_count >> 11);
164114902Sscottl	return 0;
165114902Sscottl}
166114902Sscottl
167114902Sscottlstatic int ipsd_detach(device_t dev)
168114902Sscottl{
169114902Sscottl	ipsdisk_softc_t *dsc;
170114902Sscottl
171114902Sscottl	DEVICE_PRINTF(2, dev,"in detach\n");
172114902Sscottl	dsc = (ipsdisk_softc_t *)device_get_softc(dev);
173114902Sscottl	if(dsc->state & IPS_DEV_OPEN)
174114902Sscottl		return (EBUSY);
175125975Sphk	disk_destroy(dsc->ipsd_disk);
176114902Sscottl	return 0;
177114902Sscottl}
178141062Sscottl
179141062Sscottlstatic int
180141062Sscottlipsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset,
181141062Sscottl	  size_t length)
182141062Sscottl{
183141062Sscottl	ipsdisk_softc_t *dsc;
184141062Sscottl	ips_softc_t *sc;
185141062Sscottl	ips_command_t *command;
186141062Sscottl	ips_io_cmd *command_struct;
187141062Sscottl	struct disk *dp;
188141062Sscottl	void *va;
189141062Sscottl	off_t off;
190141062Sscottl	size_t len;
191141062Sscottl	int error = 0;
192141062Sscottl
193141062Sscottl	dp = arg;
194141062Sscottl	dsc = dp->d_drv1;
195141062Sscottl
196141062Sscottl	if (dsc == NULL)
197141062Sscottl		return (EINVAL);
198198329Sbrueffer	sc = dsc->sc;
199141062Sscottl
200141062Sscottl	if (ips_get_free_cmd(sc, &command, 0) != 0) {
201141062Sscottl		printf("ipsd: failed to get cmd for dump\n");
202141062Sscottl		return (ENOMEM);
203141062Sscottl	}
204141062Sscottl
205141062Sscottl	command->data_dmatag = sc->sg_dmatag;
206141062Sscottl	command->callback = ipsd_dump_block_complete;
207141062Sscottl
208141062Sscottl	command_struct = (ips_io_cmd *)command->command_buffer;
209141062Sscottl	command_struct->id = command->id;
210141062Sscottl	command_struct->drivenum= sc->drives[dsc->disk_number].drivenum;
211141062Sscottl
212141062Sscottl	off = offset;
213141062Sscottl	va = virtual;
214141062Sscottl
215141062Sscottl	while (length > 0) {
216141062Sscottl		len =
217141062Sscottl		    (length > IPS_MAX_IO_SIZE) ? IPS_MAX_IO_SIZE : length;
218141062Sscottl
219141062Sscottl		command_struct->lba = off / IPS_BLKSIZE;
220141062Sscottl
221141062Sscottl		if (bus_dmamap_load(command->data_dmatag, command->data_dmamap,
222141062Sscottl		    va, len, ipsd_dump_map_sg, command, BUS_DMA_NOWAIT) != 0) {
223141062Sscottl			error = EIO;
224141062Sscottl			break;
225141062Sscottl		}
226150535Sscottl		if (COMMAND_ERROR(command)) {
227141062Sscottl			error = EIO;
228141062Sscottl			break;
229141062Sscottl		}
230141062Sscottl
231141062Sscottl		length -= len;
232141062Sscottl		off += len;
233141062Sscottl		va = (uint8_t *)va + len;
234141062Sscottl	}
235141062Sscottl
236141062Sscottl	ips_insert_free_cmd(command->sc, command);
237141062Sscottl	return (error);
238141062Sscottl}
239141062Sscottl
240141062Sscottlstatic void
241141062Sscottlipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
242141062Sscottl{
243141062Sscottl	ips_softc_t *sc;
244141062Sscottl	ips_command_t *command;
245141062Sscottl	ips_sg_element_t *sg_list;
246141062Sscottl	ips_io_cmd *command_struct;
247141062Sscottl	int i, length;
248141062Sscottl
249141062Sscottl	command = (ips_command_t *)arg;
250141062Sscottl	sc = command->sc;
251141062Sscottl	length = 0;
252141062Sscottl
253141062Sscottl	if (error) {
254141062Sscottl		printf("ipsd_dump_map_sg: error %d\n", error);
255150535Sscottl		ips_set_error(command, error);
256141062Sscottl		return;
257141062Sscottl	}
258141062Sscottl
259141062Sscottl	command_struct = (ips_io_cmd *)command->command_buffer;
260141062Sscottl
261141062Sscottl	if (nsegs != 1) {
262141062Sscottl		command_struct->segnum = nsegs;
263141062Sscottl		sg_list = (ips_sg_element_t *)((uint8_t *)
264141062Sscottl		    command->command_buffer + IPS_COMMAND_LEN);
265141062Sscottl		for (i = 0; i < nsegs; i++) {
266141062Sscottl			sg_list[i].addr = segs[i].ds_addr;
267141062Sscottl			sg_list[i].len = segs[i].ds_len;
268141062Sscottl			length += segs[i].ds_len;
269141062Sscottl		}
270141062Sscottl		command_struct->buffaddr =
271141062Sscottl		    (uint32_t)command->command_phys_addr + IPS_COMMAND_LEN;
272141062Sscottl		command_struct->command = IPS_SG_WRITE_CMD;
273141062Sscottl	} else {
274141062Sscottl		command_struct->buffaddr = segs[0].ds_addr;
275141062Sscottl		length = segs[0].ds_len;
276141062Sscottl		command_struct->segnum = 0;
277141062Sscottl		command_struct->command = IPS_WRITE_CMD;
278141062Sscottl	}
279141062Sscottl
280141062Sscottl	length = (length + IPS_BLKSIZE - 1) / IPS_BLKSIZE;
281141062Sscottl	command_struct->length = length;
282141062Sscottl	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
283141062Sscottl	    BUS_DMASYNC_PREWRITE);
284141062Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
285141062Sscottl	    BUS_DMASYNC_PREWRITE);
286141062Sscottl
287141062Sscottl	sc->ips_issue_cmd(command);
288141062Sscottl	sc->ips_poll_cmd(command);
289141062Sscottl	return;
290141062Sscottl}
291141062Sscottl
292141062Sscottlstatic void
293141062Sscottlipsd_dump_block_complete(ips_command_t *command)
294141062Sscottl{
295141062Sscottl
296150535Sscottl	if (COMMAND_ERROR(command))
297141062Sscottl		printf("ipsd_dump completion error= 0x%x\n",
298141062Sscottl		    command->status.value);
299141062Sscottl
300141062Sscottl	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
301141062Sscottl	    BUS_DMASYNC_POSTWRITE);
302141062Sscottl	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
303141062Sscottl}
304