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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * PCI Control Block object
31 */
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/systm.h> /* timeout() */
35 #include <sys/async.h>
36 #include <sys/sunddi.h>
37 #include <sys/ddi_impldefs.h>
38 #include <sys/pci/pci_obj.h>
39 #include <sys/machsystm.h>
40
41 #ifdef _STARFIRE
42 #include <sys/starfire.h>
43 #endif /* _STARFIRE */
44
45 /*LINTLIBRARY*/
46
47 void
cb_create(pci_t * pci_p)48 cb_create(pci_t *pci_p)
49 {
50 cb_t *cb_p = (cb_t *)kmem_zalloc(sizeof (cb_t), KM_SLEEP);
51
52 mutex_init(&cb_p->cb_intr_lock, NULL, MUTEX_DRIVER, NULL);
53 pci_p->pci_cb_p = cb_p;
54 cb_p->cb_pci_cmn_p = pci_p->pci_common_p;
55
56 pci_cb_setup(pci_p);
57 }
58
59 void
cb_destroy(pci_t * pci_p)60 cb_destroy(pci_t *pci_p)
61 {
62 cb_t *cb_p = pci_p->pci_cb_p;
63
64 intr_dist_rem(cb_intr_dist, cb_p);
65 pci_cb_teardown(pci_p);
66 pci_p->pci_cb_p = NULL;
67 mutex_destroy(&cb_p->cb_intr_lock);
68 kmem_free(cb_p, sizeof (cb_t));
69 }
70
71 static void
cb_set_nintr_reg(cb_t * cb_p,ib_ino_t ino,uint64_t value)72 cb_set_nintr_reg(cb_t *cb_p, ib_ino_t ino, uint64_t value)
73 {
74 uint64_t pa = cb_ino_to_clr_pa(cb_p, ino);
75
76 DEBUG3(DBG_CB|DBG_CONT, NULL,
77 "pci-%x cb_set_nintr_reg: ino=%x PA=%016llx\n",
78 cb_p->cb_pci_cmn_p->pci_common_id, ino, pa);
79
80 stdphysio(pa, value);
81 (void) lddphysio(pa); /* flush the previous write */
82 }
83
84 /*
85 * enable an internal interrupt source:
86 * if an interrupt is shared by both sides, record it in cb_inos[] and
87 * cb will own its distribution.
88 */
89 void
cb_enable_nintr(pci_t * pci_p,enum cb_nintr_index idx)90 cb_enable_nintr(pci_t *pci_p, enum cb_nintr_index idx)
91 {
92 cb_t *cb_p = pci_p->pci_cb_p;
93 ib_ino_t ino = IB_MONDO_TO_INO(pci_p->pci_inos[idx]);
94 ib_mondo_t mondo = CB_INO_TO_MONDO(cb_p, ino);
95 uint32_t cpu_id;
96 uint64_t reg, pa;
97
98 ASSERT(idx < CBNINTR_MAX);
99 pa = cb_ino_to_map_pa(cb_p, ino);
100
101 mutex_enter(&cb_p->cb_intr_lock);
102 cpu_id = intr_dist_cpuid();
103
104 #ifdef _STARFIRE
105 cpu_id = pc_translate_tgtid(cb_p->cb_ittrans_cookie, cpu_id,
106 IB_GET_MAPREG_INO(ino));
107 #endif /* _STARFIRE */
108
109 reg = ib_get_map_reg(mondo, cpu_id);
110 stdphysio(pa, reg);
111
112 ASSERT(cb_p->cb_inos[idx] == 0);
113 cb_p->cb_inos[idx] = ino;
114
115 cb_set_nintr_reg(cb_p, ino, COMMON_CLEAR_INTR_REG_IDLE);
116 mutex_exit(&cb_p->cb_intr_lock);
117
118 DEBUG3(DBG_CB|DBG_CONT, NULL,
119 "pci-%x cb_enable_nintr: ino=%x cpu_id=%x\n",
120 pci_p->pci_id, ino, cpu_id);
121 DEBUG2(DBG_CB|DBG_CONT, NULL, "\tPA=%016llx data=%016llx\n", pa, reg);
122 }
123
124 static void
cb_disable_nintr_reg(cb_t * cb_p,ib_ino_t ino,int wait)125 cb_disable_nintr_reg(cb_t *cb_p, ib_ino_t ino, int wait)
126 {
127 uint64_t tmp, map_reg_pa = cb_ino_to_map_pa(cb_p, ino);
128 ASSERT(MUTEX_HELD(&cb_p->cb_intr_lock));
129
130 /* mark interrupt invalid in mapping register */
131 tmp = lddphysio(map_reg_pa) & ~COMMON_INTR_MAP_REG_VALID;
132 stdphysio(map_reg_pa, tmp);
133 (void) lddphysio(map_reg_pa); /* flush previous write */
134
135 if (wait) {
136 hrtime_t start_time;
137 uint64_t state_reg_pa = cb_p->cb_obsta_pa;
138 uint_t shift = (ino & 0x1f) << 1;
139
140 /* busy wait if there is interrupt being processed */
141 /* unless panic or timeout for interrupt pending is reached */
142 start_time = gethrtime();
143 while ((((lddphysio(state_reg_pa) >> shift) &
144 COMMON_CLEAR_INTR_REG_MASK) ==
145 COMMON_CLEAR_INTR_REG_PENDING) && !panicstr) {
146 if (gethrtime() - start_time > pci_intrpend_timeout) {
147 cmn_err(CE_WARN,
148 "pci@%x cb_disable_nintr_reg(%lx,%x) timeout",
149 cb_p->cb_pci_cmn_p->pci_common_id,
150 map_reg_pa,
151 CB_INO_TO_MONDO(cb_p, ino));
152 break;
153 }
154 }
155 }
156 }
157
158 void
cb_disable_nintr(cb_t * cb_p,enum cb_nintr_index idx,int wait)159 cb_disable_nintr(cb_t *cb_p, enum cb_nintr_index idx, int wait)
160 {
161 ib_ino_t ino = cb_p->cb_inos[idx];
162 ASSERT(idx < CBNINTR_MAX);
163 ASSERT(ino);
164
165 mutex_enter(&cb_p->cb_intr_lock);
166 cb_disable_nintr_reg(cb_p, ino, wait);
167 cb_set_nintr_reg(cb_p, ino, COMMON_CLEAR_INTR_REG_PENDING);
168 cb_p->cb_inos[idx] = 0;
169 mutex_exit(&cb_p->cb_intr_lock);
170 #ifdef _STARFIRE
171 pc_ittrans_cleanup(cb_p->cb_ittrans_cookie,
172 (volatile uint64_t *)(uintptr_t)ino);
173 #endif /* _STARFIRE */
174 }
175
176 void
cb_clear_nintr(cb_t * cb_p,enum cb_nintr_index idx)177 cb_clear_nintr(cb_t *cb_p, enum cb_nintr_index idx)
178 {
179 ib_ino_t ino = cb_p->cb_inos[idx];
180 ASSERT(idx < CBNINTR_MAX);
181 ASSERT(ino);
182 cb_set_nintr_reg(cb_p, ino, COMMON_CLEAR_INTR_REG_IDLE);
183 }
184
185 void
cb_intr_dist(void * arg)186 cb_intr_dist(void *arg)
187 {
188 int i;
189 cb_t *cb_p = (cb_t *)arg;
190
191 mutex_enter(&cb_p->cb_intr_lock);
192 for (i = 0; i < cb_p->cb_no_of_inos; i++) {
193 uint64_t mr_pa;
194 volatile uint64_t imr;
195 ib_mondo_t mondo;
196 uint32_t cpu_id;
197
198 ib_ino_t ino = cb_p->cb_inos[i];
199 if (!ino) /* skip non-shared interrupts */
200 continue;
201
202 mr_pa = cb_ino_to_map_pa(cb_p, ino);
203 imr = lddphysio(mr_pa);
204 if (!IB_INO_INTR_ISON(imr))
205 continue;
206
207 mondo = CB_INO_TO_MONDO(cb_p, ino);
208 cpu_id = intr_dist_cpuid();
209 #ifdef _STARFIRE
210 cpu_id = pc_translate_tgtid(cb_p->cb_ittrans_cookie, cpu_id,
211 IB_GET_MAPREG_INO(ino));
212 #else
213 if (ib_map_reg_get_cpu(imr) == cpu_id)
214 continue; /* same cpu target, no re-program */
215 #endif
216 cb_disable_nintr_reg(cb_p, ino, IB_INTR_WAIT);
217 stdphysio(mr_pa, ib_get_map_reg(mondo, cpu_id));
218 (void) lddphysio(mr_pa); /* flush previous write */
219 }
220 mutex_exit(&cb_p->cb_intr_lock);
221 }
222
223 void
cb_suspend(cb_t * cb_p)224 cb_suspend(cb_t *cb_p)
225 {
226 int i, inos = cb_p->cb_no_of_inos;
227 ASSERT(!cb_p->cb_imr_save);
228 cb_p->cb_imr_save = kmem_alloc(inos * sizeof (uint64_t), KM_SLEEP);
229
230 /*
231 * save the internal interrupts' mapping registers content
232 *
233 * The PBM IMR really doesn't need to be saved, as it is
234 * different per side and is handled by pbm_suspend/resume.
235 * But it complicates the logic.
236 */
237 for (i = 0; i < inos; i++) {
238 uint64_t pa;
239 ib_ino_t ino = cb_p->cb_inos[i];
240 if (!ino)
241 continue;
242 pa = cb_ino_to_map_pa(cb_p, ino);
243 cb_p->cb_imr_save[i] = lddphysio(pa);
244 }
245 }
246
247 void
cb_resume(cb_t * cb_p)248 cb_resume(cb_t *cb_p)
249 {
250 int i;
251 for (i = 0; i < cb_p->cb_no_of_inos; i++) {
252 uint64_t pa;
253 ib_ino_t ino = cb_p->cb_inos[i];
254 if (!ino)
255 continue;
256 pa = cb_ino_to_map_pa(cb_p, ino);
257 cb_set_nintr_reg(cb_p, ino, COMMON_CLEAR_INTR_REG_IDLE);
258 stdphysio(pa, cb_p->cb_imr_save[i]); /* restore IMR */
259 }
260 kmem_free(cb_p->cb_imr_save, cb_p->cb_no_of_inos * sizeof (uint64_t));
261 cb_p->cb_imr_save = NULL;
262 }
263