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