1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * 1394 mass storage SBP-2 bus routines
31 */
32
33#include <sys/param.h>
34#include <sys/errno.h>
35#include <sys/cred.h>
36#include <sys/conf.h>
37#include <sys/modctl.h>
38#include <sys/stat.h>
39#include <sys/ddi.h>
40#include <sys/sunddi.h>
41
42#include <sys/sbp2/bus.h>
43#include <sys/1394/targets/scsa1394/impl.h>
44
45static ddi_iblock_cookie_t scsa1394_bus_get_iblock_cookie(void *);
46static uint_t	scsa1394_bus_get_node_id(void *);
47static int	scsa1394_bus_alloc_cmd(void *, void **, int);
48static void	scsa1394_bus_free_cmd(void *, void *);
49static int	scsa1394_bus_rq(void *, void *, uint64_t, uint32_t *, int *);
50static int	scsa1394_bus_rb(void *, void *, uint64_t, mblk_t **, int,
51		int *);
52static int	scsa1394_bus_wq(void *, void *, uint64_t, uint32_t, int *);
53static int	scsa1394_bus_wb(void *, void *, uint64_t, mblk_t *, int,
54		int *);
55static int	scsa1394_bus_alloc_buf(void *, sbp2_bus_buf_t *);
56static int	scsa1394_bus_alloc_buf_phys(void *, sbp2_bus_buf_t *);
57static void	scsa1394_bus_free_buf_phys(void *, sbp2_bus_buf_t *);
58static int	scsa1394_bus_alloc_buf_normal(void *, sbp2_bus_buf_t *,
59		boolean_t);
60static void	scsa1394_bus_free_buf_normal(void *, sbp2_bus_buf_t *);
61static void	scsa1394_bus_free_buf(void *, sbp2_bus_buf_t *);
62static int	scsa1394_bus_sync_buf(void *, sbp2_bus_buf_t *, off_t, size_t,
63		int);
64static void	scsa1394_bus_buf_rw_done(void *, sbp2_bus_buf_t *, void *, int);
65
66/* callbacks */
67static void	scsa1394_bus_recv_read_request(cmd1394_cmd_t *);
68static void	scsa1394_bus_recv_write_request(cmd1394_cmd_t *);
69
70sbp2_bus_t scsa1394_sbp2_bus = {
71	SBP2_BUS_REV,			/* rev */
72	0xFFFFF0000000LL,		/* csr_base */
73	IEEE1394_CONFIG_ROM_ADDR,	/* cfgrom_addr */
74	scsa1394_bus_get_iblock_cookie,	/* get_iblock_cookie */
75	scsa1394_bus_get_node_id,	/* get_node_id */
76	scsa1394_bus_alloc_buf,		/* alloc_buf */
77	scsa1394_bus_free_buf,		/* free_buf */
78	scsa1394_bus_sync_buf,		/* sync_buf */
79	scsa1394_bus_buf_rw_done,	/* buf_rd_done */
80	scsa1394_bus_buf_rw_done,	/* buf_wr_done */
81	scsa1394_bus_alloc_cmd,		/* alloc_cmd */
82	scsa1394_bus_free_cmd,		/* free_cmd */
83	scsa1394_bus_rq,		/* rq */
84	scsa1394_bus_rb,		/* rb */
85	scsa1394_bus_wq,		/* wq */
86	scsa1394_bus_wb			/* wb */
87};
88
89/*
90 * fault injector
91 *
92 * global on/off switch
93 */
94int scsa1394_bus_fi_on = 0;
95
96/* fault probabilities per operation, in tenths of percent, i.e. 10 is 1% */
97int scsa1394_bus_fi_prob_alloc_buf = 10;
98int scsa1394_bus_fi_prob_alloc_cmd = 10;
99int scsa1394_bus_fi_prob_rq = 10;
100int scsa1394_bus_fi_prob_rb = 10;
101int scsa1394_bus_fi_prob_wq = 10;
102int scsa1394_bus_fi_prob_wb = 10;
103
104#define	SCSA1394_BUS_FI_POSITIVE(p) (scsa1394_bus_fi_on &&	\
105	((p) > 0) && ((gethrtime() % (p)) == 0))
106
107/*
108 * translate command result to SBP2 error code
109 */
110static int
111scsa1394_bus_rw_result2code(int result)
112{
113	int	code;
114
115	switch (result) {
116	case CMD1394_EDEVICE_BUSY:
117		code = SBP2_EBUSY;
118		break;
119	case CMD1394_EADDRESS_ERROR:
120		code = SBP2_EADDR;
121		break;
122	case CMD1394_ETIMEOUT:
123	case CMD1394_ERETRIES_EXCEEDED:
124		code = SBP2_ETIMEOUT;
125		break;
126	case CMD1394_EDEVICE_REMOVED:
127		code = SBP2_ENODEV;
128		break;
129	default:
130		code = SBP2_EIO;
131		break;
132	}
133	return (code);
134}
135
136static ddi_iblock_cookie_t
137scsa1394_bus_get_iblock_cookie(void *hdl)
138{
139	scsa1394_state_t *sp = hdl;
140
141	return (sp->s_attachinfo.iblock_cookie);
142}
143
144static uint_t
145scsa1394_bus_get_node_id(void *hdl)
146{
147	scsa1394_state_t *sp = hdl;
148
149	return (sp->s_attachinfo.localinfo.local_nodeID);
150}
151
152
153/*ARGSUSED*/
154static int
155scsa1394_bus_alloc_cmd(void *hdl, void **cmdp, int flags)
156{
157	scsa1394_state_t *sp = hdl;
158	cmd1394_cmd_t	*cmd;
159
160	if (SCSA1394_BUS_FI_POSITIVE(scsa1394_bus_fi_prob_alloc_cmd)) {
161		return (SBP2_ENOMEM);
162	}
163
164	if (t1394_alloc_cmd(sp->s_t1394_hdl, 0, &cmd) != DDI_SUCCESS) {
165		return (SBP2_ENOMEM);
166	}
167	*cmdp = cmd;
168	return (SBP2_SUCCESS);
169}
170
171
172static void
173scsa1394_bus_free_cmd(void *hdl, void *argcmd)
174{
175	scsa1394_state_t *sp = hdl;
176	cmd1394_cmd_t	*cmd = argcmd;
177
178	(void) t1394_free_cmd(sp->s_t1394_hdl, 0, &cmd);
179}
180
181
182/*ARGSUSED*/
183static int
184scsa1394_bus_rq(void *hdl, void *argcmd, uint64_t addr, uint32_t *q, int *berr)
185{
186	scsa1394_state_t *sp = hdl;
187	cmd1394_cmd_t	*cmd = argcmd;
188
189	if (SCSA1394_BUS_FI_POSITIVE(scsa1394_bus_fi_prob_rq)) {
190		return (SBP2_EIO);
191	}
192
193	cmd->cmd_addr = addr;
194	cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
195	cmd->cmd_options = CMD1394_BLOCKING;
196
197	if ((t1394_read(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
198	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
199		*berr = cmd->cmd_result;
200		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
201	}
202
203	*q = cmd->cmd_u.q.quadlet_data;
204	return (SBP2_SUCCESS);
205}
206
207
208/*ARGSUSED*/
209static int
210scsa1394_bus_rb(void *hdl, void *argcmd, uint64_t addr, mblk_t **bpp, int len,
211    int *berr)
212{
213	scsa1394_state_t *sp = hdl;
214	cmd1394_cmd_t	*cmd = argcmd;
215	mblk_t		*bp = *bpp;
216
217	/* caller wants us to allocate memory */
218	if ((bp == NULL) && ((bp = allocb(len, BPRI_HI)) == NULL)) {
219		return (SBP2_ENOMEM);
220	}
221
222	cmd->cmd_addr = addr;
223	cmd->cmd_type = CMD1394_ASYNCH_RD_BLOCK;
224	cmd->cmd_u.b.data_block = bp;
225	cmd->cmd_u.b.blk_length = len;
226	cmd->cmd_options = CMD1394_BLOCKING;
227
228	if ((t1394_read(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
229	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
230		freeb(bp);
231		*berr = cmd->cmd_result;
232		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
233	}
234
235	*bpp = bp;
236	return (SBP2_SUCCESS);
237}
238
239
240/*ARGSUSED*/
241static int
242scsa1394_bus_wq(void *hdl, void *argcmd, uint64_t addr, uint32_t q, int *berr)
243{
244	scsa1394_state_t *sp = hdl;
245	cmd1394_cmd_t	*cmd = argcmd;
246
247	cmd->cmd_addr = addr;
248	cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD;
249	cmd->cmd_u.q.quadlet_data = q;
250	cmd->cmd_options = CMD1394_BLOCKING;
251
252	if ((t1394_write(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
253	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
254		*berr = cmd->cmd_result;
255		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
256	}
257
258	return (SBP2_SUCCESS);
259}
260
261
262/*ARGSUSED*/
263static int
264scsa1394_bus_wb(void *hdl, void *argcmd, uint64_t addr, mblk_t *bp, int len,
265    int *berr)
266{
267	scsa1394_state_t *sp = hdl;
268	cmd1394_cmd_t	*cmd = argcmd;
269
270	cmd->cmd_addr = addr;
271	cmd->cmd_type = CMD1394_ASYNCH_WR_BLOCK;
272	cmd->cmd_u.b.data_block = bp;
273	cmd->cmd_u.b.blk_length = len;
274	cmd->cmd_options = CMD1394_BLOCKING;
275
276	if ((t1394_write(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
277	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
278		*berr = cmd->cmd_result;
279		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
280	}
281
282	return (SBP2_SUCCESS);
283}
284
285
286/*ARGSUSED*/
287static int
288scsa1394_bus_alloc_buf(void *hdl, sbp2_bus_buf_t *buf)
289{
290	if (SCSA1394_BUS_FI_POSITIVE(scsa1394_bus_fi_prob_alloc_buf)) {
291		return (SBP2_ENOMEM);
292	}
293
294	if (buf->bb_flags & SBP2_BUS_BUF_DMA) {
295		return (scsa1394_bus_alloc_buf_phys(hdl, buf));
296	} else {
297		return (scsa1394_bus_alloc_buf_normal(hdl, buf,
298		    ((buf->bb_flags & SBP2_BUS_BUF_POSTED) != 0)));
299	}
300}
301
302
303static void
304scsa1394_bus_free_buf(void *hdl, sbp2_bus_buf_t *buf)
305{
306	if (buf->bb_flags & SBP2_BUS_BUF_DMA) {
307		scsa1394_bus_free_buf_phys(hdl, buf);
308	} else {
309		scsa1394_bus_free_buf_normal(hdl, buf);
310	}
311}
312
313
314static int
315scsa1394_bus_alloc_buf_phys(void *hdl, sbp2_bus_buf_t *buf)
316{
317	scsa1394_state_t	*sp = hdl;
318	scsa1394_bus_buf_t	*sbb;		/* bus private structure */
319	size_t			real_length;	/* real allocated length */
320	ddi_dma_cookie_t	cookie;		/* cookies */
321	uint_t			ccount;		/* cookie count */
322	t1394_alloc_addr_t	aa;
323	int			result;
324
325	/* allocate bus private structure */
326	sbb = kmem_zalloc(sizeof (scsa1394_bus_buf_t), KM_SLEEP);
327	sbb->sbb_state = sp;
328
329	/* allocate DMA resources */
330	if (ddi_dma_alloc_handle(sp->s_dip, &sp->s_attachinfo.dma_attr,
331	    DDI_DMA_SLEEP, NULL, &sbb->sbb_dma_hdl) != DDI_SUCCESS) {
332		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
333		return (SBP2_ENOMEM);
334	}
335
336	if (ddi_dma_mem_alloc(sbb->sbb_dma_hdl, buf->bb_len,
337	    &sp->s_attachinfo.acc_attr,
338	    buf->bb_flags & (DDI_DMA_STREAMING | DDI_DMA_CONSISTENT),
339	    DDI_DMA_SLEEP, NULL, &buf->bb_kaddr, &real_length,
340	    &sbb->sbb_acc_hdl) != DDI_SUCCESS) {
341		ddi_dma_free_handle(&sbb->sbb_dma_hdl);
342		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
343		return (SBP2_ENOMEM);
344	}
345
346	buf->bb_flags &= ~DDI_DMA_PARTIAL;
347	if (ddi_dma_addr_bind_handle(sbb->sbb_dma_hdl, NULL, buf->bb_kaddr,
348	    buf->bb_len, buf->bb_flags, DDI_DMA_SLEEP, NULL,
349	    &cookie, &ccount) != DDI_DMA_MAPPED) {
350		ddi_dma_mem_free(&sbb->sbb_acc_hdl);
351		ddi_dma_free_handle(&sbb->sbb_dma_hdl);
352		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
353		return (SBP2_ENOMEM);
354	}
355	ASSERT(ccount == 1);
356	buf->bb_paddr = cookie.dmac_address;	/* 32-bit address */
357
358	/* allocate 1394 resources */
359	bzero(&aa, sizeof (aa));
360	aa.aa_type = T1394_ADDR_FIXED;
361	aa.aa_length = buf->bb_len;
362	if (buf->bb_flags & SBP2_BUS_BUF_RD) {
363		aa.aa_enable |= T1394_ADDR_RDENBL;
364	}
365	if (buf->bb_flags & SBP2_BUS_BUF_WR) {
366		aa.aa_enable |= T1394_ADDR_WRENBL;
367	}
368	aa.aa_address = buf->bb_paddr;		/* PCI-1394 mapping is 1-1 */
369
370	if (t1394_alloc_addr(sp->s_t1394_hdl, &aa, 0, &result) != DDI_SUCCESS) {
371		(void) ddi_dma_unbind_handle(sbb->sbb_dma_hdl);
372		ddi_dma_mem_free(&sbb->sbb_acc_hdl);
373		ddi_dma_free_handle(&sbb->sbb_dma_hdl);
374		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
375		return (SBP2_ENOMEM);
376	}
377	sbb->sbb_addr_hdl = aa.aa_hdl;
378	buf->bb_baddr = aa.aa_address;
379
380	buf->bb_hdl = sbb;
381	return (SBP2_SUCCESS);
382}
383
384
385static void
386scsa1394_bus_free_buf_phys(void *hdl, sbp2_bus_buf_t *buf)
387{
388	scsa1394_state_t	*sp = hdl;
389	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
390
391	(void) t1394_free_addr(sp->s_t1394_hdl, &sbb->sbb_addr_hdl, 0);
392	(void) ddi_dma_unbind_handle(sbb->sbb_dma_hdl);
393	ddi_dma_mem_free(&sbb->sbb_acc_hdl);
394	ddi_dma_free_handle(&sbb->sbb_dma_hdl);
395	kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
396	buf->bb_hdl = NULL;
397}
398
399
400static int
401scsa1394_bus_alloc_buf_normal(void *hdl, sbp2_bus_buf_t *buf, boolean_t posted)
402{
403	scsa1394_state_t 	*sp = hdl;
404	scsa1394_bus_buf_t	*sbb;		/* bus private structure */
405	t1394_alloc_addr_t	aa;
406	int			result;
407
408	/* allocate bus private structure */
409	sbb = kmem_zalloc(sizeof (scsa1394_bus_buf_t), KM_SLEEP);
410	sbb->sbb_state = sp;
411
412	/* allocate 1394 resources */
413	bzero(&aa, sizeof (aa));
414	aa.aa_type = posted ? T1394_ADDR_POSTED_WRITE : T1394_ADDR_NORMAL;
415	aa.aa_length = buf->bb_len;
416	if (buf->bb_flags & SBP2_BUS_BUF_RD) {
417		aa.aa_enable |= T1394_ADDR_RDENBL;
418		aa.aa_evts.recv_read_request = scsa1394_bus_recv_read_request;
419	}
420	if (buf->bb_flags & SBP2_BUS_BUF_WR) {
421		aa.aa_enable |= T1394_ADDR_WRENBL;
422		aa.aa_evts.recv_write_request = scsa1394_bus_recv_write_request;
423	}
424	aa.aa_arg = buf;
425
426	if (t1394_alloc_addr(sp->s_t1394_hdl, &aa, 0, &result) != DDI_SUCCESS) {
427		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
428		return (SBP2_ENOMEM);
429	}
430	sbb->sbb_addr_hdl = aa.aa_hdl;
431	buf->bb_baddr = aa.aa_address;
432
433	buf->bb_hdl = sbb;
434	return (SBP2_SUCCESS);
435}
436
437static void
438scsa1394_bus_free_buf_normal(void *hdl, sbp2_bus_buf_t *buf)
439{
440	scsa1394_state_t 	*sp = hdl;
441	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
442
443	(void) t1394_free_addr(sp->s_t1394_hdl, &sbb->sbb_addr_hdl, 0);
444	kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
445	buf->bb_hdl = NULL;
446}
447
448/*ARGSUSED*/
449static int
450scsa1394_bus_sync_buf(void *hdl, sbp2_bus_buf_t *buf, off_t offset,
451    size_t length, int type)
452{
453	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
454
455	if (buf->bb_flags & SBP2_BUS_BUF_DMA) {
456		return (ddi_dma_sync(sbb->sbb_dma_hdl, offset, length, type));
457	} else {
458		return (SBP2_SUCCESS);
459	}
460}
461
462/*ARGSUSED*/
463static void
464scsa1394_bus_buf_rw_done(void *hdl, sbp2_bus_buf_t *buf, void *reqh, int error)
465{
466	scsa1394_state_t	*sp = hdl;
467	cmd1394_cmd_t		*req = reqh;
468
469	/* complete request */
470	switch (error) {
471	case SBP2_BUS_BUF_SUCCESS:
472		req->cmd_result = IEEE1394_RESP_COMPLETE;
473		break;
474	case SBP2_BUS_BUF_ELENGTH:
475		req->cmd_result = IEEE1394_RESP_DATA_ERROR;
476		break;
477	case SBP2_BUS_BUF_EBUSY:
478		req->cmd_result = IEEE1394_RESP_CONFLICT_ERROR;
479		break;
480	default:
481		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
482	}
483	(void) t1394_recv_request_done(sp->s_t1394_hdl, req, 0);
484}
485
486
487/*
488 *
489 * --- callbacks
490 *
491 */
492static void
493scsa1394_bus_recv_read_request(cmd1394_cmd_t *req)
494{
495	sbp2_bus_buf_t		*buf = req->cmd_callback_arg;
496	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
497	scsa1394_state_t	*sp = sbb->sbb_state;
498
499	/* XXX sanity checks: addr, etc */
500	if (req->cmd_type == CMD1394_ASYNCH_RD_QUAD) {
501		if (buf->bb_rq_cb) {
502			buf->bb_rq_cb(buf, req, &req->cmd_u.q.quadlet_data);
503			return;
504		}
505	} else {
506		if (buf->bb_rb_cb) {
507			buf->bb_rb_cb(buf, req, &req->cmd_u.b.data_block,
508			    req->cmd_u.b.blk_length);
509			return;
510		}
511	}
512	scsa1394_bus_buf_rw_done(sp, buf, req, SBP2_BUS_BUF_FAILURE);
513}
514
515
516static void
517scsa1394_bus_recv_write_request(cmd1394_cmd_t *req)
518{
519	sbp2_bus_buf_t		*buf = req->cmd_callback_arg;
520	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
521	scsa1394_state_t	*sp = sbb->sbb_state;
522
523	/* XXX sanity checks: addr, etc */
524	if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) {
525		if (buf->bb_wq_cb) {
526			buf->bb_wq_cb(buf, req, req->cmd_u.q.quadlet_data);
527			return;
528		}
529	} else {
530		if (buf->bb_wb_cb) {
531			buf->bb_wb_cb(buf, req, &req->cmd_u.b.data_block);
532			return;
533		}
534	}
535	scsa1394_bus_buf_rw_done(sp, buf, req, SBP2_BUS_BUF_FAILURE);
536}
537