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
46int
47hci1394_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 */
178int
179hci1394_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
213void
214hci1394_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 */
247void
248hci1394_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 */
261void
262hci1394_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