xref: /onnv-gate/usr/src/uts/sun4u/io/rmc_comm.c (revision 2749:c14e3011cb92)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*2749Sarutz  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  *
261708Sstevel  * The "rmc_comm" driver provides access to the RMC so that its clients need
271708Sstevel  * not be concerned with the details of the access mechanism, which in this
281708Sstevel  * case is implemented via a packet-based protocol over a serial link via a
291708Sstevel  * 16550 compatible serial port.
301708Sstevel  */
311708Sstevel 
321708Sstevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
331708Sstevel 
341708Sstevel /*
351708Sstevel  *  Header files
361708Sstevel  */
371708Sstevel #include <sys/conf.h>
381708Sstevel #include <sys/cyclic.h>
391708Sstevel #include <sys/membar.h>
401708Sstevel #include <sys/modctl.h>
411708Sstevel #include <sys/strlog.h>
421708Sstevel #include <sys/types.h>
431708Sstevel #include <sys/sunddi.h>
441708Sstevel #include <sys/ddi.h>
451708Sstevel #include <sys/rmc_comm_dp_boot.h>
461708Sstevel #include <sys/rmc_comm_dp.h>
471708Sstevel #include <sys/rmc_comm_drvintf.h>
481708Sstevel #include <sys/rmc_comm.h>
491708Sstevel #include <sys/cpu_sgnblk_defs.h>
501708Sstevel 
511708Sstevel /*
521708Sstevel  * Local definitions
531708Sstevel  */
541708Sstevel 
551708Sstevel #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
561708Sstevel 
571708Sstevel #define	MYNAME			"rmc_comm"
581708Sstevel #define	NOMAJOR			(~(major_t)0)
591708Sstevel #define	DUMMY_VALUE		(~(int8_t)0)
601708Sstevel 
611708Sstevel /*
621708Sstevel  * Local data
631708Sstevel  */
641708Sstevel static void *rmc_comm_statep;
651708Sstevel static major_t rmc_comm_major = NOMAJOR;
661708Sstevel static kmutex_t rmc_comm_attach_lock;
671708Sstevel static ddi_device_acc_attr_t rmc_comm_dev_acc_attr[1] =
681708Sstevel {
691708Sstevel 	DDI_DEVICE_ATTR_V0,
701708Sstevel 	DDI_STRUCTURE_LE_ACC,
711708Sstevel 	DDI_STRICTORDER_ACC
721708Sstevel };
731708Sstevel static int watchdog_was_active;
741708Sstevel extern int watchdog_activated;
751708Sstevel extern int watchdog_enable;
761708Sstevel 
771708Sstevel /*
781708Sstevel  * prototypes
791708Sstevel  */
801708Sstevel 
811708Sstevel extern void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
821708Sstevel static void sio_put_reg(struct rmc_comm_state *, uint_t, uint8_t);
831708Sstevel static uint8_t sio_get_reg(struct rmc_comm_state *, uint_t);
841708Sstevel static void sio_check_fault_status(struct rmc_comm_state *);
851708Sstevel static boolean_t sio_data_ready(struct rmc_comm_state *);
861708Sstevel static void rmc_comm_set_irq(struct rmc_comm_state *, boolean_t);
871708Sstevel static uint_t rmc_comm_hi_intr(caddr_t);
881708Sstevel static uint_t rmc_comm_softint(caddr_t);
891708Sstevel static void rmc_comm_cyclic(void *);
901708Sstevel static void rmc_comm_hw_reset(struct rmc_comm_state *);
911708Sstevel static void rmc_comm_offline(struct rmc_comm_state *);
921708Sstevel static int rmc_comm_online(struct rmc_comm_state *, dev_info_t *);
931708Sstevel static void rmc_comm_unattach(struct rmc_comm_state *, dev_info_t *, int,
941708Sstevel     boolean_t, boolean_t, boolean_t);
951708Sstevel static int rmc_comm_attach(dev_info_t *, ddi_attach_cmd_t);
961708Sstevel static int rmc_comm_detach(dev_info_t *, ddi_detach_cmd_t);
971708Sstevel 
981708Sstevel /*
991708Sstevel  * for client leaf drivers to register their desire for rmc_comm
1001708Sstevel  * to stay attached
1011708Sstevel  */
1021708Sstevel int
1031708Sstevel rmc_comm_register()
1041708Sstevel {
1051708Sstevel 	struct rmc_comm_state *rcs;
1061708Sstevel 
1071708Sstevel 	mutex_enter(&rmc_comm_attach_lock);
1081708Sstevel 	rcs = ddi_get_soft_state(rmc_comm_statep, 0);
1091708Sstevel 	if ((rcs == NULL) || (!rcs->is_attached)) {
1101708Sstevel 		mutex_exit(&rmc_comm_attach_lock);
1111708Sstevel 		return (DDI_FAILURE);
1121708Sstevel 	}
1131708Sstevel 	rcs->n_registrations++;
1141708Sstevel 	mutex_exit(&rmc_comm_attach_lock);
1151708Sstevel 	return (DDI_SUCCESS);
1161708Sstevel }
1171708Sstevel 
1181708Sstevel void
1191708Sstevel rmc_comm_unregister()
1201708Sstevel {
1211708Sstevel 	struct rmc_comm_state *rcs;
1221708Sstevel 
1231708Sstevel 	mutex_enter(&rmc_comm_attach_lock);
1241708Sstevel 	rcs = ddi_get_soft_state(rmc_comm_statep, 0);
1251708Sstevel 	ASSERT(rcs != NULL);
1261708Sstevel 	ASSERT(rcs->n_registrations != 0);
1271708Sstevel 	rcs->n_registrations--;
1281708Sstevel 	mutex_exit(&rmc_comm_attach_lock);
1291708Sstevel }
1301708Sstevel 
1311708Sstevel /*
1321708Sstevel  * to get the soft state structure of a specific instance
1331708Sstevel  */
1341708Sstevel struct rmc_comm_state *
1351708Sstevel rmc_comm_getstate(dev_info_t *dip, int instance, const char *caller)
1361708Sstevel {
1371708Sstevel 	struct rmc_comm_state *rcs = NULL;
1381708Sstevel 	dev_info_t *sdip = NULL;
1391708Sstevel 	major_t dmaj = NOMAJOR;
1401708Sstevel 
1411708Sstevel 	if (dip != NULL) {
1421708Sstevel 		/*
1431708Sstevel 		 * Use the instance number from the <dip>; also,
1441708Sstevel 		 * check that it really corresponds to this driver
1451708Sstevel 		 */
1461708Sstevel 		instance = ddi_get_instance(dip);
1471708Sstevel 		dmaj = ddi_driver_major(dip);
1481708Sstevel 		if (rmc_comm_major == NOMAJOR && dmaj != NOMAJOR)
1491708Sstevel 			rmc_comm_major = dmaj;
1501708Sstevel 		else if (dmaj != rmc_comm_major) {
1511708Sstevel 			cmn_err(CE_WARN,
1521708Sstevel 			    "%s: major number mismatch (%d vs. %d) in %s(),"
1531708Sstevel 			    "probably due to child misconfiguration",
1541708Sstevel 			    MYNAME, rmc_comm_major, dmaj, caller);
1551708Sstevel 			instance = -1;
1561708Sstevel 		}
1571708Sstevel 	}
1581708Sstevel 	if (instance >= 0)
1591708Sstevel 		rcs = ddi_get_soft_state(rmc_comm_statep, instance);
1601708Sstevel 	if (rcs != NULL) {
1611708Sstevel 		sdip = rcs->dip;
1621708Sstevel 		if (dip == NULL && sdip == NULL)
1631708Sstevel 			rcs = NULL;
1641708Sstevel 		else if (dip != NULL && sdip != NULL && sdip != dip) {
1651708Sstevel 			cmn_err(CE_WARN,
1661708Sstevel 			    "%s: devinfo mismatch (%p vs. %p) in %s(), "
1671708Sstevel 			    "probably due to child misconfiguration", MYNAME,
1681708Sstevel 			    (void *)dip, (void *)sdip, caller);
1691708Sstevel 			rcs = NULL;
1701708Sstevel 		}
1711708Sstevel 	}
1721708Sstevel 
1731708Sstevel 	return (rcs);
1741708Sstevel }
1751708Sstevel 
1761708Sstevel 
1771708Sstevel /*
1781708Sstevel  * Lowest-level serial I/O chip register read/write
1791708Sstevel  */
1801708Sstevel static void
1811708Sstevel sio_put_reg(struct rmc_comm_state *rcs, uint_t reg, uint8_t val)
1821708Sstevel {
1831708Sstevel 	DPRINTF(rcs, DSER, (CE_CONT, "REG[%d]<-$%02x", reg, val));
1841708Sstevel 
1851708Sstevel 	if (rcs->sd_state.sio_handle != NULL && !rcs->sd_state.sio_fault) {
1861708Sstevel 		/*
1871708Sstevel 		 * The chip is mapped as "I/O" (e.g. with the side-effect
1881708Sstevel 		 * bit on SPARC), therefore accesses are required to be
1891708Sstevel 		 * in-order, with no value cacheing.  However, there can
1901708Sstevel 		 * still be write-behind buffering, so it is not guaranteed
1911708Sstevel 		 * that a write actually reaches the chip in a given time.
1921708Sstevel 		 *
1931708Sstevel 		 * To force the access right through to the chip, we follow
1941708Sstevel 		 * the write with another write (to the SCRATCH register)
1951708Sstevel 		 * and a read (of the value just written to the SCRATCH
1961708Sstevel 		 * register).  The SCRATCH register is specifically provided
1971708Sstevel 		 * for temporary data and has no effect on the SIO's own
1981708Sstevel 		 * operation, making it ideal as a synchronising mechanism.
1991708Sstevel 		 *
2001708Sstevel 		 * If we didn't do this, it would be possible that the new
2011708Sstevel 		 * value wouldn't reach the chip (and have the *intended*
2021708Sstevel 		 * side-effects, such as disabling interrupts), for such a
2031708Sstevel 		 * long time that the processor could execute a *lot* of
2041708Sstevel 		 * instructions - including exiting the interrupt service
2051708Sstevel 		 * routine and re-enabling interrupts.  This effect was
2061708Sstevel 		 * observed to lead to spurious (unclaimed) interrupts in
2071708Sstevel 		 * some circumstances.
2081708Sstevel 		 *
2091708Sstevel 		 * This will no longer be needed once "synchronous" access
2101708Sstevel 		 * handles are available (see PSARC/2000/269 and 2000/531).
2111708Sstevel 		 */
2121708Sstevel 		ddi_put8(rcs->sd_state.sio_handle,
2131708Sstevel 		    rcs->sd_state.sio_regs + reg, val);
2141708Sstevel 		ddi_put8(rcs->sd_state.sio_handle,
2151708Sstevel 		    rcs->sd_state.sio_regs + SIO_SCR, val);
2161708Sstevel 		membar_sync();
2171708Sstevel 		(void) ddi_get8(rcs->sd_state.sio_handle,
2181708Sstevel 		    rcs->sd_state.sio_regs + SIO_SCR);
2191708Sstevel 	}
2201708Sstevel }
2211708Sstevel 
2221708Sstevel static uint8_t
2231708Sstevel sio_get_reg(struct rmc_comm_state *rcs, uint_t reg)
2241708Sstevel {
2251708Sstevel 	uint8_t val;
2261708Sstevel 
2271708Sstevel 	if (rcs->sd_state.sio_handle && !rcs->sd_state.sio_fault)
2281708Sstevel 		val = ddi_get8(rcs->sd_state.sio_handle,
2291708Sstevel 		    rcs->sd_state.sio_regs + reg);
2301708Sstevel 	else
2311708Sstevel 		val = DUMMY_VALUE;
2321708Sstevel 	DPRINTF(rcs, DSER, (CE_CONT, "$%02x<-REG[%d]", val, reg));
2331708Sstevel 	return (val);
2341708Sstevel }
2351708Sstevel 
2361708Sstevel static void
2371708Sstevel sio_check_fault_status(struct rmc_comm_state *rcs)
2381708Sstevel {
2391708Sstevel 	rcs->sd_state.sio_fault =
2401708Sstevel 		ddi_check_acc_handle(rcs->sd_state.sio_handle) != DDI_SUCCESS;
2411708Sstevel }
2421708Sstevel 
2431708Sstevel boolean_t
2441708Sstevel rmc_comm_faulty(struct rmc_comm_state *rcs)
2451708Sstevel {
2461708Sstevel 	if (!rcs->sd_state.sio_fault)
2471708Sstevel 		sio_check_fault_status(rcs);
2481708Sstevel 	return (rcs->sd_state.sio_fault);
2491708Sstevel }
2501708Sstevel 
2511708Sstevel /*
2521708Sstevel  * Check for data ready.
2531708Sstevel  */
2541708Sstevel static boolean_t
2551708Sstevel sio_data_ready(struct rmc_comm_state *rcs)
2561708Sstevel {
2571708Sstevel 	uint8_t status;
2581708Sstevel 
2591708Sstevel 	/*
2601708Sstevel 	 * Data is available if the RXDA bit in the LSR is nonzero
2611708Sstevel 	 * (if reading it didn't incur a fault).
2621708Sstevel 	 */
2631708Sstevel 	status = sio_get_reg(rcs, SIO_LSR);
2641708Sstevel 	return ((status & SIO_LSR_RXDA) != 0 && !rmc_comm_faulty(rcs));
2651708Sstevel }
2661708Sstevel 
2671708Sstevel /*
2681708Sstevel  * Enable/disable interrupts
2691708Sstevel  */
2701708Sstevel static void
2711708Sstevel rmc_comm_set_irq(struct rmc_comm_state *rcs, boolean_t newstate)
2721708Sstevel {
2731708Sstevel 	uint8_t val;
2741708Sstevel 
2751708Sstevel 	val = newstate ? SIO_IER_RXHDL_IE : 0;
2761708Sstevel 	sio_put_reg(rcs, SIO_IER, SIO_IER_STD | val);
2771708Sstevel 	rcs->sd_state.hw_int_enabled = newstate;
2781708Sstevel }
2791708Sstevel 
2801708Sstevel /*
2811708Sstevel  * High-level interrupt handler:
2821708Sstevel  *	Checks whether initialisation is complete (to avoid a race
2831708Sstevel  *	with mutex_init()), and whether chip interrupts are enabled.
2841708Sstevel  *	If not, the interrupt's not for us, so just return UNCLAIMED.
2851708Sstevel  *	Otherwise, disable the interrupt, trigger a softint, and return
2861708Sstevel  *	CLAIMED.  The softint handler will then do all the real work.
2871708Sstevel  *
2881708Sstevel  *	NOTE: the chip interrupt capability is only re-enabled once the
2891708Sstevel  *	receive code has run, but that can be called from a poll loop
2901708Sstevel  *	or cyclic callback as well as from the softint.  So it's *not*
2911708Sstevel  *	guaranteed that there really is a chip interrupt pending here,
2921708Sstevel  *	'cos the work may already have been done and the reason for the
2931708Sstevel  *	interrupt gone away before we get here.
2941708Sstevel  *
2951708Sstevel  *	OTOH, if we come through here twice without the receive code
2961708Sstevel  *	having run in between, that's definitely wrong.  In such an
2971708Sstevel  *	event, we would notice that chip interrupts haven't yet been
2981708Sstevel  *	re-enabled and return UNCLAIMED, allowing the system's jabber
2991708Sstevel  *	protect code (if any) to do its job.
3001708Sstevel  */
3011708Sstevel static uint_t
3021708Sstevel rmc_comm_hi_intr(caddr_t arg)
3031708Sstevel {
3041708Sstevel 	struct rmc_comm_state *rcs = (void *)arg;
3051708Sstevel 	uint_t claim;
3061708Sstevel 
3071708Sstevel 	claim = DDI_INTR_UNCLAIMED;
3081708Sstevel 	if (rcs->sd_state.cycid != CYCLIC_NONE) {
309*2749Sarutz 		/*
310*2749Sarutz 		 * Handle the case where this interrupt fires during
311*2749Sarutz 		 * panic processing.  If that occurs, then a thread
312*2749Sarutz 		 * in rmc_comm might have been idled while holding
313*2749Sarutz 		 * hw_mutex.  If so, that thread will never make
314*2749Sarutz 		 * progress, and so we do not want to unconditionally
315*2749Sarutz 		 * grab hw_mutex.
316*2749Sarutz 		 */
317*2749Sarutz 		if (ddi_in_panic() != 0) {
318*2749Sarutz 			if (mutex_tryenter(rcs->sd_state.hw_mutex) == 0) {
319*2749Sarutz 				return (claim);
320*2749Sarutz 			}
321*2749Sarutz 		} else {
322*2749Sarutz 			mutex_enter(rcs->sd_state.hw_mutex);
323*2749Sarutz 		}
3241708Sstevel 		if (rcs->sd_state.hw_int_enabled) {
3251708Sstevel 			rmc_comm_set_irq(rcs, B_FALSE);
3261708Sstevel 			ddi_trigger_softintr(rcs->sd_state.softid);
3271708Sstevel 			claim = DDI_INTR_CLAIMED;
3281708Sstevel 		}
3291708Sstevel 		mutex_exit(rcs->sd_state.hw_mutex);
3301708Sstevel 	}
3311708Sstevel 	return (claim);
3321708Sstevel }
3331708Sstevel 
3341708Sstevel /*
3351708Sstevel  * Packet receive handler
3361708Sstevel  *
3371708Sstevel  * This routine should be called from the low-level softint, or the
3381708Sstevel  * cyclic callback, or rmc_comm_cmd() (for polled operation), with the
3391708Sstevel  * low-level mutex already held.
3401708Sstevel  */
3411708Sstevel void
3421708Sstevel rmc_comm_serdev_receive(struct rmc_comm_state *rcs)
3431708Sstevel {
3441708Sstevel 	uint8_t data;
3451708Sstevel 
3461708Sstevel 	DPRINTF(rcs, DSER, (CE_CONT, "serdev_receive: soft int handler\n"));
3471708Sstevel 
3481708Sstevel 	/*
3491708Sstevel 	 * Check for access faults before starting the receive
3501708Sstevel 	 * loop (we don't want to cause bus errors or suchlike
3511708Sstevel 	 * unpleasantness in the event that the SIO has died).
3521708Sstevel 	 */
3531708Sstevel 	if (!rmc_comm_faulty(rcs)) {
3541708Sstevel 
3551708Sstevel 		char *rx_buf = rcs->sd_state.serdev_rx_buf;
3561708Sstevel 		uint16_t rx_buflen = 0;
3571708Sstevel 
3581708Sstevel 		/*
3591708Sstevel 		 * Read bytes from the FIFO until they're all gone
3601708Sstevel 		 * or our buffer overflows (which must be an error)
3611708Sstevel 		 */
3621708Sstevel 
3631708Sstevel 		/*
3641708Sstevel 		 * At the moment, the receive buffer is overwritten any
3651708Sstevel 		 * time data is received from the serial device.
3661708Sstevel 		 * This should not pose problems (probably!) as the data
3671708Sstevel 		 * protocol is half-duplex
3681708Sstevel 		 * Otherwise, a circular buffer must be implemented!
3691708Sstevel 		 */
3701708Sstevel 		mutex_enter(rcs->sd_state.hw_mutex);
3711708Sstevel 		while (sio_data_ready(rcs)) {
3721708Sstevel 			data = sio_get_reg(rcs, SIO_RXD);
3731708Sstevel 			rx_buf[rx_buflen++] = data;
3741708Sstevel 			if (rx_buflen >= SIO_MAX_RXBUF_SIZE)
3751708Sstevel 				break;
3761708Sstevel 		}
3771708Sstevel 		rcs->sd_state.serdev_rx_count = rx_buflen;
3781708Sstevel 
3791708Sstevel 		DATASCOPE(rcs, 'R', rx_buf, rx_buflen)
3801708Sstevel 
3811708Sstevel 		rmc_comm_set_irq(rcs, B_TRUE);
3821708Sstevel 		mutex_exit(rcs->sd_state.hw_mutex);
3831708Sstevel 
3841708Sstevel 		/*
3851708Sstevel 		 * call up the data protocol receive handler
3861708Sstevel 		 */
3871708Sstevel 		rmc_comm_dp_drecv(rcs, (uint8_t *)rx_buf, rx_buflen);
3881708Sstevel 	}
3891708Sstevel }
3901708Sstevel 
3911708Sstevel /*
3921708Sstevel  * Low-level softint handler
3931708Sstevel  *
3941708Sstevel  * This routine should be triggered whenever there's a byte to be read
3951708Sstevel  */
3961708Sstevel static uint_t
3971708Sstevel rmc_comm_softint(caddr_t arg)
3981708Sstevel {
3991708Sstevel 	struct rmc_comm_state *rcs = (void *)arg;
4001708Sstevel 
4011708Sstevel 	mutex_enter(rcs->dp_state.dp_mutex);
4021708Sstevel 	rmc_comm_serdev_receive(rcs);
4031708Sstevel 	mutex_exit(rcs->dp_state.dp_mutex);
4041708Sstevel 	return (DDI_INTR_CLAIMED);
4051708Sstevel }
4061708Sstevel 
4071708Sstevel /*
4081708Sstevel  * Cyclic handler: just calls the receive routine, in case interrupts
4091708Sstevel  * are not being delivered and in order to handle command timeout
4101708Sstevel  */
4111708Sstevel static void
4121708Sstevel rmc_comm_cyclic(void *arg)
4131708Sstevel {
4141708Sstevel 	struct rmc_comm_state *rcs = (void *)arg;
4151708Sstevel 
4161708Sstevel 	mutex_enter(rcs->dp_state.dp_mutex);
4171708Sstevel 	rmc_comm_serdev_receive(rcs);
4181708Sstevel 	mutex_exit(rcs->dp_state.dp_mutex);
4191708Sstevel }
4201708Sstevel 
4211708Sstevel /*
4221708Sstevel  * Serial protocol
4231708Sstevel  *
4241708Sstevel  * This routine builds a command and sets it in progress.
4251708Sstevel  */
4261708Sstevel void
4271708Sstevel rmc_comm_serdev_send(struct rmc_comm_state *rcs, char *buf, int buflen)
4281708Sstevel {
4291708Sstevel 	uint8_t *p;
4301708Sstevel 	uint8_t status;
4311708Sstevel 
4321708Sstevel 	/*
4331708Sstevel 	 * Check and update the SIO h/w fault status before accessing
4341708Sstevel 	 * the chip registers.  If there's a (new or previous) fault,
4351708Sstevel 	 * we'll run through the protocol but won't really touch the
4361708Sstevel 	 * hardware and all commands will timeout.  If a previously
4371708Sstevel 	 * discovered fault has now gone away (!), then we can (try to)
4381708Sstevel 	 * proceed with the new command (probably a probe).
4391708Sstevel 	 */
4401708Sstevel 	sio_check_fault_status(rcs);
4411708Sstevel 
4421708Sstevel 	/*
4431708Sstevel 	 * Send the command now by stuffing the packet into the Tx FIFO.
4441708Sstevel 	 */
4451708Sstevel 	DATASCOPE(rcs, 'S', buf, buflen)
4461708Sstevel 
4471708Sstevel 	mutex_enter(rcs->sd_state.hw_mutex);
4481708Sstevel 	p = (uint8_t *)buf;
4491708Sstevel 	while (p < (uint8_t *)&buf[buflen]) {
4501708Sstevel 
4511708Sstevel 		/*
4521708Sstevel 		 * before writing to the TX holding register, we make sure that
4531708Sstevel 		 * it is empty. In this case, there will be no chance to
4541708Sstevel 		 * overflow the serial device FIFO (but, on the other hand,
4551708Sstevel 		 * it may introduce some latency)
4561708Sstevel 		 */
4571708Sstevel 		status = sio_get_reg(rcs, SIO_LSR);
4581708Sstevel 		while ((status & SIO_LSR_XHRE) == 0) {
4591708Sstevel 			drv_usecwait(100);
4601708Sstevel 			status = sio_get_reg(rcs, SIO_LSR);
4611708Sstevel 		}
4621708Sstevel 		sio_put_reg(rcs, SIO_TXD, *p++);
4631708Sstevel 	}
4641708Sstevel 	mutex_exit(rcs->sd_state.hw_mutex);
4651708Sstevel }
4661708Sstevel 
4671708Sstevel /*
4681708Sstevel  * wait for the tx fifo to drain - used for urgent nowait requests
4691708Sstevel  */
4701708Sstevel void
4711708Sstevel rmc_comm_serdev_drain(struct rmc_comm_state *rcs)
4721708Sstevel {
4731708Sstevel 	uint8_t status;
4741708Sstevel 
4751708Sstevel 	mutex_enter(rcs->sd_state.hw_mutex);
4761708Sstevel 	status = sio_get_reg(rcs, SIO_LSR);
4771708Sstevel 	while ((status & SIO_LSR_XHRE) == 0) {
4781708Sstevel 		drv_usecwait(100);
4791708Sstevel 		status = sio_get_reg(rcs, SIO_LSR);
4801708Sstevel 	}
4811708Sstevel 	mutex_exit(rcs->sd_state.hw_mutex);
4821708Sstevel }
4831708Sstevel 
4841708Sstevel /*
4851708Sstevel  * Hardware setup - put the SIO chip in the required operational
4861708Sstevel  * state,  with all our favourite parameters programmed correctly.
4871708Sstevel  * This routine leaves all SIO interrupts disabled.
4881708Sstevel  */
4891708Sstevel 
4901708Sstevel static void
4911708Sstevel rmc_comm_hw_reset(struct rmc_comm_state *rcs)
4921708Sstevel {
4931708Sstevel 	uint16_t divisor;
4941708Sstevel 
4951708Sstevel 	/*
4961708Sstevel 	 * Disable interrupts, soft reset Tx and Rx circuitry,
4971708Sstevel 	 * reselect standard modes (bits/char, parity, etc).
4981708Sstevel 	 */
4991708Sstevel 	rmc_comm_set_irq(rcs, B_FALSE);
5001708Sstevel 	sio_put_reg(rcs, SIO_FCR, SIO_FCR_RXSR | SIO_FCR_TXSR);
5011708Sstevel 	sio_put_reg(rcs, SIO_LCR, SIO_LCR_STD);
5021708Sstevel 
5031708Sstevel 	/*
5041708Sstevel 	 * Select the proper baud rate; if the value is invalid
5051708Sstevel 	 * (presumably 0, i.e. not specified, but also if the
5061708Sstevel 	 * "baud" property is set to some silly value), we assume
5071708Sstevel 	 * the default.
5081708Sstevel 	 */
5091708Sstevel 	if (rcs->baud < SIO_BAUD_MIN || rcs->baud > SIO_BAUD_MAX) {
5101708Sstevel 		divisor = SIO_BAUD_TO_DIVISOR(SIO_BAUD_DEFAULT) *
5111708Sstevel 		    rcs->baud_divisor_factor;
5121708Sstevel 	} else {
5131708Sstevel 		divisor = SIO_BAUD_TO_DIVISOR(rcs->baud) *
5141708Sstevel 		    rcs->baud_divisor_factor;
5151708Sstevel 	}
5161708Sstevel 
5171708Sstevel 	/*
5181708Sstevel 	 * According to the datasheet, it is forbidden for the divisor
5191708Sstevel 	 * register to be zero.  So when loading the register in two
5201708Sstevel 	 * steps, we have to make sure that the temporary value formed
5211708Sstevel 	 * between loads is nonzero.  However, we can't rely on either
5221708Sstevel 	 * half already having a nonzero value, as the datasheet also
5231708Sstevel 	 * says that these registers are indeterminate after a reset!
5241708Sstevel 	 * So, we explicitly set the low byte to a non-zero value first;
5251708Sstevel 	 * then we can safely load the high byte, and then the correct
5261708Sstevel 	 * value for the low byte, without the result ever being zero.
5271708Sstevel 	 */
5281708Sstevel 	sio_put_reg(rcs, SIO_BSR, SIO_BSR_BANK1);
5291708Sstevel 	sio_put_reg(rcs, SIO_LBGDL, 0xff);
5301708Sstevel 	sio_put_reg(rcs, SIO_LBGDH, divisor >> 8);
5311708Sstevel 	sio_put_reg(rcs, SIO_LBGDL, divisor & 0xff);
5321708Sstevel 	sio_put_reg(rcs, SIO_BSR, SIO_BSR_BANK0);
5331708Sstevel 
5341708Sstevel 	/*
5351708Sstevel 	 * Program the remaining device registers as required
5361708Sstevel 	 */
5371708Sstevel 	sio_put_reg(rcs, SIO_MCR, SIO_MCR_STD);
5381708Sstevel 	sio_put_reg(rcs, SIO_FCR, SIO_FCR_STD);
5391708Sstevel }
5401708Sstevel 
5411708Sstevel /*
5421708Sstevel  * Higher-level setup & teardown
5431708Sstevel  */
5441708Sstevel static void
5451708Sstevel rmc_comm_offline(struct rmc_comm_state *rcs)
5461708Sstevel {
5471708Sstevel 	if (rcs->sd_state.sio_handle != NULL)
5481708Sstevel 		ddi_regs_map_free(&rcs->sd_state.sio_handle);
5491708Sstevel 	rcs->sd_state.sio_handle = NULL;
5501708Sstevel 	rcs->sd_state.sio_regs = NULL;
5511708Sstevel }
5521708Sstevel 
5531708Sstevel static int
5541708Sstevel rmc_comm_online(struct rmc_comm_state *rcs, dev_info_t *dip)
5551708Sstevel {
5561708Sstevel 	ddi_acc_handle_t h;
5571708Sstevel 	caddr_t p;
5581708Sstevel 	int nregs;
5591708Sstevel 	int err;
5601708Sstevel 
5611708Sstevel 	if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS)
5621708Sstevel 		nregs = 0;
5631708Sstevel 	switch (nregs) {
5641708Sstevel 	default:
5651708Sstevel 	case 1:
5661708Sstevel 		/*
5671708Sstevel 		 *  regset 0 represents the SIO operating registers
5681708Sstevel 		 */
5691708Sstevel 		err = ddi_regs_map_setup(dip, 0, &p, 0, 0,
5701708Sstevel 		    rmc_comm_dev_acc_attr, &h);
5711708Sstevel 		if (err != DDI_SUCCESS)
5721708Sstevel 			return (EIO);
5731708Sstevel 		rcs->sd_state.sio_handle = h;
5741708Sstevel 		rcs->sd_state.sio_regs = (void *)p;
5751708Sstevel 		break;
5761708Sstevel 	case 0:
5771708Sstevel 		/*
5781708Sstevel 		 *  If no registers are defined, succeed vacuously;
5791708Sstevel 		 *  commands will be accepted, but we fake the accesses.
5801708Sstevel 		 */
5811708Sstevel 		break;
5821708Sstevel 	}
5831708Sstevel 
5841708Sstevel 	/*
5851708Sstevel 	 * Now that the registers are mapped, we can initialise the SIO h/w
5861708Sstevel 	 */
5871708Sstevel 	rmc_comm_hw_reset(rcs);
5881708Sstevel 	return (0);
5891708Sstevel }
5901708Sstevel 
5911708Sstevel 
5921708Sstevel /*
5931708Sstevel  * Initialization of the serial device (data structure, mutex, cv, hardware
5941708Sstevel  * and so on). It is called from the attach routine.
5951708Sstevel  */
5961708Sstevel 
5971708Sstevel int
5981708Sstevel rmc_comm_serdev_init(struct rmc_comm_state *rcs, dev_info_t *dip)
5991708Sstevel {
6001708Sstevel 	cyc_handler_t cychand;
6011708Sstevel 	cyc_time_t cyctime;
6021708Sstevel 	int err = DDI_SUCCESS;
6031708Sstevel 
6041708Sstevel 	rcs->sd_state.cycid = CYCLIC_NONE;
6051708Sstevel 
6061708Sstevel 	/*
6071708Sstevel 	 *  Online the hardware ...
6081708Sstevel 	 */
6091708Sstevel 	err = rmc_comm_online(rcs, dip);
6101708Sstevel 	if (err != 0)
6111708Sstevel 		return (-1);
6121708Sstevel 
6131708Sstevel 	/*
6141708Sstevel 	 * call ddi_get_soft_iblock_cookie() to retrieve the
6151708Sstevel 	 * the interrupt block cookie so that the mutexes are initialized
6161708Sstevel 	 * before adding the interrupt (to avoid a potential race condition).
6171708Sstevel 	 */
6181708Sstevel 
6191708Sstevel 	err = ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
6201708Sstevel 	    &rcs->dp_state.dp_iblk);
6211708Sstevel 	if (err != DDI_SUCCESS)
6221708Sstevel 		return (-1);
6231708Sstevel 
6241708Sstevel 	err = ddi_get_iblock_cookie(dip, 0, &rcs->sd_state.hw_iblk);
6251708Sstevel 	if (err != DDI_SUCCESS)
6261708Sstevel 		return (-1);
6271708Sstevel 
6281708Sstevel 	/*
6291708Sstevel 	 * initialize mutex here before adding hw/sw interrupt handlers
6301708Sstevel 	 */
6311708Sstevel 	mutex_init(rcs->dp_state.dp_mutex, NULL, MUTEX_DRIVER,
6321708Sstevel 	    rcs->dp_state.dp_iblk);
6331708Sstevel 
6341708Sstevel 	mutex_init(rcs->sd_state.hw_mutex, NULL, MUTEX_DRIVER,
6351708Sstevel 	    rcs->sd_state.hw_iblk);
6361708Sstevel 
6371708Sstevel 	/*
6381708Sstevel 	 * Install soft and hard interrupt handler(s)
6391708Sstevel 	 *
6401708Sstevel 	 * the soft intr. handler will need the data protocol lock (dp_mutex)
6411708Sstevel 	 * So, data protocol mutex and iblock cookie are created/initialized
6421708Sstevel 	 * here
6431708Sstevel 	 */
6441708Sstevel 
6451708Sstevel 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, &rcs->sd_state.softid,
6461708Sstevel 	    &rcs->dp_state.dp_iblk, NULL, rmc_comm_softint, (caddr_t)rcs);
6471708Sstevel 	if (err != DDI_SUCCESS) {
6481708Sstevel 		mutex_destroy(rcs->dp_state.dp_mutex);
6491708Sstevel 		mutex_destroy(rcs->sd_state.hw_mutex);
6501708Sstevel 		return (-1);
6511708Sstevel 	}
6521708Sstevel 
6531708Sstevel 	/*
6541708Sstevel 	 * hardware interrupt
6551708Sstevel 	 */
6561708Sstevel 
6571708Sstevel 	if (rcs->sd_state.sio_handle != NULL) {
6581708Sstevel 		err = ddi_add_intr(dip, 0, &rcs->sd_state.hw_iblk, NULL,
6591708Sstevel 			rmc_comm_hi_intr, (caddr_t)rcs);
6601708Sstevel 
6611708Sstevel 		/*
6621708Sstevel 		 * did we successfully install the h/w interrupt handler?
6631708Sstevel 		 */
6641708Sstevel 		if (err != DDI_SUCCESS) {
6651708Sstevel 			ddi_remove_softintr(rcs->sd_state.softid);
6661708Sstevel 			mutex_destroy(rcs->dp_state.dp_mutex);
6671708Sstevel 			mutex_destroy(rcs->sd_state.hw_mutex);
6681708Sstevel 			return (-1);
6691708Sstevel 		}
6701708Sstevel 	}
6711708Sstevel 
6721708Sstevel 
6731708Sstevel 	/*
6741708Sstevel 	 * Start cyclic callbacks
6751708Sstevel 	 */
6761708Sstevel 
6771708Sstevel 	cychand.cyh_func = rmc_comm_cyclic;
6781708Sstevel 	cychand.cyh_arg = rcs;
6791708Sstevel 	cychand.cyh_level = CY_LOW_LEVEL;
6801708Sstevel 	cyctime.cyt_when = 0;			/* from the next second	*/
6811708Sstevel 	cyctime.cyt_interval = 5*RMC_COMM_ONE_SEC; /* call at 5s intervals */
6821708Sstevel 	mutex_enter(&cpu_lock);
6831708Sstevel 	rcs->sd_state.cycid = cyclic_add(&cychand, &cyctime);
6841708Sstevel 	mutex_exit(&cpu_lock);
6851708Sstevel 
6861708Sstevel 	return (0);
6871708Sstevel }
6881708Sstevel 
6891708Sstevel /*
6901708Sstevel  * Termination of the serial device (data structure, mutex, cv, hardware
6911708Sstevel  * and so on). It is called from the detach routine.
6921708Sstevel  */
6931708Sstevel 
6941708Sstevel void
6951708Sstevel rmc_comm_serdev_fini(struct rmc_comm_state *rcs, dev_info_t *dip)
6961708Sstevel {
6971708Sstevel 	rmc_comm_hw_reset(rcs);
6981708Sstevel 
6991708Sstevel 	if (rcs->sd_state.cycid != CYCLIC_NONE) {
7001708Sstevel 		mutex_enter(&cpu_lock);
7011708Sstevel 		cyclic_remove(rcs->sd_state.cycid);
7021708Sstevel 		mutex_exit(&cpu_lock);
7031708Sstevel 
7041708Sstevel 		if (rcs->sd_state.sio_handle != NULL)
7051708Sstevel 			ddi_remove_intr(dip, 0, rcs->sd_state.hw_iblk);
7061708Sstevel 
7071708Sstevel 		ddi_remove_softintr(rcs->sd_state.softid);
7081708Sstevel 
7091708Sstevel 		mutex_destroy(rcs->sd_state.hw_mutex);
7101708Sstevel 
7111708Sstevel 		mutex_destroy(rcs->dp_state.dp_mutex);
7121708Sstevel 	}
7131708Sstevel 	rmc_comm_offline(rcs);
7141708Sstevel }
7151708Sstevel 
7161708Sstevel /*
7171708Sstevel  * device driver entry routines (init/fini, attach/detach, ...)
7181708Sstevel  */
7191708Sstevel 
7201708Sstevel /*
7211708Sstevel  *  Clean up on detach or failure of attach
7221708Sstevel  */
7231708Sstevel static void
7241708Sstevel rmc_comm_unattach(struct rmc_comm_state *rcs, dev_info_t *dip, int instance,
7251708Sstevel     boolean_t drvi_init, boolean_t dp_init, boolean_t sd_init)
7261708Sstevel {
7271708Sstevel 	if (rcs != NULL) {
7281708Sstevel 		/*
7291708Sstevel 		 * disable interrupts now
7301708Sstevel 		 */
7311708Sstevel 		rmc_comm_set_irq(rcs, B_FALSE);
7321708Sstevel 
7331708Sstevel 		/*
7341708Sstevel 		 * driver interface termination (if it has been initialized)
7351708Sstevel 		 */
7361708Sstevel 		if (drvi_init)
7371708Sstevel 			rmc_comm_drvintf_fini(rcs);
7381708Sstevel 
7391708Sstevel 		/*
7401708Sstevel 		 * data protocol termination (if it has been initialized)
7411708Sstevel 		 */
7421708Sstevel 		if (dp_init)
7431708Sstevel 			rmc_comm_dp_fini(rcs);
7441708Sstevel 
7451708Sstevel 		/*
7461708Sstevel 		 * serial device termination (if it has been initialized)
7471708Sstevel 		 */
7481708Sstevel 		if (sd_init)
7491708Sstevel 			rmc_comm_serdev_fini(rcs, dip);
7501708Sstevel 
7511708Sstevel 		ddi_set_driver_private(dip, NULL);
7521708Sstevel 	}
7531708Sstevel 	ddi_soft_state_free(rmc_comm_statep, instance);
7541708Sstevel }
7551708Sstevel 
7561708Sstevel /*
7571708Sstevel  *  Autoconfiguration routines
7581708Sstevel  */
7591708Sstevel 
7601708Sstevel static int
7611708Sstevel rmc_comm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
7621708Sstevel {
7631708Sstevel 	struct rmc_comm_state *rcs = NULL;
7641708Sstevel 	sig_state_t *current_sgn_p;
7651708Sstevel 	int instance;
7661708Sstevel 
7671708Sstevel 	/*
7681708Sstevel 	 * only allow one instance
7691708Sstevel 	 */
7701708Sstevel 	instance = ddi_get_instance(dip);
7711708Sstevel 	if (instance != 0)
7721708Sstevel 		return (DDI_FAILURE);
7731708Sstevel 
7741708Sstevel 	switch (cmd) {
7751708Sstevel 	default:
7761708Sstevel 		return (DDI_FAILURE);
7771708Sstevel 
7781708Sstevel 	case DDI_RESUME:
7791708Sstevel 		if ((rcs = rmc_comm_getstate(dip, instance,
7801708Sstevel 		    "rmc_comm_attach")) == NULL)
7811708Sstevel 			return (DDI_FAILURE);	/* this "can't happen" */
7821708Sstevel 
7831708Sstevel 		rmc_comm_hw_reset(rcs);
7841708Sstevel 		rmc_comm_set_irq(rcs, B_TRUE);
7851708Sstevel 		rcs->dip = dip;
7861708Sstevel 
7871708Sstevel 		mutex_enter(&tod_lock);
7881708Sstevel 		if (watchdog_enable && tod_ops.tod_set_watchdog_timer != NULL &&
7891708Sstevel 		    watchdog_was_active) {
7901708Sstevel 			(void) tod_ops.tod_set_watchdog_timer(0);
7911708Sstevel 		}
7921708Sstevel 		mutex_exit(&tod_lock);
7931708Sstevel 
7941708Sstevel 		mutex_enter(rcs->dp_state.dp_mutex);
7951708Sstevel 		dp_reset(rcs, INITIAL_SEQID, 1, 1);
7961708Sstevel 		mutex_exit(rcs->dp_state.dp_mutex);
7971708Sstevel 
7981708Sstevel 		current_sgn_p = (sig_state_t *)modgetsymvalue(
7991708Sstevel 			"current_sgn", 0);
8001708Sstevel 		if ((current_sgn_p != NULL) &&
8011708Sstevel 			(current_sgn_p->state_t.sig != 0)) {
8021708Sstevel 			CPU_SIGNATURE(current_sgn_p->state_t.sig,
8031708Sstevel 				current_sgn_p->state_t.state,
8041708Sstevel 				current_sgn_p->state_t.sub_state, -1);
8051708Sstevel 		}
8061708Sstevel 		return (DDI_SUCCESS);
8071708Sstevel 
8081708Sstevel 	case DDI_ATTACH:
8091708Sstevel 		break;
8101708Sstevel 	}
8111708Sstevel 
8121708Sstevel 	/*
8131708Sstevel 	 *  Allocate the soft-state structure
8141708Sstevel 	 */
8151708Sstevel 	if (ddi_soft_state_zalloc(rmc_comm_statep, instance) != DDI_SUCCESS)
8161708Sstevel 		return (DDI_FAILURE);
8171708Sstevel 	if ((rcs = rmc_comm_getstate(dip, instance, "rmc_comm_attach")) ==
8181708Sstevel 	    NULL) {
8191708Sstevel 		rmc_comm_unattach(rcs, dip, instance, 0, 0, 0);
8201708Sstevel 		return (DDI_FAILURE);
8211708Sstevel 	}
8221708Sstevel 	ddi_set_driver_private(dip, rcs);
8231708Sstevel 
8241708Sstevel 	rcs->dip = NULL;
8251708Sstevel 
8261708Sstevel 	/*
8271708Sstevel 	 *  Set various options from .conf properties
8281708Sstevel 	 */
8291708Sstevel 	rcs->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8301708Sstevel 	    "baud-rate", 0);
8311708Sstevel 	rcs->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8321708Sstevel 	    "debug", 0);
8331708Sstevel 
8341708Sstevel 	/*
8351708Sstevel 	 * the baud divisor factor tells us how to scale the result of
8361708Sstevel 	 * the SIO_BAUD_TO_DIVISOR macro for platforms which do not
8371708Sstevel 	 * use the standard 24MHz uart clock
8381708Sstevel 	 */
8391708Sstevel 	rcs->baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
8401708Sstevel 	    DDI_PROP_DONTPASS, "baud-divisor-factor", SIO_BAUD_DIVISOR_MIN);
8411708Sstevel 
8421708Sstevel 	/*
8431708Sstevel 	 * try to be reasonable if the scale factor contains a silly value
8441708Sstevel 	 */
8451708Sstevel 	if ((rcs->baud_divisor_factor < SIO_BAUD_DIVISOR_MIN) ||
8461708Sstevel 	    (rcs->baud_divisor_factor > SIO_BAUD_DIVISOR_MAX))
8471708Sstevel 	    rcs->baud_divisor_factor = SIO_BAUD_DIVISOR_MIN;
8481708Sstevel 
8491708Sstevel 	/*
8501708Sstevel 	 * initialize serial device
8511708Sstevel 	 */
8521708Sstevel 	if (rmc_comm_serdev_init(rcs, dip) != 0) {
8531708Sstevel 		rmc_comm_unattach(rcs, dip, instance, 0, 0, 0);
8541708Sstevel 		return (DDI_FAILURE);
8551708Sstevel 	}
8561708Sstevel 
8571708Sstevel 	/*
8581708Sstevel 	 * initialize data protocol
8591708Sstevel 	 */
8601708Sstevel 	rmc_comm_dp_init(rcs);
8611708Sstevel 
8621708Sstevel 	/*
8631708Sstevel 	 * initialize driver interface
8641708Sstevel 	 */
8651708Sstevel 	if (rmc_comm_drvintf_init(rcs) != 0) {
8661708Sstevel 		rmc_comm_unattach(rcs, dip, instance, 0, 1, 1);
8671708Sstevel 		return (DDI_FAILURE);
8681708Sstevel 	}
8691708Sstevel 
8701708Sstevel 	/*
8711708Sstevel 	 *  Initialise devinfo-related fields
8721708Sstevel 	 */
8731708Sstevel 	rcs->majornum = ddi_driver_major(dip);
8741708Sstevel 	rcs->instance = instance;
8751708Sstevel 	rcs->dip = dip;
8761708Sstevel 
8771708Sstevel 	/*
8781708Sstevel 	 * enable interrupts now
8791708Sstevel 	 */
8801708Sstevel 	rmc_comm_set_irq(rcs, B_TRUE);
8811708Sstevel 
8821708Sstevel 	/*
8831708Sstevel 	 *  All done, report success
8841708Sstevel 	 */
8851708Sstevel 	ddi_report_dev(dip);
8861708Sstevel 	mutex_enter(&rmc_comm_attach_lock);
8871708Sstevel 	rcs->is_attached = B_TRUE;
8881708Sstevel 	mutex_exit(&rmc_comm_attach_lock);
8891708Sstevel 	return (DDI_SUCCESS);
8901708Sstevel }
8911708Sstevel 
8921708Sstevel static int
8931708Sstevel rmc_comm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8941708Sstevel {
8951708Sstevel 	struct rmc_comm_state *rcs;
8961708Sstevel 	int instance;
8971708Sstevel 
8981708Sstevel 	instance = ddi_get_instance(dip);
8991708Sstevel 	if ((rcs = rmc_comm_getstate(dip, instance, "rmc_comm_detach")) == NULL)
9001708Sstevel 		return (DDI_FAILURE);	/* this "can't happen" */
9011708Sstevel 
9021708Sstevel 	switch (cmd) {
9031708Sstevel 	case DDI_SUSPEND:
9041708Sstevel 		mutex_enter(&tod_lock);
9051708Sstevel 		if (watchdog_enable && watchdog_activated &&
9061708Sstevel 		    tod_ops.tod_clear_watchdog_timer != NULL) {
9071708Sstevel 			watchdog_was_active = 1;
9081708Sstevel 			(void) tod_ops.tod_clear_watchdog_timer();
9091708Sstevel 		} else {
9101708Sstevel 			watchdog_was_active = 0;
9111708Sstevel 		}
9121708Sstevel 		mutex_exit(&tod_lock);
9131708Sstevel 
9141708Sstevel 		rcs->dip = NULL;
9151708Sstevel 		rmc_comm_hw_reset(rcs);
9161708Sstevel 
9171708Sstevel 		return (DDI_SUCCESS);
9181708Sstevel 
9191708Sstevel 	case DDI_DETACH:
9201708Sstevel 		/*
9211708Sstevel 		 * reject detach if any client(s) still registered
9221708Sstevel 		 */
9231708Sstevel 		mutex_enter(&rmc_comm_attach_lock);
9241708Sstevel 		if (rcs->n_registrations != 0) {
9251708Sstevel 			mutex_exit(&rmc_comm_attach_lock);
9261708Sstevel 			return (DDI_FAILURE);
9271708Sstevel 		}
9281708Sstevel 		/*
9291708Sstevel 		 * Committed to complete the detach;
9301708Sstevel 		 * mark as no longer attached, to prevent new clients
9311708Sstevel 		 * registering (as part of a coincident attach)
9321708Sstevel 		 */
9331708Sstevel 		rcs->is_attached = B_FALSE;
9341708Sstevel 		mutex_exit(&rmc_comm_attach_lock);
9351708Sstevel 		rmc_comm_unattach(rcs, dip, instance, 1, 1, 1);
9361708Sstevel 		return (DDI_SUCCESS);
9371708Sstevel 
9381708Sstevel 	default:
9391708Sstevel 		return (DDI_FAILURE);
9401708Sstevel 	}
9411708Sstevel }
9421708Sstevel 
9431708Sstevel /*ARGSUSED*/
9441708Sstevel static int
9451708Sstevel rmc_comm_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
9461708Sstevel {
9471708Sstevel 	struct rmc_comm_state *rcs;
9481708Sstevel 
9491708Sstevel 	if ((rcs = rmc_comm_getstate(dip, -1, "rmc_comm_reset")) == NULL)
9501708Sstevel 		return (DDI_FAILURE);
9511708Sstevel 	rmc_comm_hw_reset(rcs);
9521708Sstevel 	return (DDI_SUCCESS);
9531708Sstevel }
9541708Sstevel 
9551708Sstevel /*
9561708Sstevel  * System interface structures
9571708Sstevel  */
9581708Sstevel static struct dev_ops rmc_comm_dev_ops =
9591708Sstevel {
9601708Sstevel 	DEVO_REV,
9611708Sstevel 	0,				/* refcount		*/
9621708Sstevel 	nodev,				/* getinfo		*/
9631708Sstevel 	nulldev,			/* identify		*/
9641708Sstevel 	nulldev,			/* probe		*/
9651708Sstevel 	rmc_comm_attach,		/* attach		*/
9661708Sstevel 	rmc_comm_detach,		/* detach		*/
9671708Sstevel 	rmc_comm_reset,			/* reset		*/
9681708Sstevel 	(struct cb_ops *)NULL,		/* driver operations	*/
9691708Sstevel 	(struct bus_ops *)NULL,		/* bus operations	*/
9701708Sstevel 	nulldev 			/* power()		*/
9711708Sstevel };
9721708Sstevel 
9731708Sstevel static struct modldrv modldrv =
9741708Sstevel {
9751708Sstevel 	&mod_driverops,
9761708Sstevel 	"rmc_comm driver, v%I%",
9771708Sstevel 	&rmc_comm_dev_ops
9781708Sstevel };
9791708Sstevel 
9801708Sstevel static struct modlinkage modlinkage =
9811708Sstevel {
9821708Sstevel 	MODREV_1,
9831708Sstevel 	{
9841708Sstevel 		&modldrv,
9851708Sstevel 		NULL
9861708Sstevel 	}
9871708Sstevel };
9881708Sstevel 
9891708Sstevel /*
9901708Sstevel  *  Dynamic loader interface code
9911708Sstevel  */
9921708Sstevel int
9931708Sstevel _init(void)
9941708Sstevel {
9951708Sstevel 	int err;
9961708Sstevel 
9971708Sstevel 	mutex_init(&rmc_comm_attach_lock, NULL, MUTEX_DRIVER, NULL);
9981708Sstevel 	err = ddi_soft_state_init(&rmc_comm_statep,
9991708Sstevel 		sizeof (struct rmc_comm_state), 0);
10001708Sstevel 	if (err == DDI_SUCCESS)
10011708Sstevel 		if ((err = mod_install(&modlinkage)) != 0) {
10021708Sstevel 			ddi_soft_state_fini(&rmc_comm_statep);
10031708Sstevel 		}
10041708Sstevel 	if (err != DDI_SUCCESS)
10051708Sstevel 		mutex_destroy(&rmc_comm_attach_lock);
10061708Sstevel 	return (err);
10071708Sstevel }
10081708Sstevel 
10091708Sstevel int
10101708Sstevel _info(struct modinfo *mip)
10111708Sstevel {
10121708Sstevel 	return (mod_info(&modlinkage, mip));
10131708Sstevel }
10141708Sstevel 
10151708Sstevel int
10161708Sstevel _fini(void)
10171708Sstevel {
10181708Sstevel 	int err;
10191708Sstevel 
10201708Sstevel 	if ((err = mod_remove(&modlinkage)) == 0) {
10211708Sstevel 		ddi_soft_state_fini(&rmc_comm_statep);
10221708Sstevel 		rmc_comm_major = NOMAJOR;
10231708Sstevel 		mutex_destroy(&rmc_comm_attach_lock);
10241708Sstevel 	}
10251708Sstevel 	return (err);
10261708Sstevel }
1027