1101100Ssos/*-
2230132Suqs * Copyright (c) 2001,2002,2003 S��ren Schmidt <sos@FreeBSD.org>
3101100Ssos * All rights reserved.
4101100Ssos *
5101100Ssos * Redistribution and use in source and binary forms, with or without
6101100Ssos * modification, are permitted provided that the following conditions
7101100Ssos * are met:
8101100Ssos * 1. Redistributions of source code must retain the above copyright
9101100Ssos *    notice, this list of conditions and the following disclaimer,
10101100Ssos *    without modification, immediately at the beginning of the file.
11101100Ssos * 2. Redistributions in binary form must reproduce the above copyright
12101100Ssos *    notice, this list of conditions and the following disclaimer in the
13101100Ssos *    documentation and/or other materials provided with the distribution.
14101100Ssos * 3. The name of the author may not be used to endorse or promote products
15101100Ssos *    derived from this software without specific prior written permission.
16101100Ssos *
17101100Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18101100Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19101100Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20101100Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21101100Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22101100Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23101100Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24101100Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25101100Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26101100Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27101100Ssos */
28101100Ssos
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD$");
31119418Sobrien
32101100Ssos#include <sys/param.h>
33101100Ssos#include <sys/systm.h>
34101100Ssos#include <sys/kernel.h>
35101100Ssos#include <sys/module.h>
36101100Ssos#include <sys/bus.h>
37101100Ssos#include <sys/bio.h>
38101100Ssos#include <sys/conf.h>
39101100Ssos#include <sys/eventhandler.h>
40101100Ssos#include <sys/malloc.h>
41101100Ssos#include <sys/lock.h>
42101100Ssos#include <sys/mutex.h>
43101100Ssos#include <vm/vm.h>
44101100Ssos#include <vm/pmap.h>
45101100Ssos#include <machine/stdarg.h>
46101100Ssos#include <machine/resource.h>
47101100Ssos#include <machine/bus.h>
48101100Ssos#include <sys/rman.h>
49119285Simp#include <dev/pci/pcivar.h>
50119285Simp#include <dev/pci/pcireg.h>
51112946Sphk#include <geom/geom_disk.h>
52101100Ssos
53101100Ssos#include "dev/pst/pst-iop.h"
54101100Ssos
55101100Ssosstruct pst_softc {
56102058Ssos    struct iop_softc		*iop;
57102058Ssos    struct i2o_lct_entry	*lct;
58102058Ssos    struct i2o_bsa_device	*info;
59125975Sphk    struct disk			*disk;
60102058Ssos    struct bio_queue_head	queue;
61101100Ssos};
62101100Ssos
63101100Ssosstruct pst_request {
64101100Ssos    struct pst_softc		*psc;		/* pointer to softc */
65101100Ssos    u_int32_t			mfa;		/* frame addreess */
66101100Ssos    struct callout_handle	timeout_handle; /* handle for untimeout */
67101100Ssos    struct bio			*bp;		/* associated bio ptr */
68101100Ssos};
69101100Ssos
70101100Ssos/* prototypes */
71111472Sphkstatic disk_strategy_t pststrategy;
72101100Ssosstatic int pst_probe(device_t);
73101100Ssosstatic int pst_attach(device_t);
74101100Ssosstatic int pst_shutdown(device_t);
75101100Ssosstatic void pst_start(struct pst_softc *);
76101100Ssosstatic void pst_done(struct iop_softc *, u_int32_t, struct i2o_single_reply *);
77101100Ssosstatic int pst_rw(struct pst_request *);
78101100Ssosstatic void pst_timeout(struct pst_request *);
79101100Ssosstatic void bpack(int8_t *, int8_t *, int);
80101100Ssos
81101100Ssos/* local vars */
82101100Ssosstatic MALLOC_DEFINE(M_PSTRAID, "pst", "Promise SuperTrak RAID driver");
83101100Ssos
84101100Ssosint
85101100Ssospst_add_raid(struct iop_softc *sc, struct i2o_lct_entry *lct)
86101100Ssos{
87101100Ssos    struct pst_softc *psc;
88101100Ssos    device_t child = device_add_child(sc->dev, "pst", -1);
89101100Ssos
90101100Ssos    if (!child)
91101100Ssos	return ENOMEM;
92111472Sphk    if (!(psc = malloc(sizeof(struct pst_softc),
93111472Sphk		       M_PSTRAID, M_NOWAIT | M_ZERO))) {
94111472Sphk	device_delete_child(sc->dev, child);
95111472Sphk	return ENOMEM;
96111472Sphk    }
97101100Ssos    psc->iop = sc;
98101100Ssos    psc->lct = lct;
99101100Ssos    device_set_softc(child, psc);
100101100Ssos    return bus_generic_attach(sc->dev);
101101100Ssos}
102101100Ssos
103101100Ssosstatic int
104101100Ssospst_probe(device_t dev)
105101100Ssos{
106101100Ssos    device_set_desc(dev, "Promise SuperTrak RAID");
107101100Ssos    return 0;
108101100Ssos}
109101100Ssos
110101100Ssosstatic int
111101100Ssospst_attach(device_t dev)
112101100Ssos{
113101100Ssos    struct pst_softc *psc = device_get_softc(dev);
114101100Ssos    struct i2o_get_param_reply *reply;
115101100Ssos    struct i2o_device_identity *ident;
116101100Ssos    int lun = device_get_unit(dev);
117101100Ssos    int8_t name [32];
118101100Ssos
119101100Ssos    if (!(reply = iop_get_util_params(psc->iop, psc->lct->local_tid,
120101100Ssos				      I2O_PARAMS_OPERATION_FIELD_GET,
121101100Ssos				      I2O_BSA_DEVICE_INFO_GROUP_NO)))
122101100Ssos	return ENODEV;
123101100Ssos
124101100Ssos    if (!(psc->info = (struct i2o_bsa_device *)
125101100Ssos	    malloc(sizeof(struct i2o_bsa_device), M_PSTRAID, M_NOWAIT))) {
126135267Ssos	contigfree(reply, PAGE_SIZE, M_PSTIOP);
127101100Ssos	return ENOMEM;
128101100Ssos    }
129101100Ssos    bcopy(reply->result, psc->info, sizeof(struct i2o_bsa_device));
130135267Ssos    contigfree(reply, PAGE_SIZE, M_PSTIOP);
131101100Ssos
132101100Ssos    if (!(reply = iop_get_util_params(psc->iop, psc->lct->local_tid,
133101100Ssos				      I2O_PARAMS_OPERATION_FIELD_GET,
134101100Ssos				      I2O_UTIL_DEVICE_IDENTITY_GROUP_NO)))
135101100Ssos	return ENODEV;
136101100Ssos    ident = (struct i2o_device_identity *)reply->result;
137101100Ssos#ifdef PSTDEBUG
138101100Ssos    printf("pst: vendor=<%.16s> product=<%.16s>\n",
139101100Ssos	   ident->vendor, ident->product);
140101100Ssos    printf("pst: description=<%.16s> revision=<%.8s>\n",
141101100Ssos	   ident->description, ident->revision);
142101100Ssos    printf("pst: capacity=%lld blocksize=%d\n",
143101100Ssos	   psc->info->capacity, psc->info->block_size);
144101100Ssos#endif
145101100Ssos    bpack(ident->vendor, ident->vendor, 16);
146101100Ssos    bpack(ident->product, ident->product, 16);
147101100Ssos    sprintf(name, "%s %s", ident->vendor, ident->product);
148135267Ssos    contigfree(reply, PAGE_SIZE, M_PSTIOP);
149101100Ssos
150101100Ssos    bioq_init(&psc->queue);
151101100Ssos
152125975Sphk    psc->disk = disk_alloc();
153125975Sphk    psc->disk->d_name = "pst";
154125975Sphk    psc->disk->d_strategy = pststrategy;
155125975Sphk    psc->disk->d_maxsize = 64 * 1024; /*I2O_SGL_MAX_SEGS * PAGE_SIZE;*/
156125975Sphk    psc->disk->d_drv1 = psc;
157125975Sphk    psc->disk->d_unit = lun;
158101100Ssos
159125975Sphk    psc->disk->d_sectorsize = psc->info->block_size;
160125975Sphk    psc->disk->d_mediasize = psc->info->capacity;
161125975Sphk    psc->disk->d_fwsectors = 63;
162125975Sphk    psc->disk->d_fwheads = 255;
163101100Ssos
164125975Sphk    disk_create(psc->disk, DISK_VERSION);
165125975Sphk
166104066Ssos    printf("pst%d: %lluMB <%.40s> [%lld/%d/%d] on %.16s\n", lun,
167104066Ssos	   (unsigned long long)psc->info->capacity / (1024 * 1024),
168104066Ssos	   name, psc->info->capacity/(512*255*63), 255, 63,
169101100Ssos	   device_get_nameunit(psc->iop->dev));
170101100Ssos
171101100Ssos    EVENTHANDLER_REGISTER(shutdown_post_sync, pst_shutdown,
172101100Ssos			  dev, SHUTDOWN_PRI_FIRST);
173101100Ssos    return 0;
174101100Ssos}
175101100Ssos
176101100Ssosstatic int
177101100Ssospst_shutdown(device_t dev)
178101100Ssos{
179101100Ssos    struct pst_softc *psc = device_get_softc(dev);
180101100Ssos    struct i2o_bsa_cache_flush_message *msg;
181101100Ssos    int mfa;
182101100Ssos
183101100Ssos    mfa = iop_get_mfa(psc->iop);
184101100Ssos    msg = (struct i2o_bsa_cache_flush_message *)(psc->iop->ibase + mfa);
185101100Ssos    bzero(msg, sizeof(struct i2o_bsa_cache_flush_message));
186101100Ssos    msg->version_offset = 0x01;
187101100Ssos    msg->message_flags = 0x0;
188101100Ssos    msg->message_size = sizeof(struct i2o_bsa_cache_flush_message) >> 2;
189101100Ssos    msg->target_address = psc->lct->local_tid;
190101100Ssos    msg->initiator_address = I2O_TID_HOST;
191101100Ssos    msg->function = I2O_BSA_CACHE_FLUSH;
192101100Ssos    msg->control_flags = 0x0; /* 0x80 = post progress reports */
193101100Ssos    if (iop_queue_wait_msg(psc->iop, mfa, (struct i2o_basic_message *)msg))
194101100Ssos	printf("pst: shutdown failed!\n");
195101100Ssos    return 0;
196101100Ssos}
197101100Ssos
198101100Ssosstatic void
199101100Ssospststrategy(struct bio *bp)
200101100Ssos{
201111472Sphk    struct pst_softc *psc = bp->bio_disk->d_drv1;
202101100Ssos
203114154Ssos    mtx_lock(&psc->iop->mtx);
204112946Sphk    bioq_disksort(&psc->queue, bp);
205101100Ssos    pst_start(psc);
206114154Ssos    mtx_unlock(&psc->iop->mtx);
207101100Ssos}
208101100Ssos
209101100Ssosstatic void
210101100Ssospst_start(struct pst_softc *psc)
211101100Ssos{
212101100Ssos    struct pst_request *request;
213101100Ssos    struct bio *bp;
214101100Ssos    u_int32_t mfa;
215101100Ssos
216114154Ssos    if (psc->iop->outstanding < (I2O_IOP_OUTBOUND_FRAME_COUNT - 1) &&
217101100Ssos	(bp = bioq_first(&psc->queue))) {
218101100Ssos	if ((mfa = iop_get_mfa(psc->iop)) != 0xffffffff) {
219112977Ssos	    bioq_remove(&psc->queue, bp);
220101100Ssos	    if (!(request = malloc(sizeof(struct pst_request),
221101100Ssos				   M_PSTRAID, M_NOWAIT | M_ZERO))) {
222101100Ssos		printf("pst: out of memory in start\n");
223112977Ssos		biofinish(request->bp, NULL, ENOMEM);
224101100Ssos		iop_free_mfa(psc->iop, mfa);
225101100Ssos		return;
226101100Ssos	    }
227114154Ssos	    psc->iop->outstanding++;
228101100Ssos	    request->psc = psc;
229101100Ssos	    request->mfa = mfa;
230101100Ssos	    request->bp = bp;
231101100Ssos	    if (pst_rw(request)) {
232111979Sphk		biofinish(request->bp, NULL, EIO);
233101100Ssos		iop_free_mfa(request->psc->iop, request->mfa);
234114154Ssos		psc->iop->outstanding--;
235101100Ssos		free(request, M_PSTRAID);
236101100Ssos	    }
237101100Ssos	}
238101100Ssos    }
239101100Ssos}
240101100Ssos
241101100Ssosstatic void
242101100Ssospst_done(struct iop_softc *sc, u_int32_t mfa, struct i2o_single_reply *reply)
243101100Ssos{
244101100Ssos    struct pst_request *request =
245101100Ssos	(struct pst_request *)reply->transaction_context;
246101100Ssos    struct pst_softc *psc = request->psc;
247101100Ssos
248101100Ssos    untimeout((timeout_t *)pst_timeout, request, request->timeout_handle);
249101100Ssos    request->bp->bio_resid = request->bp->bio_bcount - reply->donecount;
250111979Sphk    biofinish(request->bp, NULL, reply->status ? EIO : 0);
251101100Ssos    free(request, M_PSTRAID);
252101100Ssos    psc->iop->reg->oqueue = mfa;
253114154Ssos    psc->iop->outstanding--;
254101100Ssos    pst_start(psc);
255101100Ssos}
256101100Ssos
257101100Ssosint
258101100Ssospst_rw(struct pst_request *request)
259101100Ssos{
260101100Ssos    struct i2o_bsa_rw_block_message *msg;
261101100Ssos    int sgl_flag;
262101100Ssos
263101100Ssos    msg = (struct i2o_bsa_rw_block_message *)
264101100Ssos	  (request->psc->iop->ibase + request->mfa);
265101100Ssos    bzero(msg, sizeof(struct i2o_bsa_rw_block_message));
266101100Ssos    msg->version_offset = 0x81;
267101100Ssos    msg->message_flags = 0x0;
268101100Ssos    msg->message_size = sizeof(struct i2o_bsa_rw_block_message) >> 2;
269101100Ssos    msg->target_address = request->psc->lct->local_tid;
270101100Ssos    msg->initiator_address = I2O_TID_HOST;
271101100Ssos    switch (request->bp->bio_cmd) {
272101100Ssos    case BIO_READ:
273101100Ssos	msg->function = I2O_BSA_BLOCK_READ;
274101100Ssos	msg->control_flags = 0x0; /* 0x0c = read cache + readahead */
275101100Ssos	msg->fetch_ahead = 0x0; /* 8 Kb */
276101100Ssos	sgl_flag = 0;
277101100Ssos	break;
278101100Ssos    case BIO_WRITE:
279101100Ssos	msg->function = I2O_BSA_BLOCK_WRITE;
280101100Ssos	msg->control_flags = 0x0; /* 0x10 = write behind cache */
281101100Ssos	msg->fetch_ahead = 0x0;
282101100Ssos	sgl_flag = I2O_SGL_DIR;
283101100Ssos	break;
284101100Ssos    default:
285114154Ssos	printf("pst: unknown command type 0x%02x\n", request->bp->bio_cmd);
286101100Ssos	return -1;
287101100Ssos    }
288101100Ssos    msg->initiator_context = (u_int32_t)pst_done;
289101100Ssos    msg->transaction_context = (u_int32_t)request;
290101100Ssos    msg->time_multiplier = 1;
291101100Ssos    msg->bytecount = request->bp->bio_bcount;
292101100Ssos    msg->lba = ((u_int64_t)request->bp->bio_pblkno) * (DEV_BSIZE * 1LL);
293114154Ssos
294101100Ssos    if (!iop_create_sgl((struct i2o_basic_message *)msg, request->bp->bio_data,
295101100Ssos			request->bp->bio_bcount, sgl_flag))
296101100Ssos	return -1;
297114154Ssos
298101100Ssos    request->psc->iop->reg->iqueue = request->mfa;
299114154Ssos
300114154Ssos    if (dumping)
301114154Ssos	request->timeout_handle.callout = NULL;
302114154Ssos    else
303114154Ssos	request->timeout_handle =
304114154Ssos	    timeout((timeout_t*)pst_timeout, request, 10 * hz);
305101100Ssos    return 0;
306101100Ssos}
307101100Ssos
308101100Ssosstatic void
309114154Ssospst_timeout(struct pst_request *request)
310114154Ssos{
311114154Ssos    printf("pst: timeout mfa=0x%08x cmd=0x%02x\n",
312114154Ssos	   request->mfa, request->bp->bio_cmd);
313114154Ssos    mtx_lock(&request->psc->iop->mtx);
314114154Ssos    iop_free_mfa(request->psc->iop, request->mfa);
315114154Ssos    if ((request->mfa = iop_get_mfa(request->psc->iop)) == 0xffffffff) {
316114154Ssos	printf("pst: timeout no mfa possible\n");
317114154Ssos	biofinish(request->bp, NULL, EIO);
318114154Ssos	request->psc->iop->outstanding--;
319114154Ssos	mtx_unlock(&request->psc->iop->mtx);
320114154Ssos	return;
321114154Ssos    }
322114154Ssos    if (pst_rw(request)) {
323114154Ssos	iop_free_mfa(request->psc->iop, request->mfa);
324114154Ssos	biofinish(request->bp, NULL, EIO);
325114154Ssos	request->psc->iop->outstanding--;
326114154Ssos    }
327114154Ssos    mtx_unlock(&request->psc->iop->mtx);
328114154Ssos}
329114154Ssos
330114154Ssosstatic void
331101100Ssosbpack(int8_t *src, int8_t *dst, int len)
332101100Ssos{
333101100Ssos    int i, j, blank;
334101100Ssos    int8_t *ptr, *buf = dst;
335101100Ssos
336101100Ssos    for (i = j = blank = 0 ; i < len; i++) {
337114154Ssos	if (blank && src[i] == ' ')
338114154Ssos	    continue;
339101100Ssos	if (blank && src[i] != ' ') {
340101100Ssos	    dst[j++] = src[i];
341101100Ssos	    blank = 0;
342101100Ssos	    continue;
343101100Ssos	}
344101100Ssos	if (src[i] == ' ') {
345101100Ssos	    blank = 1;
346101100Ssos	    if (i == 0)
347101100Ssos		continue;
348101100Ssos	}
349101100Ssos	dst[j++] = src[i];
350101100Ssos    }
351101100Ssos    if (j < len)
352101100Ssos	dst[j] = 0x00;
353101100Ssos    for (ptr = buf; ptr < buf+len; ++ptr)
354101100Ssos	if (!*ptr)
355101100Ssos	    *ptr = ' ';
356101100Ssos    for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr)
357101100Ssos        *ptr = 0;
358101100Ssos}
359101100Ssos
360101100Ssosstatic device_method_t pst_methods[] = {
361101100Ssos    DEVMETHOD(device_probe,	pst_probe),
362101100Ssos    DEVMETHOD(device_attach,	pst_attach),
363101100Ssos    { 0, 0 }
364101100Ssos};
365101100Ssos
366101100Ssosstatic driver_t pst_driver = {
367101100Ssos    "pst",
368101100Ssos    pst_methods,
369101100Ssos    sizeof(struct pst_softc),
370101100Ssos};
371101100Ssos
372101100Ssosstatic devclass_t pst_devclass;
373101100Ssos
374101100SsosDRIVER_MODULE(pst, pstpci, pst_driver, pst_devclass, 0, 0);
375