xref: /illumos-gate/usr/src/uts/sun/io/dada/impl/dcd_hba.c (revision cd210bb4)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/note.h>
27 
28 /*
29  * Generic SCSI Host Bus Adapter interface implementation
30  */
31 
32 #include <sys/dada/dada.h>
33 
34 extern int dcd_options;
35 
36 static kmutex_t	dcd_hba_mutex;
37 
38 kmutex_t	dcd_log_mutex;
39 
40 struct dcd_hba_inst {
41 	dev_info_t	*inst_dip;
42 	dcd_hba_tran_t	*inst_hba_tran;
43 	struct dcd_hba_inst	*inst_next;
44 	struct	dcd_hba_inst	*inst_prev;
45 };
46 
47 static struct dcd_hba_inst	*dcd_hba_list	= NULL;
48 static struct dcd_hba_inst	*dcd_hba_list_tail = NULL;
49 
50 
51 _NOTE(READ_ONLY_DATA(dev_ops))
52 
53 kmutex_t	dcd_flag_nointr_mutex;
54 kcondvar_t	dcd_flag_nointr_cv;
55 
56 
57 /*
58  * Called from _init when loading the dcd module.
59  */
60 void
dcd_initialize_hba_interface()61 dcd_initialize_hba_interface()
62 {
63 	mutex_init(&dcd_hba_mutex, NULL, MUTEX_DRIVER, NULL);
64 	mutex_init(&dcd_flag_nointr_mutex, NULL, MUTEX_DRIVER, NULL);
65 	cv_init(&dcd_flag_nointr_cv, NULL, CV_DRIVER, NULL);
66 	mutex_init(&dcd_log_mutex, NULL, MUTEX_DRIVER, NULL);
67 }
68 
69 /*
70  * Called from fini() when unloading the dcd module.
71  */
72 
73 void
dcd_uninitialize_hba_interface()74 dcd_uninitialize_hba_interface()
75 {
76 	mutex_destroy(&dcd_hba_mutex);
77 	cv_destroy(&dcd_flag_nointr_cv);
78 	mutex_destroy(&dcd_flag_nointr_mutex);
79 	mutex_destroy(&dcd_log_mutex);
80 }
81 
82 
83 /*
84  * Called by an HBA from _init()
85  */
86 /* ARGSUSED */
87 int
dcd_hba_init(struct modlinkage * modlp)88 dcd_hba_init(struct modlinkage *modlp)
89 {
90 
91 	return (0);
92 }
93 
94 
95 
96 #ifdef NOTNEEDED
97 /* ARGSUSED */
98 int
dcd_hba_attach(dev_info_t * dip,ddi_dma_lim_t * hba_lim,dcd_hba_tran_t * hba_tran,int flags,void * hba_options)99 dcd_hba_attach(dev_info_t *dip,
100 	ddi_dma_lim_t	*hba_lim,
101 	dcd_hba_tran_t	*hba_tran,
102 	int		flags,
103 	void		*hba_options)
104 {
105 
106 	ddi_dma_attr_t		hba_dma_attr;
107 
108 	bzero(&hba_dma_attr, sizeof (ddi_dma_attr_t));
109 
110 	hba_dma_attr.dma_attr_burstsizes = hba_lim->dlim_burstsizes;
111 	hba_dma_attr.dma_attr_minxfer = hba_lim->dlim_minxfer;
112 
113 	return (dcd_hba_attach_setup(dip, &hba_dma_attr, hba_tran, flags));
114 }
115 #endif
116 
117 
118 int
dcd_hba_attach(dev_info_t * dip,ddi_dma_attr_t * hba_dma_attr,dcd_hba_tran_t * hba_tran,int flags)119 dcd_hba_attach(
120 	dev_info_t	*dip,
121 	ddi_dma_attr_t	*hba_dma_attr,
122 	dcd_hba_tran_t	*hba_tran,
123 	int		flags)
124 {
125 
126 	struct dcd_hba_inst	*elem;
127 	int			value;
128 	int			len;
129 	char			*prop_name;
130 	char			*errmsg =
131 	    "dcd_hba_attach: cannott create property '%s' for %s%d\n";
132 
133 	/*
134 	 * Link this instance into the list
135 	 */
136 	elem = kmem_alloc(sizeof (struct dcd_hba_inst), KM_SLEEP);
137 
138 	elem->inst_dip = dip;
139 	elem->inst_hba_tran = hba_tran;
140 
141 	mutex_enter(&dcd_hba_mutex);
142 	elem->inst_next = NULL;
143 	elem->inst_prev = dcd_hba_list_tail;
144 
145 	if (dcd_hba_list == NULL) {
146 		dcd_hba_list = elem;
147 	}
148 	if (dcd_hba_list_tail) {
149 		dcd_hba_list_tail->inst_next = elem;
150 	}
151 	dcd_hba_list_tail = elem;
152 	mutex_exit(&dcd_hba_mutex);
153 
154 
155 	/*
156 	 * Save all the improtant HBA information that must be accessed
157 	 * later.
158 	 */
159 
160 	hba_tran->tran_hba_dip = dip;
161 	hba_tran->tran_hba_flags = flags;
162 
163 	/*
164 	 * Note: We only need dma_attr_minxfer and dma_attr_burstsize
165 	 * from the DMA atrributes
166 	 */
167 
168 	hba_tran->tran_min_xfer = hba_dma_attr->dma_attr_minxfer;
169 	hba_tran->tran_min_burst_size =
170 	    (1<<(ddi_ffs(hba_dma_attr->dma_attr_burstsizes)-1));
171 	hba_tran->tran_max_burst_size =
172 	    (1<<(ddi_fls(hba_dma_attr->dma_attr_burstsizes)-1));
173 
174 
175 
176 	prop_name = "dcd_options";
177 	len = 0;
178 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN, 0, prop_name,
179 	    NULL, &len) == DDI_PROP_NOT_FOUND) {
180 		value = dcd_options;
181 		if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip,
182 		    prop_name, value) != DDI_PROP_SUCCESS) {
183 			cmn_err(CE_CONT, errmsg, prop_name,
184 			    ddi_get_name(dip), ddi_get_instance(dip));
185 		}
186 	}
187 
188 
189 	/*
190 	 * XXX : This needs to be removed when code cleanup
191 	 * ddi_set_driver_private(dip, (caddr_t)hba_tran);
192 	 */
193 #ifdef DEBUG1
194 	printf("Called Set driver private with dip %x, tran %x\n",
195 	    dip, hba_tran);
196 #endif
197 
198 	return (DDI_SUCCESS);
199 }
200 
201 
202 /*
203  * called by an HBA to detach an instance of the driver
204  */
205 
206 int
dcd_hba_detach(dev_info_t * dip)207 dcd_hba_detach(dev_info_t *dip)
208 {
209 
210 	dcd_hba_tran_t	*hba;
211 	struct dcd_hba_inst 	*elem;
212 
213 	hba = ddi_get_driver_private(dip);
214 	ddi_set_driver_private(dip, NULL);
215 	ASSERT(hba != NULL);
216 
217 	hba->tran_hba_dip = (dev_info_t *)NULL;
218 	hba->tran_hba_flags = 0;
219 	hba->tran_min_burst_size = (uchar_t)0;
220 	hba->tran_max_burst_size = (uchar_t)0;
221 
222 
223 	/*
224 	 * Remove HBA instance from dcd_hba_list
225 	 */
226 
227 	mutex_enter(&dcd_hba_mutex);
228 
229 	for (elem = dcd_hba_list; elem != (struct dcd_hba_inst *)NULL;
230 	    elem = elem->inst_next) {
231 		if (elem->inst_dip == dip)
232 			break;
233 	}
234 
235 	if (elem == (struct dcd_hba_inst *)NULL) {
236 		cmn_err(CE_NOTE, "dcd_hba_attach: Unknown HBA instance\n");
237 		mutex_exit(&dcd_hba_mutex);
238 	}
239 
240 	if (elem == dcd_hba_list) {
241 		dcd_hba_list = elem->inst_next;
242 		dcd_hba_list->inst_prev = (struct dcd_hba_inst *)NULL;
243 	} else if (elem == dcd_hba_list_tail) {
244 		dcd_hba_list_tail = elem->inst_prev;
245 		dcd_hba_list_tail->inst_next = (struct dcd_hba_inst *)NULL;
246 	} else {
247 		elem->inst_prev->inst_next = elem->inst_next;
248 		elem->inst_next->inst_prev = elem->inst_prev;
249 	}
250 	mutex_exit(&dcd_hba_mutex);
251 
252 	kmem_free(elem, sizeof (struct dcd_hba_inst));
253 
254 	return (DDI_SUCCESS);
255 }
256 
257 void
dcd_hba_fini()258 dcd_hba_fini()
259 {
260 
261 }
262 
263 /* ARGSUSED */
264 dcd_hba_tran_t *
dcd_hba_tran_alloc(dev_info_t * dip,int flags)265 dcd_hba_tran_alloc(
266 	dev_info_t	*dip,
267 	int		flags)
268 {
269 
270 	return (kmem_zalloc(sizeof (dcd_hba_tran_t),
271 	    (flags & DCD_HBA_CANSLEEP) ? KM_SLEEP: KM_NOSLEEP));
272 }
273 
274 
275 void
dcd_hba_tran_free(dcd_hba_tran_t * hba_tran)276 dcd_hba_tran_free(dcd_hba_tran_t	*hba_tran)
277 {
278 
279 	kmem_free(hba_tran, sizeof (dcd_hba_tran_t));
280 }
281 
282 
283 /*
284  * XXX: Do we really need the following routines.
285  */
286 
287 /*
288  * private wrapper for dcd_pkt's allocated via scsi_hba_pkt_alloc
289  */
290 
291 struct dcd_pkt_wrapper {
292 	struct dcd_pkt	dcd_pkt;
293 	int		pkt_wrapper_len;
294 };
295 
296 _NOTE(SCHEME_PROTECTS_DATA("unique per thread", dcd_pkt_wrapper))
297 
298 /*
299  * Round up all allocations so that we can gurentee
300  * long-long alignment. This is the same alignment
301  * provided by kmem_alloc().
302  */
303 
304 #define	ROUNDUP(x)	(((x) + 0x07) & ~0x07)
305 
306 /*
307  * Called by an HBA to allocate a dcd_pkt
308  */
309 
310 /* ARGSUSED */
311 struct dcd_pkt *
dcd_hba_pkt_alloc(struct dcd_address * ap,int cmdlen,int statuslen,int tgtlen,int hbalen,int (* callback)(caddr_t arg),caddr_t arg)312 dcd_hba_pkt_alloc(
313 	struct dcd_address	*ap,
314 	int			cmdlen,
315 	int			statuslen,
316 	int			tgtlen,
317 	int			hbalen,
318 	int			(*callback)(caddr_t arg),
319 	caddr_t			arg)
320 {
321 
322 	struct dcd_pkt	*pkt;
323 	struct dcd_pkt_wrapper	*hba_pkt;
324 	caddr_t		p;
325 	int		pktlen;
326 
327 
328 	/*
329 	 * Sanity check
330 	 */
331 	if (callback != SLEEP_FUNC && callback != NULL_FUNC) {
332 		cmn_err(CE_PANIC, " dcd_hba_pkt_alloc: callback must be"
333 		    " either SLEEP or NULL\n");
334 	}
335 
336 
337 	/*
338 	 * Round up so everything gets allocated on long-word boundaries.
339 	 */
340 
341 	cmdlen = ROUNDUP(cmdlen);
342 	tgtlen = ROUNDUP(tgtlen);
343 	hbalen = ROUNDUP(hbalen);
344 	statuslen = ROUNDUP(statuslen);
345 	pktlen = sizeof (struct dcd_pkt_wrapper) +
346 	    cmdlen + tgtlen +hbalen + statuslen;
347 
348 	hba_pkt = kmem_zalloc(pktlen,
349 	    (callback = SLEEP_FUNC) ? KM_SLEEP: KM_NOSLEEP);
350 
351 	if (hba_pkt == NULL) {
352 		ASSERT(callback == NULL_FUNC);
353 		return (NULL);
354 	}
355 
356 	/*
357 	 * Set up or private info on this pkt
358 	 */
359 	hba_pkt->pkt_wrapper_len = pktlen;
360 	pkt = &hba_pkt->dcd_pkt;
361 	p = (caddr_t)(hba_pkt + 1);
362 
363 	/*
364 	 * set up pointers to private data areas, cdb and status.
365 	 */
366 	if (hbalen > 0) {
367 		pkt->pkt_ha_private = (ataopaque_t)p;
368 		p += hbalen;
369 	}
370 
371 	if (tgtlen > 0) {
372 		pkt->pkt_private = (ataopaque_t)p;
373 		p += tgtlen;
374 	}
375 
376 	if (statuslen > 0) {
377 		pkt->pkt_scbp = (uchar_t *)p;
378 		p += statuslen;
379 	}
380 
381 	if (cmdlen > 0) {
382 		pkt->pkt_cdbp = (void *)p;
383 	}
384 
385 	/*
386 	 * Initialize the pkt's dcd_address
387 	 */
388 	pkt->pkt_address = *ap;
389 #ifdef DEBUG1
390 	printf("da_target %x, da_lun %x, a_hba_tran %x\n",
391 	    pkt->pkt_address.da_target, pkt->pkt_address.da_lun,
392 	    pkt->pkt_address.a_hba_tran);
393 	printf("From address : da_target %x, da_lun %x, a_hba_tran %x\n",
394 	    ap->da_target, ap->da_lun, ap->a_hba_tran);
395 	printf("Pkt %x\n", pkt);
396 
397 #endif
398 	return (pkt);
399 }
400 
401 
402 /* ARGSUSED */
403 void
dcd_hba_pkt_free(struct dcd_address * ap,struct dcd_pkt * pkt)404 dcd_hba_pkt_free(
405 	struct dcd_address *ap,
406 	struct dcd_pkt	   *pkt)
407 {
408 
409 	kmem_free((struct dcd_pkt_wrapper *)pkt,
410 	    ((struct dcd_pkt_wrapper *)pkt)->pkt_wrapper_len);
411 }
412 
413 
414 /*
415  * Called by an HBA to map strings to capability indices
416  */
417 
418 int
dcd_hba_lookup_capstr(char * capstr)419 dcd_hba_lookup_capstr(char		*capstr)
420 {
421 
422 	/*
423 	 * Capability strings, masking the '-' vs '_'.
424 	 */
425 	static struct cap_strings {
426 		char *cap_string;
427 		int   cap_index;
428 	} cap_string[] = {
429 		{ "dma-max",		DCD_CAP_DMA_MAX		},
430 		{ "dma_max",		DCD_CAP_DMA_MAX		},
431 		{ "ultraata",		DCD_CAP_ULTRA_ATA	},
432 		{ "busmaster",		DCD_CAP_BUS_MASTER	},
433 		{ "overlap",		DCD_CAP_OVERLAP		},
434 		{ "parity",		DCD_CAP_PARITY		},
435 		{ "sector-size",	DCD_CAP_SECTOR_SIZE	},
436 		{ "total-sectors",	DCD_CAP_TOTAL_SECTORS	},
437 		{ "geometry",		DCD_CAP_GEOMETRY	},
438 		{ "block-mode",		DCD_CAP_BLOCKMODE	},
439 		{ "block-factor",	DCD_CAP_BLOCKFACTOR	},
440 		{ "dma-support",		DCD_CAP_DMA_SUPPORT	},
441 		{ "pio-support", 	DCD_CAP_PIO_SUPPORT	},
442 		{ "lba-addressing",	DCD_CAP_LBA_ADDRESSING  },
443 		{ NULL, 0					}
444 	};
445 	struct cap_strings *cp;
446 
447 	for (cp = cap_string; cp->cap_string != NULL; cp++) {
448 		if (strcmp(cp->cap_string, capstr) == 0) {
449 			return (cp->cap_index);
450 		}
451 	}
452 
453 	return (-1);
454 }
455