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