103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23*19397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel * 2603831d35Sstevel * The "rmc_comm" driver provides access to the RMC so that its clients need 2703831d35Sstevel * not be concerned with the details of the access mechanism, which in this 2803831d35Sstevel * case is implemented via a packet-based protocol over a serial link via a 2903831d35Sstevel * 16550 compatible serial port. 3003831d35Sstevel */ 3103831d35Sstevel 3203831d35Sstevel 3303831d35Sstevel /* 3403831d35Sstevel * Header files 3503831d35Sstevel */ 3603831d35Sstevel #include <sys/conf.h> 3703831d35Sstevel #include <sys/membar.h> 3803831d35Sstevel #include <sys/modctl.h> 3903831d35Sstevel #include <sys/strlog.h> 4003831d35Sstevel #include <sys/types.h> 4103831d35Sstevel #include <sys/sunddi.h> 4203831d35Sstevel #include <sys/ddi.h> 4303831d35Sstevel #include <sys/rmc_comm_dp_boot.h> 4403831d35Sstevel #include <sys/rmc_comm_dp.h> 4503831d35Sstevel #include <sys/rmc_comm_drvintf.h> 4603831d35Sstevel #include <sys/rmc_comm.h> 4703831d35Sstevel #include <sys/cpu_sgnblk_defs.h> 4803831d35Sstevel 4903831d35Sstevel /* 5003831d35Sstevel * Local definitions 5103831d35Sstevel */ 5203831d35Sstevel #define MYNAME "rmc_comm" 5303831d35Sstevel #define NOMAJOR (~(major_t)0) 5403831d35Sstevel #define DUMMY_VALUE (~(int8_t)0) 5503831d35Sstevel 5603831d35Sstevel /* 5703831d35Sstevel * Local data 5803831d35Sstevel */ 5903831d35Sstevel static void *rmc_comm_statep; 6003831d35Sstevel static major_t rmc_comm_major = NOMAJOR; 6103831d35Sstevel static kmutex_t rmc_comm_attach_lock; 6203831d35Sstevel static ddi_device_acc_attr_t rmc_comm_dev_acc_attr[1] = 6303831d35Sstevel { 6403831d35Sstevel DDI_DEVICE_ATTR_V0, 6503831d35Sstevel DDI_STRUCTURE_LE_ACC, 6603831d35Sstevel DDI_STRICTORDER_ACC 6703831d35Sstevel }; 6803831d35Sstevel static int watchdog_was_active; 6903831d35Sstevel extern int watchdog_activated; 7003831d35Sstevel extern int watchdog_enable; 7103831d35Sstevel 7203831d35Sstevel /* 7303831d35Sstevel * prototypes 7403831d35Sstevel */ 7503831d35Sstevel 7603831d35Sstevel extern void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t); 7703831d35Sstevel static void sio_put_reg(struct rmc_comm_state *, uint_t, uint8_t); 7803831d35Sstevel static uint8_t sio_get_reg(struct rmc_comm_state *, uint_t); 7903831d35Sstevel static void sio_check_fault_status(struct rmc_comm_state *); 8003831d35Sstevel static boolean_t sio_data_ready(struct rmc_comm_state *); 8103831d35Sstevel static void rmc_comm_set_irq(struct rmc_comm_state *, boolean_t); 8203831d35Sstevel static uint_t rmc_comm_hi_intr(caddr_t); 8303831d35Sstevel static uint_t rmc_comm_softint(caddr_t); 8403831d35Sstevel static void rmc_comm_cyclic(void *); 8503831d35Sstevel static void rmc_comm_hw_reset(struct rmc_comm_state *); 8603831d35Sstevel static void rmc_comm_offline(struct rmc_comm_state *); 8703831d35Sstevel static int rmc_comm_online(struct rmc_comm_state *, dev_info_t *); 8803831d35Sstevel static void rmc_comm_unattach(struct rmc_comm_state *, dev_info_t *, int, 8903831d35Sstevel boolean_t, boolean_t, boolean_t); 9003831d35Sstevel static int rmc_comm_attach(dev_info_t *, ddi_attach_cmd_t); 9103831d35Sstevel static int rmc_comm_detach(dev_info_t *, ddi_detach_cmd_t); 9203831d35Sstevel 9303831d35Sstevel /* 9403831d35Sstevel * for client leaf drivers to register their desire for rmc_comm 9503831d35Sstevel * to stay attached 9603831d35Sstevel */ 9703831d35Sstevel int 9803831d35Sstevel rmc_comm_register() 9903831d35Sstevel { 10003831d35Sstevel struct rmc_comm_state *rcs; 10103831d35Sstevel 10203831d35Sstevel mutex_enter(&rmc_comm_attach_lock); 10303831d35Sstevel rcs = ddi_get_soft_state(rmc_comm_statep, 0); 10403831d35Sstevel if ((rcs == NULL) || (!rcs->is_attached)) { 10503831d35Sstevel mutex_exit(&rmc_comm_attach_lock); 10603831d35Sstevel return (DDI_FAILURE); 10703831d35Sstevel } 10803831d35Sstevel rcs->n_registrations++; 10903831d35Sstevel mutex_exit(&rmc_comm_attach_lock); 11003831d35Sstevel return (DDI_SUCCESS); 11103831d35Sstevel } 11203831d35Sstevel 11303831d35Sstevel void 11403831d35Sstevel rmc_comm_unregister() 11503831d35Sstevel { 11603831d35Sstevel struct rmc_comm_state *rcs; 11703831d35Sstevel 11803831d35Sstevel mutex_enter(&rmc_comm_attach_lock); 11903831d35Sstevel rcs = ddi_get_soft_state(rmc_comm_statep, 0); 12003831d35Sstevel ASSERT(rcs != NULL); 12103831d35Sstevel ASSERT(rcs->n_registrations != 0); 12203831d35Sstevel rcs->n_registrations--; 12303831d35Sstevel mutex_exit(&rmc_comm_attach_lock); 12403831d35Sstevel } 12503831d35Sstevel 12603831d35Sstevel /* 12703831d35Sstevel * to get the soft state structure of a specific instance 12803831d35Sstevel */ 12903831d35Sstevel struct rmc_comm_state * 13003831d35Sstevel rmc_comm_getstate(dev_info_t *dip, int instance, const char *caller) 13103831d35Sstevel { 13203831d35Sstevel struct rmc_comm_state *rcs = NULL; 13303831d35Sstevel dev_info_t *sdip = NULL; 13403831d35Sstevel major_t dmaj = NOMAJOR; 13503831d35Sstevel 13603831d35Sstevel if (dip != NULL) { 13703831d35Sstevel /* 13803831d35Sstevel * Use the instance number from the <dip>; also, 13903831d35Sstevel * check that it really corresponds to this driver 14003831d35Sstevel */ 14103831d35Sstevel instance = ddi_get_instance(dip); 14203831d35Sstevel dmaj = ddi_driver_major(dip); 14303831d35Sstevel if (rmc_comm_major == NOMAJOR && dmaj != NOMAJOR) 14403831d35Sstevel rmc_comm_major = dmaj; 14503831d35Sstevel else if (dmaj != rmc_comm_major) { 14603831d35Sstevel cmn_err(CE_WARN, 14703831d35Sstevel "%s: major number mismatch (%d vs. %d) in %s()," 14803831d35Sstevel "probably due to child misconfiguration", 14903831d35Sstevel MYNAME, rmc_comm_major, dmaj, caller); 15003831d35Sstevel instance = -1; 15103831d35Sstevel } 15203831d35Sstevel } 15303831d35Sstevel if (instance >= 0) 15403831d35Sstevel rcs = ddi_get_soft_state(rmc_comm_statep, instance); 15503831d35Sstevel if (rcs != NULL) { 15603831d35Sstevel sdip = rcs->dip; 15703831d35Sstevel if (dip == NULL && sdip == NULL) 15803831d35Sstevel rcs = NULL; 15903831d35Sstevel else if (dip != NULL && sdip != NULL && sdip != dip) { 16003831d35Sstevel cmn_err(CE_WARN, 16103831d35Sstevel "%s: devinfo mismatch (%p vs. %p) in %s(), " 16203831d35Sstevel "probably due to child misconfiguration", MYNAME, 16303831d35Sstevel (void *)dip, (void *)sdip, caller); 16403831d35Sstevel rcs = NULL; 16503831d35Sstevel } 16603831d35Sstevel } 16703831d35Sstevel 16803831d35Sstevel return (rcs); 16903831d35Sstevel } 17003831d35Sstevel 17103831d35Sstevel 17203831d35Sstevel /* 17303831d35Sstevel * Lowest-level serial I/O chip register read/write 17403831d35Sstevel */ 17503831d35Sstevel static void 17603831d35Sstevel sio_put_reg(struct rmc_comm_state *rcs, uint_t reg, uint8_t val) 17703831d35Sstevel { 17803831d35Sstevel DPRINTF(rcs, DSER, (CE_CONT, "REG[%d]<-$%02x", reg, val)); 17903831d35Sstevel 18003831d35Sstevel if (rcs->sd_state.sio_handle != NULL && !rcs->sd_state.sio_fault) { 18103831d35Sstevel /* 18203831d35Sstevel * The chip is mapped as "I/O" (e.g. with the side-effect 18303831d35Sstevel * bit on SPARC), therefore accesses are required to be 18403831d35Sstevel * in-order, with no value cacheing. However, there can 18503831d35Sstevel * still be write-behind buffering, so it is not guaranteed 18603831d35Sstevel * that a write actually reaches the chip in a given time. 18703831d35Sstevel * 18803831d35Sstevel * To force the access right through to the chip, we follow 18903831d35Sstevel * the write with another write (to the SCRATCH register) 19003831d35Sstevel * and a read (of the value just written to the SCRATCH 19103831d35Sstevel * register). The SCRATCH register is specifically provided 19203831d35Sstevel * for temporary data and has no effect on the SIO's own 19303831d35Sstevel * operation, making it ideal as a synchronising mechanism. 19403831d35Sstevel * 19503831d35Sstevel * If we didn't do this, it would be possible that the new 19603831d35Sstevel * value wouldn't reach the chip (and have the *intended* 19703831d35Sstevel * side-effects, such as disabling interrupts), for such a 19803831d35Sstevel * long time that the processor could execute a *lot* of 19903831d35Sstevel * instructions - including exiting the interrupt service 20003831d35Sstevel * routine and re-enabling interrupts. This effect was 20103831d35Sstevel * observed to lead to spurious (unclaimed) interrupts in 20203831d35Sstevel * some circumstances. 20303831d35Sstevel * 20403831d35Sstevel * This will no longer be needed once "synchronous" access 20503831d35Sstevel * handles are available (see PSARC/2000/269 and 2000/531). 20603831d35Sstevel */ 20703831d35Sstevel ddi_put8(rcs->sd_state.sio_handle, 20803831d35Sstevel rcs->sd_state.sio_regs + reg, val); 20903831d35Sstevel ddi_put8(rcs->sd_state.sio_handle, 21003831d35Sstevel rcs->sd_state.sio_regs + SIO_SCR, val); 21103831d35Sstevel membar_sync(); 21203831d35Sstevel (void) ddi_get8(rcs->sd_state.sio_handle, 21303831d35Sstevel rcs->sd_state.sio_regs + SIO_SCR); 21403831d35Sstevel } 21503831d35Sstevel } 21603831d35Sstevel 21703831d35Sstevel static uint8_t 21803831d35Sstevel sio_get_reg(struct rmc_comm_state *rcs, uint_t reg) 21903831d35Sstevel { 22003831d35Sstevel uint8_t val; 22103831d35Sstevel 22203831d35Sstevel if (rcs->sd_state.sio_handle && !rcs->sd_state.sio_fault) 22303831d35Sstevel val = ddi_get8(rcs->sd_state.sio_handle, 22403831d35Sstevel rcs->sd_state.sio_regs + reg); 22503831d35Sstevel else 22603831d35Sstevel val = DUMMY_VALUE; 22703831d35Sstevel DPRINTF(rcs, DSER, (CE_CONT, "$%02x<-REG[%d]", val, reg)); 22803831d35Sstevel return (val); 22903831d35Sstevel } 23003831d35Sstevel 23103831d35Sstevel static void 23203831d35Sstevel sio_check_fault_status(struct rmc_comm_state *rcs) 23303831d35Sstevel { 23403831d35Sstevel rcs->sd_state.sio_fault = 23503831d35Sstevel ddi_check_acc_handle(rcs->sd_state.sio_handle) != DDI_SUCCESS; 23603831d35Sstevel } 23703831d35Sstevel 23803831d35Sstevel boolean_t 23903831d35Sstevel rmc_comm_faulty(struct rmc_comm_state *rcs) 24003831d35Sstevel { 24103831d35Sstevel if (!rcs->sd_state.sio_fault) 24203831d35Sstevel sio_check_fault_status(rcs); 24303831d35Sstevel return (rcs->sd_state.sio_fault); 24403831d35Sstevel } 24503831d35Sstevel 24603831d35Sstevel /* 24703831d35Sstevel * Check for data ready. 24803831d35Sstevel */ 24903831d35Sstevel static boolean_t 25003831d35Sstevel sio_data_ready(struct rmc_comm_state *rcs) 25103831d35Sstevel { 25203831d35Sstevel uint8_t status; 25303831d35Sstevel 25403831d35Sstevel /* 25503831d35Sstevel * Data is available if the RXDA bit in the LSR is nonzero 25603831d35Sstevel * (if reading it didn't incur a fault). 25703831d35Sstevel */ 25803831d35Sstevel status = sio_get_reg(rcs, SIO_LSR); 25903831d35Sstevel return ((status & SIO_LSR_RXDA) != 0 && !rmc_comm_faulty(rcs)); 26003831d35Sstevel } 26103831d35Sstevel 26203831d35Sstevel /* 26303831d35Sstevel * Enable/disable interrupts 26403831d35Sstevel */ 26503831d35Sstevel static void 26603831d35Sstevel rmc_comm_set_irq(struct rmc_comm_state *rcs, boolean_t newstate) 26703831d35Sstevel { 26803831d35Sstevel uint8_t val; 26903831d35Sstevel 27003831d35Sstevel val = newstate ? SIO_IER_RXHDL_IE : 0; 27103831d35Sstevel sio_put_reg(rcs, SIO_IER, SIO_IER_STD | val); 27203831d35Sstevel rcs->sd_state.hw_int_enabled = newstate; 27303831d35Sstevel } 27403831d35Sstevel 27503831d35Sstevel /* 27603831d35Sstevel * High-level interrupt handler: 27703831d35Sstevel * Checks whether initialisation is complete (to avoid a race 27803831d35Sstevel * with mutex_init()), and whether chip interrupts are enabled. 27903831d35Sstevel * If not, the interrupt's not for us, so just return UNCLAIMED. 28003831d35Sstevel * Otherwise, disable the interrupt, trigger a softint, and return 28103831d35Sstevel * CLAIMED. The softint handler will then do all the real work. 28203831d35Sstevel * 28303831d35Sstevel * NOTE: the chip interrupt capability is only re-enabled once the 28403831d35Sstevel * receive code has run, but that can be called from a poll loop 28503831d35Sstevel * or cyclic callback as well as from the softint. So it's *not* 28603831d35Sstevel * guaranteed that there really is a chip interrupt pending here, 28703831d35Sstevel * 'cos the work may already have been done and the reason for the 28803831d35Sstevel * interrupt gone away before we get here. 28903831d35Sstevel * 29003831d35Sstevel * OTOH, if we come through here twice without the receive code 29103831d35Sstevel * having run in between, that's definitely wrong. In such an 29203831d35Sstevel * event, we would notice that chip interrupts haven't yet been 29303831d35Sstevel * re-enabled and return UNCLAIMED, allowing the system's jabber 29403831d35Sstevel * protect code (if any) to do its job. 29503831d35Sstevel */ 29603831d35Sstevel static uint_t 29703831d35Sstevel rmc_comm_hi_intr(caddr_t arg) 29803831d35Sstevel { 29903831d35Sstevel struct rmc_comm_state *rcs = (void *)arg; 30003831d35Sstevel uint_t claim; 30103831d35Sstevel 30203831d35Sstevel claim = DDI_INTR_UNCLAIMED; 303dd4eeefdSeota if (rcs->sd_state.cycid != NULL) { 3046fa6856eSarutz /* 3056fa6856eSarutz * Handle the case where this interrupt fires during 3066fa6856eSarutz * panic processing. If that occurs, then a thread 3076fa6856eSarutz * in rmc_comm might have been idled while holding 3086fa6856eSarutz * hw_mutex. If so, that thread will never make 3096fa6856eSarutz * progress, and so we do not want to unconditionally 3106fa6856eSarutz * grab hw_mutex. 3116fa6856eSarutz */ 3126fa6856eSarutz if (ddi_in_panic() != 0) { 3136fa6856eSarutz if (mutex_tryenter(rcs->sd_state.hw_mutex) == 0) { 3146fa6856eSarutz return (claim); 3156fa6856eSarutz } 3166fa6856eSarutz } else { 31703831d35Sstevel mutex_enter(rcs->sd_state.hw_mutex); 3186fa6856eSarutz } 31903831d35Sstevel if (rcs->sd_state.hw_int_enabled) { 32003831d35Sstevel rmc_comm_set_irq(rcs, B_FALSE); 32103831d35Sstevel ddi_trigger_softintr(rcs->sd_state.softid); 32203831d35Sstevel claim = DDI_INTR_CLAIMED; 32303831d35Sstevel } 32403831d35Sstevel mutex_exit(rcs->sd_state.hw_mutex); 32503831d35Sstevel } 32603831d35Sstevel return (claim); 32703831d35Sstevel } 32803831d35Sstevel 32903831d35Sstevel /* 33003831d35Sstevel * Packet receive handler 33103831d35Sstevel * 33203831d35Sstevel * This routine should be called from the low-level softint, or the 33303831d35Sstevel * cyclic callback, or rmc_comm_cmd() (for polled operation), with the 33403831d35Sstevel * low-level mutex already held. 33503831d35Sstevel */ 33603831d35Sstevel void 33703831d35Sstevel rmc_comm_serdev_receive(struct rmc_comm_state *rcs) 33803831d35Sstevel { 33903831d35Sstevel uint8_t data; 34003831d35Sstevel 34103831d35Sstevel DPRINTF(rcs, DSER, (CE_CONT, "serdev_receive: soft int handler\n")); 34203831d35Sstevel 34303831d35Sstevel /* 34403831d35Sstevel * Check for access faults before starting the receive 34503831d35Sstevel * loop (we don't want to cause bus errors or suchlike 34603831d35Sstevel * unpleasantness in the event that the SIO has died). 34703831d35Sstevel */ 34803831d35Sstevel if (!rmc_comm_faulty(rcs)) { 34903831d35Sstevel 35003831d35Sstevel char *rx_buf = rcs->sd_state.serdev_rx_buf; 35103831d35Sstevel uint16_t rx_buflen = 0; 35203831d35Sstevel 35303831d35Sstevel /* 35403831d35Sstevel * Read bytes from the FIFO until they're all gone 35503831d35Sstevel * or our buffer overflows (which must be an error) 35603831d35Sstevel */ 35703831d35Sstevel 35803831d35Sstevel /* 35903831d35Sstevel * At the moment, the receive buffer is overwritten any 36003831d35Sstevel * time data is received from the serial device. 36103831d35Sstevel * This should not pose problems (probably!) as the data 36203831d35Sstevel * protocol is half-duplex 36303831d35Sstevel * Otherwise, a circular buffer must be implemented! 36403831d35Sstevel */ 36503831d35Sstevel mutex_enter(rcs->sd_state.hw_mutex); 36603831d35Sstevel while (sio_data_ready(rcs)) { 36703831d35Sstevel data = sio_get_reg(rcs, SIO_RXD); 36803831d35Sstevel rx_buf[rx_buflen++] = data; 36903831d35Sstevel if (rx_buflen >= SIO_MAX_RXBUF_SIZE) 37003831d35Sstevel break; 37103831d35Sstevel } 37203831d35Sstevel rcs->sd_state.serdev_rx_count = rx_buflen; 37303831d35Sstevel 37403831d35Sstevel DATASCOPE(rcs, 'R', rx_buf, rx_buflen) 37503831d35Sstevel 37603831d35Sstevel rmc_comm_set_irq(rcs, B_TRUE); 37703831d35Sstevel mutex_exit(rcs->sd_state.hw_mutex); 37803831d35Sstevel 37903831d35Sstevel /* 38003831d35Sstevel * call up the data protocol receive handler 38103831d35Sstevel */ 38203831d35Sstevel rmc_comm_dp_drecv(rcs, (uint8_t *)rx_buf, rx_buflen); 38303831d35Sstevel } 38403831d35Sstevel } 38503831d35Sstevel 38603831d35Sstevel /* 38703831d35Sstevel * Low-level softint handler 38803831d35Sstevel * 38903831d35Sstevel * This routine should be triggered whenever there's a byte to be read 39003831d35Sstevel */ 39103831d35Sstevel static uint_t 39203831d35Sstevel rmc_comm_softint(caddr_t arg) 39303831d35Sstevel { 39403831d35Sstevel struct rmc_comm_state *rcs = (void *)arg; 39503831d35Sstevel 39603831d35Sstevel mutex_enter(rcs->dp_state.dp_mutex); 39703831d35Sstevel rmc_comm_serdev_receive(rcs); 39803831d35Sstevel mutex_exit(rcs->dp_state.dp_mutex); 39903831d35Sstevel return (DDI_INTR_CLAIMED); 40003831d35Sstevel } 40103831d35Sstevel 40203831d35Sstevel /* 40303831d35Sstevel * Cyclic handler: just calls the receive routine, in case interrupts 40403831d35Sstevel * are not being delivered and in order to handle command timeout 40503831d35Sstevel */ 40603831d35Sstevel static void 40703831d35Sstevel rmc_comm_cyclic(void *arg) 40803831d35Sstevel { 40903831d35Sstevel struct rmc_comm_state *rcs = (void *)arg; 41003831d35Sstevel 41103831d35Sstevel mutex_enter(rcs->dp_state.dp_mutex); 41203831d35Sstevel rmc_comm_serdev_receive(rcs); 41303831d35Sstevel mutex_exit(rcs->dp_state.dp_mutex); 41403831d35Sstevel } 41503831d35Sstevel 41603831d35Sstevel /* 41703831d35Sstevel * Serial protocol 41803831d35Sstevel * 41903831d35Sstevel * This routine builds a command and sets it in progress. 42003831d35Sstevel */ 42103831d35Sstevel void 42203831d35Sstevel rmc_comm_serdev_send(struct rmc_comm_state *rcs, char *buf, int buflen) 42303831d35Sstevel { 42403831d35Sstevel uint8_t *p; 42503831d35Sstevel uint8_t status; 42603831d35Sstevel 42703831d35Sstevel /* 42803831d35Sstevel * Check and update the SIO h/w fault status before accessing 42903831d35Sstevel * the chip registers. If there's a (new or previous) fault, 43003831d35Sstevel * we'll run through the protocol but won't really touch the 43103831d35Sstevel * hardware and all commands will timeout. If a previously 43203831d35Sstevel * discovered fault has now gone away (!), then we can (try to) 43303831d35Sstevel * proceed with the new command (probably a probe). 43403831d35Sstevel */ 43503831d35Sstevel sio_check_fault_status(rcs); 43603831d35Sstevel 43703831d35Sstevel /* 43803831d35Sstevel * Send the command now by stuffing the packet into the Tx FIFO. 43903831d35Sstevel */ 44003831d35Sstevel DATASCOPE(rcs, 'S', buf, buflen) 44103831d35Sstevel 44203831d35Sstevel mutex_enter(rcs->sd_state.hw_mutex); 44303831d35Sstevel p = (uint8_t *)buf; 44403831d35Sstevel while (p < (uint8_t *)&buf[buflen]) { 44503831d35Sstevel 44603831d35Sstevel /* 44703831d35Sstevel * before writing to the TX holding register, we make sure that 44803831d35Sstevel * it is empty. In this case, there will be no chance to 44903831d35Sstevel * overflow the serial device FIFO (but, on the other hand, 45003831d35Sstevel * it may introduce some latency) 45103831d35Sstevel */ 45203831d35Sstevel status = sio_get_reg(rcs, SIO_LSR); 45303831d35Sstevel while ((status & SIO_LSR_XHRE) == 0) { 45403831d35Sstevel drv_usecwait(100); 45503831d35Sstevel status = sio_get_reg(rcs, SIO_LSR); 45603831d35Sstevel } 45703831d35Sstevel sio_put_reg(rcs, SIO_TXD, *p++); 45803831d35Sstevel } 45903831d35Sstevel mutex_exit(rcs->sd_state.hw_mutex); 46003831d35Sstevel } 46103831d35Sstevel 46203831d35Sstevel /* 46303831d35Sstevel * wait for the tx fifo to drain - used for urgent nowait requests 46403831d35Sstevel */ 46503831d35Sstevel void 46603831d35Sstevel rmc_comm_serdev_drain(struct rmc_comm_state *rcs) 46703831d35Sstevel { 46803831d35Sstevel uint8_t status; 46903831d35Sstevel 47003831d35Sstevel mutex_enter(rcs->sd_state.hw_mutex); 47103831d35Sstevel status = sio_get_reg(rcs, SIO_LSR); 47203831d35Sstevel while ((status & SIO_LSR_XHRE) == 0) { 47303831d35Sstevel drv_usecwait(100); 47403831d35Sstevel status = sio_get_reg(rcs, SIO_LSR); 47503831d35Sstevel } 47603831d35Sstevel mutex_exit(rcs->sd_state.hw_mutex); 47703831d35Sstevel } 47803831d35Sstevel 47903831d35Sstevel /* 48003831d35Sstevel * Hardware setup - put the SIO chip in the required operational 48103831d35Sstevel * state, with all our favourite parameters programmed correctly. 48203831d35Sstevel * This routine leaves all SIO interrupts disabled. 48303831d35Sstevel */ 48403831d35Sstevel 48503831d35Sstevel static void 48603831d35Sstevel rmc_comm_hw_reset(struct rmc_comm_state *rcs) 48703831d35Sstevel { 48803831d35Sstevel uint16_t divisor; 48903831d35Sstevel 49003831d35Sstevel /* 49103831d35Sstevel * Disable interrupts, soft reset Tx and Rx circuitry, 49203831d35Sstevel * reselect standard modes (bits/char, parity, etc). 49303831d35Sstevel */ 49403831d35Sstevel rmc_comm_set_irq(rcs, B_FALSE); 49503831d35Sstevel sio_put_reg(rcs, SIO_FCR, SIO_FCR_RXSR | SIO_FCR_TXSR); 49603831d35Sstevel sio_put_reg(rcs, SIO_LCR, SIO_LCR_STD); 49703831d35Sstevel 49803831d35Sstevel /* 49903831d35Sstevel * Select the proper baud rate; if the value is invalid 50003831d35Sstevel * (presumably 0, i.e. not specified, but also if the 50103831d35Sstevel * "baud" property is set to some silly value), we assume 50203831d35Sstevel * the default. 50303831d35Sstevel */ 50403831d35Sstevel if (rcs->baud < SIO_BAUD_MIN || rcs->baud > SIO_BAUD_MAX) { 50503831d35Sstevel divisor = SIO_BAUD_TO_DIVISOR(SIO_BAUD_DEFAULT) * 50603831d35Sstevel rcs->baud_divisor_factor; 50703831d35Sstevel } else { 50803831d35Sstevel divisor = SIO_BAUD_TO_DIVISOR(rcs->baud) * 50903831d35Sstevel rcs->baud_divisor_factor; 51003831d35Sstevel } 51103831d35Sstevel 51203831d35Sstevel /* 51303831d35Sstevel * According to the datasheet, it is forbidden for the divisor 51403831d35Sstevel * register to be zero. So when loading the register in two 51503831d35Sstevel * steps, we have to make sure that the temporary value formed 51603831d35Sstevel * between loads is nonzero. However, we can't rely on either 51703831d35Sstevel * half already having a nonzero value, as the datasheet also 51803831d35Sstevel * says that these registers are indeterminate after a reset! 51903831d35Sstevel * So, we explicitly set the low byte to a non-zero value first; 52003831d35Sstevel * then we can safely load the high byte, and then the correct 52103831d35Sstevel * value for the low byte, without the result ever being zero. 52203831d35Sstevel */ 52303831d35Sstevel sio_put_reg(rcs, SIO_BSR, SIO_BSR_BANK1); 52403831d35Sstevel sio_put_reg(rcs, SIO_LBGDL, 0xff); 52503831d35Sstevel sio_put_reg(rcs, SIO_LBGDH, divisor >> 8); 52603831d35Sstevel sio_put_reg(rcs, SIO_LBGDL, divisor & 0xff); 52703831d35Sstevel sio_put_reg(rcs, SIO_BSR, SIO_BSR_BANK0); 52803831d35Sstevel 52903831d35Sstevel /* 53003831d35Sstevel * Program the remaining device registers as required 53103831d35Sstevel */ 53203831d35Sstevel sio_put_reg(rcs, SIO_MCR, SIO_MCR_STD); 53303831d35Sstevel sio_put_reg(rcs, SIO_FCR, SIO_FCR_STD); 53403831d35Sstevel } 53503831d35Sstevel 53603831d35Sstevel /* 53703831d35Sstevel * Higher-level setup & teardown 53803831d35Sstevel */ 53903831d35Sstevel static void 54003831d35Sstevel rmc_comm_offline(struct rmc_comm_state *rcs) 54103831d35Sstevel { 54203831d35Sstevel if (rcs->sd_state.sio_handle != NULL) 54303831d35Sstevel ddi_regs_map_free(&rcs->sd_state.sio_handle); 54403831d35Sstevel rcs->sd_state.sio_handle = NULL; 54503831d35Sstevel rcs->sd_state.sio_regs = NULL; 54603831d35Sstevel } 54703831d35Sstevel 54803831d35Sstevel static int 54903831d35Sstevel rmc_comm_online(struct rmc_comm_state *rcs, dev_info_t *dip) 55003831d35Sstevel { 55103831d35Sstevel ddi_acc_handle_t h; 55203831d35Sstevel caddr_t p; 55303831d35Sstevel int nregs; 55403831d35Sstevel int err; 55503831d35Sstevel 55603831d35Sstevel if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) 55703831d35Sstevel nregs = 0; 55803831d35Sstevel switch (nregs) { 55903831d35Sstevel default: 56003831d35Sstevel case 1: 56103831d35Sstevel /* 56203831d35Sstevel * regset 0 represents the SIO operating registers 56303831d35Sstevel */ 56403831d35Sstevel err = ddi_regs_map_setup(dip, 0, &p, 0, 0, 56503831d35Sstevel rmc_comm_dev_acc_attr, &h); 56603831d35Sstevel if (err != DDI_SUCCESS) 56703831d35Sstevel return (EIO); 56803831d35Sstevel rcs->sd_state.sio_handle = h; 56903831d35Sstevel rcs->sd_state.sio_regs = (void *)p; 57003831d35Sstevel break; 57103831d35Sstevel case 0: 57203831d35Sstevel /* 57303831d35Sstevel * If no registers are defined, succeed vacuously; 57403831d35Sstevel * commands will be accepted, but we fake the accesses. 57503831d35Sstevel */ 57603831d35Sstevel break; 57703831d35Sstevel } 57803831d35Sstevel 57903831d35Sstevel /* 58003831d35Sstevel * Now that the registers are mapped, we can initialise the SIO h/w 58103831d35Sstevel */ 58203831d35Sstevel rmc_comm_hw_reset(rcs); 58303831d35Sstevel return (0); 58403831d35Sstevel } 58503831d35Sstevel 58603831d35Sstevel 58703831d35Sstevel /* 58803831d35Sstevel * Initialization of the serial device (data structure, mutex, cv, hardware 58903831d35Sstevel * and so on). It is called from the attach routine. 59003831d35Sstevel */ 59103831d35Sstevel 59203831d35Sstevel int 59303831d35Sstevel rmc_comm_serdev_init(struct rmc_comm_state *rcs, dev_info_t *dip) 59403831d35Sstevel { 59503831d35Sstevel int err = DDI_SUCCESS; 59603831d35Sstevel 597dd4eeefdSeota rcs->sd_state.cycid = NULL; 59803831d35Sstevel 59903831d35Sstevel /* 60003831d35Sstevel * Online the hardware ... 60103831d35Sstevel */ 60203831d35Sstevel err = rmc_comm_online(rcs, dip); 60303831d35Sstevel if (err != 0) 60403831d35Sstevel return (-1); 60503831d35Sstevel 60603831d35Sstevel /* 60703831d35Sstevel * call ddi_get_soft_iblock_cookie() to retrieve the 60803831d35Sstevel * the interrupt block cookie so that the mutexes are initialized 60903831d35Sstevel * before adding the interrupt (to avoid a potential race condition). 61003831d35Sstevel */ 61103831d35Sstevel 61203831d35Sstevel err = ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 61303831d35Sstevel &rcs->dp_state.dp_iblk); 61403831d35Sstevel if (err != DDI_SUCCESS) 61503831d35Sstevel return (-1); 61603831d35Sstevel 61703831d35Sstevel err = ddi_get_iblock_cookie(dip, 0, &rcs->sd_state.hw_iblk); 61803831d35Sstevel if (err != DDI_SUCCESS) 61903831d35Sstevel return (-1); 62003831d35Sstevel 62103831d35Sstevel /* 62203831d35Sstevel * initialize mutex here before adding hw/sw interrupt handlers 62303831d35Sstevel */ 62403831d35Sstevel mutex_init(rcs->dp_state.dp_mutex, NULL, MUTEX_DRIVER, 62503831d35Sstevel rcs->dp_state.dp_iblk); 62603831d35Sstevel 62703831d35Sstevel mutex_init(rcs->sd_state.hw_mutex, NULL, MUTEX_DRIVER, 62803831d35Sstevel rcs->sd_state.hw_iblk); 62903831d35Sstevel 63003831d35Sstevel /* 63103831d35Sstevel * Install soft and hard interrupt handler(s) 63203831d35Sstevel * 63303831d35Sstevel * the soft intr. handler will need the data protocol lock (dp_mutex) 63403831d35Sstevel * So, data protocol mutex and iblock cookie are created/initialized 63503831d35Sstevel * here 63603831d35Sstevel */ 63703831d35Sstevel 63803831d35Sstevel err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, &rcs->sd_state.softid, 63903831d35Sstevel &rcs->dp_state.dp_iblk, NULL, rmc_comm_softint, (caddr_t)rcs); 64003831d35Sstevel if (err != DDI_SUCCESS) { 64103831d35Sstevel mutex_destroy(rcs->dp_state.dp_mutex); 64203831d35Sstevel mutex_destroy(rcs->sd_state.hw_mutex); 64303831d35Sstevel return (-1); 64403831d35Sstevel } 64503831d35Sstevel 64603831d35Sstevel /* 64703831d35Sstevel * hardware interrupt 64803831d35Sstevel */ 64903831d35Sstevel 65003831d35Sstevel if (rcs->sd_state.sio_handle != NULL) { 65103831d35Sstevel err = ddi_add_intr(dip, 0, &rcs->sd_state.hw_iblk, NULL, 65203831d35Sstevel rmc_comm_hi_intr, (caddr_t)rcs); 65303831d35Sstevel 65403831d35Sstevel /* 65503831d35Sstevel * did we successfully install the h/w interrupt handler? 65603831d35Sstevel */ 65703831d35Sstevel if (err != DDI_SUCCESS) { 65803831d35Sstevel ddi_remove_softintr(rcs->sd_state.softid); 65903831d35Sstevel mutex_destroy(rcs->dp_state.dp_mutex); 66003831d35Sstevel mutex_destroy(rcs->sd_state.hw_mutex); 66103831d35Sstevel return (-1); 66203831d35Sstevel } 66303831d35Sstevel } 66403831d35Sstevel 66503831d35Sstevel /* 666dd4eeefdSeota * Start periodical callbacks 66703831d35Sstevel */ 668dd4eeefdSeota rcs->sd_state.cycid = ddi_periodic_add(rmc_comm_cyclic, rcs, 669dd4eeefdSeota 5 * RMC_COMM_ONE_SEC, DDI_IPL_1); 67003831d35Sstevel return (0); 67103831d35Sstevel } 67203831d35Sstevel 67303831d35Sstevel /* 67403831d35Sstevel * Termination of the serial device (data structure, mutex, cv, hardware 67503831d35Sstevel * and so on). It is called from the detach routine. 67603831d35Sstevel */ 67703831d35Sstevel 67803831d35Sstevel void 67903831d35Sstevel rmc_comm_serdev_fini(struct rmc_comm_state *rcs, dev_info_t *dip) 68003831d35Sstevel { 68103831d35Sstevel rmc_comm_hw_reset(rcs); 68203831d35Sstevel 683dd4eeefdSeota if (rcs->sd_state.cycid != NULL) { 684dd4eeefdSeota ddi_periodic_delete(rcs->sd_state.cycid); 685dd4eeefdSeota rcs->sd_state.cycid = NULL; 68603831d35Sstevel 68703831d35Sstevel if (rcs->sd_state.sio_handle != NULL) 68803831d35Sstevel ddi_remove_intr(dip, 0, rcs->sd_state.hw_iblk); 68903831d35Sstevel 69003831d35Sstevel ddi_remove_softintr(rcs->sd_state.softid); 69103831d35Sstevel 69203831d35Sstevel mutex_destroy(rcs->sd_state.hw_mutex); 69303831d35Sstevel 69403831d35Sstevel mutex_destroy(rcs->dp_state.dp_mutex); 69503831d35Sstevel } 69603831d35Sstevel rmc_comm_offline(rcs); 69703831d35Sstevel } 69803831d35Sstevel 69903831d35Sstevel /* 70003831d35Sstevel * device driver entry routines (init/fini, attach/detach, ...) 70103831d35Sstevel */ 70203831d35Sstevel 70303831d35Sstevel /* 70403831d35Sstevel * Clean up on detach or failure of attach 70503831d35Sstevel */ 70603831d35Sstevel static void 70703831d35Sstevel rmc_comm_unattach(struct rmc_comm_state *rcs, dev_info_t *dip, int instance, 70803831d35Sstevel boolean_t drvi_init, boolean_t dp_init, boolean_t sd_init) 70903831d35Sstevel { 71003831d35Sstevel if (rcs != NULL) { 71103831d35Sstevel /* 71203831d35Sstevel * disable interrupts now 71303831d35Sstevel */ 71403831d35Sstevel rmc_comm_set_irq(rcs, B_FALSE); 71503831d35Sstevel 71603831d35Sstevel /* 71703831d35Sstevel * driver interface termination (if it has been initialized) 71803831d35Sstevel */ 71903831d35Sstevel if (drvi_init) 72003831d35Sstevel rmc_comm_drvintf_fini(rcs); 72103831d35Sstevel 72203831d35Sstevel /* 72303831d35Sstevel * data protocol termination (if it has been initialized) 72403831d35Sstevel */ 72503831d35Sstevel if (dp_init) 72603831d35Sstevel rmc_comm_dp_fini(rcs); 72703831d35Sstevel 72803831d35Sstevel /* 72903831d35Sstevel * serial device termination (if it has been initialized) 73003831d35Sstevel */ 73103831d35Sstevel if (sd_init) 73203831d35Sstevel rmc_comm_serdev_fini(rcs, dip); 73303831d35Sstevel 73403831d35Sstevel ddi_set_driver_private(dip, NULL); 73503831d35Sstevel } 73603831d35Sstevel ddi_soft_state_free(rmc_comm_statep, instance); 73703831d35Sstevel } 73803831d35Sstevel 73903831d35Sstevel /* 74003831d35Sstevel * Autoconfiguration routines 74103831d35Sstevel */ 74203831d35Sstevel 74303831d35Sstevel static int 74403831d35Sstevel rmc_comm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 74503831d35Sstevel { 74603831d35Sstevel struct rmc_comm_state *rcs = NULL; 74703831d35Sstevel sig_state_t *current_sgn_p; 74803831d35Sstevel int instance; 74903831d35Sstevel 75003831d35Sstevel /* 75103831d35Sstevel * only allow one instance 75203831d35Sstevel */ 75303831d35Sstevel instance = ddi_get_instance(dip); 75403831d35Sstevel if (instance != 0) 75503831d35Sstevel return (DDI_FAILURE); 75603831d35Sstevel 75703831d35Sstevel switch (cmd) { 75803831d35Sstevel default: 75903831d35Sstevel return (DDI_FAILURE); 76003831d35Sstevel 76103831d35Sstevel case DDI_RESUME: 76203831d35Sstevel if ((rcs = rmc_comm_getstate(dip, instance, 76303831d35Sstevel "rmc_comm_attach")) == NULL) 76403831d35Sstevel return (DDI_FAILURE); /* this "can't happen" */ 76503831d35Sstevel 76603831d35Sstevel rmc_comm_hw_reset(rcs); 76703831d35Sstevel rmc_comm_set_irq(rcs, B_TRUE); 76803831d35Sstevel rcs->dip = dip; 76903831d35Sstevel 77003831d35Sstevel mutex_enter(&tod_lock); 77103831d35Sstevel if (watchdog_enable && tod_ops.tod_set_watchdog_timer != NULL && 77203831d35Sstevel watchdog_was_active) { 77303831d35Sstevel (void) tod_ops.tod_set_watchdog_timer(0); 77403831d35Sstevel } 77503831d35Sstevel mutex_exit(&tod_lock); 77603831d35Sstevel 77703831d35Sstevel mutex_enter(rcs->dp_state.dp_mutex); 77803831d35Sstevel dp_reset(rcs, INITIAL_SEQID, 1, 1); 77903831d35Sstevel mutex_exit(rcs->dp_state.dp_mutex); 78003831d35Sstevel 78103831d35Sstevel current_sgn_p = (sig_state_t *)modgetsymvalue( 78203831d35Sstevel "current_sgn", 0); 78303831d35Sstevel if ((current_sgn_p != NULL) && 78403831d35Sstevel (current_sgn_p->state_t.sig != 0)) { 78503831d35Sstevel CPU_SIGNATURE(current_sgn_p->state_t.sig, 78603831d35Sstevel current_sgn_p->state_t.state, 78703831d35Sstevel current_sgn_p->state_t.sub_state, -1); 78803831d35Sstevel } 78903831d35Sstevel return (DDI_SUCCESS); 79003831d35Sstevel 79103831d35Sstevel case DDI_ATTACH: 79203831d35Sstevel break; 79303831d35Sstevel } 79403831d35Sstevel 79503831d35Sstevel /* 79603831d35Sstevel * Allocate the soft-state structure 79703831d35Sstevel */ 79803831d35Sstevel if (ddi_soft_state_zalloc(rmc_comm_statep, instance) != DDI_SUCCESS) 79903831d35Sstevel return (DDI_FAILURE); 80003831d35Sstevel if ((rcs = rmc_comm_getstate(dip, instance, "rmc_comm_attach")) == 80103831d35Sstevel NULL) { 80203831d35Sstevel rmc_comm_unattach(rcs, dip, instance, 0, 0, 0); 80303831d35Sstevel return (DDI_FAILURE); 80403831d35Sstevel } 80503831d35Sstevel ddi_set_driver_private(dip, rcs); 80603831d35Sstevel 80703831d35Sstevel rcs->dip = NULL; 80803831d35Sstevel 80903831d35Sstevel /* 81003831d35Sstevel * Set various options from .conf properties 81103831d35Sstevel */ 81203831d35Sstevel rcs->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 81303831d35Sstevel "baud-rate", 0); 81403831d35Sstevel rcs->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 81503831d35Sstevel "debug", 0); 81603831d35Sstevel 81703831d35Sstevel /* 81803831d35Sstevel * the baud divisor factor tells us how to scale the result of 81903831d35Sstevel * the SIO_BAUD_TO_DIVISOR macro for platforms which do not 82003831d35Sstevel * use the standard 24MHz uart clock 82103831d35Sstevel */ 82203831d35Sstevel rcs->baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 82303831d35Sstevel DDI_PROP_DONTPASS, "baud-divisor-factor", SIO_BAUD_DIVISOR_MIN); 82403831d35Sstevel 82503831d35Sstevel /* 82603831d35Sstevel * try to be reasonable if the scale factor contains a silly value 82703831d35Sstevel */ 82803831d35Sstevel if ((rcs->baud_divisor_factor < SIO_BAUD_DIVISOR_MIN) || 82903831d35Sstevel (rcs->baud_divisor_factor > SIO_BAUD_DIVISOR_MAX)) 83003831d35Sstevel rcs->baud_divisor_factor = SIO_BAUD_DIVISOR_MIN; 83103831d35Sstevel 83203831d35Sstevel /* 83303831d35Sstevel * initialize serial device 83403831d35Sstevel */ 83503831d35Sstevel if (rmc_comm_serdev_init(rcs, dip) != 0) { 83603831d35Sstevel rmc_comm_unattach(rcs, dip, instance, 0, 0, 0); 83703831d35Sstevel return (DDI_FAILURE); 83803831d35Sstevel } 83903831d35Sstevel 84003831d35Sstevel /* 84103831d35Sstevel * initialize data protocol 84203831d35Sstevel */ 84303831d35Sstevel rmc_comm_dp_init(rcs); 84403831d35Sstevel 84503831d35Sstevel /* 84603831d35Sstevel * initialize driver interface 84703831d35Sstevel */ 84803831d35Sstevel if (rmc_comm_drvintf_init(rcs) != 0) { 84903831d35Sstevel rmc_comm_unattach(rcs, dip, instance, 0, 1, 1); 85003831d35Sstevel return (DDI_FAILURE); 85103831d35Sstevel } 85203831d35Sstevel 85303831d35Sstevel /* 85403831d35Sstevel * Initialise devinfo-related fields 85503831d35Sstevel */ 85603831d35Sstevel rcs->majornum = ddi_driver_major(dip); 85703831d35Sstevel rcs->instance = instance; 85803831d35Sstevel rcs->dip = dip; 85903831d35Sstevel 86003831d35Sstevel /* 86103831d35Sstevel * enable interrupts now 86203831d35Sstevel */ 86303831d35Sstevel rmc_comm_set_irq(rcs, B_TRUE); 86403831d35Sstevel 86503831d35Sstevel /* 86603831d35Sstevel * All done, report success 86703831d35Sstevel */ 86803831d35Sstevel ddi_report_dev(dip); 86903831d35Sstevel mutex_enter(&rmc_comm_attach_lock); 87003831d35Sstevel rcs->is_attached = B_TRUE; 87103831d35Sstevel mutex_exit(&rmc_comm_attach_lock); 87203831d35Sstevel return (DDI_SUCCESS); 87303831d35Sstevel } 87403831d35Sstevel 87503831d35Sstevel static int 87603831d35Sstevel rmc_comm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 87703831d35Sstevel { 87803831d35Sstevel struct rmc_comm_state *rcs; 87903831d35Sstevel int instance; 88003831d35Sstevel 88103831d35Sstevel instance = ddi_get_instance(dip); 88203831d35Sstevel if ((rcs = rmc_comm_getstate(dip, instance, "rmc_comm_detach")) == NULL) 88303831d35Sstevel return (DDI_FAILURE); /* this "can't happen" */ 88403831d35Sstevel 88503831d35Sstevel switch (cmd) { 88603831d35Sstevel case DDI_SUSPEND: 88703831d35Sstevel mutex_enter(&tod_lock); 88803831d35Sstevel if (watchdog_enable && watchdog_activated && 88903831d35Sstevel tod_ops.tod_clear_watchdog_timer != NULL) { 89003831d35Sstevel watchdog_was_active = 1; 89103831d35Sstevel (void) tod_ops.tod_clear_watchdog_timer(); 89203831d35Sstevel } else { 89303831d35Sstevel watchdog_was_active = 0; 89403831d35Sstevel } 89503831d35Sstevel mutex_exit(&tod_lock); 89603831d35Sstevel 89703831d35Sstevel rcs->dip = NULL; 89803831d35Sstevel rmc_comm_hw_reset(rcs); 89903831d35Sstevel 90003831d35Sstevel return (DDI_SUCCESS); 90103831d35Sstevel 90203831d35Sstevel case DDI_DETACH: 90303831d35Sstevel /* 90403831d35Sstevel * reject detach if any client(s) still registered 90503831d35Sstevel */ 90603831d35Sstevel mutex_enter(&rmc_comm_attach_lock); 90703831d35Sstevel if (rcs->n_registrations != 0) { 90803831d35Sstevel mutex_exit(&rmc_comm_attach_lock); 90903831d35Sstevel return (DDI_FAILURE); 91003831d35Sstevel } 91103831d35Sstevel /* 91203831d35Sstevel * Committed to complete the detach; 91303831d35Sstevel * mark as no longer attached, to prevent new clients 91403831d35Sstevel * registering (as part of a coincident attach) 91503831d35Sstevel */ 91603831d35Sstevel rcs->is_attached = B_FALSE; 91703831d35Sstevel mutex_exit(&rmc_comm_attach_lock); 91803831d35Sstevel rmc_comm_unattach(rcs, dip, instance, 1, 1, 1); 91903831d35Sstevel return (DDI_SUCCESS); 92003831d35Sstevel 92103831d35Sstevel default: 92203831d35Sstevel return (DDI_FAILURE); 92303831d35Sstevel } 92403831d35Sstevel } 92503831d35Sstevel 92603831d35Sstevel /*ARGSUSED*/ 92703831d35Sstevel static int 92803831d35Sstevel rmc_comm_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) 92903831d35Sstevel { 93003831d35Sstevel struct rmc_comm_state *rcs; 93103831d35Sstevel 93203831d35Sstevel if ((rcs = rmc_comm_getstate(dip, -1, "rmc_comm_reset")) == NULL) 93303831d35Sstevel return (DDI_FAILURE); 93403831d35Sstevel rmc_comm_hw_reset(rcs); 93503831d35Sstevel return (DDI_SUCCESS); 93603831d35Sstevel } 93703831d35Sstevel 93803831d35Sstevel /* 93903831d35Sstevel * System interface structures 94003831d35Sstevel */ 94103831d35Sstevel static struct dev_ops rmc_comm_dev_ops = 94203831d35Sstevel { 94303831d35Sstevel DEVO_REV, 94403831d35Sstevel 0, /* refcount */ 94503831d35Sstevel nodev, /* getinfo */ 94603831d35Sstevel nulldev, /* identify */ 94703831d35Sstevel nulldev, /* probe */ 94803831d35Sstevel rmc_comm_attach, /* attach */ 94903831d35Sstevel rmc_comm_detach, /* detach */ 95003831d35Sstevel rmc_comm_reset, /* reset */ 95103831d35Sstevel (struct cb_ops *)NULL, /* driver operations */ 95203831d35Sstevel (struct bus_ops *)NULL, /* bus operations */ 953*19397407SSherry Moore nulldev, /* power() */ 954*19397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 95503831d35Sstevel }; 95603831d35Sstevel 95703831d35Sstevel static struct modldrv modldrv = 95803831d35Sstevel { 95903831d35Sstevel &mod_driverops, 960*19397407SSherry Moore "rmc_comm driver", 96103831d35Sstevel &rmc_comm_dev_ops 96203831d35Sstevel }; 96303831d35Sstevel 96403831d35Sstevel static struct modlinkage modlinkage = 96503831d35Sstevel { 96603831d35Sstevel MODREV_1, 96703831d35Sstevel { 96803831d35Sstevel &modldrv, 96903831d35Sstevel NULL 97003831d35Sstevel } 97103831d35Sstevel }; 97203831d35Sstevel 97303831d35Sstevel /* 97403831d35Sstevel * Dynamic loader interface code 97503831d35Sstevel */ 97603831d35Sstevel int 97703831d35Sstevel _init(void) 97803831d35Sstevel { 97903831d35Sstevel int err; 98003831d35Sstevel 98103831d35Sstevel mutex_init(&rmc_comm_attach_lock, NULL, MUTEX_DRIVER, NULL); 98203831d35Sstevel err = ddi_soft_state_init(&rmc_comm_statep, 98303831d35Sstevel sizeof (struct rmc_comm_state), 0); 98403831d35Sstevel if (err == DDI_SUCCESS) 98503831d35Sstevel if ((err = mod_install(&modlinkage)) != 0) { 98603831d35Sstevel ddi_soft_state_fini(&rmc_comm_statep); 98703831d35Sstevel } 98803831d35Sstevel if (err != DDI_SUCCESS) 98903831d35Sstevel mutex_destroy(&rmc_comm_attach_lock); 99003831d35Sstevel return (err); 99103831d35Sstevel } 99203831d35Sstevel 99303831d35Sstevel int 99403831d35Sstevel _info(struct modinfo *mip) 99503831d35Sstevel { 99603831d35Sstevel return (mod_info(&modlinkage, mip)); 99703831d35Sstevel } 99803831d35Sstevel 99903831d35Sstevel int 100003831d35Sstevel _fini(void) 100103831d35Sstevel { 100203831d35Sstevel int err; 100303831d35Sstevel 100403831d35Sstevel if ((err = mod_remove(&modlinkage)) == 0) { 100503831d35Sstevel ddi_soft_state_fini(&rmc_comm_statep); 100603831d35Sstevel rmc_comm_major = NOMAJOR; 100703831d35Sstevel mutex_destroy(&rmc_comm_attach_lock); 100803831d35Sstevel } 100903831d35Sstevel return (err); 101003831d35Sstevel } 1011