1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/mkdev.h>
31*0Sstevel@tonic-gate #include <sys/ddi.h>
32*0Sstevel@tonic-gate #include <sys/sunddi.h>
33*0Sstevel@tonic-gate #include <vm/seg_kmem.h>
34*0Sstevel@tonic-gate #include <sys/machparam.h>
35*0Sstevel@tonic-gate #include <sys/ontrap.h>
36*0Sstevel@tonic-gate #include <sys/pci.h>
37*0Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
38*0Sstevel@tonic-gate #include <sys/pci_cfgspace.h>
39*0Sstevel@tonic-gate #include <sys/pci_tools.h>
40*0Sstevel@tonic-gate #include <sys/pci_tools_var.h>
41*0Sstevel@tonic-gate #include "pci_var.h"
42*0Sstevel@tonic-gate #include <sys/promif.h>
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate #define	SUCCESS	0
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate int pcitool_debug = 0;
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate /*
49*0Sstevel@tonic-gate  * Offsets of BARS in config space.  First entry of 0 means config space.
50*0Sstevel@tonic-gate  * Entries here correlate to pcitool_bars_t enumerated type.
51*0Sstevel@tonic-gate  */
52*0Sstevel@tonic-gate static uint8_t pci_bars[] = {
53*0Sstevel@tonic-gate 	0x0,
54*0Sstevel@tonic-gate 	PCI_CONF_BASE0,
55*0Sstevel@tonic-gate 	PCI_CONF_BASE1,
56*0Sstevel@tonic-gate 	PCI_CONF_BASE2,
57*0Sstevel@tonic-gate 	PCI_CONF_BASE3,
58*0Sstevel@tonic-gate 	PCI_CONF_BASE4,
59*0Sstevel@tonic-gate 	PCI_CONF_BASE5,
60*0Sstevel@tonic-gate 	PCI_CONF_ROM
61*0Sstevel@tonic-gate };
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate static uint64_t pcitool_swap_endian(uint64_t data, int size);
64*0Sstevel@tonic-gate static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg,
65*0Sstevel@tonic-gate     boolean_t write_flag);
66*0Sstevel@tonic-gate static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg,
67*0Sstevel@tonic-gate     boolean_t write_flag);
68*0Sstevel@tonic-gate static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg,
69*0Sstevel@tonic-gate     uint64_t virt_addr, boolean_t write_flag);
70*0Sstevel@tonic-gate static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages);
71*0Sstevel@tonic-gate static void pcitool_unmap(uint64_t virt_addr, size_t num_pages);
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate /*
74*0Sstevel@tonic-gate  * A note about ontrap handling:
75*0Sstevel@tonic-gate  *
76*0Sstevel@tonic-gate  * X86 systems on which this module was tested return FFs instead of bus errors
77*0Sstevel@tonic-gate  * when accessing devices with invalid addresses.  Ontrap handling, which
78*0Sstevel@tonic-gate  * gracefully handles kernel bus errors, is installed anyway, in case future
79*0Sstevel@tonic-gate  * X86 platforms require it.
80*0Sstevel@tonic-gate  */
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate /*
83*0Sstevel@tonic-gate  * Main function for handling interrupt CPU binding requests and queries.
84*0Sstevel@tonic-gate  * Need to implement later
85*0Sstevel@tonic-gate  */
86*0Sstevel@tonic-gate /*ARGSUSED*/
87*0Sstevel@tonic-gate int
88*0Sstevel@tonic-gate pcitool_intr_admn(dev_t dev, void *arg, int cmd, int mode)
89*0Sstevel@tonic-gate {
90*0Sstevel@tonic-gate 	return (ENOTSUP);
91*0Sstevel@tonic-gate }
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate /*
95*0Sstevel@tonic-gate  * Perform register accesses on the nexus device itself.
96*0Sstevel@tonic-gate  * No explicit PCI nexus device for X86, so not applicable.
97*0Sstevel@tonic-gate  */
98*0Sstevel@tonic-gate /*ARGSUSED*/
99*0Sstevel@tonic-gate int
100*0Sstevel@tonic-gate pcitool_bus_reg_ops(dev_t dev, void *arg, int cmd, int mode)
101*0Sstevel@tonic-gate {
102*0Sstevel@tonic-gate 	return (ENOTSUP);
103*0Sstevel@tonic-gate }
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate /* Swap endianness. */
106*0Sstevel@tonic-gate static uint64_t
107*0Sstevel@tonic-gate pcitool_swap_endian(uint64_t data, int size)
108*0Sstevel@tonic-gate {
109*0Sstevel@tonic-gate 	typedef union {
110*0Sstevel@tonic-gate 		uint64_t data64;
111*0Sstevel@tonic-gate 		uint8_t data8[8];
112*0Sstevel@tonic-gate 	} data_split_t;
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	data_split_t orig_data;
115*0Sstevel@tonic-gate 	data_split_t returned_data;
116*0Sstevel@tonic-gate 	int i;
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate 	orig_data.data64 = data;
119*0Sstevel@tonic-gate 	returned_data.data64 = 0;
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	for (i = 0; i < size; i++) {
122*0Sstevel@tonic-gate 		returned_data.data8[i] = orig_data.data8[size - 1 - i];
123*0Sstevel@tonic-gate 	}
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 	return (returned_data.data64);
126*0Sstevel@tonic-gate }
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate /* Access device.  prg is modified. */
130*0Sstevel@tonic-gate /*ARGSUSED*/
131*0Sstevel@tonic-gate static int
132*0Sstevel@tonic-gate pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag)
133*0Sstevel@tonic-gate {
134*0Sstevel@tonic-gate 	int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
135*0Sstevel@tonic-gate 	boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
136*0Sstevel@tonic-gate 	int rval = SUCCESS;
137*0Sstevel@tonic-gate 	uint64_t local_data;
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	/*
140*0Sstevel@tonic-gate 	 * NOTE: there is no way to verify whether or not the address is valid.
141*0Sstevel@tonic-gate 	 * The put functions return void and the get functions return ff on
142*0Sstevel@tonic-gate 	 * error.
143*0Sstevel@tonic-gate 	 */
144*0Sstevel@tonic-gate 	prg->status = PCITOOL_SUCCESS;
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	if (write_flag) {
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 		if (big_endian) {
149*0Sstevel@tonic-gate 			local_data = pcitool_swap_endian(prg->data, size);
150*0Sstevel@tonic-gate 		} else {
151*0Sstevel@tonic-gate 			local_data = prg->data;
152*0Sstevel@tonic-gate 		}
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 		switch (size) {
155*0Sstevel@tonic-gate 		case 1:
156*0Sstevel@tonic-gate 			(*pci_putb_func)(prg->bus_no, prg->dev_no,
157*0Sstevel@tonic-gate 			    prg->func_no, prg->offset, local_data);
158*0Sstevel@tonic-gate 			break;
159*0Sstevel@tonic-gate 		case 2:
160*0Sstevel@tonic-gate 			(*pci_putw_func)(prg->bus_no, prg->dev_no,
161*0Sstevel@tonic-gate 			    prg->func_no, prg->offset, local_data);
162*0Sstevel@tonic-gate 			break;
163*0Sstevel@tonic-gate 		case 4:
164*0Sstevel@tonic-gate 			(*pci_putl_func)(prg->bus_no, prg->dev_no,
165*0Sstevel@tonic-gate 			    prg->func_no, prg->offset, local_data);
166*0Sstevel@tonic-gate 			break;
167*0Sstevel@tonic-gate 		default:
168*0Sstevel@tonic-gate 			rval = ENOTSUP;
169*0Sstevel@tonic-gate 			prg->status = PCITOOL_INVALID_SIZE;
170*0Sstevel@tonic-gate 			break;
171*0Sstevel@tonic-gate 		}
172*0Sstevel@tonic-gate 	} else {
173*0Sstevel@tonic-gate 		switch (size) {
174*0Sstevel@tonic-gate 		case 1:
175*0Sstevel@tonic-gate 			local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no,
176*0Sstevel@tonic-gate 			    prg->func_no, prg->offset);
177*0Sstevel@tonic-gate 			break;
178*0Sstevel@tonic-gate 		case 2:
179*0Sstevel@tonic-gate 			local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no,
180*0Sstevel@tonic-gate 			    prg->func_no, prg->offset);
181*0Sstevel@tonic-gate 			break;
182*0Sstevel@tonic-gate 		case 4:
183*0Sstevel@tonic-gate 			local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no,
184*0Sstevel@tonic-gate 			    prg->func_no, prg->offset);
185*0Sstevel@tonic-gate 			break;
186*0Sstevel@tonic-gate 		default:
187*0Sstevel@tonic-gate 			rval = ENOTSUP;
188*0Sstevel@tonic-gate 			prg->status = PCITOOL_INVALID_SIZE;
189*0Sstevel@tonic-gate 			break;
190*0Sstevel@tonic-gate 		}
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 		if (rval == SUCCESS) {
193*0Sstevel@tonic-gate 			if (big_endian) {
194*0Sstevel@tonic-gate 				prg->data =
195*0Sstevel@tonic-gate 				    pcitool_swap_endian(local_data, size);
196*0Sstevel@tonic-gate 			} else {
197*0Sstevel@tonic-gate 				prg->data = local_data;
198*0Sstevel@tonic-gate 			}
199*0Sstevel@tonic-gate 		}
200*0Sstevel@tonic-gate 	}
201*0Sstevel@tonic-gate 	prg->phys_addr = 0;	/* Config space is not memory mapped on X86. */
202*0Sstevel@tonic-gate 	return (rval);
203*0Sstevel@tonic-gate }
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate /*ARGSUSED*/
207*0Sstevel@tonic-gate static int
208*0Sstevel@tonic-gate pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag)
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate 	int port = (int)prg->phys_addr;
211*0Sstevel@tonic-gate 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
212*0Sstevel@tonic-gate 	boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
213*0Sstevel@tonic-gate 	int rval = SUCCESS;
214*0Sstevel@tonic-gate 	on_trap_data_t otd;
215*0Sstevel@tonic-gate 	uint64_t local_data;
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 	/*
219*0Sstevel@tonic-gate 	 * on_trap works like setjmp.
220*0Sstevel@tonic-gate 	 *
221*0Sstevel@tonic-gate 	 * A non-zero return here means on_trap has returned from an error.
222*0Sstevel@tonic-gate 	 *
223*0Sstevel@tonic-gate 	 * A zero return here means that on_trap has just returned from setup.
224*0Sstevel@tonic-gate 	 */
225*0Sstevel@tonic-gate 	if (on_trap(&otd, OT_DATA_ACCESS)) {
226*0Sstevel@tonic-gate 		no_trap();
227*0Sstevel@tonic-gate 		if (pcitool_debug)
228*0Sstevel@tonic-gate 			prom_printf(
229*0Sstevel@tonic-gate 			    "pcitool_mem_access: on_trap caught an error...\n");
230*0Sstevel@tonic-gate 		prg->status = PCITOOL_INVALID_ADDRESS;
231*0Sstevel@tonic-gate 		return (EFAULT);
232*0Sstevel@tonic-gate 	}
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	if (write_flag) {
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 		if (big_endian) {
237*0Sstevel@tonic-gate 			local_data = pcitool_swap_endian(prg->data, size);
238*0Sstevel@tonic-gate 		} else {
239*0Sstevel@tonic-gate 			local_data = prg->data;
240*0Sstevel@tonic-gate 		}
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 		if (pcitool_debug)
243*0Sstevel@tonic-gate 			prom_printf("Writing %ld byte(s) to port 0x%x\n",
244*0Sstevel@tonic-gate 			    size, port);
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 		switch (size) {
247*0Sstevel@tonic-gate 		case 1:
248*0Sstevel@tonic-gate 			outb(port, (uint8_t)local_data);
249*0Sstevel@tonic-gate 			break;
250*0Sstevel@tonic-gate 		case 2:
251*0Sstevel@tonic-gate 			outw(port, (uint16_t)local_data);
252*0Sstevel@tonic-gate 			break;
253*0Sstevel@tonic-gate 		case 4:
254*0Sstevel@tonic-gate 			outl(port, (uint32_t)local_data);
255*0Sstevel@tonic-gate 			break;
256*0Sstevel@tonic-gate 		default:
257*0Sstevel@tonic-gate 			rval = ENOTSUP;
258*0Sstevel@tonic-gate 			prg->status = PCITOOL_INVALID_SIZE;
259*0Sstevel@tonic-gate 			break;
260*0Sstevel@tonic-gate 		}
261*0Sstevel@tonic-gate 	} else {
262*0Sstevel@tonic-gate 		if (pcitool_debug)
263*0Sstevel@tonic-gate 			prom_printf("Reading %ld byte(s) from port 0x%x\n",
264*0Sstevel@tonic-gate 			    size, port);
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 		switch (size) {
267*0Sstevel@tonic-gate 		case 1:
268*0Sstevel@tonic-gate 			local_data = inb(port);
269*0Sstevel@tonic-gate 			break;
270*0Sstevel@tonic-gate 		case 2:
271*0Sstevel@tonic-gate 			local_data = inw(port);
272*0Sstevel@tonic-gate 			break;
273*0Sstevel@tonic-gate 		case 4:
274*0Sstevel@tonic-gate 			local_data = inl(port);
275*0Sstevel@tonic-gate 			break;
276*0Sstevel@tonic-gate 		default:
277*0Sstevel@tonic-gate 			rval = ENOTSUP;
278*0Sstevel@tonic-gate 			prg->status = PCITOOL_INVALID_SIZE;
279*0Sstevel@tonic-gate 			break;
280*0Sstevel@tonic-gate 		}
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 		if (rval == SUCCESS) {
283*0Sstevel@tonic-gate 			if (big_endian) {
284*0Sstevel@tonic-gate 				prg->data =
285*0Sstevel@tonic-gate 				    pcitool_swap_endian(local_data, size);
286*0Sstevel@tonic-gate 			} else {
287*0Sstevel@tonic-gate 				prg->data = local_data;
288*0Sstevel@tonic-gate 			}
289*0Sstevel@tonic-gate 		}
290*0Sstevel@tonic-gate 	}
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	no_trap();
293*0Sstevel@tonic-gate 	return (rval);
294*0Sstevel@tonic-gate }
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate /*ARGSUSED*/
297*0Sstevel@tonic-gate static int
298*0Sstevel@tonic-gate pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr,
299*0Sstevel@tonic-gate     boolean_t write_flag)
300*0Sstevel@tonic-gate {
301*0Sstevel@tonic-gate 	size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr);
302*0Sstevel@tonic-gate 	boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr);
303*0Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
304*0Sstevel@tonic-gate 	on_trap_data_t otd;
305*0Sstevel@tonic-gate 	uint64_t local_data;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	/*
308*0Sstevel@tonic-gate 	 * on_trap works like setjmp.
309*0Sstevel@tonic-gate 	 *
310*0Sstevel@tonic-gate 	 * A non-zero return here means on_trap has returned from an error.
311*0Sstevel@tonic-gate 	 *
312*0Sstevel@tonic-gate 	 * A zero return here means that on_trap has just returned from setup.
313*0Sstevel@tonic-gate 	 */
314*0Sstevel@tonic-gate 	if (on_trap(&otd, OT_DATA_ACCESS)) {
315*0Sstevel@tonic-gate 		no_trap();
316*0Sstevel@tonic-gate 		if (pcitool_debug)
317*0Sstevel@tonic-gate 			prom_printf(
318*0Sstevel@tonic-gate 			    "pcitool_mem_access: on_trap caught an error...\n");
319*0Sstevel@tonic-gate 		prg->status = PCITOOL_INVALID_ADDRESS;
320*0Sstevel@tonic-gate 		return (EFAULT);
321*0Sstevel@tonic-gate 	}
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	if (write_flag) {
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 		if (big_endian) {
326*0Sstevel@tonic-gate 			local_data = pcitool_swap_endian(prg->data, size);
327*0Sstevel@tonic-gate 		} else {
328*0Sstevel@tonic-gate 			local_data = prg->data;
329*0Sstevel@tonic-gate 		}
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 		switch (size) {
332*0Sstevel@tonic-gate 		case 1:
333*0Sstevel@tonic-gate 			*((uint8_t *)(uintptr_t)virt_addr) = local_data;
334*0Sstevel@tonic-gate 			break;
335*0Sstevel@tonic-gate 		case 2:
336*0Sstevel@tonic-gate 			*((uint16_t *)(uintptr_t)virt_addr) = local_data;
337*0Sstevel@tonic-gate 			break;
338*0Sstevel@tonic-gate 		case 4:
339*0Sstevel@tonic-gate 			*((uint32_t *)(uintptr_t)virt_addr) = local_data;
340*0Sstevel@tonic-gate 			break;
341*0Sstevel@tonic-gate 		case 8:
342*0Sstevel@tonic-gate 			*((uint64_t *)(uintptr_t)virt_addr) = local_data;
343*0Sstevel@tonic-gate 			break;
344*0Sstevel@tonic-gate 		default:
345*0Sstevel@tonic-gate 			rval = ENOTSUP;
346*0Sstevel@tonic-gate 			prg->status = PCITOOL_INVALID_SIZE;
347*0Sstevel@tonic-gate 			break;
348*0Sstevel@tonic-gate 		}
349*0Sstevel@tonic-gate 	} else {
350*0Sstevel@tonic-gate 		switch (size) {
351*0Sstevel@tonic-gate 		case 1:
352*0Sstevel@tonic-gate 			local_data = *((uint8_t *)(uintptr_t)virt_addr);
353*0Sstevel@tonic-gate 			break;
354*0Sstevel@tonic-gate 		case 2:
355*0Sstevel@tonic-gate 			local_data = *((uint16_t *)(uintptr_t)virt_addr);
356*0Sstevel@tonic-gate 			break;
357*0Sstevel@tonic-gate 		case 4:
358*0Sstevel@tonic-gate 			local_data = *((uint32_t *)(uintptr_t)virt_addr);
359*0Sstevel@tonic-gate 			break;
360*0Sstevel@tonic-gate 		case 8:
361*0Sstevel@tonic-gate 			local_data = *((uint64_t *)(uintptr_t)virt_addr);
362*0Sstevel@tonic-gate 			break;
363*0Sstevel@tonic-gate 		default:
364*0Sstevel@tonic-gate 			rval = ENOTSUP;
365*0Sstevel@tonic-gate 			prg->status = PCITOOL_INVALID_SIZE;
366*0Sstevel@tonic-gate 			break;
367*0Sstevel@tonic-gate 		}
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 		if (rval == SUCCESS) {
370*0Sstevel@tonic-gate 			if (big_endian) {
371*0Sstevel@tonic-gate 				prg->data =
372*0Sstevel@tonic-gate 				    pcitool_swap_endian(local_data, size);
373*0Sstevel@tonic-gate 			} else {
374*0Sstevel@tonic-gate 				prg->data = local_data;
375*0Sstevel@tonic-gate 			}
376*0Sstevel@tonic-gate 		}
377*0Sstevel@tonic-gate 	}
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	no_trap();
380*0Sstevel@tonic-gate 	return (rval);
381*0Sstevel@tonic-gate }
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate /*
384*0Sstevel@tonic-gate  * Map up to 2 pages which contain the address we want to access.
385*0Sstevel@tonic-gate  *
386*0Sstevel@tonic-gate  * Mapping should span no more than 8 bytes.  With X86 it is possible for an
387*0Sstevel@tonic-gate  * 8 byte value to start on a 4 byte boundary, so it can cross a page boundary.
388*0Sstevel@tonic-gate  * We'll never have to map more than two pages.
389*0Sstevel@tonic-gate  */
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate static uint64_t
392*0Sstevel@tonic-gate pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages)
393*0Sstevel@tonic-gate {
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate 	uint64_t page_base = phys_addr & ~MMU_PAGEOFFSET;
396*0Sstevel@tonic-gate 	uint64_t offset = phys_addr & MMU_PAGEOFFSET;
397*0Sstevel@tonic-gate 	void *virt_base;
398*0Sstevel@tonic-gate 	uint64_t returned_addr;
399*0Sstevel@tonic-gate 
400*0Sstevel@tonic-gate 	if (pcitool_debug)
401*0Sstevel@tonic-gate 		prom_printf("pcitool_map: Called with PA:0x%p\n",
402*0Sstevel@tonic-gate 		    (uint8_t *)(uintptr_t)phys_addr);
403*0Sstevel@tonic-gate 
404*0Sstevel@tonic-gate 	*num_pages = 1;
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	/* Desired mapping would span more than two pages. */
407*0Sstevel@tonic-gate 	if ((offset + size) > (MMU_PAGESIZE * 2)) {
408*0Sstevel@tonic-gate 		if (pcitool_debug)
409*0Sstevel@tonic-gate 			prom_printf("boundary violation: "
410*0Sstevel@tonic-gate 			    "offset:0x%" PRIx64 ", size:%ld, pagesize:0x%x\n",
411*0Sstevel@tonic-gate 			    offset, size, MMU_PAGESIZE);
412*0Sstevel@tonic-gate 		return (NULL);
413*0Sstevel@tonic-gate 
414*0Sstevel@tonic-gate 	} else if ((offset + size) > MMU_PAGESIZE) {
415*0Sstevel@tonic-gate 		(*num_pages)++;
416*0Sstevel@tonic-gate 	}
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	/* Get page(s) of virtual space. */
419*0Sstevel@tonic-gate 	virt_base = vmem_alloc(heap_arena, ptob(*num_pages), VM_NOSLEEP);
420*0Sstevel@tonic-gate 	if (virt_base == NULL) {
421*0Sstevel@tonic-gate 		if (pcitool_debug)
422*0Sstevel@tonic-gate 			prom_printf("Couldn't get virtual base address.\n");
423*0Sstevel@tonic-gate 		return (NULL);
424*0Sstevel@tonic-gate 	}
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	if (pcitool_debug)
427*0Sstevel@tonic-gate 		prom_printf("Got base virtual address:0x%p\n", virt_base);
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	/* Now map the allocated virtual space to the physical address. */
430*0Sstevel@tonic-gate 	hat_devload(kas.a_hat, virt_base, mmu_ptob(*num_pages),
431*0Sstevel@tonic-gate 	    mmu_btop(page_base), PROT_READ | PROT_WRITE | HAT_STRICTORDER,
432*0Sstevel@tonic-gate 	    HAT_LOAD_LOCK);
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 	returned_addr = ((uintptr_t)(virt_base)) + offset;
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 	if (pcitool_debug)
437*0Sstevel@tonic-gate 		prom_printf("pcitool_map: returning VA:0x%p\n",
438*0Sstevel@tonic-gate 		    (void *)(uintptr_t)returned_addr);
439*0Sstevel@tonic-gate 
440*0Sstevel@tonic-gate 	return (returned_addr);
441*0Sstevel@tonic-gate }
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate /* Unmap the mapped page(s). */
444*0Sstevel@tonic-gate static void
445*0Sstevel@tonic-gate pcitool_unmap(uint64_t virt_addr, size_t num_pages)
446*0Sstevel@tonic-gate {
447*0Sstevel@tonic-gate 	void *base_virt_addr = (void *)(uintptr_t)(virt_addr & ~MMU_PAGEOFFSET);
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	hat_unload(kas.a_hat, base_virt_addr, ptob(num_pages),
450*0Sstevel@tonic-gate 	    HAT_UNLOAD_UNLOCK);
451*0Sstevel@tonic-gate 	vmem_free(heap_arena, base_virt_addr, ptob(num_pages));
452*0Sstevel@tonic-gate }
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate /* Perform register accesses on PCI leaf devices. */
456*0Sstevel@tonic-gate int
457*0Sstevel@tonic-gate pcitool_dev_reg_ops(dev_t dev, void *arg, int cmd, int mode)
458*0Sstevel@tonic-gate {
459*0Sstevel@tonic-gate 	pci_state_t	*pci_p = PCI_DEV_TO_STATE(dev);
460*0Sstevel@tonic-gate 	dev_info_t	*dip = pci_p->pci_dip;
461*0Sstevel@tonic-gate 	boolean_t	write_flag = B_FALSE;
462*0Sstevel@tonic-gate 	int		rval = 0;
463*0Sstevel@tonic-gate 	pcitool_reg_t	prg;
464*0Sstevel@tonic-gate 	uint8_t		size;
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 	uint64_t	base_addr;
467*0Sstevel@tonic-gate 	uint64_t	virt_addr;
468*0Sstevel@tonic-gate 	size_t		num_virt_pages;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	switch (cmd) {
471*0Sstevel@tonic-gate 	case (PCITOOL_DEVICE_SET_REG):
472*0Sstevel@tonic-gate 		write_flag = B_TRUE;
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	/*FALLTHRU*/
475*0Sstevel@tonic-gate 	case (PCITOOL_DEVICE_GET_REG):
476*0Sstevel@tonic-gate 		if (pcitool_debug)
477*0Sstevel@tonic-gate 			prom_printf("pci_dev_reg_ops set/get reg\n");
478*0Sstevel@tonic-gate 		if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
479*0Sstevel@tonic-gate 		    DDI_SUCCESS) {
480*0Sstevel@tonic-gate 			if (pcitool_debug)
481*0Sstevel@tonic-gate 				prom_printf("Error reading arguments\n");
482*0Sstevel@tonic-gate 			return (EFAULT);
483*0Sstevel@tonic-gate 		}
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 		if (prg.barnum >= (sizeof (pci_bars) / sizeof (pci_bars[0]))) {
486*0Sstevel@tonic-gate 			prg.status = PCITOOL_OUT_OF_RANGE;
487*0Sstevel@tonic-gate 			rval = EINVAL;
488*0Sstevel@tonic-gate 			goto done_reg;
489*0Sstevel@tonic-gate 		}
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate 		if (pcitool_debug)
492*0Sstevel@tonic-gate 			prom_printf("raw bus:0x%x, dev:0x%x, func:0x%x\n",
493*0Sstevel@tonic-gate 			    prg.bus_no, prg.dev_no, prg.func_no);
494*0Sstevel@tonic-gate 		/* Validate address arguments of bus / dev / func */
495*0Sstevel@tonic-gate 		if (((prg.bus_no &
496*0Sstevel@tonic-gate 		    (PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT)) !=
497*0Sstevel@tonic-gate 		    prg.bus_no) ||
498*0Sstevel@tonic-gate 		    ((prg.dev_no &
499*0Sstevel@tonic-gate 		    (PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT)) !=
500*0Sstevel@tonic-gate 		    prg.dev_no) ||
501*0Sstevel@tonic-gate 		    ((prg.func_no &
502*0Sstevel@tonic-gate 		    (PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT)) !=
503*0Sstevel@tonic-gate 		    prg.func_no)) {
504*0Sstevel@tonic-gate 			prg.status = PCITOOL_INVALID_ADDRESS;
505*0Sstevel@tonic-gate 			rval = EINVAL;
506*0Sstevel@tonic-gate 			goto done_reg;
507*0Sstevel@tonic-gate 		}
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 		size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr);
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 		/* Proper config space desired. */
512*0Sstevel@tonic-gate 		if (prg.barnum == 0) {
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 			if (prg.offset > 0xFF) {
515*0Sstevel@tonic-gate 				prg.status = PCITOOL_OUT_OF_RANGE;
516*0Sstevel@tonic-gate 				rval = EINVAL;
517*0Sstevel@tonic-gate 				goto done_reg;
518*0Sstevel@tonic-gate 			}
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 			if (pcitool_debug)
521*0Sstevel@tonic-gate 				prom_printf(
522*0Sstevel@tonic-gate 				    "config access: offset:0x%" PRIx64 ", "
523*0Sstevel@tonic-gate 				    "phys_addr:0x%" PRIx64 "\n",
524*0Sstevel@tonic-gate 				    prg.offset, prg.phys_addr);
525*0Sstevel@tonic-gate 			/* Access device.  prg is modified. */
526*0Sstevel@tonic-gate 			rval = pcitool_cfg_access(dip, &prg, write_flag);
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 			if (pcitool_debug)
529*0Sstevel@tonic-gate 				prom_printf(
530*0Sstevel@tonic-gate 				    "config access: data:0x%" PRIx64 "\n",
531*0Sstevel@tonic-gate 				    prg.data);
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 		/* IO/ MEM/ MEM64 space. */
534*0Sstevel@tonic-gate 		} else {
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 			pcitool_reg_t	prg2;
537*0Sstevel@tonic-gate 			bcopy(&prg, &prg2, sizeof (pcitool_reg_t));
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 			/*
540*0Sstevel@tonic-gate 			 * Translate BAR number into offset of the BAR in
541*0Sstevel@tonic-gate 			 * the device's config space.
542*0Sstevel@tonic-gate 			 */
543*0Sstevel@tonic-gate 			prg2.offset = pci_bars[prg2.barnum];
544*0Sstevel@tonic-gate 			prg2.acc_attr =
545*0Sstevel@tonic-gate 			    PCITOOL_ACC_ATTR_SIZE_4 | PCITOOL_ACC_ATTR_ENDN_LTL;
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 			if (pcitool_debug)
548*0Sstevel@tonic-gate 				prom_printf(
549*0Sstevel@tonic-gate 				    "barnum:%d, bar_offset:0x%" PRIx64 "\n",
550*0Sstevel@tonic-gate 				    prg2.barnum, prg2.offset);
551*0Sstevel@tonic-gate 			/*
552*0Sstevel@tonic-gate 			 * Get Bus Address Register (BAR) from config space.
553*0Sstevel@tonic-gate 			 * prg2.offset is the offset into config space of the
554*0Sstevel@tonic-gate 			 * BAR desired.  prg.status is modified on error.
555*0Sstevel@tonic-gate 			 */
556*0Sstevel@tonic-gate 			rval = pcitool_cfg_access(dip, &prg2, B_FALSE);
557*0Sstevel@tonic-gate 			if (rval != SUCCESS) {
558*0Sstevel@tonic-gate 				if (pcitool_debug)
559*0Sstevel@tonic-gate 					prom_printf("BAR access failed\n");
560*0Sstevel@tonic-gate 				prg.status = prg2.status;
561*0Sstevel@tonic-gate 				goto done_reg;
562*0Sstevel@tonic-gate 			}
563*0Sstevel@tonic-gate 			/*
564*0Sstevel@tonic-gate 			 * Reference proper PCI space based on the BAR.
565*0Sstevel@tonic-gate 			 * If 64 bit MEM space, need to load other half of the
566*0Sstevel@tonic-gate 			 * BAR first.
567*0Sstevel@tonic-gate 			 */
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 			if (pcitool_debug)
570*0Sstevel@tonic-gate 				prom_printf("bar returned is 0x%" PRIx64 "\n",
571*0Sstevel@tonic-gate 				    prg2.data);
572*0Sstevel@tonic-gate 			if (!prg2.data) {
573*0Sstevel@tonic-gate 				if (pcitool_debug)
574*0Sstevel@tonic-gate 					prom_printf("BAR data == 0\n");
575*0Sstevel@tonic-gate 				rval = EINVAL;
576*0Sstevel@tonic-gate 				prg.status = PCITOOL_INVALID_ADDRESS;
577*0Sstevel@tonic-gate 				goto done_reg;
578*0Sstevel@tonic-gate 			}
579*0Sstevel@tonic-gate 			if (prg2.data == 0xffffffff) {
580*0Sstevel@tonic-gate 				if (pcitool_debug)
581*0Sstevel@tonic-gate 					prom_printf("BAR data == -1\n");
582*0Sstevel@tonic-gate 				rval = EINVAL;
583*0Sstevel@tonic-gate 				prg.status = PCITOOL_INVALID_ADDRESS;
584*0Sstevel@tonic-gate 				goto done_reg;
585*0Sstevel@tonic-gate 			}
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate 			/*
588*0Sstevel@tonic-gate 			 * BAR has bits saying this space is IO space, unless
589*0Sstevel@tonic-gate 			 * this is the ROM address register.
590*0Sstevel@tonic-gate 			 */
591*0Sstevel@tonic-gate 			if (((PCI_BASE_SPACE_M & prg2.data) ==
592*0Sstevel@tonic-gate 			    PCI_BASE_SPACE_IO) &&
593*0Sstevel@tonic-gate 			    (prg2.offset != PCI_CONF_ROM)) {
594*0Sstevel@tonic-gate 				if (pcitool_debug)
595*0Sstevel@tonic-gate 					prom_printf("IO space\n");
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 				prg2.data &= PCI_BASE_IO_ADDR_M;
598*0Sstevel@tonic-gate 				prg.phys_addr = prg2.data + prg.offset;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 				rval = pcitool_io_access(dip, &prg, write_flag);
601*0Sstevel@tonic-gate 				if ((rval != SUCCESS) && (pcitool_debug))
602*0Sstevel@tonic-gate 					prom_printf("IO access failed\n");
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 				goto done_reg;
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 
607*0Sstevel@tonic-gate 			/*
608*0Sstevel@tonic-gate 			 * BAR has bits saying this space is 64 bit memory
609*0Sstevel@tonic-gate 			 * space, unless this is the ROM address register.
610*0Sstevel@tonic-gate 			 *
611*0Sstevel@tonic-gate 			 * The 64 bit address stored in two BAR cells is not
612*0Sstevel@tonic-gate 			 * necessarily aligned on an 8-byte boundary.
613*0Sstevel@tonic-gate 			 * Need to keep the first 4 bytes read,
614*0Sstevel@tonic-gate 			 * and do a separate read of the high 4 bytes.
615*0Sstevel@tonic-gate 			 */
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 			} else if ((PCI_BASE_TYPE_ALL & prg2.data) &&
618*0Sstevel@tonic-gate 			    (prg2.offset != PCI_CONF_ROM)) {
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 				uint32_t low_bytes =
621*0Sstevel@tonic-gate 				    (uint32_t)(prg2.data & ~PCI_BASE_TYPE_ALL);
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 				/*
624*0Sstevel@tonic-gate 				 * Don't try to read the next 4 bytes
625*0Sstevel@tonic-gate 				 * past the end of BARs.
626*0Sstevel@tonic-gate 				 */
627*0Sstevel@tonic-gate 				if (prg2.offset >= PCI_CONF_BASE5) {
628*0Sstevel@tonic-gate 					prg.status = PCITOOL_OUT_OF_RANGE;
629*0Sstevel@tonic-gate 					rval = EIO;
630*0Sstevel@tonic-gate 					goto done_reg;
631*0Sstevel@tonic-gate 				}
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 				/*
634*0Sstevel@tonic-gate 				 * Access device.
635*0Sstevel@tonic-gate 				 * prg2.status is modified on error.
636*0Sstevel@tonic-gate 				 */
637*0Sstevel@tonic-gate 				prg2.offset += 4;
638*0Sstevel@tonic-gate 				rval = pcitool_cfg_access(dip, &prg2, B_FALSE);
639*0Sstevel@tonic-gate 				if (rval != SUCCESS) {
640*0Sstevel@tonic-gate 					prg.status = prg2.status;
641*0Sstevel@tonic-gate 					goto done_reg;
642*0Sstevel@tonic-gate 				}
643*0Sstevel@tonic-gate 
644*0Sstevel@tonic-gate 				if (prg2.data == 0xffffffff) {
645*0Sstevel@tonic-gate 					prg.status = PCITOOL_INVALID_ADDRESS;
646*0Sstevel@tonic-gate 					prg.status = EFAULT;
647*0Sstevel@tonic-gate 					goto done_reg;
648*0Sstevel@tonic-gate 				}
649*0Sstevel@tonic-gate 
650*0Sstevel@tonic-gate 				prg2.data = (prg2.data << 32) + low_bytes;
651*0Sstevel@tonic-gate 				if (pcitool_debug)
652*0Sstevel@tonic-gate 					prom_printf(
653*0Sstevel@tonic-gate 					    "64 bit mem space.  "
654*0Sstevel@tonic-gate 					    "64-bit bar is 0x%" PRIx64 "\n",
655*0Sstevel@tonic-gate 					    prg2.data);
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 			/* Mem32 space, including ROM */
658*0Sstevel@tonic-gate 			} else {
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 				if (prg2.offset == PCI_CONF_ROM) {
661*0Sstevel@tonic-gate 					if (pcitool_debug)
662*0Sstevel@tonic-gate 						prom_printf(
663*0Sstevel@tonic-gate 						    "Additional ROM "
664*0Sstevel@tonic-gate 						    "checking\n");
665*0Sstevel@tonic-gate 					/* Can't write to ROM */
666*0Sstevel@tonic-gate 					if (write_flag) {
667*0Sstevel@tonic-gate 						prg.status = PCITOOL_ROM_WRITE;
668*0Sstevel@tonic-gate 						rval = EIO;
669*0Sstevel@tonic-gate 						goto done_reg;
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 					/* ROM disabled for reading */
672*0Sstevel@tonic-gate 					} else if (!(prg2.data & 0x00000001)) {
673*0Sstevel@tonic-gate 						prg.status =
674*0Sstevel@tonic-gate 						    PCITOOL_ROM_DISABLED;
675*0Sstevel@tonic-gate 						rval = EIO;
676*0Sstevel@tonic-gate 						goto done_reg;
677*0Sstevel@tonic-gate 					}
678*0Sstevel@tonic-gate 				}
679*0Sstevel@tonic-gate 
680*0Sstevel@tonic-gate 				if (pcitool_debug)
681*0Sstevel@tonic-gate 					prom_printf("32 bit mem space\n");
682*0Sstevel@tonic-gate 			}
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 			/* Common code for all IO/MEM range spaces. */
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate 			base_addr = prg2.data;
687*0Sstevel@tonic-gate 			if (pcitool_debug)
688*0Sstevel@tonic-gate 				prom_printf(
689*0Sstevel@tonic-gate 				    "addr portion of bar is 0x%" PRIx64 ", "
690*0Sstevel@tonic-gate 				    "base=0x%" PRIx64 ", "
691*0Sstevel@tonic-gate 				    "offset:0x%" PRIx64 "\n",
692*0Sstevel@tonic-gate 				    prg2.data, base_addr, prg.offset);
693*0Sstevel@tonic-gate 			/*
694*0Sstevel@tonic-gate 			 * Use offset provided by caller to index into
695*0Sstevel@tonic-gate 			 * desired space, then access.
696*0Sstevel@tonic-gate 			 * Note that prg.status is modified on error.
697*0Sstevel@tonic-gate 			 */
698*0Sstevel@tonic-gate 			prg.phys_addr = base_addr + prg.offset;
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 			virt_addr = pcitool_map(prg.phys_addr, size,
701*0Sstevel@tonic-gate 			    &num_virt_pages);
702*0Sstevel@tonic-gate 			if (virt_addr == NULL) {
703*0Sstevel@tonic-gate 				prg.status = PCITOOL_IO_ERROR;
704*0Sstevel@tonic-gate 				rval = EIO;
705*0Sstevel@tonic-gate 				goto done_reg;
706*0Sstevel@tonic-gate 			}
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 			rval = pcitool_mem_access(dip, &prg, virt_addr,
709*0Sstevel@tonic-gate 			    write_flag);
710*0Sstevel@tonic-gate 			pcitool_unmap(virt_addr, num_virt_pages);
711*0Sstevel@tonic-gate 		}
712*0Sstevel@tonic-gate done_reg:
713*0Sstevel@tonic-gate 		if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) !=
714*0Sstevel@tonic-gate 		    DDI_SUCCESS) {
715*0Sstevel@tonic-gate 			if (pcitool_debug)
716*0Sstevel@tonic-gate 				prom_printf("Error returning arguments.\n");
717*0Sstevel@tonic-gate 			rval = EFAULT;
718*0Sstevel@tonic-gate 		}
719*0Sstevel@tonic-gate 		break;
720*0Sstevel@tonic-gate 	default:
721*0Sstevel@tonic-gate 		rval = ENOTTY;
722*0Sstevel@tonic-gate 		break;
723*0Sstevel@tonic-gate 	}
724*0Sstevel@tonic-gate 	return (rval);
725*0Sstevel@tonic-gate }
726