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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * hci1394_detach.c
30  *    HBA detach() routine with associated funtions.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/kmem.h>
35 #include <sys/conf.h>
36 #include <sys/ddi.h>
37 #include <sys/modctl.h>
38 #include <sys/stat.h>
39 #include <sys/sunddi.h>
40 
41 #include <sys/1394/h1394.h>
42 #include <sys/1394/adapters/hci1394.h>
43 #include <sys/1394/adapters/hci1394_extern.h>
44 
45 
46 
47 int
48 hci1394_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
49 {
50 	hci1394_state_t *soft_state;
51 
52 
53 	TNF_PROBE_0_DEBUG(hci1394_detach_enter, HCI1394_TNF_HAL_STACK, "");
54 
55 	soft_state = ddi_get_soft_state(hci1394_statep, ddi_get_instance(dip));
56 	if (soft_state == NULL) {
57 		TNF_PROBE_1(hci1394_detach_ssn_fail, HCI1394_TNF_HAL_ERROR, "",
58 		    tnf_string, errmsg, "soft_state = NULL");
59 		TNF_PROBE_0_DEBUG(hci1394_detach_exit, HCI1394_TNF_HAL_STACK,
60 		    "");
61 		return (DDI_FAILURE);
62 	}
63 
64 	switch (cmd) {
65 	case DDI_DETACH:
66 		/* Don't allow the HW to generate any more interrupts */
67 		hci1394_ohci_intr_master_disable(soft_state->ohci);
68 		hci1394_ohci_it_intr_disable(soft_state->ohci, 0xFFFFFFFF);
69 		hci1394_ohci_ir_intr_disable(soft_state->ohci, 0xFFFFFFFF);
70 
71 		/* Clear any pending interrupts - no longer valid */
72 		hci1394_ohci_intr_clear(soft_state->ohci, 0xFFFFFFFF);
73 		hci1394_ohci_it_intr_clear(soft_state->ohci, 0xFFFFFFFF);
74 		hci1394_ohci_ir_intr_clear(soft_state->ohci, 0xFFFFFFFF);
75 
76 		/* Make sure we tell others on the bus we are dropping out */
77 		(void) hci1394_ohci_phy_clr(soft_state->ohci, 4, 0xc0);
78 		ddi_put32(soft_state->ohci->ohci_reg_handle,
79 		    &soft_state->ohci->ohci_regs->link_ctrl_clr,
80 		    0xFFFFFFFF);
81 
82 		/* unregister interrupt handler */
83 		hci1394_isr_handler_fini(soft_state);
84 
85 		/* don't accept anymore commands from services layer */
86 		(void) hci1394_state_set(&soft_state->drvinfo,
87 		    HCI1394_SHUTDOWN);
88 
89 		/* Do a long reset on the bus so every one knows we are gone */
90 		(void) hci1394_ohci_bus_reset_nroot(soft_state->ohci);
91 
92 		/* Reset the OHCI HW */
93 		(void) hci1394_ohci_soft_reset(soft_state->ohci);
94 
95 		/* Flush out async DMA Q's (cancels pendingQ timeouts too) */
96 		hci1394_async_flush(soft_state->async);
97 
98 		(void) h1394_detach(&soft_state->drvinfo.di_sl_private,
99 		    DDI_DETACH);
100 
101 		/* remove the minor node */
102 		ddi_remove_minor_node(dip, "devctl");
103 
104 		/* cleanup */
105 		hci1394_detach_hardware(soft_state);
106 
107 		/* cleanup Solaris interrupt stuff */
108 		hci1394_isr_fini(soft_state);
109 
110 		/* cleanup soft state stuff */
111 		hci1394_soft_state_fini(soft_state);
112 
113 		/* free soft state */
114 		ddi_soft_state_free(hci1394_statep,
115 		    soft_state->drvinfo.di_instance);
116 
117 		TNF_PROBE_0_DEBUG(hci1394_detach_exit, HCI1394_TNF_HAL_STACK,
118 		    "");
119 		return (DDI_SUCCESS);
120 
121 	case DDI_SUSPEND:
122 		/* Don't allow the HW to generate any more interrupts */
123 		hci1394_ohci_intr_master_disable(soft_state->ohci);
124 		hci1394_ohci_it_intr_disable(soft_state->ohci, 0xFFFFFFFF);
125 		hci1394_ohci_ir_intr_disable(soft_state->ohci, 0xFFFFFFFF);
126 
127 		/* Clear any pending interrupts - no longer valid */
128 		hci1394_ohci_intr_clear(soft_state->ohci, 0xFFFFFFFF);
129 		hci1394_ohci_it_intr_clear(soft_state->ohci, 0xFFFFFFFF);
130 		hci1394_ohci_ir_intr_clear(soft_state->ohci, 0xFFFFFFFF);
131 
132 		/* Make sure we tell others on the bus we are dropping out */
133 		(void) hci1394_ohci_phy_clr(soft_state->ohci, 4, 0xc0);
134 		ddi_put32(soft_state->ohci->ohci_reg_handle,
135 		    &soft_state->ohci->ohci_regs->link_ctrl_clr,
136 		    0xFFFFFFFF);
137 
138 		/* don't accept anymore commands from services layer */
139 		(void) hci1394_state_set(&soft_state->drvinfo,
140 		    HCI1394_SHUTDOWN);
141 
142 		/* Do a long reset on the bus so every one knows we are gone */
143 		(void) hci1394_ohci_bus_reset_nroot(soft_state->ohci);
144 
145 		/* Reset the OHCI HW */
146 		(void) hci1394_ohci_soft_reset(soft_state->ohci);
147 
148 		/* Make sure async engine is ready to suspend */
149 		hci1394_async_suspend(soft_state->async);
150 
151 		(void) h1394_detach(&soft_state->drvinfo.di_sl_private,
152 		    DDI_SUSPEND);
153 
154 		TNF_PROBE_0_DEBUG(hci1394_detach_exit, HCI1394_TNF_HAL_STACK,
155 		    "");
156 		return (DDI_SUCCESS);
157 
158 	default:
159 		TNF_PROBE_1(hci1394_detach_fail, HCI1394_TNF_HAL_ERROR, "",
160 		    tnf_string, errmsg, "in detach default");
161 		break;
162 	}
163 
164 	TNF_PROBE_0_DEBUG(hci1394_detach_exit, HCI1394_TNF_HAL_STACK, "");
165 
166 	return (DDI_FAILURE);
167 }
168 
169 
170 void
171 hci1394_detach_hardware(hci1394_state_t *soft_state)
172 {
173 	ASSERT(soft_state != NULL);
174 	TNF_PROBE_0_DEBUG(hci1394_detach_hardware_enter, HCI1394_TNF_HAL_STACK,
175 	    "");
176 
177 	/* free up vendor specific registers */
178 	hci1394_vendor_fini(&soft_state->vendor);
179 
180 	/* cleanup isoch layer */
181 	hci1394_isoch_fini(&soft_state->isoch);
182 
183 	/* cleanup async layer */
184 	hci1394_async_fini(&soft_state->async);
185 
186 	/* Free up csr register space */
187 	hci1394_csr_fini(&soft_state->csr);
188 
189 	/* free up OpenHCI registers */
190 	hci1394_ohci_fini(&soft_state->ohci);
191 
192 	/* free up PCI config space */
193 	hci1394_pci_fini(soft_state);
194 
195 	TNF_PROBE_0_DEBUG(hci1394_detach_hardware_exit, HCI1394_TNF_HAL_STACK,
196 	    "");
197 }
198 
199 
200 /*
201  * hci1394_pci_fini()
202  *    Cleanup after a PCI init.
203  */
204 void
205 hci1394_pci_fini(hci1394_state_t *soft_state)
206 {
207 	ASSERT(soft_state != NULL);
208 	TNF_PROBE_0_DEBUG(hci1394_pci_fini_enter, HCI1394_TNF_HAL_STACK, "");
209 	pci_config_teardown(&soft_state->pci_config);
210 	TNF_PROBE_0_DEBUG(hci1394_pci_fini_exit, HCI1394_TNF_HAL_STACK, "");
211 }
212 
213 
214 /*
215  * hci1394_soft_state_fini()
216  *    Cleanup any mutex's, etc. in soft_state.
217  */
218 void
219 hci1394_soft_state_fini(hci1394_state_t *soft_state)
220 {
221 	ASSERT(soft_state != NULL);
222 	TNF_PROBE_0_DEBUG(hci1394_soft_state_fini_enter, HCI1394_TNF_HAL_STACK,
223 	    "");
224 	mutex_destroy(&soft_state->drvinfo.di_drvstate.ds_mutex);
225 	TNF_PROBE_0_DEBUG(hci1394_soft_state_fini_exit, HCI1394_TNF_HAL_STACK,
226 	    "");
227 }
228