xref: /onnv-gate/usr/src/uts/sun4/io/px/px_devctl.c (revision 4397:8359c43a28c3)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51540Skini  * Common Development and Distribution License (the "License").
61540Skini  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*4397Sschwartz  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * PCI nexus HotPlug devctl interface
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/conf.h>
330Sstevel@tonic-gate #include <sys/kmem.h>
340Sstevel@tonic-gate #include <sys/async.h>
350Sstevel@tonic-gate #include <sys/sysmacros.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate #include <sys/sunndi.h>
380Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
390Sstevel@tonic-gate #include <sys/open.h>
400Sstevel@tonic-gate #include <sys/errno.h>
410Sstevel@tonic-gate #include <sys/file.h>
42624Sschwartz #include <sys/policy.h>
430Sstevel@tonic-gate #include <sys/hotplug/pci/pcihp.h>
440Sstevel@tonic-gate #include "px_obj.h"
45624Sschwartz #include <sys/pci_tools.h>
46777Sschwartz #include "px_tools_ext.h"
470Sstevel@tonic-gate #include "pcie_pwr.h"
480Sstevel@tonic-gate 
490Sstevel@tonic-gate /*LINTLIBRARY*/
500Sstevel@tonic-gate 
510Sstevel@tonic-gate static int px_open(dev_t *devp, int flags, int otyp, cred_t *credp);
520Sstevel@tonic-gate static int px_close(dev_t dev, int flags, int otyp, cred_t *credp);
530Sstevel@tonic-gate static int px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
540Sstevel@tonic-gate 						cred_t *credp, int *rvalp);
550Sstevel@tonic-gate static int px_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
560Sstevel@tonic-gate     int flags, char *name, caddr_t valuep, int *lengthp);
570Sstevel@tonic-gate 
580Sstevel@tonic-gate struct cb_ops px_cb_ops = {
590Sstevel@tonic-gate 	px_open,			/* open */
600Sstevel@tonic-gate 	px_close,			/* close */
610Sstevel@tonic-gate 	nodev,				/* strategy */
620Sstevel@tonic-gate 	nodev,				/* print */
630Sstevel@tonic-gate 	nodev,				/* dump */
640Sstevel@tonic-gate 	nodev,				/* read */
650Sstevel@tonic-gate 	nodev,				/* write */
660Sstevel@tonic-gate 	px_ioctl,			/* ioctl */
670Sstevel@tonic-gate 	nodev,				/* devmap */
680Sstevel@tonic-gate 	nodev,				/* mmap */
690Sstevel@tonic-gate 	nodev,				/* segmap */
700Sstevel@tonic-gate 	nochpoll,			/* poll */
710Sstevel@tonic-gate 	px_prop_op,			/* cb_prop_op */
720Sstevel@tonic-gate 	NULL,				/* streamtab */
730Sstevel@tonic-gate 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
740Sstevel@tonic-gate 	CB_REV,				/* rev */
750Sstevel@tonic-gate 	nodev,				/* int (*cb_aread)() */
760Sstevel@tonic-gate 	nodev				/* int (*cb_awrite)() */
770Sstevel@tonic-gate };
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /* ARGSUSED3 */
800Sstevel@tonic-gate static int
810Sstevel@tonic-gate px_open(dev_t *devp, int flags, int otyp, cred_t *credp)
820Sstevel@tonic-gate {
830Sstevel@tonic-gate 	px_t *px_p;
841531Skini 	int rval;
851531Skini 	uint_t orig_px_soft_state;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	/*
880Sstevel@tonic-gate 	 * Make sure the open is for the right file type.
890Sstevel@tonic-gate 	 */
900Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
910Sstevel@tonic-gate 		return (EINVAL);
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 	/*
940Sstevel@tonic-gate 	 * Get the soft state structure for the device.
950Sstevel@tonic-gate 	 */
96909Segillett 	px_p = PX_DEV_TO_SOFTSTATE(*devp);
970Sstevel@tonic-gate 	if (px_p == NULL)
980Sstevel@tonic-gate 		return (ENXIO);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate 	/*
1010Sstevel@tonic-gate 	 * Handle the open by tracking the device state.
1020Sstevel@tonic-gate 	 */
1030Sstevel@tonic-gate 	DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags);
1040Sstevel@tonic-gate 	mutex_enter(&px_p->px_mutex);
1051531Skini 	orig_px_soft_state = px_p->px_soft_state;
1060Sstevel@tonic-gate 	if (flags & FEXCL) {
1070Sstevel@tonic-gate 		if (px_p->px_soft_state != PX_SOFT_STATE_CLOSED) {
1080Sstevel@tonic-gate 			mutex_exit(&px_p->px_mutex);
1090Sstevel@tonic-gate 			DBG(DBG_OPEN, px_p->px_dip, "busy\n");
1100Sstevel@tonic-gate 			return (EBUSY);
1110Sstevel@tonic-gate 		}
1120Sstevel@tonic-gate 		px_p->px_soft_state = PX_SOFT_STATE_OPEN_EXCL;
1130Sstevel@tonic-gate 	} else {
1140Sstevel@tonic-gate 		if (px_p->px_soft_state == PX_SOFT_STATE_OPEN_EXCL) {
1150Sstevel@tonic-gate 			mutex_exit(&px_p->px_mutex);
1160Sstevel@tonic-gate 			DBG(DBG_OPEN, px_p->px_dip, "busy\n");
1170Sstevel@tonic-gate 			return (EBUSY);
1180Sstevel@tonic-gate 		}
1190Sstevel@tonic-gate 		px_p->px_soft_state = PX_SOFT_STATE_OPEN;
1200Sstevel@tonic-gate 	}
1211531Skini 
1221531Skini 	if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
1231531Skini 		if (rval = (pcihp_get_cb_ops())->cb_open(devp, flags,
1241531Skini 		    otyp, credp)) {
1251531Skini 			px_p->px_soft_state = orig_px_soft_state;
1261531Skini 			mutex_exit(&px_p->px_mutex);
1271531Skini 			return (rval);
1281531Skini 		}
1291531Skini 
1300Sstevel@tonic-gate 	px_p->px_open_count++;
1310Sstevel@tonic-gate 	mutex_exit(&px_p->px_mutex);
1320Sstevel@tonic-gate 	return (0);
1330Sstevel@tonic-gate }
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate /* ARGSUSED */
1370Sstevel@tonic-gate static int
1380Sstevel@tonic-gate px_close(dev_t dev, int flags, int otyp, cred_t *credp)
1390Sstevel@tonic-gate {
1400Sstevel@tonic-gate 	px_t *px_p;
1411531Skini 	int rval;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
1440Sstevel@tonic-gate 		return (EINVAL);
1450Sstevel@tonic-gate 
146909Segillett 	px_p = PX_DEV_TO_SOFTSTATE(dev);
1470Sstevel@tonic-gate 	if (px_p == NULL)
1480Sstevel@tonic-gate 		return (ENXIO);
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	DBG(DBG_CLOSE, px_p->px_dip, "dev=%x: flags=%x\n", dev, flags);
1510Sstevel@tonic-gate 	mutex_enter(&px_p->px_mutex);
1521531Skini 
1531531Skini 	if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
1541531Skini 		if (rval = (pcihp_get_cb_ops())->cb_close(dev, flags,
1551531Skini 		    otyp, credp)) {
1561531Skini 			mutex_exit(&px_p->px_mutex);
1571531Skini 			return (rval);
1581531Skini 		}
1591531Skini 
1600Sstevel@tonic-gate 	px_p->px_soft_state = PX_SOFT_STATE_CLOSED;
1610Sstevel@tonic-gate 	px_p->px_open_count = 0;
1620Sstevel@tonic-gate 	mutex_exit(&px_p->px_mutex);
1630Sstevel@tonic-gate 	return (0);
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate /* ARGSUSED */
1670Sstevel@tonic-gate static int
1680Sstevel@tonic-gate px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
1690Sstevel@tonic-gate {
1700Sstevel@tonic-gate 	px_t *px_p;
1710Sstevel@tonic-gate 	dev_info_t *dip;
1720Sstevel@tonic-gate 	struct devctl_iocdata *dcp;
1730Sstevel@tonic-gate 	uint_t bus_state;
1740Sstevel@tonic-gate 	int rv = DDI_SUCCESS;
175624Sschwartz 	int minor = getminor(dev);
1760Sstevel@tonic-gate 
177909Segillett 	px_p = PX_DEV_TO_SOFTSTATE(dev);
1780Sstevel@tonic-gate 	if (px_p == NULL)
1790Sstevel@tonic-gate 		return (ENXIO);
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	dip = px_p->px_dip;
1820Sstevel@tonic-gate 	DBG(DBG_IOCTL, dip, "dev=%x: cmd=%x\n", dev, cmd);
183624Sschwartz 
1840Sstevel@tonic-gate #ifdef	PX_DMA_TEST
1850Sstevel@tonic-gate 	if (IS_DMATEST(cmd)) {
1860Sstevel@tonic-gate 		*rvalp = px_dma_test(cmd, dip, px_p, arg);
1870Sstevel@tonic-gate 		return (0);
1880Sstevel@tonic-gate 	}
1890Sstevel@tonic-gate #endif	/* PX_DMA_TEST */
1900Sstevel@tonic-gate 
191624Sschwartz 	switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
192624Sschwartz 
1930Sstevel@tonic-gate 	/*
1940Sstevel@tonic-gate 	 * PCI tools.
1950Sstevel@tonic-gate 	 */
196624Sschwartz 	case PCI_TOOL_REG_MINOR_NUM:
1970Sstevel@tonic-gate 
198624Sschwartz 		switch (cmd) {
199624Sschwartz 		case PCITOOL_DEVICE_SET_REG:
200624Sschwartz 		case PCITOOL_DEVICE_GET_REG:
201624Sschwartz 
202624Sschwartz 			/* Require full privileges. */
203624Sschwartz 			if (secpolicy_kmdb(credp))
204624Sschwartz 				rv = EPERM;
205624Sschwartz 			else
206624Sschwartz 				rv = pxtool_dev_reg_ops(dip,
207624Sschwartz 				    (void *)arg, cmd, mode);
208624Sschwartz 			break;
209624Sschwartz 
210624Sschwartz 		case PCITOOL_NEXUS_SET_REG:
211624Sschwartz 		case PCITOOL_NEXUS_GET_REG:
212624Sschwartz 
213624Sschwartz 			/* Require full privileges. */
214624Sschwartz 			if (secpolicy_kmdb(credp))
215624Sschwartz 				rv = EPERM;
216624Sschwartz 			else
217624Sschwartz 				rv = pxtool_bus_reg_ops(dip,
218624Sschwartz 				    (void *)arg, cmd, mode);
219624Sschwartz 			break;
220624Sschwartz 
221624Sschwartz 		default:
222624Sschwartz 			rv = ENOTTY;
223624Sschwartz 		}
224624Sschwartz 		return (rv);
225624Sschwartz 
226624Sschwartz 	case PCI_TOOL_INTR_MINOR_NUM:
227624Sschwartz 
228624Sschwartz 		switch (cmd) {
229624Sschwartz 		case PCITOOL_DEVICE_SET_INTR:
230624Sschwartz 
231624Sschwartz 		/*
232624Sschwartz 		 * Require PRIV_SYS_RES_CONFIG,
233624Sschwartz 		 * same as psradm
234624Sschwartz 		 */
235624Sschwartz 		if (secpolicy_ponline(credp)) {
236624Sschwartz 			rv = EPERM;
237624Sschwartz 			break;
238624Sschwartz 		}
239624Sschwartz 
240624Sschwartz 		/*FALLTHRU*/
241624Sschwartz 		/* These require no special privileges. */
242624Sschwartz 		case PCITOOL_DEVICE_GET_INTR:
243*4397Sschwartz 		case PCITOOL_SYSTEM_INTR_INFO:
244624Sschwartz 			rv = pxtool_intr(dip, (void *)arg, cmd, mode);
245624Sschwartz 			break;
246624Sschwartz 
247624Sschwartz 		default:
248624Sschwartz 			rv = ENOTTY;
249624Sschwartz 		}
250624Sschwartz 		return (rv);
251624Sschwartz 
252624Sschwartz 	default:
2531531Skini 		if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)
2541531Skini 			return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd,
2551531Skini 			    arg, mode, credp, rvalp));
256624Sschwartz 		break;
257624Sschwartz 	}
258624Sschwartz 
259624Sschwartz 	if ((cmd & ~PPMREQ_MASK) == PPMREQ) {
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 		/* Need privileges to use these ioctls. */
2620Sstevel@tonic-gate 		if (drv_priv(credp)) {
2630Sstevel@tonic-gate 			DBG(DBG_TOOLS, dip,
2640Sstevel@tonic-gate 			    "px_tools: Insufficient privileges\n");
2650Sstevel@tonic-gate 
266624Sschwartz 			return (EPERM);
2670Sstevel@tonic-gate 		}
268624Sschwartz 		return (px_lib_pmctl(cmd, px_p));
2690Sstevel@tonic-gate 	}
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	/*
2720Sstevel@tonic-gate 	 * We can use the generic implementation for these ioctls
2730Sstevel@tonic-gate 	 */
2740Sstevel@tonic-gate 	switch (cmd) {
2750Sstevel@tonic-gate 	case DEVCTL_DEVICE_GETSTATE:
2760Sstevel@tonic-gate 	case DEVCTL_DEVICE_ONLINE:
2770Sstevel@tonic-gate 	case DEVCTL_DEVICE_OFFLINE:
2780Sstevel@tonic-gate 	case DEVCTL_BUS_GETSTATE:
2790Sstevel@tonic-gate 		return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0));
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	/*
2830Sstevel@tonic-gate 	 * read devctl ioctl data
2840Sstevel@tonic-gate 	 */
2850Sstevel@tonic-gate 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
2860Sstevel@tonic-gate 		return (EFAULT);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	switch (cmd) {
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	case DEVCTL_DEVICE_RESET:
2910Sstevel@tonic-gate 		DBG(DBG_IOCTL, dip, "DEVCTL_DEVICE_RESET\n");
2920Sstevel@tonic-gate 		rv = ENOTSUP;
2930Sstevel@tonic-gate 		break;
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	case DEVCTL_BUS_QUIESCE:
2970Sstevel@tonic-gate 		DBG(DBG_IOCTL, dip, "DEVCTL_BUS_QUIESCE\n");
2980Sstevel@tonic-gate 		if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
2990Sstevel@tonic-gate 			if (bus_state == BUS_QUIESCED)
3000Sstevel@tonic-gate 				break;
3010Sstevel@tonic-gate 		(void) ndi_set_bus_state(dip, BUS_QUIESCED);
3020Sstevel@tonic-gate 		break;
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	case DEVCTL_BUS_UNQUIESCE:
3050Sstevel@tonic-gate 		DBG(DBG_IOCTL, dip, "DEVCTL_BUS_UNQUIESCE\n");
3060Sstevel@tonic-gate 		if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
3070Sstevel@tonic-gate 			if (bus_state == BUS_ACTIVE)
3080Sstevel@tonic-gate 				break;
3090Sstevel@tonic-gate 		(void) ndi_set_bus_state(dip, BUS_ACTIVE);
3100Sstevel@tonic-gate 		break;
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	case DEVCTL_BUS_RESET:
3130Sstevel@tonic-gate 		DBG(DBG_IOCTL, dip, "DEVCTL_BUS_RESET\n");
3140Sstevel@tonic-gate 		rv = ENOTSUP;
3150Sstevel@tonic-gate 		break;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	case DEVCTL_BUS_RESETALL:
3180Sstevel@tonic-gate 		DBG(DBG_IOCTL, dip, "DEVCTL_BUS_RESETALL\n");
3190Sstevel@tonic-gate 		rv = ENOTSUP;
3200Sstevel@tonic-gate 		break;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	default:
3230Sstevel@tonic-gate 		rv = ENOTTY;
3240Sstevel@tonic-gate 	}
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	ndi_dc_freehdl(dcp);
3270Sstevel@tonic-gate 	return (rv);
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate static int px_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
3310Sstevel@tonic-gate     int flags, char *name, caddr_t valuep, int *lengthp)
3320Sstevel@tonic-gate {
3330Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3340Sstevel@tonic-gate 	    "hotplug-capable"))
3350Sstevel@tonic-gate 		return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip,
3360Sstevel@tonic-gate 		    prop_op, flags, name, valuep, lengthp));
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
3390Sstevel@tonic-gate }
340