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