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