summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/sys/ddi_intr_impl.h
blob: b56a6a4c1fd41b49bdacc21c706e1048ce0f1b07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_SYS_DDI_INTR_IMPL_H
#define	_SYS_DDI_INTR_IMPL_H

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Sun DDI interrupt implementation specific definitions
 */

#ifdef	__cplusplus
extern "C" {
#endif

#ifdef _KERNEL

/*
 * Typedef for interrupt ops
 */
typedef enum {
	DDI_INTROP_SUPPORTED_TYPES = 1,	/* 1 get supported interrupts types */
	DDI_INTROP_NINTRS,		/* 2 get num of interrupts supported */
	DDI_INTROP_ALLOC,		/* 3 allocate interrupt handle */
	DDI_INTROP_GETPRI,		/* 4 get priority */
	DDI_INTROP_SETPRI,		/* 5 set priority */
	DDI_INTROP_ADDISR,		/* 6 add interrupt handler */
	DDI_INTROP_DUPVEC,		/* 7 duplicate interrupt handler */
	DDI_INTROP_ENABLE,		/* 8 enable interrupt */
	DDI_INTROP_BLOCKENABLE,		/* 9 block enable interrupts */
	DDI_INTROP_BLOCKDISABLE,	/* 10 block disable interrupts */
	DDI_INTROP_DISABLE,		/* 11 disable interrupt */
	DDI_INTROP_REMISR,		/* 12 remove interrupt handler */
	DDI_INTROP_FREE,		/* 13 free interrupt handle */
	DDI_INTROP_GETCAP,		/* 14 get capacity */
	DDI_INTROP_SETCAP,		/* 15 set capacity */
	DDI_INTROP_SETMASK,		/* 16 set mask */
	DDI_INTROP_CLRMASK,		/* 17 clear mask */
	DDI_INTROP_GETPENDING,		/* 18 get pending interrupt */
	DDI_INTROP_NAVAIL		/* 19 get num of available interrupts */
} ddi_intr_op_t;

/* Version number used in the handles */
#define	DDI_INTR_VERSION_1	1
#define	DDI_INTR_VERSION	DDI_INTR_VERSION_1

/*
 * One such data structure is allocated per ddi_intr_handle_t
 * This is the incore copy of the regular interrupt info.
 */
typedef struct ddi_intr_handle_impl {
	dev_info_t		*ih_dip;	/* dip associated with handle */
	uint16_t		ih_type;	/* interrupt type being used */
	ushort_t		ih_inum;	/* interrupt number */
	uint32_t		ih_vector;	/* vector number */
	uint16_t		ih_ver;		/* Version */
	uint_t			ih_state;	/* interrupt handle state */
	uint_t			ih_cap;		/* interrupt capabilities */
	uint_t			ih_pri;		/* priority - bus dependent */
	krwlock_t		ih_rwlock;	/* read/write lock per handle */

	uint_t			(*ih_cb_func)(caddr_t, caddr_t);
	void			*ih_cb_arg1;
	void			*ih_cb_arg2;

	/*
	 * The following 3 members are used to support MSI-X specific features
	 */
	uint_t			ih_flags;	/* Misc flags */
	uint_t			ih_dup_cnt;	/* # of dupped msi-x vectors */
	struct ddi_intr_handle_impl	*ih_main;
						/* pntr to the main vector */
	/*
	 * The next set of members are for 'scratch' purpose only.
	 * The DDI interrupt framework uses them internally and their
	 * interpretation is left to the framework. For now,
	 *	scratch1	- used to send NINTRs information
	 *			  to various nexus drivers.
	 *	scratch2	- used to send 'behavior' flag
	 *			  information to the nexus drivers
	 *			  from ddi_intr_alloc().  It is also
	 *			  used to send 'h_array' to the nexus drivers
	 *			  for ddi_intr_block_enable/disable() on x86.
	 *	private		- On X86 it usually carries a pointer to
	 *			  ihdl_plat_t.  Not used on SPARC platforms.
	 */
	void			*ih_private;	/* Platform specific data */
	uint_t			ih_scratch1;	/* Scratch1: #interrupts */
	void			*ih_scratch2;	/* Scratch2: flag/h_array */
} ddi_intr_handle_impl_t;

/* values for ih_state (strictly for interrupt handle) */
#define	DDI_IHDL_STATE_ALLOC	0x01	/* Allocated. ddi_intr_alloc() called */
#define	DDI_IHDL_STATE_ADDED	0x02	/* Added interrupt handler */
					/* ddi_intr_add_handler() called */
#define	DDI_IHDL_STATE_ENABLE	0x04	/* Enabled. ddi_intr_enable() called */

#define	DDI_INTR_IS_MSI_OR_MSIX(type) \
	((type) == DDI_INTR_TYPE_MSI || (type) == DDI_INTR_TYPE_MSIX)

#define	DDI_INTR_BEHAVIOR_FLAG_VALID(f) \
	    (((f) == DDI_INTR_ALLOC_NORMAL) || ((f) == DDI_INTR_ALLOC_STRICT))

#define	DDI_INTR_TYPE_FLAG_VALID(t) \
	    (((t) == DDI_INTR_TYPE_FIXED) || \
	    ((t) == DDI_INTR_TYPE_MSI) || \
	    ((t) == DDI_INTR_TYPE_MSIX))

/* values for ih_flags */
#define	DDI_INTR_MSIX_DUP	0x01	/* MSI-X vector which has been dupped */

/* Maximum number of MSI resources to allocate */
#define	DDI_MAX_MSI_ALLOC	2

/*
 * The following MSI-X limits will change with Interrupt Resource Management
 * (IRM) support.
 */
/* Default number of MSI-X resources to allocate */
#define	DDI_DEFAULT_MSIX_ALLOC	2

/* Maximum number of MSI-X resources to allocate */
#define	DDI_MAX_MSIX_ALLOC	8

struct av_softinfo;

/*
 * One such data structure is allocated per ddi_soft_intr_handle
 * This is the incore copy of the softint info.
 */
typedef struct ddi_softint_hdl_impl {
	dev_info_t	*ih_dip;		/* dip associated with handle */
	uint_t		ih_pri;			/* priority - bus dependent */
	krwlock_t	ih_rwlock;		/* read/write lock per handle */
	struct av_softinfo *ih_pending;		/* whether softint is pending */

	uint_t		(*ih_cb_func)(caddr_t, caddr_t);
						/* cb function for soft ints */
	void		*ih_cb_arg1;		/* arg1 of callback function */
	void		*ih_cb_arg2;		/* arg2 passed to "trigger" */

	/*
	 * The next member is for 'scratch' purpose only.
	 * The DDI interrupt framework uses it internally and its
	 * interpretation is left to the framework.
	 *	private		- used by the DDI framework to pass back
	 *			  and forth 'softid' information on SPARC
	 *			  side only. Not used on X86 platform.
	 */
	void		*ih_private;		/* Platform specific data */
} ddi_softint_hdl_impl_t;

/* Softint internal implementation defines */
#define	DDI_SOFT_INTR_PRI_M	4
#define	DDI_SOFT_INTR_PRI_H	6

/*
 * One such data structure is allocated for MSI-X enabled
 * device. If no MSI-X is enabled then it is NULL
 */
typedef struct ddi_intr_msix {
	/* MSI-X Table related information */
	ddi_acc_handle_t	msix_tbl_hdl;		/* MSI-X table handle */
	uint32_t		*msix_tbl_addr;		/* MSI-X table addr */
	uint32_t		msix_tbl_offset;	/* MSI-X table offset */

	/* MSI-X PBA Table related information */
	ddi_acc_handle_t	msix_pba_hdl;		/* MSI-X PBA handle */
	uint32_t		*msix_pba_addr;		/* MSI-X PBA addr */
	uint32_t		msix_pba_offset;	/* MSI-X PBA offset */

	ddi_device_acc_attr_t	msix_dev_attr;		/* MSI-X device attr */
} ddi_intr_msix_t;


/*
 * One such data structure is allocated for each dip.
 * It has interrupt related information that can be
 * stored/retrieved for convenience.
 */
typedef struct devinfo_intr {
	/* These three fields show what the device is capable of */
	uint_t		devi_intr_sup_types;	/* Intrs supported by device */

	ddi_intr_msix_t	*devi_msix_p;		/* MSI-X info, if supported */

	/* Next three fields show current status for the device */
	uint_t		devi_intr_curr_type;	/* Interrupt type being used */
	uint_t		devi_intr_sup_nintrs;	/* #intr supported */
	uint_t		devi_intr_curr_nintrs;	/* #intr currently being used */

	ddi_intr_handle_t **devi_intr_handle_p;	/* Hdl for legacy intr APIs */

#if defined(__i386) || defined(__amd64)
	/* Save the PCI config space handle */
	ddi_acc_handle_t devi_cfg_handle;
	int		 devi_cap_ptr;		/* MSI or MSI-X cap pointer */
#endif
} devinfo_intr_t;

#define	NEXUS_HAS_INTR_OP(dip)	\
	((DEVI(dip)->devi_ops->devo_bus_ops) && \
	(DEVI(dip)->devi_ops->devo_bus_ops->busops_rev >= BUSO_REV_9) && \
	(DEVI(dip)->devi_ops->devo_bus_ops->bus_intr_op))

int	i_ddi_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
	    ddi_intr_handle_impl_t *hdlp, void *result);

int	i_ddi_add_softint(ddi_softint_hdl_impl_t *);
void	i_ddi_remove_softint(ddi_softint_hdl_impl_t *);
int	i_ddi_trigger_softint(ddi_softint_hdl_impl_t *, void *);
int	i_ddi_set_softint_pri(ddi_softint_hdl_impl_t *, uint_t);

void	i_ddi_intr_devi_init(dev_info_t *dip);
void	i_ddi_intr_devi_fini(dev_info_t *dip);

uint_t	i_ddi_intr_get_supported_types(dev_info_t *dip);
void	i_ddi_intr_set_supported_types(dev_info_t *dip, int sup_type);
uint_t	i_ddi_intr_get_current_type(dev_info_t *dip);
void	i_ddi_intr_set_current_type(dev_info_t *dip, int intr_type);
uint_t	i_ddi_intr_get_supported_nintrs(dev_info_t *dip, int intr_type);
void	i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs);
uint_t	i_ddi_intr_get_current_nintrs(dev_info_t *dip);
void	i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs);

ddi_intr_handle_t *i_ddi_get_intr_handle(dev_info_t *dip, int inum);
void	i_ddi_set_intr_handle(dev_info_t *dip, int inum,
	    ddi_intr_handle_t *hdlp);

ddi_intr_msix_t	*i_ddi_get_msix(dev_info_t *dip);
void	i_ddi_set_msix(dev_info_t *dip, ddi_intr_msix_t *msix_p);

#if defined(__i386) || defined(__amd64)
ddi_acc_handle_t	i_ddi_get_pci_config_handle(dev_info_t *dip);
void	i_ddi_set_pci_config_handle(dev_info_t *dip, ddi_acc_handle_t handle);
int	i_ddi_get_msi_msix_cap_ptr(dev_info_t *dip);
void	i_ddi_set_msi_msix_cap_ptr(dev_info_t *dip, int cap_ptr);
#endif

uint_t	i_ddi_get_msix_alloc_limit(dev_info_t *dip);

int32_t i_ddi_get_intr_weight(dev_info_t *);
int32_t i_ddi_set_intr_weight(dev_info_t *, int32_t);

void	i_ddi_alloc_intr_phdl(ddi_intr_handle_impl_t *);
void	i_ddi_free_intr_phdl(ddi_intr_handle_impl_t *);

#define	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, func, arg1, arg2) \
	hdlp->ih_cb_func = func; \
	hdlp->ih_cb_arg1 = arg1; \
	hdlp->ih_cb_arg2 = arg2;

#ifdef DEBUG
#define	I_DDI_VERIFY_MSIX_HANDLE(hdlp)					\
	if ((hdlp->ih_type == DDI_INTR_TYPE_MSIX) && 			\
	    (hdlp->ih_flags & DDI_INTR_MSIX_DUP)) {			\
		ASSERT(hdlp->ih_dip == hdlp->ih_main->ih_dip);		\
		ASSERT(hdlp->ih_type == hdlp->ih_main->ih_type);	\
		ASSERT(hdlp->ih_vector == hdlp->ih_main->ih_vector);	\
		ASSERT(hdlp->ih_ver == hdlp->ih_main->ih_ver);		\
		ASSERT(hdlp->ih_cap == hdlp->ih_main->ih_cap);		\
		ASSERT(hdlp->ih_pri == hdlp->ih_main->ih_pri);		\
	}
#else
#define	I_DDI_VERIFY_MSIX_HANDLE(hdlp)
#endif

#else	/* _KERNEL */

typedef struct devinfo_intr devinfo_intr_t;

#endif	/* _KERNEL */

/*
 * Used only by old DDI interrupt interfaces.
 */

/*
 * This structure represents one interrupt possible from the given
 * device. It is used in an array for devices with multiple interrupts.
 */
struct intrspec {
	uint_t intrspec_pri;		/* interrupt priority */
	uint_t intrspec_vec;		/* vector # (0 if none) */
	uint_t (*intrspec_func)();	/* function to call for interrupt, */
					/* If (uint_t (*)()) 0, none. */
					/* If (uint_t (*)()) 1, then */
};

#ifdef _KERNEL

/*
 * Figure out how many FIXED nintrs are supported
 */
int	i_ddi_get_intx_nintrs(dev_info_t *dip);

/*
 * NOTE:
 *	The following 4 busops entry points are obsoleted with version
 *	9 or greater. Use i_ddi_intr_op interface in place of these
 *	obsolete interfaces.
 *
 *	Remove these busops entry points and all related data structures
 *	in future minor/major solaris release.
 */
typedef enum {DDI_INTR_CTLOPS_NONE} ddi_intr_ctlop_t;

/* The following are obsolete interfaces */
ddi_intrspec_t	i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip,
	    uint_t inumber);

int	i_ddi_add_intrspec(dev_info_t *dip, dev_info_t *rdip,
	    ddi_intrspec_t intrspec, ddi_iblock_cookie_t *iblock_cookiep,
	    ddi_idevice_cookie_t *idevice_cookiep,
	    uint_t (*int_handler)(caddr_t int_handler_arg),
	    caddr_t int_handler_arg, int kind);

void	i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip,
	    ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie);

int	i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip,
	    ddi_intr_ctlop_t op, void *arg, void *val);

#endif	/* _KERNEL */

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_DDI_INTR_IMPL_H */