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 /* 23d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel /* 2803831d35Sstevel * IOSRAM leaf driver to SBBC nexus driver. This driver is used 2903831d35Sstevel * by Starcat Domain SW to read/write from/to the IO sram. 3003831d35Sstevel */ 3103831d35Sstevel 3203831d35Sstevel #include <sys/types.h> 3303831d35Sstevel #include <sys/conf.h> 3403831d35Sstevel #include <sys/ddi.h> 3503831d35Sstevel #include <sys/sunddi.h> 3603831d35Sstevel #include <sys/ddi_impldefs.h> 3703831d35Sstevel #include <sys/obpdefs.h> 3803831d35Sstevel #include <sys/promif.h> 3903831d35Sstevel #include <sys/prom_plat.h> 4003831d35Sstevel #include <sys/cmn_err.h> 4103831d35Sstevel #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 4203831d35Sstevel #include <sys/modctl.h> /* for modldrv */ 4303831d35Sstevel #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */ 4403831d35Sstevel #include <sys/errno.h> 4503831d35Sstevel #include <sys/kmem.h> 4603831d35Sstevel #include <sys/kstat.h> 4703831d35Sstevel #include <sys/debug.h> 4803831d35Sstevel 4903831d35Sstevel #include <sys/axq.h> 5003831d35Sstevel #include <sys/iosramreg.h> 5103831d35Sstevel #include <sys/iosramio.h> 5203831d35Sstevel #include <sys/iosramvar.h> 5303831d35Sstevel 5403831d35Sstevel 5503831d35Sstevel #if defined(DEBUG) 5603831d35Sstevel int iosram_debug = 0; 5703831d35Sstevel static void iosram_dprintf(const char *fmt, ...); 5803831d35Sstevel #define DPRINTF(level, arg) \ 5903831d35Sstevel { if (iosram_debug >= level) iosram_dprintf arg; } 6003831d35Sstevel #else /* !DEBUG */ 6103831d35Sstevel #define DPRINTF(level, arg) 6203831d35Sstevel #endif /* !DEBUG */ 6303831d35Sstevel 6403831d35Sstevel 6503831d35Sstevel /* 6603831d35Sstevel * IOSRAM module global state 6703831d35Sstevel */ 6803831d35Sstevel static void *iosramsoft_statep; /* IOSRAM state pointer */ 6903831d35Sstevel static kmutex_t iosram_mutex; /* mutex lock */ 7003831d35Sstevel 7103831d35Sstevel static iosram_chunk_t *chunks = NULL; /* array of TOC entries */ 7203831d35Sstevel static int nchunks = 0; /* # of TOC entries */ 7303831d35Sstevel static iosram_chunk_t *iosram_hashtab[IOSRAM_HASHSZ]; /* key hash table */ 7403831d35Sstevel 7503831d35Sstevel static kcondvar_t iosram_tswitch_wait; /* tunnel switch wait cv */ 7603831d35Sstevel static int iosram_tswitch_wakeup = 0; /* flag indicationg one or */ 7703831d35Sstevel /* more threads waiting on */ 7803831d35Sstevel /* iosram_tswitch_wait cv */ 7903831d35Sstevel static int iosram_tswitch_active = 0; /* tunnel switch active flag */ 8003831d35Sstevel static int iosram_tswitch_aborted = 0; /* tunnel switch abort flag */ 8103831d35Sstevel static clock_t iosram_tswitch_tstamp = 0; /* lbolt of last tswitch end */ 8203831d35Sstevel static kcondvar_t iosram_rw_wait; /* read/write wait cv */ 8303831d35Sstevel static int iosram_rw_wakeup = 0; /* flag indicationg one or */ 8403831d35Sstevel /* more threads waiting on */ 8503831d35Sstevel /* iosram_rw_wait cv */ 8603831d35Sstevel static int iosram_rw_active = 0; /* # threads accessing IOSRAM */ 8703831d35Sstevel #if defined(DEBUG) 8803831d35Sstevel static int iosram_rw_active_max = 0; 8903831d35Sstevel #endif 9003831d35Sstevel 9103831d35Sstevel static struct iosramsoft *iosram_new_master = NULL; /* new tunnel target */ 9203831d35Sstevel static struct iosramsoft *iosram_master = NULL; /* master tunnel */ 9303831d35Sstevel static struct iosramsoft *iosram_instances = NULL; /* list of softstates */ 9403831d35Sstevel 9503831d35Sstevel static ddi_acc_handle_t iosram_handle = NULL; /* master IOSRAM map handle */ 9603831d35Sstevel 9703831d35Sstevel static void (*iosram_hdrchange_handler)() = NULL; 9803831d35Sstevel 9903831d35Sstevel #if IOSRAM_STATS 10003831d35Sstevel static struct iosram_stat iosram_stats; /* IOSRAM statistics */ 10103831d35Sstevel static void iosram_print_stats(); /* forward declaration */ 10203831d35Sstevel #endif /* IOSRAM_STATS */ 10303831d35Sstevel 10403831d35Sstevel 10503831d35Sstevel #if IOSRAM_LOG 10603831d35Sstevel kmutex_t iosram_log_mutex; 10703831d35Sstevel int iosram_log_level = 1; 10803831d35Sstevel int iosram_log_print = 0; /* print log when recorded */ 10903831d35Sstevel uint32_t iosram_logseq; 11003831d35Sstevel iosram_log_t iosram_logbuf[IOSRAM_MAXLOG]; 11103831d35Sstevel static void iosram_print_log(int cnt); /* forward declaration */ 11203831d35Sstevel #endif /* IOSRAM_LOG */ 11303831d35Sstevel 11403831d35Sstevel 11503831d35Sstevel /* driver entry point fn definitions */ 11603831d35Sstevel static int iosram_open(dev_t *, int, int, cred_t *); 11703831d35Sstevel static int iosram_close(dev_t, int, int, cred_t *); 11803831d35Sstevel static int iosram_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 11903831d35Sstevel 12003831d35Sstevel /* configuration entry point fn definitions */ 12103831d35Sstevel static int iosram_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 12203831d35Sstevel static int iosram_attach(dev_info_t *, ddi_attach_cmd_t); 12303831d35Sstevel static int iosram_detach(dev_info_t *, ddi_detach_cmd_t); 12403831d35Sstevel 12503831d35Sstevel 12603831d35Sstevel /* forward declaractions */ 12703831d35Sstevel static iosram_chunk_t *iosram_find_chunk(uint32_t key); 12803831d35Sstevel static void iosram_set_master(struct iosramsoft *softp); 12903831d35Sstevel static int iosram_is_chosen(struct iosramsoft *softp); 13003831d35Sstevel static int iosram_tunnel_capable(struct iosramsoft *softp); 13103831d35Sstevel static int iosram_read_toc(struct iosramsoft *softp); 13203831d35Sstevel static void iosram_init_hashtab(void); 13303831d35Sstevel static void iosram_update_addrs(struct iosramsoft *softp); 13403831d35Sstevel 13503831d35Sstevel static int iosram_setup_map(struct iosramsoft *softp); 13603831d35Sstevel static void iosram_remove_map(struct iosramsoft *softp); 13703831d35Sstevel static int iosram_add_intr(iosramsoft_t *); 13803831d35Sstevel static int iosram_remove_intr(iosramsoft_t *); 13903831d35Sstevel 14003831d35Sstevel static void iosram_add_instance(struct iosramsoft *softp); 14103831d35Sstevel static void iosram_remove_instance(int instance); 14203831d35Sstevel static int iosram_switch_tunnel(iosramsoft_t *softp); 14303831d35Sstevel static void iosram_abort_tswitch(); 14403831d35Sstevel 14503831d35Sstevel #if defined(DEBUG) 14603831d35Sstevel /* forward declaractions for debugging */ 14703831d35Sstevel static int iosram_get_keys(iosram_toc_entry_t *buf, uint32_t *len); 14803831d35Sstevel static void iosram_print_cback(); 14903831d35Sstevel static void iosram_print_state(int); 15003831d35Sstevel static void iosram_print_flags(); 15103831d35Sstevel #endif 15203831d35Sstevel 15303831d35Sstevel 15403831d35Sstevel 15503831d35Sstevel /* 15603831d35Sstevel * cb_ops 15703831d35Sstevel */ 15803831d35Sstevel static struct cb_ops iosram_cb_ops = { 15903831d35Sstevel iosram_open, /* cb_open */ 16003831d35Sstevel iosram_close, /* cb_close */ 16103831d35Sstevel nodev, /* cb_strategy */ 16203831d35Sstevel nodev, /* cb_print */ 16303831d35Sstevel nodev, /* cb_dump */ 16403831d35Sstevel nodev, /* cb_read */ 16503831d35Sstevel nodev, /* cb_write */ 16603831d35Sstevel iosram_ioctl, /* cb_ioctl */ 16703831d35Sstevel nodev, /* cb_devmap */ 16803831d35Sstevel nodev, /* cb_mmap */ 16903831d35Sstevel nodev, /* cb_segmap */ 17003831d35Sstevel nochpoll, /* cb_chpoll */ 17103831d35Sstevel ddi_prop_op, /* cb_prop_op */ 17203831d35Sstevel NULL, /* cb_stream */ 17303831d35Sstevel (int)(D_NEW | D_MP | D_HOTPLUG) /* cb_flag */ 17403831d35Sstevel }; 17503831d35Sstevel 17603831d35Sstevel /* 17703831d35Sstevel * Declare ops vectors for auto configuration. 17803831d35Sstevel */ 17903831d35Sstevel struct dev_ops iosram_ops = { 18003831d35Sstevel DEVO_REV, /* devo_rev */ 18103831d35Sstevel 0, /* devo_refcnt */ 18203831d35Sstevel iosram_getinfo, /* devo_getinfo */ 18303831d35Sstevel nulldev, /* devo_identify */ 18403831d35Sstevel nulldev, /* devo_probe */ 18503831d35Sstevel iosram_attach, /* devo_attach */ 18603831d35Sstevel iosram_detach, /* devo_detach */ 18703831d35Sstevel nodev, /* devo_reset */ 18803831d35Sstevel &iosram_cb_ops, /* devo_cb_ops */ 18903831d35Sstevel (struct bus_ops *)NULL, /* devo_bus_ops */ 19019397407SSherry Moore nulldev, /* devo_power */ 19119397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 19203831d35Sstevel }; 19303831d35Sstevel 19403831d35Sstevel /* 19503831d35Sstevel * Loadable module support. 19603831d35Sstevel */ 19703831d35Sstevel extern struct mod_ops mod_driverops; 19803831d35Sstevel 19903831d35Sstevel static struct modldrv iosrammodldrv = { 20003831d35Sstevel &mod_driverops, /* type of module - driver */ 20119397407SSherry Moore "IOSRAM Leaf driver", 20203831d35Sstevel &iosram_ops, 20303831d35Sstevel }; 20403831d35Sstevel 20503831d35Sstevel static struct modlinkage iosrammodlinkage = { 20603831d35Sstevel MODREV_1, 20703831d35Sstevel &iosrammodldrv, 20803831d35Sstevel NULL 20903831d35Sstevel }; 21003831d35Sstevel 21103831d35Sstevel 21203831d35Sstevel int 21303831d35Sstevel _init(void) 21403831d35Sstevel { 21503831d35Sstevel int error; 21603831d35Sstevel int i; 21703831d35Sstevel 21803831d35Sstevel mutex_init(&iosram_mutex, NULL, MUTEX_DRIVER, (void *)NULL); 21903831d35Sstevel cv_init(&iosram_tswitch_wait, NULL, CV_DRIVER, NULL); 22003831d35Sstevel cv_init(&iosram_rw_wait, NULL, CV_DRIVER, NULL); 22103831d35Sstevel #if defined(IOSRAM_LOG) 22203831d35Sstevel mutex_init(&iosram_log_mutex, NULL, MUTEX_DRIVER, (void *)NULL); 22303831d35Sstevel #endif 22403831d35Sstevel 22503831d35Sstevel DPRINTF(1, ("_init:IOSRAM\n")); 22603831d35Sstevel 22703831d35Sstevel for (i = 0; i < IOSRAM_HASHSZ; i++) { 22803831d35Sstevel iosram_hashtab[i] = NULL; 22903831d35Sstevel } 23003831d35Sstevel 23103831d35Sstevel if ((error = ddi_soft_state_init(&iosramsoft_statep, 23203831d35Sstevel sizeof (struct iosramsoft), 1)) != 0) { 23303831d35Sstevel goto failed; 23403831d35Sstevel } 23503831d35Sstevel if ((error = mod_install(&iosrammodlinkage)) != 0) { 23603831d35Sstevel ddi_soft_state_fini(&iosramsoft_statep); 23703831d35Sstevel goto failed; 23803831d35Sstevel } 23903831d35Sstevel 24003831d35Sstevel IOSRAMLOG(0, "_init:IOSRAM ... error:%d statep:%p\n", 24103831d35Sstevel error, iosramsoft_statep, NULL, NULL); 24203831d35Sstevel 24303831d35Sstevel return (error); 24403831d35Sstevel 24503831d35Sstevel failed: 24603831d35Sstevel cv_destroy(&iosram_tswitch_wait); 24703831d35Sstevel cv_destroy(&iosram_rw_wait); 24803831d35Sstevel mutex_destroy(&iosram_mutex); 24903831d35Sstevel #if defined(IOSRAM_LOG) 25003831d35Sstevel mutex_destroy(&iosram_log_mutex); 25103831d35Sstevel #endif 25203831d35Sstevel IOSRAMLOG(0, "_init:IOSRAM ... error:%d statep:%p\n", 25303831d35Sstevel error, iosramsoft_statep, NULL, NULL); 25403831d35Sstevel 25503831d35Sstevel return (error); 25603831d35Sstevel } 25703831d35Sstevel 25803831d35Sstevel 25903831d35Sstevel int 26003831d35Sstevel _fini(void) 26103831d35Sstevel { 26203831d35Sstevel #ifndef DEBUG 26303831d35Sstevel return (EBUSY); 26403831d35Sstevel #else /* !DEBUG */ 26503831d35Sstevel int error; 26603831d35Sstevel 26703831d35Sstevel if ((error = mod_remove(&iosrammodlinkage)) == 0) { 26803831d35Sstevel ddi_soft_state_fini(&iosramsoft_statep); 26903831d35Sstevel 27003831d35Sstevel cv_destroy(&iosram_tswitch_wait); 27103831d35Sstevel cv_destroy(&iosram_rw_wait); 27203831d35Sstevel mutex_destroy(&iosram_mutex); 27303831d35Sstevel #if defined(IOSRAM_LOG) 27403831d35Sstevel mutex_destroy(&iosram_log_mutex); 27503831d35Sstevel #endif 27603831d35Sstevel } 27703831d35Sstevel DPRINTF(1, ("_fini:IOSRAM error:%d\n", error)); 27803831d35Sstevel 27903831d35Sstevel return (error); 28003831d35Sstevel #endif /* !DEBUG */ 28103831d35Sstevel } 28203831d35Sstevel 28303831d35Sstevel 28403831d35Sstevel int 28503831d35Sstevel _info(struct modinfo *modinfop) 28603831d35Sstevel { 28703831d35Sstevel return (mod_info(&iosrammodlinkage, modinfop)); 28803831d35Sstevel } 28903831d35Sstevel 29003831d35Sstevel 29103831d35Sstevel static int 29203831d35Sstevel iosram_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 29303831d35Sstevel { 29403831d35Sstevel int instance; 29503831d35Sstevel int propval; 29603831d35Sstevel int length; 29703831d35Sstevel char name[32]; 29803831d35Sstevel struct iosramsoft *softp; 29903831d35Sstevel 30003831d35Sstevel instance = ddi_get_instance(dip); 30103831d35Sstevel 302*07d06da5SSurya Prakki DPRINTF(1, ("iosram(%d): attach dip:%p\n", instance, (void *)dip)); 30303831d35Sstevel 30403831d35Sstevel IOSRAMLOG(1, "ATTACH: dip:%p instance %d ... start\n", 30503831d35Sstevel dip, instance, NULL, NULL); 30603831d35Sstevel switch (cmd) { 30703831d35Sstevel case DDI_ATTACH: 30803831d35Sstevel break; 30903831d35Sstevel case DDI_RESUME: 31003831d35Sstevel if (!(softp = ddi_get_soft_state(iosramsoft_statep, 31103831d35Sstevel instance))) { 31203831d35Sstevel return (DDI_FAILURE); 31303831d35Sstevel } 31403831d35Sstevel mutex_enter(&iosram_mutex); 31503831d35Sstevel mutex_enter(&softp->intr_mutex); 31603831d35Sstevel if (!softp->suspended) { 31703831d35Sstevel mutex_exit(&softp->intr_mutex); 31803831d35Sstevel mutex_exit(&iosram_mutex); 31903831d35Sstevel return (DDI_FAILURE); 32003831d35Sstevel } 32103831d35Sstevel softp->suspended = 0; 32203831d35Sstevel 32303831d35Sstevel /* 32403831d35Sstevel * enable SBBC interrupts if SBBC is mapped in 32503831d35Sstevel * restore the value saved during detach 32603831d35Sstevel */ 32703831d35Sstevel if (softp->sbbc_region) { 32803831d35Sstevel ddi_put32(softp->sbbc_handle, 32903831d35Sstevel &(softp->sbbc_region->int_enable.reg), 33003831d35Sstevel softp->int_enable_sav); 33103831d35Sstevel } 33203831d35Sstevel 33303831d35Sstevel /* 33403831d35Sstevel * Trigger soft interrupt handler to process any pending 33503831d35Sstevel * interrupts. 33603831d35Sstevel */ 33703831d35Sstevel if (softp->intr_pending && !softp->intr_busy && 33803831d35Sstevel (softp->softintr_id != NULL)) { 33903831d35Sstevel ddi_trigger_softintr(softp->softintr_id); 34003831d35Sstevel } 34103831d35Sstevel 34203831d35Sstevel mutex_exit(&softp->intr_mutex); 34303831d35Sstevel mutex_exit(&iosram_mutex); 34403831d35Sstevel 34503831d35Sstevel return (DDI_SUCCESS); 34603831d35Sstevel 34703831d35Sstevel default: 34803831d35Sstevel return (DDI_FAILURE); 34903831d35Sstevel } 35003831d35Sstevel 35103831d35Sstevel if (ddi_soft_state_zalloc(iosramsoft_statep, instance) != 0) { 35203831d35Sstevel return (DDI_FAILURE); 35303831d35Sstevel } 35403831d35Sstevel 35503831d35Sstevel if ((softp = ddi_get_soft_state(iosramsoft_statep, instance)) == NULL) { 35603831d35Sstevel return (DDI_FAILURE); 35703831d35Sstevel } 35803831d35Sstevel softp->dip = dip; 35903831d35Sstevel softp->instance = instance; 36003831d35Sstevel softp->sbbc_region = NULL; 36103831d35Sstevel 36203831d35Sstevel /* 36303831d35Sstevel * If this instance is not tunnel capable, we don't attach it. 36403831d35Sstevel */ 36503831d35Sstevel if (iosram_tunnel_capable(softp) == 0) { 36603831d35Sstevel DPRINTF(1, ("iosram(%d): not tunnel_capable\n", instance)); 36703831d35Sstevel IOSRAMLOG(1, "ATTACH(%d): not tunnel_capable\n", instance, NULL, 36803831d35Sstevel NULL, NULL); 36903831d35Sstevel goto attach_fail; 37003831d35Sstevel } 37103831d35Sstevel 37203831d35Sstevel /* 37303831d35Sstevel * Need to create an "interrupt-priorities" property to define the PIL 37403831d35Sstevel * to be used with the interrupt service routine. 37503831d35Sstevel */ 37603831d35Sstevel if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 37703831d35Sstevel "interrupt-priorities", &length) == DDI_PROP_NOT_FOUND) { 37803831d35Sstevel DPRINTF(1, ("iosram(%d): creating interrupt priority property", 37903831d35Sstevel instance)); 38003831d35Sstevel propval = IOSRAM_PIL; 38103831d35Sstevel if (ddi_prop_create(DDI_DEV_T_NONE, dip, 0, 38203831d35Sstevel "interrupt-priorities", (caddr_t)&propval, sizeof (propval)) 38303831d35Sstevel != DDI_PROP_SUCCESS) { 38403831d35Sstevel cmn_err(CE_WARN, 38503831d35Sstevel "iosram_attach: failed to create property"); 38603831d35Sstevel goto attach_fail; 38703831d35Sstevel } 38803831d35Sstevel } 38903831d35Sstevel 39003831d35Sstevel /* 39103831d35Sstevel * Get interrupts cookies and initialize per-instance mutexes 39203831d35Sstevel */ 39303831d35Sstevel if (ddi_get_iblock_cookie(softp->dip, 0, &softp->real_iblk) 39403831d35Sstevel != DDI_SUCCESS) { 39503831d35Sstevel IOSRAMLOG(1, "ATTACH(%d): cannot get soft intr cookie\n", 39603831d35Sstevel instance, NULL, NULL, NULL); 39703831d35Sstevel goto attach_fail; 39803831d35Sstevel } 39903831d35Sstevel mutex_init(&softp->intr_mutex, NULL, MUTEX_DRIVER, 40003831d35Sstevel (void *)softp->real_iblk); 40103831d35Sstevel 40203831d35Sstevel /* 40303831d35Sstevel * Add this instance to the iosram_instances list so that it can be used 40403831d35Sstevel * for tunnel in future. 40503831d35Sstevel */ 40603831d35Sstevel mutex_enter(&iosram_mutex); 40703831d35Sstevel softp->state = IOSRAM_STATE_INIT; 40803831d35Sstevel iosram_add_instance(softp); 40903831d35Sstevel 41003831d35Sstevel /* 41103831d35Sstevel * If this is the chosen IOSRAM and there is no master IOSRAM yet, then 41203831d35Sstevel * let's set this instance as the master. 41303831d35Sstevel */ 41403831d35Sstevel if (iosram_master == NULL && iosram_is_chosen(softp)) { 415*07d06da5SSurya Prakki (void) iosram_switch_tunnel(softp); 41603831d35Sstevel 41703831d35Sstevel /* 41803831d35Sstevel * XXX Do we need to panic if unable to setup master IOSRAM? 41903831d35Sstevel */ 42003831d35Sstevel if (iosram_master == NULL) { 42103831d35Sstevel cmn_err(CE_WARN, 42203831d35Sstevel "iosram(%d): can't setup master tunnel\n", 42303831d35Sstevel instance); 42403831d35Sstevel softp->state = 0; 42503831d35Sstevel iosram_remove_instance(softp->instance); 42603831d35Sstevel mutex_exit(&iosram_mutex); 42703831d35Sstevel mutex_destroy(&softp->intr_mutex); 42803831d35Sstevel goto attach_fail; 42903831d35Sstevel } 43003831d35Sstevel } 43103831d35Sstevel 43203831d35Sstevel mutex_exit(&iosram_mutex); 43303831d35Sstevel 43403831d35Sstevel /* 43503831d35Sstevel * Create minor node 43603831d35Sstevel */ 43703831d35Sstevel (void) sprintf(name, "iosram%d", instance); 43803831d35Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL, NULL) == 43903831d35Sstevel DDI_FAILURE) { 44003831d35Sstevel /* 44103831d35Sstevel * Minor node seems to be needed only for debugging purposes. 44203831d35Sstevel * Therefore, there is no need to fail this attach request. 44303831d35Sstevel * Simply print a message out. 44403831d35Sstevel */ 44503831d35Sstevel cmn_err(CE_NOTE, "!iosram(%d): can't create minor node\n", 44603831d35Sstevel instance); 44703831d35Sstevel } 44803831d35Sstevel ddi_report_dev(dip); 44903831d35Sstevel 45003831d35Sstevel DPRINTF(1, ("iosram_attach(%d): success.\n", instance)); 45103831d35Sstevel IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... success softp:%p\n", 45203831d35Sstevel dip, instance, softp, NULL); 45303831d35Sstevel 45403831d35Sstevel return (DDI_SUCCESS); 45503831d35Sstevel 45603831d35Sstevel attach_fail: 45703831d35Sstevel DPRINTF(1, ("iosram_attach(%d):failed.\n", instance)); 45803831d35Sstevel IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... failed.\n", 45903831d35Sstevel dip, instance, NULL, NULL); 46003831d35Sstevel 46103831d35Sstevel ddi_soft_state_free(iosramsoft_statep, instance); 46203831d35Sstevel return (DDI_FAILURE); 46303831d35Sstevel } 46403831d35Sstevel 46503831d35Sstevel 46603831d35Sstevel static int 46703831d35Sstevel iosram_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 46803831d35Sstevel { 46903831d35Sstevel int instance; 47003831d35Sstevel struct iosramsoft *softp; 47103831d35Sstevel 47203831d35Sstevel instance = ddi_get_instance(dip); 47303831d35Sstevel if (!(softp = ddi_get_soft_state(iosramsoft_statep, instance))) { 47403831d35Sstevel return (DDI_FAILURE); 47503831d35Sstevel } 47603831d35Sstevel 47703831d35Sstevel IOSRAMLOG(1, "DETACH: dip:%p instance %d softp:%p\n", 47803831d35Sstevel dip, instance, softp, NULL); 47903831d35Sstevel 48003831d35Sstevel switch (cmd) { 48103831d35Sstevel case DDI_DETACH: 48203831d35Sstevel break; 48303831d35Sstevel case DDI_SUSPEND: 48403831d35Sstevel mutex_enter(&iosram_mutex); 48503831d35Sstevel mutex_enter(&softp->intr_mutex); 48603831d35Sstevel if (softp->suspended) { 48703831d35Sstevel mutex_exit(&softp->intr_mutex); 48803831d35Sstevel mutex_exit(&iosram_mutex); 48903831d35Sstevel return (DDI_FAILURE); 49003831d35Sstevel } 49103831d35Sstevel softp->suspended = 1; 49203831d35Sstevel /* 49303831d35Sstevel * Disable SBBC interrupts if SBBC is mapped in 49403831d35Sstevel */ 49503831d35Sstevel if (softp->sbbc_region) { 49603831d35Sstevel /* save current interrupt enable register */ 49703831d35Sstevel softp->int_enable_sav = ddi_get32(softp->sbbc_handle, 49803831d35Sstevel &(softp->sbbc_region->int_enable.reg)); 49903831d35Sstevel ddi_put32(softp->sbbc_handle, 50003831d35Sstevel &(softp->sbbc_region->int_enable.reg), 0x0); 50103831d35Sstevel } 50203831d35Sstevel mutex_exit(&softp->intr_mutex); 50303831d35Sstevel mutex_exit(&iosram_mutex); 50403831d35Sstevel return (DDI_SUCCESS); 50503831d35Sstevel 50603831d35Sstevel default: 50703831d35Sstevel return (DDI_FAILURE); 50803831d35Sstevel } 50903831d35Sstevel 51003831d35Sstevel 51103831d35Sstevel /* 51203831d35Sstevel * Indicate that this instance is being detached so that this instance 51303831d35Sstevel * does not become a target for tunnel switch in future. 51403831d35Sstevel */ 51503831d35Sstevel mutex_enter(&iosram_mutex); 51603831d35Sstevel softp->state |= IOSRAM_STATE_DETACH; 51703831d35Sstevel 51803831d35Sstevel /* 51903831d35Sstevel * If this instance is currently the master or the target of the tunnel 52003831d35Sstevel * switch, then we need to wait and switch tunnel, if necessary. 52103831d35Sstevel */ 52203831d35Sstevel if (iosram_master == softp || (softp->state & IOSRAM_STATE_TSWITCH)) { 52303831d35Sstevel mutex_exit(&iosram_mutex); 524*07d06da5SSurya Prakki (void) iosram_switchfrom(instance); 52503831d35Sstevel mutex_enter(&iosram_mutex); 52603831d35Sstevel } 52703831d35Sstevel 52803831d35Sstevel /* 52903831d35Sstevel * If the tunnel switch is in progress and we are the master or target 53003831d35Sstevel * of tunnel relocation, then we can't detach this instance right now. 53103831d35Sstevel */ 53203831d35Sstevel if (softp->state & IOSRAM_STATE_TSWITCH) { 53303831d35Sstevel softp->state &= ~IOSRAM_STATE_DETACH; 53403831d35Sstevel mutex_exit(&iosram_mutex); 53503831d35Sstevel return (DDI_FAILURE); 53603831d35Sstevel } 53703831d35Sstevel 53803831d35Sstevel /* 53903831d35Sstevel * We can't allow master IOSRAM to be detached as we won't be able to 54003831d35Sstevel * communicate otherwise. 54103831d35Sstevel */ 54203831d35Sstevel if (iosram_master == softp) { 54303831d35Sstevel softp->state &= ~IOSRAM_STATE_DETACH; 54403831d35Sstevel mutex_exit(&iosram_mutex); 54503831d35Sstevel return (DDI_FAILURE); 54603831d35Sstevel } 54703831d35Sstevel 54803831d35Sstevel /* 54903831d35Sstevel * Now remove our instance from the iosram_instances list. 55003831d35Sstevel */ 55103831d35Sstevel iosram_remove_instance(instance); 55203831d35Sstevel mutex_exit(&iosram_mutex); 55303831d35Sstevel 55403831d35Sstevel /* 55503831d35Sstevel * Instances should only ever be mapped if they are the master and/or 55603831d35Sstevel * participating in a tunnel switch. Neither should be the case here. 55703831d35Sstevel */ 55803831d35Sstevel ASSERT((softp->state & IOSRAM_STATE_MAPPED) == 0); 55903831d35Sstevel 56003831d35Sstevel /* 56103831d35Sstevel * Destroy per-instance mutexes 56203831d35Sstevel */ 56303831d35Sstevel mutex_destroy(&softp->intr_mutex); 56403831d35Sstevel 56503831d35Sstevel ddi_remove_minor_node(dip, NULL); 56603831d35Sstevel 56703831d35Sstevel /* 56803831d35Sstevel * Finally remove our soft state structure 56903831d35Sstevel */ 57003831d35Sstevel ddi_soft_state_free(iosramsoft_statep, instance); 57103831d35Sstevel 57203831d35Sstevel return (DDI_SUCCESS); 57303831d35Sstevel } 57403831d35Sstevel 57503831d35Sstevel 57603831d35Sstevel /* ARGSUSED0 */ 57703831d35Sstevel static int 57803831d35Sstevel iosram_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 57903831d35Sstevel void **result) 58003831d35Sstevel { 58103831d35Sstevel dev_t dev = (dev_t)arg; 58203831d35Sstevel struct iosramsoft *softp; 58303831d35Sstevel int instance, ret; 58403831d35Sstevel 58503831d35Sstevel instance = getminor(dev); 58603831d35Sstevel 58703831d35Sstevel IOSRAMLOG(2, "GETINFO: dip:%x instance %d dev:%x infocmd:%x\n", 58803831d35Sstevel dip, instance, dev, infocmd); 58903831d35Sstevel 59003831d35Sstevel switch (infocmd) { 59103831d35Sstevel case DDI_INFO_DEVT2DEVINFO: 59203831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance); 59303831d35Sstevel if (softp == NULL) { 59403831d35Sstevel *result = NULL; 59503831d35Sstevel ret = DDI_FAILURE; 59603831d35Sstevel } else { 59703831d35Sstevel *result = softp->dip; 59803831d35Sstevel ret = DDI_SUCCESS; 59903831d35Sstevel } 60003831d35Sstevel break; 60103831d35Sstevel case DDI_INFO_DEVT2INSTANCE: 60203831d35Sstevel *result = (void *)(uintptr_t)instance; 60303831d35Sstevel ret = DDI_SUCCESS; 60403831d35Sstevel break; 60503831d35Sstevel default: 60603831d35Sstevel ret = DDI_FAILURE; 60703831d35Sstevel break; 60803831d35Sstevel } 60903831d35Sstevel 61003831d35Sstevel return (ret); 61103831d35Sstevel } 61203831d35Sstevel 61303831d35Sstevel 61403831d35Sstevel /*ARGSUSED1*/ 61503831d35Sstevel static int 61603831d35Sstevel iosram_open(dev_t *dev, int flag, int otype, cred_t *credp) 61703831d35Sstevel { 61803831d35Sstevel struct iosramsoft *softp; 61903831d35Sstevel int instance; 62003831d35Sstevel 62103831d35Sstevel instance = getminor(*dev); 62203831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance); 62303831d35Sstevel 62403831d35Sstevel if (softp == NULL) { 62503831d35Sstevel return (ENXIO); 62603831d35Sstevel } 62703831d35Sstevel 62803831d35Sstevel IOSRAMLOG(1, "OPEN: dev:%p otype:%x ... instance:%d softp:%p\n", 62903831d35Sstevel *dev, otype, softp->instance, softp); 63003831d35Sstevel 63103831d35Sstevel return (0); 63203831d35Sstevel } 63303831d35Sstevel 63403831d35Sstevel 63503831d35Sstevel /*ARGSUSED1*/ 63603831d35Sstevel static int 63703831d35Sstevel iosram_close(dev_t dev, int flag, int otype, cred_t *credp) 63803831d35Sstevel { 63903831d35Sstevel struct iosramsoft *softp; 64003831d35Sstevel int instance; 64103831d35Sstevel 64203831d35Sstevel instance = getminor(dev); 64303831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance); 64403831d35Sstevel if (softp == NULL) { 64503831d35Sstevel return (ENXIO); 64603831d35Sstevel } 64703831d35Sstevel 64803831d35Sstevel IOSRAMLOG(1, "CLOSE: dev:%p otype:%x ... instance:%d softp:%p\n", 64903831d35Sstevel dev, otype, softp->instance, softp); 65003831d35Sstevel 65103831d35Sstevel return (0); 65203831d35Sstevel } 65303831d35Sstevel 65403831d35Sstevel 65503831d35Sstevel int 65603831d35Sstevel iosram_rd(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr) 65703831d35Sstevel { 65803831d35Sstevel iosram_chunk_t *chunkp; 65903831d35Sstevel uint32_t chunk_len; 66003831d35Sstevel uint8_t *iosramp; 66103831d35Sstevel ddi_acc_handle_t handle; 66203831d35Sstevel int boff; 66303831d35Sstevel union { 66403831d35Sstevel uchar_t cbuf[UINT32SZ]; 66503831d35Sstevel uint32_t data; 66603831d35Sstevel } word; 66703831d35Sstevel 66803831d35Sstevel int error = 0; 66903831d35Sstevel uint8_t *buf = (uint8_t *)dptr; 67003831d35Sstevel 67103831d35Sstevel /* 67203831d35Sstevel * We try to read from the IOSRAM using double word or word access 67303831d35Sstevel * provided both "off" and "buf" are (or can be) double word or word 67403831d35Sstevel * aligned. Othewise, we try to align the "off" to a word boundary and 67503831d35Sstevel * then try to read data from the IOSRAM using word access, but store it 67603831d35Sstevel * into buf buffer using byte access. 67703831d35Sstevel * 67803831d35Sstevel * If the leading/trailing portion of the IOSRAM data is not word 67903831d35Sstevel * aligned, it will always be copied using byte access. 68003831d35Sstevel */ 68103831d35Sstevel IOSRAMLOG(1, "RD: key: 0x%x off:%x len:%x buf:%p\n", 68203831d35Sstevel key, off, len, buf); 68303831d35Sstevel 68403831d35Sstevel /* 68503831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, make 68603831d35Sstevel * sure the requested read is within the chunk's bounds and no tunnel 68703831d35Sstevel * switch is active. 68803831d35Sstevel */ 68903831d35Sstevel mutex_enter(&iosram_mutex); 69003831d35Sstevel chunkp = iosram_find_chunk(key); 69103831d35Sstevel chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0; 69203831d35Sstevel 69303831d35Sstevel if (iosram_master == NULL) { 69403831d35Sstevel error = EIO; 69503831d35Sstevel } else if (chunkp == NULL) { 69603831d35Sstevel error = EINVAL; 69703831d35Sstevel } else if ((off >= chunk_len) || (len > chunk_len) || 69803831d35Sstevel ((off + len) > chunk_len)) { 69903831d35Sstevel error = EMSGSIZE; 70003831d35Sstevel } else if (iosram_tswitch_active) { 70103831d35Sstevel error = EAGAIN; 70203831d35Sstevel } 70303831d35Sstevel 70403831d35Sstevel if (error) { 70503831d35Sstevel mutex_exit(&iosram_mutex); 70603831d35Sstevel return (error); 70703831d35Sstevel } 70803831d35Sstevel 70903831d35Sstevel /* 71003831d35Sstevel * Bump reference count to indicate #thread accessing IOSRAM and release 71103831d35Sstevel * the lock. 71203831d35Sstevel */ 71303831d35Sstevel iosram_rw_active++; 71403831d35Sstevel #if defined(DEBUG) 71503831d35Sstevel if (iosram_rw_active > iosram_rw_active_max) { 71603831d35Sstevel iosram_rw_active_max = iosram_rw_active; 71703831d35Sstevel } 71803831d35Sstevel #endif 71903831d35Sstevel mutex_exit(&iosram_mutex); 72003831d35Sstevel 72103831d35Sstevel IOSRAM_STAT(read); 72203831d35Sstevel IOSRAM_STAT_ADD(bread, len); 72303831d35Sstevel 72403831d35Sstevel /* Get starting address and map handle */ 72503831d35Sstevel iosramp = chunkp->basep + off; 72603831d35Sstevel handle = iosram_handle; 72703831d35Sstevel 72803831d35Sstevel /* 72903831d35Sstevel * Align the off to word boundary and then try reading/writing data 73003831d35Sstevel * using double word or word access. 73103831d35Sstevel */ 73203831d35Sstevel if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) { 73303831d35Sstevel int cnt = UINT32SZ - boff; 73403831d35Sstevel 73503831d35Sstevel if (cnt > len) { 73603831d35Sstevel cnt = len; 73703831d35Sstevel } 73803831d35Sstevel IOSRAMLOG(2, 73903831d35Sstevel "RD: align rep_get8(buf:%p sramp:%p cnt:%x) len:%x\n", 74003831d35Sstevel buf, iosramp, cnt, len); 74103831d35Sstevel ddi_rep_get8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR); 74203831d35Sstevel buf += cnt; 74303831d35Sstevel iosramp += cnt; 74403831d35Sstevel len -= cnt; 74503831d35Sstevel } 74603831d35Sstevel 74703831d35Sstevel if ((len >= UINT64SZ) && 74803831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) { 74903831d35Sstevel /* 75003831d35Sstevel * Both source and destination are double word aligned 75103831d35Sstevel */ 75203831d35Sstevel int cnt = len/UINT64SZ; 75303831d35Sstevel 75403831d35Sstevel IOSRAMLOG(2, 75503831d35Sstevel "RD: rep_get64(buf:%p sramp:%p cnt:%x) len:%x\n", 75603831d35Sstevel buf, iosramp, cnt, len); 75703831d35Sstevel ddi_rep_get64(handle, (uint64_t *)buf, (uint64_t *)iosramp, 75803831d35Sstevel cnt, DDI_DEV_AUTOINCR); 75903831d35Sstevel iosramp += cnt * UINT64SZ; 76003831d35Sstevel buf += cnt * UINT64SZ; 76103831d35Sstevel len -= cnt * UINT64SZ; 76203831d35Sstevel 76303831d35Sstevel /* 76403831d35Sstevel * read remaining data using word and byte access 76503831d35Sstevel */ 76603831d35Sstevel if (len >= UINT32SZ) { 76703831d35Sstevel IOSRAMLOG(2, 76803831d35Sstevel "RD: get32(buf:%p sramp:%p) len:%x\n", 76903831d35Sstevel buf, iosramp, len, NULL); 77003831d35Sstevel *(uint32_t *)buf = ddi_get32(handle, 77103831d35Sstevel (uint32_t *)iosramp); 77203831d35Sstevel iosramp += UINT32SZ; 77303831d35Sstevel buf += UINT32SZ; 77403831d35Sstevel len -= UINT32SZ; 77503831d35Sstevel } 77603831d35Sstevel 77703831d35Sstevel if (len != 0) { 77819397407SSherry Moore ddi_rep_get8(handle, buf, iosramp, len, 77919397407SSherry Moore DDI_DEV_AUTOINCR); 78003831d35Sstevel } 78103831d35Sstevel } else if ((len >= UINT32SZ) && 78203831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) { 78303831d35Sstevel /* 78403831d35Sstevel * Both source and destination are word aligned 78503831d35Sstevel */ 78603831d35Sstevel int cnt = len/UINT32SZ; 78703831d35Sstevel 78803831d35Sstevel IOSRAMLOG(2, 78903831d35Sstevel "RD: rep_get32(buf:%p sramp:%p cnt:%x) len:%x\n", 79003831d35Sstevel buf, iosramp, cnt, len); 79103831d35Sstevel ddi_rep_get32(handle, (uint32_t *)buf, (uint32_t *)iosramp, 79203831d35Sstevel cnt, DDI_DEV_AUTOINCR); 79303831d35Sstevel iosramp += cnt * UINT32SZ; 79403831d35Sstevel buf += cnt * UINT32SZ; 79503831d35Sstevel len -= cnt * UINT32SZ; 79603831d35Sstevel 79703831d35Sstevel /* 79803831d35Sstevel * copy the remainder using byte access 79903831d35Sstevel */ 80003831d35Sstevel if (len != 0) { 80119397407SSherry Moore ddi_rep_get8(handle, buf, iosramp, len, 80219397407SSherry Moore DDI_DEV_AUTOINCR); 80303831d35Sstevel } 80403831d35Sstevel } else if (len != 0) { 80503831d35Sstevel /* 80603831d35Sstevel * We know that the "off" (i.e. iosramp) is at least word 80703831d35Sstevel * aligned. We need to read IOSRAM word at a time and copy it 80803831d35Sstevel * byte at a time. 80903831d35Sstevel */ 81003831d35Sstevel ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0); 81103831d35Sstevel 81203831d35Sstevel IOSRAMLOG(2, 81303831d35Sstevel "RD: unaligned get32(buf:%p sramp:%p) len:%x\n", 81403831d35Sstevel buf, iosramp, len, NULL); 81503831d35Sstevel for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) { 81603831d35Sstevel word.data = ddi_get32(handle, (uint32_t *)iosramp); 81703831d35Sstevel *buf++ = word.cbuf[0]; 81803831d35Sstevel *buf++ = word.cbuf[1]; 81903831d35Sstevel *buf++ = word.cbuf[2]; 82003831d35Sstevel *buf++ = word.cbuf[3]; 82103831d35Sstevel } 82203831d35Sstevel 82303831d35Sstevel /* 82403831d35Sstevel * copy the remaining data using byte access 82503831d35Sstevel */ 82603831d35Sstevel if (len != 0) { 82703831d35Sstevel ddi_rep_get8(handle, buf, iosramp, len, 82803831d35Sstevel DDI_DEV_AUTOINCR); 82903831d35Sstevel } 83003831d35Sstevel } 83103831d35Sstevel 83203831d35Sstevel /* 83303831d35Sstevel * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and any 83403831d35Sstevel * threads are waiting for r/w activity to complete, wake them up. 83503831d35Sstevel */ 83603831d35Sstevel mutex_enter(&iosram_mutex); 83703831d35Sstevel ASSERT(iosram_rw_active > 0); 83803831d35Sstevel 83903831d35Sstevel if ((--iosram_rw_active == 0) && iosram_rw_wakeup) { 84003831d35Sstevel iosram_rw_wakeup = 0; 84103831d35Sstevel cv_broadcast(&iosram_rw_wait); 84203831d35Sstevel } 84303831d35Sstevel mutex_exit(&iosram_mutex); 84403831d35Sstevel 84503831d35Sstevel return (error); 84603831d35Sstevel } 84703831d35Sstevel 84803831d35Sstevel 84903831d35Sstevel /* 85003831d35Sstevel * _iosram_write(key, off, len, dptr, force) 85103831d35Sstevel * Internal common routine to write to the IOSRAM. 85203831d35Sstevel */ 85303831d35Sstevel static int 85403831d35Sstevel _iosram_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr, int force) 85503831d35Sstevel { 85603831d35Sstevel iosram_chunk_t *chunkp; 85703831d35Sstevel uint32_t chunk_len; 85803831d35Sstevel uint8_t *iosramp; 85903831d35Sstevel ddi_acc_handle_t handle; 86003831d35Sstevel int boff; 86103831d35Sstevel union { 86203831d35Sstevel uint8_t cbuf[UINT32SZ]; 86303831d35Sstevel uint32_t data; 86403831d35Sstevel } word; 86503831d35Sstevel 86603831d35Sstevel int error = 0; 86703831d35Sstevel uint8_t *buf = (uint8_t *)dptr; 86803831d35Sstevel 86903831d35Sstevel /* 87003831d35Sstevel * We try to write to the IOSRAM using double word or word access 87103831d35Sstevel * provided both "off" and "buf" are (or can be) double word or word 87203831d35Sstevel * aligned. Othewise, we try to align the "off" to a word boundary and 87303831d35Sstevel * then try to write data to the IOSRAM using word access, but read data 87403831d35Sstevel * from the buf buffer using byte access. 87503831d35Sstevel * 87603831d35Sstevel * If the leading/trailing portion of the IOSRAM data is not word 87703831d35Sstevel * aligned, it will always be written using byte access. 87803831d35Sstevel */ 87903831d35Sstevel IOSRAMLOG(1, "WR: key: 0x%x off:%x len:%x buf:%p\n", 88003831d35Sstevel key, off, len, buf); 88103831d35Sstevel 88203831d35Sstevel /* 88303831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, make 88403831d35Sstevel * sure the requested write is within the chunk's bounds and no tunnel 88503831d35Sstevel * switch is active. 88603831d35Sstevel */ 88703831d35Sstevel mutex_enter(&iosram_mutex); 88803831d35Sstevel chunkp = iosram_find_chunk(key); 88903831d35Sstevel chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0; 89003831d35Sstevel 89103831d35Sstevel if (iosram_master == NULL) { 89203831d35Sstevel error = EIO; 89303831d35Sstevel } else if (chunkp == NULL) { 89403831d35Sstevel error = EINVAL; 89503831d35Sstevel } else if ((off >= chunk_len) || (len > chunk_len) || 89603831d35Sstevel ((off+len) > chunk_len)) { 89703831d35Sstevel error = EMSGSIZE; 89803831d35Sstevel } else if (iosram_tswitch_active && !force) { 89903831d35Sstevel error = EAGAIN; 90003831d35Sstevel } 90103831d35Sstevel 90203831d35Sstevel if (error) { 90303831d35Sstevel mutex_exit(&iosram_mutex); 90403831d35Sstevel return (error); 90503831d35Sstevel } 90603831d35Sstevel 90703831d35Sstevel /* 90803831d35Sstevel * If this is a forced write and there's a tunnel switch in progress, 90903831d35Sstevel * abort the switch. 91003831d35Sstevel */ 91103831d35Sstevel if (iosram_tswitch_active && force) { 91203831d35Sstevel cmn_err(CE_NOTE, "!iosram: Aborting tswitch on force_write"); 91303831d35Sstevel iosram_abort_tswitch(); 91403831d35Sstevel } 91503831d35Sstevel 91603831d35Sstevel /* 91703831d35Sstevel * Bump reference count to indicate #thread accessing IOSRAM 91803831d35Sstevel * and release the lock. 91903831d35Sstevel */ 92003831d35Sstevel iosram_rw_active++; 92103831d35Sstevel #if defined(DEBUG) 92203831d35Sstevel if (iosram_rw_active > iosram_rw_active_max) { 92303831d35Sstevel iosram_rw_active_max = iosram_rw_active; 92403831d35Sstevel } 92503831d35Sstevel #endif 92603831d35Sstevel mutex_exit(&iosram_mutex); 92703831d35Sstevel 92803831d35Sstevel 92903831d35Sstevel IOSRAM_STAT(write); 93003831d35Sstevel IOSRAM_STAT_ADD(bwrite, len); 93103831d35Sstevel 93203831d35Sstevel /* Get starting address and map handle */ 93303831d35Sstevel iosramp = chunkp->basep + off; 93403831d35Sstevel handle = iosram_handle; 93503831d35Sstevel 93603831d35Sstevel /* 93703831d35Sstevel * Align the off to word boundary and then try reading/writing 93803831d35Sstevel * data using double word or word access. 93903831d35Sstevel */ 94003831d35Sstevel if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) { 94103831d35Sstevel int cnt = UINT32SZ - boff; 94203831d35Sstevel 94303831d35Sstevel if (cnt > len) { 94403831d35Sstevel cnt = len; 94503831d35Sstevel } 94603831d35Sstevel IOSRAMLOG(2, 94703831d35Sstevel "WR: align rep_put8(buf:%p sramp:%p cnt:%x) len:%x\n", 94803831d35Sstevel buf, iosramp, cnt, len); 94903831d35Sstevel ddi_rep_put8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR); 95003831d35Sstevel buf += cnt; 95103831d35Sstevel iosramp += cnt; 95203831d35Sstevel len -= cnt; 95303831d35Sstevel } 95403831d35Sstevel 95503831d35Sstevel if ((len >= UINT64SZ) && 95603831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) { 95703831d35Sstevel /* 95803831d35Sstevel * Both source and destination are double word aligned 95903831d35Sstevel */ 96003831d35Sstevel int cnt = len/UINT64SZ; 96103831d35Sstevel 96203831d35Sstevel IOSRAMLOG(2, 96303831d35Sstevel "WR: rep_put64(buf:%p sramp:%p cnt:%x) len:%x\n", 96403831d35Sstevel buf, iosramp, cnt, len); 96503831d35Sstevel ddi_rep_put64(handle, (uint64_t *)buf, (uint64_t *)iosramp, 96603831d35Sstevel cnt, DDI_DEV_AUTOINCR); 96703831d35Sstevel iosramp += cnt * UINT64SZ; 96803831d35Sstevel buf += cnt * UINT64SZ; 96903831d35Sstevel len -= cnt * UINT64SZ; 97003831d35Sstevel 97103831d35Sstevel /* 97203831d35Sstevel * Copy the remaining data using word & byte access 97303831d35Sstevel */ 97403831d35Sstevel if (len >= UINT32SZ) { 97503831d35Sstevel IOSRAMLOG(2, 97603831d35Sstevel "WR: put32(buf:%p sramp:%p) len:%x\n", buf, iosramp, 97703831d35Sstevel len, NULL); 97803831d35Sstevel ddi_put32(handle, (uint32_t *)iosramp, 97903831d35Sstevel *(uint32_t *)buf); 98003831d35Sstevel iosramp += UINT32SZ; 98103831d35Sstevel buf += UINT32SZ; 98203831d35Sstevel len -= UINT32SZ; 98303831d35Sstevel } 98403831d35Sstevel 98503831d35Sstevel if (len != 0) { 98603831d35Sstevel ddi_rep_put8(handle, buf, iosramp, len, 98703831d35Sstevel DDI_DEV_AUTOINCR); 98803831d35Sstevel } 98903831d35Sstevel } else if ((len >= UINT32SZ) && 99003831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) { 99103831d35Sstevel /* 99203831d35Sstevel * Both source and destination are word aligned 99303831d35Sstevel */ 99403831d35Sstevel int cnt = len/UINT32SZ; 99503831d35Sstevel 99603831d35Sstevel IOSRAMLOG(2, 99703831d35Sstevel "WR: rep_put32(buf:%p sramp:%p cnt:%x) len:%x\n", 99803831d35Sstevel buf, iosramp, cnt, len); 99903831d35Sstevel ddi_rep_put32(handle, (uint32_t *)buf, (uint32_t *)iosramp, 100003831d35Sstevel cnt, DDI_DEV_AUTOINCR); 100103831d35Sstevel iosramp += cnt * UINT32SZ; 100203831d35Sstevel buf += cnt * UINT32SZ; 100303831d35Sstevel len -= cnt * UINT32SZ; 100403831d35Sstevel 100503831d35Sstevel /* 100603831d35Sstevel * copy the remainder using byte access 100703831d35Sstevel */ 100803831d35Sstevel if (len != 0) { 100903831d35Sstevel ddi_rep_put8(handle, buf, iosramp, len, 101003831d35Sstevel DDI_DEV_AUTOINCR); 101103831d35Sstevel } 101203831d35Sstevel } else if (len != 0) { 101303831d35Sstevel /* 101403831d35Sstevel * We know that the "off" is at least word aligned. We 101503831d35Sstevel * need to read data from buf buffer byte at a time, and 101603831d35Sstevel * write it to the IOSRAM word at a time. 101703831d35Sstevel */ 101803831d35Sstevel 101903831d35Sstevel ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0); 102003831d35Sstevel 102103831d35Sstevel IOSRAMLOG(2, 102203831d35Sstevel "WR: unaligned put32(buf:%p sramp:%p) len:%x\n", 102303831d35Sstevel buf, iosramp, len, NULL); 102403831d35Sstevel for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) { 102503831d35Sstevel word.cbuf[0] = *buf++; 102603831d35Sstevel word.cbuf[1] = *buf++; 102703831d35Sstevel word.cbuf[2] = *buf++; 102803831d35Sstevel word.cbuf[3] = *buf++; 102903831d35Sstevel ddi_put32(handle, (uint32_t *)iosramp, word.data); 103003831d35Sstevel } 103103831d35Sstevel 103203831d35Sstevel /* 103303831d35Sstevel * copy the remaining data using byte access 103403831d35Sstevel */ 103503831d35Sstevel if (len != 0) { 103603831d35Sstevel ddi_rep_put8(handle, buf, iosramp, 103703831d35Sstevel len, DDI_DEV_AUTOINCR); 103803831d35Sstevel } 103903831d35Sstevel } 104003831d35Sstevel 104103831d35Sstevel /* 104203831d35Sstevel * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and 104303831d35Sstevel * any threads are waiting for r/w activity to complete, wake them up. 104403831d35Sstevel */ 104503831d35Sstevel mutex_enter(&iosram_mutex); 104603831d35Sstevel ASSERT(iosram_rw_active > 0); 104703831d35Sstevel 104803831d35Sstevel if ((--iosram_rw_active == 0) && iosram_rw_wakeup) { 104903831d35Sstevel iosram_rw_wakeup = 0; 105003831d35Sstevel cv_broadcast(&iosram_rw_wait); 105103831d35Sstevel } 105203831d35Sstevel mutex_exit(&iosram_mutex); 105303831d35Sstevel 105403831d35Sstevel return (error); 105503831d35Sstevel } 105603831d35Sstevel 105703831d35Sstevel 105803831d35Sstevel int 105903831d35Sstevel iosram_force_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr) 106003831d35Sstevel { 106103831d35Sstevel return (_iosram_write(key, off, len, dptr, 1 /* force */)); 106203831d35Sstevel } 106303831d35Sstevel 106403831d35Sstevel 106503831d35Sstevel int 106603831d35Sstevel iosram_wr(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr) 106703831d35Sstevel { 106803831d35Sstevel return (_iosram_write(key, off, len, dptr, 0)); 106903831d35Sstevel } 107003831d35Sstevel 107103831d35Sstevel 107203831d35Sstevel /* 107303831d35Sstevel * iosram_register(key, handler, arg) 107403831d35Sstevel * Register a handler and an arg for the specified chunk. This handler 107503831d35Sstevel * will be invoked when an interrupt is received from the other side and 107603831d35Sstevel * the int_pending flag for the corresponding key is marked 107703831d35Sstevel * IOSRAM_INT_TO_DOM. 107803831d35Sstevel */ 107903831d35Sstevel /* ARGSUSED */ 108003831d35Sstevel int 108103831d35Sstevel iosram_register(uint32_t key, void (*handler)(), void *arg) 108203831d35Sstevel { 108303831d35Sstevel struct iosram_chunk *chunkp; 108403831d35Sstevel int error = 0; 108503831d35Sstevel 108603831d35Sstevel /* 108703831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, and no 108803831d35Sstevel * other callback is registered, proceed with the registration. 108903831d35Sstevel */ 109003831d35Sstevel mutex_enter(&iosram_mutex); 109103831d35Sstevel chunkp = iosram_find_chunk(key); 109203831d35Sstevel 109303831d35Sstevel if (iosram_master == NULL) { 109403831d35Sstevel error = EIO; 109503831d35Sstevel } else if (chunkp == NULL) { 109603831d35Sstevel error = EINVAL; 109703831d35Sstevel } else if (chunkp->cback.handler != NULL) { 109803831d35Sstevel error = EBUSY; 109903831d35Sstevel } else { 110003831d35Sstevel chunkp->cback.busy = 0; 110103831d35Sstevel chunkp->cback.unregister = 0; 110203831d35Sstevel chunkp->cback.handler = handler; 110303831d35Sstevel chunkp->cback.arg = arg; 110403831d35Sstevel } 110503831d35Sstevel mutex_exit(&iosram_mutex); 110603831d35Sstevel 110703831d35Sstevel IOSRAMLOG(1, "REG: key: 0x%x hdlr:%p arg:%p error:%d\n", 110803831d35Sstevel key, handler, arg, error); 110903831d35Sstevel 111003831d35Sstevel return (error); 111103831d35Sstevel } 111203831d35Sstevel 111303831d35Sstevel 111403831d35Sstevel /* 111503831d35Sstevel * iosram_unregister() 111603831d35Sstevel * Unregister handler associated with the specified chunk. 111703831d35Sstevel */ 111803831d35Sstevel int 111903831d35Sstevel iosram_unregister(uint32_t key) 112003831d35Sstevel { 112103831d35Sstevel struct iosram_chunk *chunkp; 112203831d35Sstevel int error = 0; 112303831d35Sstevel 112403831d35Sstevel /* 112503831d35Sstevel * Acquire lock and look for the requested chunk. If it exists and has 112603831d35Sstevel * a callback registered, unregister it. 112703831d35Sstevel */ 112803831d35Sstevel mutex_enter(&iosram_mutex); 112903831d35Sstevel chunkp = iosram_find_chunk(key); 113003831d35Sstevel 113103831d35Sstevel if (iosram_master == NULL) { 113203831d35Sstevel error = EIO; 113303831d35Sstevel } else if (chunkp == NULL) { 113403831d35Sstevel error = EINVAL; 113503831d35Sstevel } else if (chunkp->cback.busy) { 113603831d35Sstevel /* 113703831d35Sstevel * If the handler is already busy (being invoked), then we flag 113803831d35Sstevel * it so it will be unregistered after the invocation completes. 113903831d35Sstevel */ 114003831d35Sstevel DPRINTF(1, ("IOSRAM(%d): unregister: delaying unreg k:0x%08x\n", 114103831d35Sstevel iosram_master->instance, key)); 114203831d35Sstevel chunkp->cback.unregister = 1; 114303831d35Sstevel } else if (chunkp->cback.handler != NULL) { 114403831d35Sstevel chunkp->cback.handler = NULL; 114503831d35Sstevel chunkp->cback.arg = NULL; 114603831d35Sstevel } 114703831d35Sstevel mutex_exit(&iosram_mutex); 114803831d35Sstevel 114903831d35Sstevel IOSRAMLOG(1, "UNREG: key:%x error:%d\n", key, error, NULL, NULL); 115003831d35Sstevel return (error); 115103831d35Sstevel } 115203831d35Sstevel 115303831d35Sstevel 115403831d35Sstevel /* 115503831d35Sstevel * iosram_get_flag(): 115603831d35Sstevel * Get data_valid and/or int_pending flags associated with the 115703831d35Sstevel * specified key. 115803831d35Sstevel */ 115903831d35Sstevel int 116003831d35Sstevel iosram_get_flag(uint32_t key, uint8_t *data_valid, uint8_t *int_pending) 116103831d35Sstevel { 116203831d35Sstevel iosram_chunk_t *chunkp; 116303831d35Sstevel iosram_flags_t flags; 116403831d35Sstevel int error = 0; 116503831d35Sstevel 116603831d35Sstevel /* 116703831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, and no 116803831d35Sstevel * tunnel switch is in progress, read the chunk's flags. 116903831d35Sstevel */ 117003831d35Sstevel mutex_enter(&iosram_mutex); 117103831d35Sstevel chunkp = iosram_find_chunk(key); 117203831d35Sstevel 117303831d35Sstevel if (iosram_master == NULL) { 117403831d35Sstevel error = EIO; 117503831d35Sstevel } else if (chunkp == NULL) { 117603831d35Sstevel error = EINVAL; 117703831d35Sstevel } else if (iosram_tswitch_active) { 117803831d35Sstevel error = EAGAIN; 117903831d35Sstevel } else { 118003831d35Sstevel IOSRAM_STAT(getflag); 118103831d35Sstevel 118203831d35Sstevel /* 118303831d35Sstevel * Read the flags 118403831d35Sstevel */ 118503831d35Sstevel ddi_rep_get8(iosram_handle, (uint8_t *)&flags, 118603831d35Sstevel (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t), 118703831d35Sstevel DDI_DEV_AUTOINCR); 118803831d35Sstevel 118903831d35Sstevel /* 119003831d35Sstevel * Get each flag value that the caller is interested in. 119103831d35Sstevel */ 119203831d35Sstevel if (data_valid != NULL) { 119303831d35Sstevel *data_valid = flags.data_valid; 119403831d35Sstevel } 119503831d35Sstevel 119603831d35Sstevel if (int_pending != NULL) { 119703831d35Sstevel *int_pending = flags.int_pending; 119803831d35Sstevel } 119903831d35Sstevel } 120003831d35Sstevel mutex_exit(&iosram_mutex); 120103831d35Sstevel 120203831d35Sstevel IOSRAMLOG(1, "GetFlag key:%x data_valid:%x int_pending:%x error:%d\n", 120303831d35Sstevel key, flags.data_valid, flags.int_pending, error); 120403831d35Sstevel return (error); 120503831d35Sstevel } 120603831d35Sstevel 120703831d35Sstevel 120803831d35Sstevel /* 120903831d35Sstevel * iosram_set_flag(): 121003831d35Sstevel * Set data_valid and int_pending flags associated with the specified key. 121103831d35Sstevel */ 121203831d35Sstevel int 121303831d35Sstevel iosram_set_flag(uint32_t key, uint8_t data_valid, uint8_t int_pending) 121403831d35Sstevel { 121503831d35Sstevel iosram_chunk_t *chunkp; 121603831d35Sstevel iosram_flags_t flags; 121703831d35Sstevel int error = 0; 121803831d35Sstevel 121903831d35Sstevel /* 122003831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, and no 122103831d35Sstevel * tunnel switch is in progress, write the chunk's flags. 122203831d35Sstevel */ 122303831d35Sstevel mutex_enter(&iosram_mutex); 122403831d35Sstevel chunkp = iosram_find_chunk(key); 122503831d35Sstevel 122603831d35Sstevel if (iosram_master == NULL) { 122703831d35Sstevel error = EIO; 122803831d35Sstevel } else if ((chunkp == NULL) || 122903831d35Sstevel ((data_valid != IOSRAM_DATA_INVALID) && 123003831d35Sstevel (data_valid != IOSRAM_DATA_VALID)) || 123103831d35Sstevel ((int_pending != IOSRAM_INT_NONE) && 123203831d35Sstevel (int_pending != IOSRAM_INT_TO_SSC) && 123303831d35Sstevel (int_pending != IOSRAM_INT_TO_DOM))) { 123403831d35Sstevel error = EINVAL; 123503831d35Sstevel } else if (iosram_tswitch_active) { 123603831d35Sstevel error = EAGAIN; 123703831d35Sstevel } else { 123803831d35Sstevel IOSRAM_STAT(setflag); 123903831d35Sstevel flags.data_valid = data_valid; 124003831d35Sstevel flags.int_pending = int_pending; 124103831d35Sstevel ddi_rep_put8(iosram_handle, (uint8_t *)&flags, 124203831d35Sstevel (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t), 124303831d35Sstevel DDI_DEV_AUTOINCR); 124403831d35Sstevel } 124503831d35Sstevel mutex_exit(&iosram_mutex); 124603831d35Sstevel 124703831d35Sstevel IOSRAMLOG(1, "SetFlag key:%x data_valid:%x int_pending:%x error:%d\n", 124803831d35Sstevel key, flags.data_valid, flags.int_pending, error); 124903831d35Sstevel return (error); 125003831d35Sstevel } 125103831d35Sstevel 125203831d35Sstevel 125303831d35Sstevel /* 125403831d35Sstevel * iosram_ctrl() 125503831d35Sstevel * This function provides access to a variety of services not available 125603831d35Sstevel * through the basic API. 125703831d35Sstevel */ 125803831d35Sstevel int 125903831d35Sstevel iosram_ctrl(uint32_t key, uint32_t cmd, void *arg) 126003831d35Sstevel { 126103831d35Sstevel struct iosram_chunk *chunkp; 126203831d35Sstevel int error = 0; 126303831d35Sstevel 126403831d35Sstevel /* 126503831d35Sstevel * Acquire lock and do some argument sanity checking. 126603831d35Sstevel */ 126703831d35Sstevel mutex_enter(&iosram_mutex); 126803831d35Sstevel chunkp = iosram_find_chunk(key); 126903831d35Sstevel 127003831d35Sstevel if (iosram_master == NULL) { 127103831d35Sstevel error = EIO; 127203831d35Sstevel } else if (chunkp == NULL) { 127303831d35Sstevel error = EINVAL; 127403831d35Sstevel } 127503831d35Sstevel 127603831d35Sstevel if (error != 0) { 127703831d35Sstevel mutex_exit(&iosram_mutex); 127803831d35Sstevel return (error); 127903831d35Sstevel } 128003831d35Sstevel 128103831d35Sstevel /* 128203831d35Sstevel * Arguments seem okay so far, so process the command. 128303831d35Sstevel */ 128403831d35Sstevel switch (cmd) { 128503831d35Sstevel case IOSRAM_CMD_CHUNKLEN: 128603831d35Sstevel /* 128703831d35Sstevel * Return the length of the chunk indicated by the key. 128803831d35Sstevel */ 128903831d35Sstevel if (arg == NULL) { 129003831d35Sstevel error = EINVAL; 129103831d35Sstevel break; 129203831d35Sstevel } 129303831d35Sstevel 129403831d35Sstevel *(uint32_t *)arg = chunkp->toc_data.len; 129503831d35Sstevel break; 129603831d35Sstevel 129703831d35Sstevel default: 129803831d35Sstevel error = ENOTSUP; 129903831d35Sstevel break; 130003831d35Sstevel } 130103831d35Sstevel 130203831d35Sstevel mutex_exit(&iosram_mutex); 130303831d35Sstevel return (error); 130403831d35Sstevel } 130503831d35Sstevel 130603831d35Sstevel 130703831d35Sstevel /* 130803831d35Sstevel * iosram_hdr_ctrl() 130903831d35Sstevel * This function provides an interface for the Mailbox Protocol 131003831d35Sstevel * implementation to use when interacting with the IOSRAM header. 131103831d35Sstevel */ 131203831d35Sstevel int 131303831d35Sstevel iosram_hdr_ctrl(uint32_t cmd, void *arg) 131403831d35Sstevel { 131503831d35Sstevel int error = 0; 131603831d35Sstevel 131703831d35Sstevel /* 131803831d35Sstevel * Acquire lock and do some argument sanity checking. 131903831d35Sstevel */ 132003831d35Sstevel mutex_enter(&iosram_mutex); 132103831d35Sstevel 132203831d35Sstevel if (iosram_master == NULL) { 132303831d35Sstevel error = EIO; 132403831d35Sstevel } 132503831d35Sstevel 132603831d35Sstevel if (error != 0) { 132703831d35Sstevel mutex_exit(&iosram_mutex); 132803831d35Sstevel return (error); 132903831d35Sstevel } 133003831d35Sstevel 133103831d35Sstevel switch (cmd) { 133203831d35Sstevel case IOSRAM_HDRCMD_GET_SMS_MBOX_VER: 133303831d35Sstevel /* 133403831d35Sstevel * Return the value of the sms_mbox_version field. 133503831d35Sstevel */ 133603831d35Sstevel if (arg == NULL) { 133703831d35Sstevel error = EINVAL; 133803831d35Sstevel break; 133903831d35Sstevel } 134003831d35Sstevel 134103831d35Sstevel *(uint32_t *)arg = IOSRAM_GET_HDRFIELD32(iosram_master, 134203831d35Sstevel sms_mbox_version); 134303831d35Sstevel break; 134403831d35Sstevel 134503831d35Sstevel case IOSRAM_HDRCMD_SET_OS_MBOX_VER: 134603831d35Sstevel /* 134703831d35Sstevel * Set the value of the os_mbox_version field. 134803831d35Sstevel */ 134903831d35Sstevel IOSRAM_SET_HDRFIELD32(iosram_master, os_mbox_version, 135003831d35Sstevel (uint32_t)(uintptr_t)arg); 135103831d35Sstevel IOSRAM_SET_HDRFIELD32(iosram_master, os_change_mask, 135203831d35Sstevel IOSRAM_HDRFIELD_OS_MBOX_VER); 1353*07d06da5SSurya Prakki (void) iosram_send_intr(); 135403831d35Sstevel break; 135503831d35Sstevel 135603831d35Sstevel case IOSRAM_HDRCMD_REG_CALLBACK: 135703831d35Sstevel iosram_hdrchange_handler = (void (*)())arg; 135803831d35Sstevel break; 135903831d35Sstevel 136003831d35Sstevel default: 136103831d35Sstevel error = ENOTSUP; 136203831d35Sstevel break; 136303831d35Sstevel } 136403831d35Sstevel 136503831d35Sstevel mutex_exit(&iosram_mutex); 136603831d35Sstevel return (error); 136703831d35Sstevel } 136803831d35Sstevel 136903831d35Sstevel 137003831d35Sstevel /* 137103831d35Sstevel * iosram_softintr() 137203831d35Sstevel * IOSRAM soft interrupt handler 137303831d35Sstevel */ 137403831d35Sstevel static uint_t 137503831d35Sstevel iosram_softintr(caddr_t arg) 137603831d35Sstevel { 137703831d35Sstevel uint32_t hdr_changes; 137803831d35Sstevel iosramsoft_t *softp = (iosramsoft_t *)arg; 137903831d35Sstevel iosram_chunk_t *chunkp; 138003831d35Sstevel void (*handler)(); 138103831d35Sstevel int i; 138203831d35Sstevel uint8_t flag; 138303831d35Sstevel 138403831d35Sstevel DPRINTF(1, ("iosram(%d): in iosram_softintr\n", softp->instance)); 138503831d35Sstevel 138603831d35Sstevel IOSRAMLOG(2, "SINTR arg/softp:%p pending:%d busy:%d\n", 138703831d35Sstevel arg, softp->intr_pending, softp->intr_busy, NULL); 138803831d35Sstevel 138903831d35Sstevel mutex_enter(&iosram_mutex); 139003831d35Sstevel mutex_enter(&softp->intr_mutex); 139103831d35Sstevel 139203831d35Sstevel /* 139303831d35Sstevel * Do not process interrupt if interrupt handler is already running or 139403831d35Sstevel * no interrupts are pending. 139503831d35Sstevel */ 139603831d35Sstevel if (softp->intr_busy || !softp->intr_pending) { 139703831d35Sstevel mutex_exit(&softp->intr_mutex); 139803831d35Sstevel mutex_exit(&iosram_mutex); 139903831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: busy=%d pending=%d\n", 140003831d35Sstevel softp->instance, softp->intr_busy, softp->intr_pending)); 140103831d35Sstevel return (softp->intr_pending ? DDI_INTR_CLAIMED : 140203831d35Sstevel DDI_INTR_UNCLAIMED); 140303831d35Sstevel } 140403831d35Sstevel 140503831d35Sstevel /* 140603831d35Sstevel * It's possible for the SC to send an interrupt on the new master 140703831d35Sstevel * before we are able to set our internal state. If so, we'll retrigger 140803831d35Sstevel * soft interrupt right after tunnel switch completion. 140903831d35Sstevel */ 141003831d35Sstevel if (softp->state & IOSRAM_STATE_TSWITCH) { 141103831d35Sstevel mutex_exit(&softp->intr_mutex); 141203831d35Sstevel mutex_exit(&iosram_mutex); 141303831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: doing switch " 141403831d35Sstevel "state=0x%x\n", softp->instance, softp->state)); 141503831d35Sstevel return (DDI_INTR_CLAIMED); 141603831d35Sstevel } 141703831d35Sstevel 141803831d35Sstevel /* 141903831d35Sstevel * Do not process interrupt if we are not the master. 142003831d35Sstevel */ 142103831d35Sstevel if (!(softp->state & IOSRAM_STATE_MASTER)) { 142203831d35Sstevel mutex_exit(&softp->intr_mutex); 142303831d35Sstevel mutex_exit(&iosram_mutex); 142403831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: no master state=0x%x\n ", 142503831d35Sstevel softp->instance, softp->state)); 142603831d35Sstevel return (DDI_INTR_CLAIMED); 142703831d35Sstevel } 142803831d35Sstevel 142903831d35Sstevel IOSRAM_STAT(sintr_recv); 143003831d35Sstevel 143103831d35Sstevel /* 143203831d35Sstevel * If the driver is suspended, then we should not process any 143303831d35Sstevel * interrupts. Instead, we trigger a soft interrupt when the driver 143403831d35Sstevel * resumes. 143503831d35Sstevel */ 143603831d35Sstevel if (softp->suspended) { 143703831d35Sstevel mutex_exit(&softp->intr_mutex); 143803831d35Sstevel mutex_exit(&iosram_mutex); 143903831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: suspended\n", 144003831d35Sstevel softp->instance)); 144103831d35Sstevel return (DDI_INTR_CLAIMED); 144203831d35Sstevel } 144303831d35Sstevel 144403831d35Sstevel /* 144503831d35Sstevel * Indicate that the IOSRAM interrupt handler is busy. Note that this 144603831d35Sstevel * includes incrementing the reader/writer count, since we don't want 144703831d35Sstevel * any tunnel switches to start up while we're processing callbacks. 144803831d35Sstevel */ 144903831d35Sstevel softp->intr_busy = 1; 145003831d35Sstevel iosram_rw_active++; 145103831d35Sstevel #if defined(DEBUG) 145203831d35Sstevel if (iosram_rw_active > iosram_rw_active_max) { 145303831d35Sstevel iosram_rw_active_max = iosram_rw_active; 145403831d35Sstevel } 145503831d35Sstevel #endif 145603831d35Sstevel 145703831d35Sstevel do { 145803831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: processing interrupt\n", 145903831d35Sstevel softp->instance)); 146003831d35Sstevel 146103831d35Sstevel softp->intr_pending = 0; 146203831d35Sstevel 146303831d35Sstevel mutex_exit(&softp->intr_mutex); 146403831d35Sstevel 146503831d35Sstevel /* 146603831d35Sstevel * Process changes to the IOSRAM header. 146703831d35Sstevel */ 146803831d35Sstevel hdr_changes = IOSRAM_GET_HDRFIELD32(iosram_master, 146903831d35Sstevel sms_change_mask); 147003831d35Sstevel if (hdr_changes != 0) { 147103831d35Sstevel int error; 147203831d35Sstevel 147303831d35Sstevel IOSRAM_SET_HDRFIELD32(iosram_master, sms_change_mask, 147403831d35Sstevel 0); 147503831d35Sstevel if (hdr_changes & IOSRAM_HDRFIELD_TOC_INDEX) { 147603831d35Sstevel /* 147703831d35Sstevel * XXX is it safe to temporarily release the 147803831d35Sstevel * iosram_mutex here? 147903831d35Sstevel */ 148003831d35Sstevel mutex_exit(&iosram_mutex); 148103831d35Sstevel error = iosram_read_toc(iosram_master); 148203831d35Sstevel mutex_enter(&iosram_mutex); 148303831d35Sstevel if (error) { 148403831d35Sstevel cmn_err(CE_WARN, "iosram_read_toc: new" 148503831d35Sstevel " TOC invalid; using old TOC."); 148603831d35Sstevel } 148703831d35Sstevel iosram_update_addrs(iosram_master); 148803831d35Sstevel } 148903831d35Sstevel 149003831d35Sstevel if (iosram_hdrchange_handler != NULL) { 149103831d35Sstevel mutex_exit(&iosram_mutex); 149203831d35Sstevel iosram_hdrchange_handler(); 149303831d35Sstevel mutex_enter(&iosram_mutex); 149403831d35Sstevel } 149503831d35Sstevel } 149603831d35Sstevel 149703831d35Sstevel /* 149803831d35Sstevel * Get data_valid/int_pending flags and generate a callback if 149903831d35Sstevel * applicable. For now, we read only those flags for which a 150003831d35Sstevel * callback has been registered. We can optimize reading of 150103831d35Sstevel * flags by reading them all at once and then process them 150203831d35Sstevel * later. 150303831d35Sstevel */ 150403831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, 150503831d35Sstevel chunkp++) { 150603831d35Sstevel #if DEBUG 150703831d35Sstevel flag = ddi_get8(iosram_handle, 150803831d35Sstevel &(chunkp->flagsp->int_pending)); 150903831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr chunk #%d " 151003831d35Sstevel "flag=0x%x handler=%p\n", 151103831d35Sstevel softp->instance, i, (int)flag, 1512*07d06da5SSurya Prakki (void *)chunkp->cback.handler)); 151303831d35Sstevel #endif 151403831d35Sstevel if ((handler = chunkp->cback.handler) == NULL) { 151503831d35Sstevel continue; 151603831d35Sstevel } 151703831d35Sstevel flag = ddi_get8(iosram_handle, 151803831d35Sstevel &(chunkp->flagsp->int_pending)); 151903831d35Sstevel if (flag == IOSRAM_INT_TO_DOM) { 152003831d35Sstevel DPRINTF(1, 152103831d35Sstevel ("IOSRAM(%d): softintr: invoking handler\n", 152203831d35Sstevel softp->instance)); 152303831d35Sstevel IOSRAMLOG(1, 152403831d35Sstevel "SINTR invoking hdlr:%p arg:%p index:%d\n", 152503831d35Sstevel handler, chunkp->cback.arg, i, NULL); 152603831d35Sstevel IOSRAM_STAT(callbacks); 152703831d35Sstevel 152803831d35Sstevel ddi_put8(iosram_handle, 152903831d35Sstevel &(chunkp->flagsp->int_pending), 153003831d35Sstevel IOSRAM_INT_NONE); 153103831d35Sstevel chunkp->cback.busy = 1; 153203831d35Sstevel mutex_exit(&iosram_mutex); 153303831d35Sstevel (*handler)(chunkp->cback.arg); 153403831d35Sstevel mutex_enter(&iosram_mutex); 153503831d35Sstevel chunkp->cback.busy = 0; 153603831d35Sstevel 153703831d35Sstevel /* 153803831d35Sstevel * If iosram_unregister was called while the 153903831d35Sstevel * callback was being invoked, complete the 154003831d35Sstevel * unregistration here. 154103831d35Sstevel */ 154203831d35Sstevel if (chunkp->cback.unregister) { 154303831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: " 154403831d35Sstevel "delayed unreg k:0x%08x\n", 154503831d35Sstevel softp->instance, 154603831d35Sstevel chunkp->toc_data.key)); 154703831d35Sstevel chunkp->cback.handler = NULL; 154803831d35Sstevel chunkp->cback.arg = NULL; 154903831d35Sstevel chunkp->cback.unregister = 0; 155003831d35Sstevel } 155103831d35Sstevel } 155203831d35Sstevel 155303831d35Sstevel /* 155403831d35Sstevel * If there's a tunnel switch waiting to run, give it 155503831d35Sstevel * higher priority than these callbacks by bailing out. 155603831d35Sstevel * They'll still be invoked on the new master iosram 155703831d35Sstevel * when the tunnel switch is done. 155803831d35Sstevel */ 155903831d35Sstevel if (iosram_tswitch_active) { 156003831d35Sstevel break; 156103831d35Sstevel } 156203831d35Sstevel } 156303831d35Sstevel 156403831d35Sstevel mutex_enter(&softp->intr_mutex); 156503831d35Sstevel 156603831d35Sstevel } while (softp->intr_pending && !softp->suspended && 156703831d35Sstevel !iosram_tswitch_active); 156803831d35Sstevel 156903831d35Sstevel /* 157003831d35Sstevel * Indicate IOSRAM interrupt handler is not BUSY any more 157103831d35Sstevel */ 157203831d35Sstevel softp->intr_busy = 0; 157303831d35Sstevel 157403831d35Sstevel ASSERT(iosram_rw_active > 0); 157503831d35Sstevel if ((--iosram_rw_active == 0) && iosram_rw_wakeup) { 157603831d35Sstevel iosram_rw_wakeup = 0; 157703831d35Sstevel cv_broadcast(&iosram_rw_wait); 157803831d35Sstevel } 157903831d35Sstevel 158003831d35Sstevel mutex_exit(&softp->intr_mutex); 158103831d35Sstevel mutex_exit(&iosram_mutex); 158203831d35Sstevel 158303831d35Sstevel DPRINTF(1, ("iosram(%d): softintr exit\n", softp->instance)); 158403831d35Sstevel 158503831d35Sstevel return (DDI_INTR_CLAIMED); 158603831d35Sstevel } 158703831d35Sstevel 158803831d35Sstevel 158903831d35Sstevel /* 159003831d35Sstevel * iosram_intr() 159103831d35Sstevel * IOSRAM real interrupt handler 159203831d35Sstevel */ 159303831d35Sstevel static uint_t 159403831d35Sstevel iosram_intr(caddr_t arg) 159503831d35Sstevel { 159603831d35Sstevel iosramsoft_t *softp = (iosramsoft_t *)arg; 159703831d35Sstevel int result = DDI_INTR_UNCLAIMED; 159803831d35Sstevel uint32_t int_status; 159903831d35Sstevel 160003831d35Sstevel DPRINTF(2, ("iosram(%d): in iosram_intr\n", softp->instance)); 160103831d35Sstevel 160203831d35Sstevel mutex_enter(&softp->intr_mutex); 160303831d35Sstevel 160403831d35Sstevel if (softp->sbbc_handle == NULL) { 160503831d35Sstevel /* 160603831d35Sstevel * The SBBC registers region is not mapped in. 160703831d35Sstevel * Set the interrupt pending flag here, and process the 160803831d35Sstevel * interrupt after the tunnel switch. 160903831d35Sstevel */ 161003831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_intr: SBBC not mapped\n", 161103831d35Sstevel softp->instance)); 161203831d35Sstevel softp->intr_pending = 1; 161303831d35Sstevel mutex_exit(&softp->intr_mutex); 161403831d35Sstevel return (DDI_INTR_UNCLAIMED); 161503831d35Sstevel } 161603831d35Sstevel 161703831d35Sstevel int_status = ddi_get32(softp->sbbc_handle, 161803831d35Sstevel &(softp->sbbc_region->int_status.reg)); 161903831d35Sstevel DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n", int_status)); 162003831d35Sstevel 162103831d35Sstevel if (int_status & IOSRAM_SBBC_INT0) { 162203831d35Sstevel result = DDI_INTR_CLAIMED; 162303831d35Sstevel DPRINTF(1, ("iosram_intr: int0 detected!\n")); 162403831d35Sstevel } 162503831d35Sstevel 162603831d35Sstevel if (int_status & IOSRAM_SBBC_INT1) { 162703831d35Sstevel result = DDI_INTR_CLAIMED; 162803831d35Sstevel DPRINTF(1, ("iosram_intr: int1 detected!\n")); 162903831d35Sstevel } 163003831d35Sstevel 163103831d35Sstevel if (result == DDI_INTR_CLAIMED) { 163203831d35Sstevel ddi_put32(softp->sbbc_handle, 163303831d35Sstevel &(softp->sbbc_region->int_status.reg), int_status); 163403831d35Sstevel int_status = ddi_get32(softp->sbbc_handle, 163503831d35Sstevel &(softp->sbbc_region->int_status.reg)); 163603831d35Sstevel DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n", 163703831d35Sstevel int_status)); 163803831d35Sstevel 163903831d35Sstevel softp->intr_pending = 1; 164003831d35Sstevel /* 164103831d35Sstevel * Trigger soft interrupt if not executing and 164203831d35Sstevel * not suspended. 164303831d35Sstevel */ 164403831d35Sstevel if (!softp->intr_busy && !softp->suspended && 164503831d35Sstevel (softp->softintr_id != NULL)) { 164603831d35Sstevel DPRINTF(1, ("iosram(%d): trigger softint\n", 164703831d35Sstevel softp->instance)); 164803831d35Sstevel ddi_trigger_softintr(softp->softintr_id); 164903831d35Sstevel } 165003831d35Sstevel } 165103831d35Sstevel 165203831d35Sstevel IOSRAM_STAT(intr_recv); 165303831d35Sstevel 165403831d35Sstevel mutex_exit(&softp->intr_mutex); 165503831d35Sstevel 165603831d35Sstevel IOSRAMLOG(2, "INTR arg/softp:%p pending:%d busy:%d\n", 165703831d35Sstevel arg, softp->intr_pending, softp->intr_busy, NULL); 165803831d35Sstevel DPRINTF(1, ("iosram(%d): iosram_intr exit\n", softp->instance)); 165903831d35Sstevel 166003831d35Sstevel return (result); 166103831d35Sstevel } 166203831d35Sstevel 166303831d35Sstevel 166403831d35Sstevel /* 166503831d35Sstevel * iosram_send_intr() 166603831d35Sstevel * Send an interrupt to the SSP side via AXQ driver 166703831d35Sstevel */ 166803831d35Sstevel int 166903831d35Sstevel iosram_send_intr() 167003831d35Sstevel { 167103831d35Sstevel IOSRAMLOG(1, "SendIntr called\n", NULL, NULL, NULL, NULL); 167203831d35Sstevel IOSRAM_STAT(intr_send); 167303831d35Sstevel DPRINTF(1, ("iosram iosram_send_intr invoked\n")); 167403831d35Sstevel 167503831d35Sstevel return (axq_cpu2ssc_intr(0)); 167603831d35Sstevel } 167703831d35Sstevel 167803831d35Sstevel 167903831d35Sstevel #if defined(DEBUG) 168003831d35Sstevel static void 168103831d35Sstevel iosram_dummy_cback(void *arg) 168203831d35Sstevel { 168303831d35Sstevel DPRINTF(1, ("iosram_dummy_cback invoked arg:%p\n", arg)); 168403831d35Sstevel } 168503831d35Sstevel #endif /* DEBUG */ 168603831d35Sstevel 168703831d35Sstevel 168803831d35Sstevel /*ARGSUSED1*/ 168903831d35Sstevel static int 169003831d35Sstevel iosram_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 169103831d35Sstevel int *rvalp) 169203831d35Sstevel { 169303831d35Sstevel struct iosramsoft *softp; 169403831d35Sstevel int error = DDI_SUCCESS; 169503831d35Sstevel 169603831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, getminor(dev)); 169703831d35Sstevel if (softp == NULL) { 169803831d35Sstevel return (ENXIO); 169903831d35Sstevel } 170003831d35Sstevel IOSRAMLOG(1, "IOCTL: dev:%p cmd:%x arg:%p ... instance %d\n", 170103831d35Sstevel dev, cmd, arg, softp->instance); 170203831d35Sstevel 170303831d35Sstevel switch (cmd) { 170403831d35Sstevel #if defined(DEBUG) 170503831d35Sstevel case IOSRAM_GET_FLAG: 170603831d35Sstevel { 170703831d35Sstevel iosram_io_t req; 170803831d35Sstevel uint8_t data_valid, int_pending; 170903831d35Sstevel 171003831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 171103831d35Sstevel return (EFAULT); 171203831d35Sstevel } 171303831d35Sstevel 171403831d35Sstevel DPRINTF(2, ("IOSRAM_GET_FLAG(key:%x\n", req.key)); 171503831d35Sstevel 171603831d35Sstevel req.retval = iosram_get_flag(req.key, &data_valid, 171703831d35Sstevel &int_pending); 171803831d35Sstevel req.data_valid = (uint32_t)data_valid; 171903831d35Sstevel req.int_pending = (uint32_t)int_pending; 172003831d35Sstevel 172103831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) { 172203831d35Sstevel DPRINTF(1, 172303831d35Sstevel ("IOSRAM_GET_FLAG: can't copyout req.retval (%x)", 172403831d35Sstevel req.retval)); 172503831d35Sstevel error = EFAULT; 172603831d35Sstevel } 172703831d35Sstevel 172803831d35Sstevel return (error); 172903831d35Sstevel } 173003831d35Sstevel 173103831d35Sstevel case IOSRAM_SET_FLAG: 173203831d35Sstevel { 173303831d35Sstevel iosram_io_t req; 173403831d35Sstevel 173503831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 173603831d35Sstevel return (EFAULT); 173703831d35Sstevel } 173803831d35Sstevel 173903831d35Sstevel DPRINTF(2, ("IOSRAM_SET_FLAG(key:%x data_valid:%x " 174003831d35Sstevel "int_pending:%x\n", req.key, req.data_valid, 174103831d35Sstevel req.int_pending)); 174203831d35Sstevel 174303831d35Sstevel req.retval = iosram_set_flag(req.key, req.data_valid, 174403831d35Sstevel req.int_pending); 174503831d35Sstevel 174603831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) { 174703831d35Sstevel DPRINTF(1, ("IOSRAM_SET_FLAG: can't copyout req.retval" 174803831d35Sstevel " (%x)\n", req.retval)); 174903831d35Sstevel error = EFAULT; 175003831d35Sstevel } 175103831d35Sstevel 175203831d35Sstevel return (error); 175303831d35Sstevel } 175403831d35Sstevel 175503831d35Sstevel case IOSRAM_RD: 175603831d35Sstevel { 175703831d35Sstevel caddr_t bufp; 175803831d35Sstevel int len; 175903831d35Sstevel iosram_io_t req; 176003831d35Sstevel 176103831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 176203831d35Sstevel return (EFAULT); 176303831d35Sstevel } 176403831d35Sstevel 176503831d35Sstevel DPRINTF(2, ("IOSRAM_RD(k:%x o:%x len:%x bufp:%p\n", req.key, 176603831d35Sstevel req.off, req.len, (void *)(uintptr_t)req.bufp)); 176703831d35Sstevel 176803831d35Sstevel len = req.len; 176903831d35Sstevel bufp = kmem_alloc(len, KM_SLEEP); 177003831d35Sstevel 177103831d35Sstevel req.retval = iosram_rd(req.key, req.off, req.len, bufp); 177203831d35Sstevel 177303831d35Sstevel if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, len, mode)) { 177403831d35Sstevel DPRINTF(1, ("IOSRAM_RD: copyout(%p, %p,%x,%x) failed\n", 1775*07d06da5SSurya Prakki (void *)bufp, (void *)(uintptr_t)req.bufp, len, 1776*07d06da5SSurya Prakki mode)); 177703831d35Sstevel error = EFAULT; 177803831d35Sstevel } else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) { 177903831d35Sstevel DPRINTF(1, ("IOSRAM_RD: can't copyout retval (%x)\n", 178003831d35Sstevel req.retval)); 178103831d35Sstevel error = EFAULT; 178203831d35Sstevel } 178303831d35Sstevel 178403831d35Sstevel kmem_free(bufp, len); 178503831d35Sstevel return (error); 178603831d35Sstevel } 178703831d35Sstevel 178803831d35Sstevel case IOSRAM_WR: 178903831d35Sstevel { 179003831d35Sstevel caddr_t bufp; 179103831d35Sstevel iosram_io_t req; 179203831d35Sstevel int len; 179303831d35Sstevel 179403831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 179503831d35Sstevel return (EFAULT); 179603831d35Sstevel } 179703831d35Sstevel 179803831d35Sstevel DPRINTF(2, ("IOSRAM_WR(k:%x o:%x len:%x bufp:%p\n", 1799*07d06da5SSurya Prakki req.key, req.off, req.len, (void *)(uintptr_t)req.bufp)); 180003831d35Sstevel len = req.len; 180103831d35Sstevel bufp = kmem_alloc(len, KM_SLEEP); 180203831d35Sstevel if (ddi_copyin((void *)(uintptr_t)req.bufp, bufp, len, mode)) { 180303831d35Sstevel error = EFAULT; 180403831d35Sstevel } else { 180503831d35Sstevel req.retval = iosram_wr(req.key, req.off, req.len, 180603831d35Sstevel bufp); 180703831d35Sstevel 180803831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), 180903831d35Sstevel mode)) { 181003831d35Sstevel error = EFAULT; 181103831d35Sstevel } 181203831d35Sstevel } 181303831d35Sstevel kmem_free(bufp, len); 181403831d35Sstevel return (error); 181503831d35Sstevel } 181603831d35Sstevel 181703831d35Sstevel case IOSRAM_TOC: 181803831d35Sstevel { 181903831d35Sstevel caddr_t bufp; 182003831d35Sstevel int len; 182103831d35Sstevel iosram_io_t req; 182203831d35Sstevel 182303831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 182403831d35Sstevel return (EFAULT); 182503831d35Sstevel } 182603831d35Sstevel 182703831d35Sstevel DPRINTF(2, ("IOSRAM_TOC (req.bufp:%x req.len:%x) \n", 182803831d35Sstevel req.bufp, req.len)); 182903831d35Sstevel 183003831d35Sstevel len = req.len; 183103831d35Sstevel bufp = kmem_alloc(len, KM_SLEEP); 183203831d35Sstevel 183303831d35Sstevel req.retval = iosram_get_keys((iosram_toc_entry_t *)bufp, 183403831d35Sstevel &req.len); 183503831d35Sstevel 183603831d35Sstevel if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, req.len, 183703831d35Sstevel mode)) { 183803831d35Sstevel DPRINTF(1, 183903831d35Sstevel ("IOSRAM_TOC: copyout(%p, %p,%x,%x) failed\n", 1840*07d06da5SSurya Prakki (void *)bufp, (void *)(uintptr_t)req.bufp, req.len, 1841*07d06da5SSurya Prakki mode)); 184203831d35Sstevel error = EFAULT; 184303831d35Sstevel } else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) { 184403831d35Sstevel DPRINTF(1, ("IOSRAM_TOC: can't copyout retval (%x)\n", 184503831d35Sstevel req.retval)); 184603831d35Sstevel error = EFAULT; 184703831d35Sstevel } 184803831d35Sstevel kmem_free(bufp, len); 184903831d35Sstevel return (error); 185003831d35Sstevel } 185103831d35Sstevel 185203831d35Sstevel case IOSRAM_SEND_INTR: 185303831d35Sstevel { 185403831d35Sstevel DPRINTF(2, ("IOSRAM_SEND_INTR\n")); 185503831d35Sstevel 185603831d35Sstevel switch ((int)arg) { 185703831d35Sstevel case 0x11: 185803831d35Sstevel case 0x22: 185903831d35Sstevel case 0x44: 186003831d35Sstevel case 0x88: 186103831d35Sstevel ddi_put32(softp->sbbc_handle, 186203831d35Sstevel &(softp->sbbc_region->int_enable.reg), (int)arg); 186303831d35Sstevel DPRINTF(1, ("Wrote 0x%x to int_enable.reg\n", 186403831d35Sstevel (int)arg)); 186503831d35Sstevel break; 186603831d35Sstevel case 0xBB: 186703831d35Sstevel ddi_put32(softp->sbbc_handle, 186803831d35Sstevel &(softp->sbbc_region->p0_int_gen.reg), 1); 186903831d35Sstevel DPRINTF(1, ("Wrote 1 to p0_int_gen.reg\n")); 187003831d35Sstevel break; 187103831d35Sstevel default: 187203831d35Sstevel error = iosram_send_intr(); 187303831d35Sstevel } 187403831d35Sstevel 187503831d35Sstevel return (error); 187603831d35Sstevel } 187703831d35Sstevel 187803831d35Sstevel case IOSRAM_PRINT_CBACK: 187903831d35Sstevel iosram_print_cback(); 188003831d35Sstevel break; 188103831d35Sstevel 188203831d35Sstevel case IOSRAM_PRINT_STATE: 188303831d35Sstevel iosram_print_state((int)arg); 188403831d35Sstevel break; 188503831d35Sstevel 188603831d35Sstevel #if IOSRAM_STATS 188703831d35Sstevel case IOSRAM_PRINT_STATS: 188803831d35Sstevel iosram_print_stats(); 188903831d35Sstevel break; 189003831d35Sstevel #endif 189103831d35Sstevel 189203831d35Sstevel #if IOSRAM_LOG 189303831d35Sstevel case IOSRAM_PRINT_LOG: 189403831d35Sstevel iosram_print_log((int)arg); 189503831d35Sstevel break; 189603831d35Sstevel #endif 189703831d35Sstevel 189803831d35Sstevel case IOSRAM_TUNNEL_SWITCH: 189903831d35Sstevel error = iosram_switchfrom((int)arg); 190003831d35Sstevel break; 190103831d35Sstevel 190203831d35Sstevel case IOSRAM_PRINT_FLAGS: 190303831d35Sstevel iosram_print_flags(); 190403831d35Sstevel break; 190503831d35Sstevel 190603831d35Sstevel case IOSRAM_REG_CBACK: 190703831d35Sstevel { 190803831d35Sstevel iosram_io_t req; 190903831d35Sstevel 191003831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 191103831d35Sstevel return (EFAULT); 191203831d35Sstevel } 191303831d35Sstevel 191403831d35Sstevel DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key)); 191503831d35Sstevel 191603831d35Sstevel req.retval = iosram_register(req.key, iosram_dummy_cback, 191703831d35Sstevel (void *)(uintptr_t)req.key); 191803831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) { 191903831d35Sstevel error = EFAULT; 192003831d35Sstevel } 192103831d35Sstevel 192203831d35Sstevel return (error); 192303831d35Sstevel } 192403831d35Sstevel 192503831d35Sstevel case IOSRAM_UNREG_CBACK: 192603831d35Sstevel { 192703831d35Sstevel iosram_io_t req; 192803831d35Sstevel 192903831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) { 193003831d35Sstevel return (EFAULT); 193103831d35Sstevel } 193203831d35Sstevel 193303831d35Sstevel DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key)); 193403831d35Sstevel 193503831d35Sstevel req.retval = iosram_unregister(req.key); 193603831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) { 193703831d35Sstevel error = EFAULT; 193803831d35Sstevel } 193903831d35Sstevel 194003831d35Sstevel return (error); 194103831d35Sstevel } 194203831d35Sstevel 194303831d35Sstevel case IOSRAM_SEMA_ACQUIRE: 194403831d35Sstevel { 194503831d35Sstevel DPRINTF(1, ("IOSRAM_SEMA_ACQUIRE\n")); 194603831d35Sstevel error = iosram_sema_acquire(NULL); 194703831d35Sstevel return (error); 194803831d35Sstevel } 194903831d35Sstevel 195003831d35Sstevel case IOSRAM_SEMA_RELEASE: 195103831d35Sstevel { 195203831d35Sstevel DPRINTF(1, ("IOSRAM_SEMA_RELEASE\n")); 195303831d35Sstevel error = iosram_sema_release(); 195403831d35Sstevel return (error); 195503831d35Sstevel } 195603831d35Sstevel 195703831d35Sstevel #endif /* DEBUG */ 195803831d35Sstevel 195903831d35Sstevel default: 196003831d35Sstevel DPRINTF(1, ("iosram_ioctl: Illegal command %x\n", cmd)); 196103831d35Sstevel error = ENOTTY; 196203831d35Sstevel } 196303831d35Sstevel 196403831d35Sstevel return (error); 196503831d35Sstevel } 196603831d35Sstevel 196703831d35Sstevel 196803831d35Sstevel /* 196903831d35Sstevel * iosram_switch_tunnel(softp) 197003831d35Sstevel * Switch master tunnel to the specified instance 197103831d35Sstevel * Must be called while holding iosram_mutex 197203831d35Sstevel */ 197303831d35Sstevel /*ARGSUSED*/ 197403831d35Sstevel static int 197503831d35Sstevel iosram_switch_tunnel(iosramsoft_t *softp) 197603831d35Sstevel { 197703831d35Sstevel #ifdef DEBUG 197803831d35Sstevel int instance = softp->instance; 197903831d35Sstevel #endif 198003831d35Sstevel int error = 0; 198103831d35Sstevel iosramsoft_t *prev_master; 198203831d35Sstevel 198303831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 198403831d35Sstevel 198503831d35Sstevel DPRINTF(1, ("tunnel switch new master:%p (%d) current master:%p (%d)\n", 1986*07d06da5SSurya Prakki (void *)softp, instance, (void *)iosram_master, 198703831d35Sstevel ((iosram_master) ? iosram_master->instance : -1))); 198803831d35Sstevel IOSRAMLOG(1, "TSWTCH: new_master:%p (%p) iosram_master:%p (%d)\n", 198903831d35Sstevel softp, instance, iosram_master, 199003831d35Sstevel ((iosram_master) ? iosram_master->instance : -1)); 199103831d35Sstevel 199203831d35Sstevel if (softp == NULL || (softp->state & IOSRAM_STATE_DETACH)) { 199303831d35Sstevel return (ENXIO); 199403831d35Sstevel } 199503831d35Sstevel if (iosram_master == softp) { 199603831d35Sstevel return (0); 199703831d35Sstevel } 199803831d35Sstevel 199903831d35Sstevel 200003831d35Sstevel /* 200103831d35Sstevel * We protect against the softp structure being deallocated by setting 200203831d35Sstevel * the IOSRAM_STATE_TSWITCH state flag. The detach routine will check 200303831d35Sstevel * for this flag and if set, it will wait for this flag to be reset or 200403831d35Sstevel * refuse the detach operation. 200503831d35Sstevel */ 200603831d35Sstevel iosram_new_master = softp; 200703831d35Sstevel softp->state |= IOSRAM_STATE_TSWITCH; 200803831d35Sstevel prev_master = iosram_master; 200903831d35Sstevel if (prev_master) { 201003831d35Sstevel prev_master->state |= IOSRAM_STATE_TSWITCH; 201103831d35Sstevel } 201203831d35Sstevel mutex_exit(&iosram_mutex); 201303831d35Sstevel 201403831d35Sstevel /* 201503831d35Sstevel * Map the target IOSRAM, read the TOC, and register interrupts if not 201603831d35Sstevel * already done. 201703831d35Sstevel */ 201803831d35Sstevel DPRINTF(1, ("iosram(%d): mapping IOSRAM and SBBC\n", 201903831d35Sstevel softp->instance)); 202003831d35Sstevel IOSRAMLOG(1, "TSWTCH: mapping instance:%d softp:%p\n", 202103831d35Sstevel instance, softp, NULL, NULL); 202203831d35Sstevel 202303831d35Sstevel if (iosram_setup_map(softp) != DDI_SUCCESS) { 202403831d35Sstevel error = ENXIO; 202503831d35Sstevel } else if ((chunks == NULL) && (iosram_read_toc(softp) != 0)) { 202603831d35Sstevel iosram_remove_map(softp); 202703831d35Sstevel error = EINVAL; 202803831d35Sstevel } else if (iosram_add_intr(softp) != DDI_SUCCESS) { 202903831d35Sstevel /* 203003831d35Sstevel * If there was no previous master, purge the TOC data that 203103831d35Sstevel * iosram_read_toc() created. 203203831d35Sstevel */ 203303831d35Sstevel if ((prev_master == NULL) && (chunks != NULL)) { 203403831d35Sstevel kmem_free(chunks, nchunks * sizeof (iosram_chunk_t)); 203503831d35Sstevel chunks = NULL; 203603831d35Sstevel nchunks = 0; 203703831d35Sstevel iosram_init_hashtab(); 203803831d35Sstevel } 203903831d35Sstevel iosram_remove_map(softp); 204003831d35Sstevel error = ENXIO; 204103831d35Sstevel } 204203831d35Sstevel 204303831d35Sstevel /* 204403831d35Sstevel * If we are asked to abort tunnel switch, do so now, before invoking 204503831d35Sstevel * the OBP callback. 204603831d35Sstevel */ 204703831d35Sstevel if (iosram_tswitch_aborted) { 204803831d35Sstevel 204903831d35Sstevel /* 205003831d35Sstevel * Once the tunnel switch is aborted, this thread should not 205103831d35Sstevel * resume. If it does, we simply log a message. We can't unmap 205203831d35Sstevel * the new master IOSRAM as it may be accessed in 205303831d35Sstevel * iosram_abort_tswitch(). It will be unmapped when it is 205403831d35Sstevel * detached. 205503831d35Sstevel */ 205603831d35Sstevel IOSRAMLOG(1, 205703831d35Sstevel "TSWTCH: aborted (pre OBP cback). Thread resumed.\n", 205803831d35Sstevel NULL, NULL, NULL, NULL); 205903831d35Sstevel error = EIO; 206003831d35Sstevel } 206103831d35Sstevel 206203831d35Sstevel if (error) { 206303831d35Sstevel IOSRAMLOG(1, 206403831d35Sstevel "TSWTCH: map failed instance:%d softp:%p error:%x\n", 206503831d35Sstevel instance, softp, error, NULL); 206603831d35Sstevel goto done; 206703831d35Sstevel } 206803831d35Sstevel 206903831d35Sstevel if (prev_master != NULL) { 207003831d35Sstevel int result; 207103831d35Sstevel 207203831d35Sstevel /* 207303831d35Sstevel * Now invoke the OBP interface to do the tunnel switch. 207403831d35Sstevel */ 207503831d35Sstevel result = prom_starcat_switch_tunnel(softp->portid, 207603831d35Sstevel OBP_TSWITCH_REQREPLY); 207703831d35Sstevel if (result != 0) { 207803831d35Sstevel error = EIO; 207903831d35Sstevel } 208003831d35Sstevel IOSRAMLOG(1, 208103831d35Sstevel "TSWTCH: OBP tswitch portid:%x result:%x error:%x\n", 208203831d35Sstevel softp->portid, result, error, NULL); 208303831d35Sstevel IOSRAM_STAT(tswitch); 208403831d35Sstevel iosram_tswitch_tstamp = ddi_get_lbolt(); 208503831d35Sstevel } 208603831d35Sstevel 208703831d35Sstevel mutex_enter(&iosram_mutex); 208803831d35Sstevel if (iosram_tswitch_aborted) { 208903831d35Sstevel /* 209003831d35Sstevel * Tunnel switch aborted. This thread should not resume. 209103831d35Sstevel * For now, we simply log a message, but don't unmap any 209203831d35Sstevel * IOSRAM at this stage as it may be accessed within the 209303831d35Sstevel * isoram_abort_tswitch(). The IOSRAM will be unmapped 209403831d35Sstevel * when that instance is detached. 209503831d35Sstevel */ 209603831d35Sstevel if (iosram_tswitch_aborted) { 209703831d35Sstevel IOSRAMLOG(1, 209803831d35Sstevel "TSWTCH: aborted (post OBP cback). Thread" 209903831d35Sstevel " resumed.\n", NULL, NULL, NULL, NULL); 210003831d35Sstevel error = EIO; 210103831d35Sstevel mutex_exit(&iosram_mutex); 210203831d35Sstevel } 210303831d35Sstevel } else if (error) { 210403831d35Sstevel /* 210503831d35Sstevel * Tunnel switch failed. Continue using previous tunnel. 210603831d35Sstevel * However, unmap new (target) IOSRAM. 210703831d35Sstevel */ 210803831d35Sstevel iosram_new_master = NULL; 210903831d35Sstevel mutex_exit(&iosram_mutex); 2110*07d06da5SSurya Prakki (void) iosram_remove_intr(softp); 211103831d35Sstevel iosram_remove_map(softp); 211203831d35Sstevel } else { 211303831d35Sstevel /* 211403831d35Sstevel * Tunnel switch was successful. Set the new master. 211503831d35Sstevel * Also unmap old master IOSRAM and remove any interrupts 211603831d35Sstevel * associated with that. 211703831d35Sstevel * 211803831d35Sstevel * Note that a call to iosram_force_write() allows access 211903831d35Sstevel * to the IOSRAM while tunnel switch is in progress. That 212003831d35Sstevel * means we need to set the new master before unmapping 212103831d35Sstevel * the old master. 212203831d35Sstevel */ 212303831d35Sstevel iosram_set_master(softp); 212403831d35Sstevel iosram_new_master = NULL; 212503831d35Sstevel mutex_exit(&iosram_mutex); 212603831d35Sstevel 212703831d35Sstevel if (prev_master) { 212803831d35Sstevel IOSRAMLOG(1, "TSWTCH: unmapping prev_master:%p (%d)\n", 212903831d35Sstevel prev_master, prev_master->instance, NULL, NULL); 2130*07d06da5SSurya Prakki (void) iosram_remove_intr(prev_master); 213103831d35Sstevel iosram_remove_map(prev_master); 213203831d35Sstevel } 213303831d35Sstevel } 213403831d35Sstevel 213503831d35Sstevel done: 213603831d35Sstevel mutex_enter(&iosram_mutex); 213703831d35Sstevel 213803831d35Sstevel /* 213903831d35Sstevel * Clear the tunnel switch flag on the source and destination 214003831d35Sstevel * instances. 214103831d35Sstevel */ 214203831d35Sstevel if (prev_master) { 214303831d35Sstevel prev_master->state &= ~IOSRAM_STATE_TSWITCH; 214403831d35Sstevel } 214503831d35Sstevel softp->state &= ~IOSRAM_STATE_TSWITCH; 214603831d35Sstevel 214703831d35Sstevel /* 214803831d35Sstevel * Since incoming interrupts could get lost during a tunnel switch, 214903831d35Sstevel * trigger a soft interrupt just in case. No harm other than a bit 215003831d35Sstevel * of wasted effort will be caused if no interrupts were dropped. 215103831d35Sstevel */ 215203831d35Sstevel mutex_enter(&softp->intr_mutex); 215303831d35Sstevel iosram_master->intr_pending = 1; 215403831d35Sstevel if ((iosram_master->softintr_id != NULL) && 215503831d35Sstevel (iosram_master->intr_busy == 0)) { 215603831d35Sstevel ddi_trigger_softintr(iosram_master->softintr_id); 215703831d35Sstevel } 215803831d35Sstevel mutex_exit(&softp->intr_mutex); 215903831d35Sstevel 216003831d35Sstevel IOSRAMLOG(1, "TSWTCH: done error:%d iosram_master:%p instance:%d\n", 216103831d35Sstevel error, iosram_master, 216203831d35Sstevel (iosram_master) ? iosram_master->instance : -1, NULL); 216303831d35Sstevel 216403831d35Sstevel return (error); 216503831d35Sstevel } 216603831d35Sstevel 216703831d35Sstevel 216803831d35Sstevel /* 216903831d35Sstevel * iosram_abort_tswitch() 217003831d35Sstevel * Must be called while holding iosram_mutex. 217103831d35Sstevel */ 217203831d35Sstevel static void 217303831d35Sstevel iosram_abort_tswitch() 217403831d35Sstevel { 217503831d35Sstevel uint32_t master_valid, new_master_valid; 217603831d35Sstevel 217703831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 217803831d35Sstevel 217903831d35Sstevel if ((!iosram_tswitch_active) || iosram_tswitch_aborted) { 218003831d35Sstevel return; 218103831d35Sstevel } 218203831d35Sstevel 218303831d35Sstevel ASSERT(iosram_master != NULL); 218403831d35Sstevel 218503831d35Sstevel IOSRAMLOG(1, "ABORT: iosram_master:%p (%d) iosram_new_master:%p (%d)\n", 218603831d35Sstevel iosram_master, iosram_master->instance, iosram_new_master, 218703831d35Sstevel (iosram_new_master == NULL) ? -1 : iosram_new_master->instance); 218803831d35Sstevel 218903831d35Sstevel /* 219003831d35Sstevel * The first call to iosram_force_write() in the middle of tunnel switch 219103831d35Sstevel * will get here. We lookup IOSRAM VALID location and setup appropriate 219203831d35Sstevel * master, if one is still valid. We also set iosram_tswitch_aborted to 219303831d35Sstevel * prevent reentering this code and to catch if the OBP callback thread 219403831d35Sstevel * somehow resumes. 219503831d35Sstevel */ 219603831d35Sstevel iosram_tswitch_aborted = 1; 219703831d35Sstevel 219803831d35Sstevel if ((iosram_new_master == NULL) || 219903831d35Sstevel (iosram_new_master = iosram_master)) { 220003831d35Sstevel /* 220103831d35Sstevel * New master hasn't been selected yet, or OBP callback 220203831d35Sstevel * succeeded and we already selected new IOSRAM as master, but 220303831d35Sstevel * system crashed in the middle of unmapping previous master or 220403831d35Sstevel * cleaning up state. Use the existing master. 220503831d35Sstevel */ 220603831d35Sstevel ASSERT(iosram_master->iosramp != NULL); 220703831d35Sstevel ASSERT(IOSRAM_GET_HDRFIELD32(iosram_master, status) == 220803831d35Sstevel IOSRAM_VALID); 220903831d35Sstevel IOSRAMLOG(1, "ABORT: master (%d) already determined.\n", 221003831d35Sstevel iosram_master->instance, NULL, NULL, NULL); 221103831d35Sstevel 221203831d35Sstevel return; 221303831d35Sstevel } 221403831d35Sstevel 221503831d35Sstevel /* 221603831d35Sstevel * System crashed in the middle of tunnel switch and we know that the 221703831d35Sstevel * new target has not been marked master yet. That means, the old 221803831d35Sstevel * master should still be mapped. We need to abort the tunnel switch 221903831d35Sstevel * and setup a valid master, if possible, so that we can write to the 222003831d35Sstevel * IOSRAM. 222103831d35Sstevel * 222203831d35Sstevel * We select a new master based upon the IOSRAM header status fields in 222303831d35Sstevel * the previous master IOSRAM and the target IOSRAM as follows: 222403831d35Sstevel * 222503831d35Sstevel * iosram_master iosram-tswitch 222603831d35Sstevel * (Prev Master) (New Target) Decision 222703831d35Sstevel * --------------- --------------- ----------- 222803831d35Sstevel * VALID don't care prev master 222903831d35Sstevel * INTRANSIT INVALID prev master 223003831d35Sstevel * INTRANSIT INTRANSIT prev master 223103831d35Sstevel * INTRANSIT VALID new target 223203831d35Sstevel * INVALID INVALID shouldn't ever happen 223303831d35Sstevel * INVALID INTRANSIT shouldn't ever happen 223403831d35Sstevel * INVALID VALID new target 223503831d35Sstevel */ 223603831d35Sstevel 223703831d35Sstevel master_valid = (iosram_master->iosramp != NULL) ? 223803831d35Sstevel IOSRAM_GET_HDRFIELD32(iosram_master, status) : IOSRAM_INVALID; 223903831d35Sstevel new_master_valid = (iosram_new_master->iosramp != NULL) ? 224003831d35Sstevel IOSRAM_GET_HDRFIELD32(iosram_new_master, status) : IOSRAM_INVALID; 224103831d35Sstevel 224203831d35Sstevel if (master_valid == IOSRAM_VALID) { 224303831d35Sstevel /* EMPTY */ 224403831d35Sstevel /* 224503831d35Sstevel * OBP hasn't been called yet or, if it has, it hasn't started 224603831d35Sstevel * copying yet. Use the existing master. Note that the new 224703831d35Sstevel * master may not be mapped yet. 224803831d35Sstevel */ 224903831d35Sstevel IOSRAMLOG(1, "ABORT: prev master(%d) is VALID\n", 225003831d35Sstevel iosram_master->instance, NULL, NULL, NULL); 225103831d35Sstevel } else if (master_valid == IOSRAM_INTRANSIT) { 225203831d35Sstevel /* 225303831d35Sstevel * The system crashed after OBP started processing the tunnel 225403831d35Sstevel * switch but before the iosram driver determined that it was 225503831d35Sstevel * complete. Use the new master if it has been marked valid, 225603831d35Sstevel * meaning that OBP finished copying data to it, or the old 225703831d35Sstevel * master otherwise. 225803831d35Sstevel */ 225903831d35Sstevel IOSRAMLOG(1, "ABORT: prev master(%d) is INTRANSIT\n", 226003831d35Sstevel iosram_master->instance, NULL, NULL, NULL); 226103831d35Sstevel 226203831d35Sstevel if (new_master_valid == IOSRAM_VALID) { 226303831d35Sstevel iosram_set_master(iosram_new_master); 226403831d35Sstevel IOSRAMLOG(1, "ABORT: new master(%d) is VALID\n", 226503831d35Sstevel iosram_new_master->instance, NULL, NULL, 226603831d35Sstevel NULL); 226703831d35Sstevel } else { 2268*07d06da5SSurya Prakki (void) prom_starcat_switch_tunnel(iosram_master->portid, 226903831d35Sstevel OBP_TSWITCH_NOREPLY); 227003831d35Sstevel 227103831d35Sstevel IOSRAMLOG(1, "ABORT: new master(%d) is INVALID\n", 227203831d35Sstevel iosram_new_master->instance, NULL, NULL, 227303831d35Sstevel NULL); 227403831d35Sstevel } 227503831d35Sstevel } else { 227603831d35Sstevel /* 227703831d35Sstevel * The system crashed after OBP marked the old master INVALID, 227803831d35Sstevel * which means the new master is the way to go. 227903831d35Sstevel */ 228003831d35Sstevel IOSRAMLOG(1, "ABORT: prev master(%d) is INVALID\n", 228103831d35Sstevel iosram_master->instance, NULL, NULL, NULL); 228203831d35Sstevel 228303831d35Sstevel ASSERT(new_master_valid == IOSRAM_VALID); 228403831d35Sstevel 228503831d35Sstevel iosram_set_master(iosram_new_master); 228603831d35Sstevel } 228703831d35Sstevel 228803831d35Sstevel IOSRAMLOG(1, "ABORT: Instance %d selected as master\n", 228903831d35Sstevel iosram_master->instance, NULL, NULL, NULL); 229003831d35Sstevel } 229103831d35Sstevel 229203831d35Sstevel 229303831d35Sstevel /* 229403831d35Sstevel * iosram_switchfrom(instance) 229503831d35Sstevel * Switch master tunnel away from the specified instance 229603831d35Sstevel */ 229703831d35Sstevel /*ARGSUSED*/ 229803831d35Sstevel int 229903831d35Sstevel iosram_switchfrom(int instance) 230003831d35Sstevel { 230103831d35Sstevel struct iosramsoft *softp; 230203831d35Sstevel int error = 0; 230303831d35Sstevel int count; 230403831d35Sstevel clock_t current_tstamp; 230503831d35Sstevel clock_t tstamp_interval; 230603831d35Sstevel struct iosramsoft *last_master = NULL; 230703831d35Sstevel static int last_master_instance = -1; 230803831d35Sstevel 230903831d35Sstevel IOSRAMLOG(1, "SwtchFrom: instance:%d iosram_master:%p (%d)\n", 231003831d35Sstevel instance, iosram_master, 231103831d35Sstevel ((iosram_master) ? iosram_master->instance : -1), NULL); 231203831d35Sstevel 231303831d35Sstevel mutex_enter(&iosram_mutex); 231403831d35Sstevel 231503831d35Sstevel /* 231603831d35Sstevel * Wait if another tunnel switch is in progress 231703831d35Sstevel */ 231803831d35Sstevel for (count = 0; iosram_tswitch_active && count < IOSRAM_TSWITCH_RETRY; 231903831d35Sstevel count++) { 232003831d35Sstevel iosram_tswitch_wakeup = 1; 232103831d35Sstevel cv_wait(&iosram_tswitch_wait, &iosram_mutex); 232203831d35Sstevel } 232303831d35Sstevel 232403831d35Sstevel if (iosram_tswitch_active) { 232503831d35Sstevel mutex_exit(&iosram_mutex); 232603831d35Sstevel return (EAGAIN); 232703831d35Sstevel } 232803831d35Sstevel 232903831d35Sstevel /* 233003831d35Sstevel * Check if the specified instance holds the tunnel. If not, 233103831d35Sstevel * then we are done. 233203831d35Sstevel */ 233303831d35Sstevel if ((iosram_master == NULL) || (iosram_master->instance != instance)) { 233403831d35Sstevel mutex_exit(&iosram_mutex); 233503831d35Sstevel return (0); 233603831d35Sstevel } 233703831d35Sstevel 233803831d35Sstevel /* 233903831d35Sstevel * Before beginning the tunnel switch process, wait for any outstanding 234003831d35Sstevel * read/write activity to complete. 234103831d35Sstevel */ 234203831d35Sstevel iosram_tswitch_active = 1; 234303831d35Sstevel while (iosram_rw_active) { 234403831d35Sstevel iosram_rw_wakeup = 1; 234503831d35Sstevel cv_wait(&iosram_rw_wait, &iosram_mutex); 234603831d35Sstevel } 234703831d35Sstevel 234803831d35Sstevel /* 234903831d35Sstevel * If a previous tunnel switch just completed, we have to make sure 235003831d35Sstevel * HWAD has enough time to find the new tunnel before we switch 235103831d35Sstevel * away from it. Otherwise, OBP's mailbox message to OSD will never 235203831d35Sstevel * get through. Just to be paranoid about synchronization of lbolt 235303831d35Sstevel * across different CPUs, make sure the current attempt isn't noted 235403831d35Sstevel * as starting _before_ the last tunnel switch completed. 235503831d35Sstevel */ 235603831d35Sstevel current_tstamp = ddi_get_lbolt(); 235703831d35Sstevel if (current_tstamp > iosram_tswitch_tstamp) { 235803831d35Sstevel tstamp_interval = current_tstamp - iosram_tswitch_tstamp; 235903831d35Sstevel } else { 236003831d35Sstevel tstamp_interval = 0; 236103831d35Sstevel } 236203831d35Sstevel if (drv_hztousec(tstamp_interval) < IOSRAM_TSWITCH_DELAY_US) { 236303831d35Sstevel mutex_exit(&iosram_mutex); 236403831d35Sstevel delay(drv_usectohz(IOSRAM_TSWITCH_DELAY_US) - tstamp_interval); 236503831d35Sstevel mutex_enter(&iosram_mutex); 236603831d35Sstevel } 236703831d35Sstevel 236803831d35Sstevel /* 236903831d35Sstevel * The specified instance holds the tunnel. We need to move it to some 237003831d35Sstevel * other IOSRAM. Try out all possible IOSRAMs listed in 237103831d35Sstevel * iosram_instances. For now, we always search from the first entry. 237203831d35Sstevel * In future, it may be desirable to start where we left off. 237303831d35Sstevel */ 237403831d35Sstevel for (softp = iosram_instances; softp != NULL; softp = softp->next) { 237503831d35Sstevel if (iosram_tswitch_aborted) { 237603831d35Sstevel break; 237703831d35Sstevel } 237803831d35Sstevel 237903831d35Sstevel /* we can't switch _to_ the instance we're switching _from_ */ 238003831d35Sstevel if (softp->instance == instance) { 238103831d35Sstevel continue; 238203831d35Sstevel } 238303831d35Sstevel 238403831d35Sstevel /* skip over instances being detached */ 238503831d35Sstevel if (softp->state & IOSRAM_STATE_DETACH) { 238603831d35Sstevel continue; 238703831d35Sstevel } 238803831d35Sstevel 238903831d35Sstevel /* 239003831d35Sstevel * Try to avoid reverting to the last instance we switched away 239103831d35Sstevel * from, as we expect that one to be detached eventually. Keep 239203831d35Sstevel * track of it, though, so we can go ahead and try switching to 239303831d35Sstevel * it if no other viable candidates are found. 239403831d35Sstevel */ 239503831d35Sstevel if (softp->instance == last_master_instance) { 239603831d35Sstevel last_master = softp; 239703831d35Sstevel continue; 239803831d35Sstevel } 239903831d35Sstevel 240003831d35Sstevel /* 240103831d35Sstevel * Do the tunnel switch. If successful, record the instance of 240203831d35Sstevel * the master we just left behind so we can try to avoid 240303831d35Sstevel * reverting to it next time. 240403831d35Sstevel */ 240503831d35Sstevel if (iosram_switch_tunnel(softp) == 0) { 240603831d35Sstevel last_master_instance = instance; 240703831d35Sstevel break; 240803831d35Sstevel } 240903831d35Sstevel } 241003831d35Sstevel 241103831d35Sstevel /* 241203831d35Sstevel * If we failed to switch the tunnel, but we skipped over an instance 241303831d35Sstevel * that had previously been switched out of because we expected it to be 241403831d35Sstevel * detached, go ahead and try it anyway (unless the tswitch was aborted 241503831d35Sstevel * or the instance we skipped is finally being detached). 241603831d35Sstevel */ 241703831d35Sstevel if ((softp == NULL) && (last_master != NULL) && 241803831d35Sstevel !iosram_tswitch_aborted && 241903831d35Sstevel !(last_master->state & IOSRAM_STATE_DETACH)) { 242003831d35Sstevel if (iosram_switch_tunnel(last_master) == 0) { 242103831d35Sstevel softp = last_master; 242203831d35Sstevel last_master_instance = instance; 242303831d35Sstevel } 242403831d35Sstevel } 242503831d35Sstevel 242603831d35Sstevel if ((softp == NULL) || (iosram_tswitch_aborted)) { 242703831d35Sstevel error = EIO; 242803831d35Sstevel } 242903831d35Sstevel 243003831d35Sstevel /* 243103831d35Sstevel * If there are additional tunnel switches queued up waiting for this 243203831d35Sstevel * one to complete, wake them up. 243303831d35Sstevel */ 243403831d35Sstevel if (iosram_tswitch_wakeup) { 243503831d35Sstevel iosram_tswitch_wakeup = 0; 243603831d35Sstevel cv_broadcast(&iosram_tswitch_wait); 243703831d35Sstevel } 243803831d35Sstevel iosram_tswitch_active = 0; 243903831d35Sstevel mutex_exit(&iosram_mutex); 244003831d35Sstevel return (error); 244103831d35Sstevel } 244203831d35Sstevel 244303831d35Sstevel 244403831d35Sstevel /* 244503831d35Sstevel * iosram_tunnel_capable(softp) 244603831d35Sstevel * Check if this IOSRAM instance is tunnel-capable by looing at 244703831d35Sstevel * "tunnel-capable" property. 244803831d35Sstevel */ 244903831d35Sstevel static int 245003831d35Sstevel iosram_tunnel_capable(struct iosramsoft *softp) 245103831d35Sstevel { 245203831d35Sstevel int proplen; 245303831d35Sstevel int tunnel_capable; 245403831d35Sstevel 245503831d35Sstevel /* 245603831d35Sstevel * Look up IOSRAM_TUNNELOK_PROP property, if any. 245703831d35Sstevel */ 245803831d35Sstevel proplen = sizeof (tunnel_capable); 245903831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, softp->dip, 246003831d35Sstevel DDI_PROP_DONTPASS, IOSRAM_TUNNELOK_PROP, (caddr_t)&tunnel_capable, 246103831d35Sstevel &proplen) != DDI_PROP_SUCCESS) { 246203831d35Sstevel tunnel_capable = 0; 246303831d35Sstevel } 246403831d35Sstevel return (tunnel_capable); 246503831d35Sstevel } 246603831d35Sstevel 246703831d35Sstevel 246803831d35Sstevel static int 246903831d35Sstevel iosram_sbbc_setup_map(struct iosramsoft *softp) 247003831d35Sstevel { 247103831d35Sstevel int rv; 247203831d35Sstevel struct ddi_device_acc_attr attr; 247303831d35Sstevel dev_info_t *dip = softp->dip; 247403831d35Sstevel uint32_t sema_val; 247503831d35Sstevel 247603831d35Sstevel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 247703831d35Sstevel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 247803831d35Sstevel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 247903831d35Sstevel 248003831d35Sstevel mutex_enter(&iosram_mutex); 248103831d35Sstevel mutex_enter(&softp->intr_mutex); 248203831d35Sstevel 248303831d35Sstevel /* 248403831d35Sstevel * Map SBBC region in 248503831d35Sstevel */ 248603831d35Sstevel if ((rv = ddi_regs_map_setup(dip, IOSRAM_SBBC_MAP_INDEX, 248703831d35Sstevel (caddr_t *)&softp->sbbc_region, 248803831d35Sstevel IOSRAM_SBBC_MAP_OFFSET, sizeof (iosram_sbbc_region_t), 248903831d35Sstevel &attr, &softp->sbbc_handle)) != DDI_SUCCESS) { 249003831d35Sstevel DPRINTF(1, ("Failed to map SBBC region.\n")); 249103831d35Sstevel mutex_exit(&softp->intr_mutex); 249203831d35Sstevel mutex_exit(&iosram_mutex); 249303831d35Sstevel return (rv); 249403831d35Sstevel } 249503831d35Sstevel 249603831d35Sstevel /* 249703831d35Sstevel * Disable SBBC interrupts. SBBC interrupts are enabled 249803831d35Sstevel * once the interrupt handler is registered. 249903831d35Sstevel */ 250003831d35Sstevel ddi_put32(softp->sbbc_handle, 250103831d35Sstevel &(softp->sbbc_region->int_enable.reg), 0x0); 250203831d35Sstevel 250303831d35Sstevel /* 250403831d35Sstevel * Clear hardware semaphore value if appropriate. 250503831d35Sstevel * When the first SBBC is mapped in by the IOSRAM driver, 250603831d35Sstevel * the value of the semaphore should be initialized only 250703831d35Sstevel * if it is not held by SMS. For subsequent SBBC's, the 250803831d35Sstevel * semaphore will be always initialized. 250903831d35Sstevel */ 251003831d35Sstevel sema_val = IOSRAM_SEMA_RD(softp); 251103831d35Sstevel 251203831d35Sstevel if (!iosram_master) { 251303831d35Sstevel /* the first SBBC is being mapped in */ 251403831d35Sstevel if (!(IOSRAM_SEMA_IS_HELD(sema_val) && 251503831d35Sstevel IOSRAM_SEMA_GET_IDX(sema_val) == IOSRAM_SEMA_SMS_IDX)) { 251603831d35Sstevel /* not held by SMS, we clear the semaphore */ 251703831d35Sstevel IOSRAM_SEMA_WR(softp, 0); 251803831d35Sstevel } 251903831d35Sstevel } else { 252003831d35Sstevel /* not the first SBBC, we clear the semaphore */ 252103831d35Sstevel IOSRAM_SEMA_WR(softp, 0); 252203831d35Sstevel } 252303831d35Sstevel 252403831d35Sstevel mutex_exit(&softp->intr_mutex); 252503831d35Sstevel mutex_exit(&iosram_mutex); 252603831d35Sstevel return (0); 252703831d35Sstevel } 252803831d35Sstevel 252903831d35Sstevel 253003831d35Sstevel static int 253103831d35Sstevel iosram_setup_map(struct iosramsoft *softp) 253203831d35Sstevel { 253303831d35Sstevel int instance = softp->instance; 253403831d35Sstevel dev_info_t *dip = softp->dip; 253503831d35Sstevel int portid; 253603831d35Sstevel int proplen; 253703831d35Sstevel caddr_t propvalue; 253803831d35Sstevel struct ddi_device_acc_attr attr; 253903831d35Sstevel 254003831d35Sstevel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 254103831d35Sstevel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 254203831d35Sstevel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; 254303831d35Sstevel 254403831d35Sstevel /* 254503831d35Sstevel * Lookup IOSRAM_REG_PROP property to find out our IOSRAM length 254603831d35Sstevel */ 254703831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip, 254803831d35Sstevel DDI_PROP_DONTPASS, IOSRAM_REG_PROP, (caddr_t)&propvalue, 254903831d35Sstevel &proplen) != DDI_PROP_SUCCESS) { 255003831d35Sstevel cmn_err(CE_WARN, "iosram(%d): can't find register property.\n", 255103831d35Sstevel instance); 255203831d35Sstevel return (DDI_FAILURE); 255303831d35Sstevel } else { 255403831d35Sstevel iosram_reg_t *regprop = (iosram_reg_t *)propvalue; 255503831d35Sstevel 255603831d35Sstevel DPRINTF(1, ("SetupMap(%d): Got reg prop: %x %x %x\n", 255703831d35Sstevel instance, regprop->addr_hi, 255803831d35Sstevel regprop->addr_lo, regprop->size)); 255903831d35Sstevel 256003831d35Sstevel softp->iosramlen = regprop->size; 256103831d35Sstevel 256203831d35Sstevel kmem_free(propvalue, proplen); 256303831d35Sstevel } 256403831d35Sstevel DPRINTF(1, ("SetupMap(%d): IOSRAM length: 0x%x\n", instance, 256503831d35Sstevel softp->iosramlen)); 256603831d35Sstevel softp->handle = NULL; 256703831d35Sstevel 256803831d35Sstevel /* 256903831d35Sstevel * To minimize boot time, we map the entire IOSRAM as opposed to 257003831d35Sstevel * mapping individual chunk via ddi_regs_map_setup() call. 257103831d35Sstevel */ 257203831d35Sstevel if (ddi_regs_map_setup(dip, 0, (caddr_t *)&softp->iosramp, 257303831d35Sstevel 0x0, softp->iosramlen, &attr, &softp->handle) != DDI_SUCCESS) { 257403831d35Sstevel cmn_err(CE_WARN, "iosram(%d): failed to map IOSRAM len:%x\n", 257503831d35Sstevel instance, softp->iosramlen); 257603831d35Sstevel iosram_remove_map(softp); 257703831d35Sstevel return (DDI_FAILURE); 257803831d35Sstevel } 257903831d35Sstevel 258003831d35Sstevel /* 258103831d35Sstevel * Lookup PORTID property on my parent hierarchy 258203831d35Sstevel */ 258303831d35Sstevel proplen = sizeof (portid); 258403831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 258503831d35Sstevel 0, IOSRAM_PORTID_PROP, (caddr_t)&portid, 258603831d35Sstevel &proplen) != DDI_PROP_SUCCESS) { 258703831d35Sstevel cmn_err(CE_WARN, "iosram(%d): can't find portid property.\n", 258803831d35Sstevel instance); 258903831d35Sstevel iosram_remove_map(softp); 259003831d35Sstevel return (DDI_FAILURE); 259103831d35Sstevel } 259203831d35Sstevel softp->portid = portid; 259303831d35Sstevel 259403831d35Sstevel if (iosram_sbbc_setup_map(softp) != DDI_SUCCESS) { 259503831d35Sstevel cmn_err(CE_WARN, "iosram(%d): can't map SBBC region.\n", 259603831d35Sstevel instance); 259703831d35Sstevel iosram_remove_map(softp); 259803831d35Sstevel return (DDI_FAILURE); 259903831d35Sstevel } 260003831d35Sstevel 260103831d35Sstevel mutex_enter(&iosram_mutex); 260203831d35Sstevel softp->state |= IOSRAM_STATE_MAPPED; 260303831d35Sstevel mutex_exit(&iosram_mutex); 260403831d35Sstevel 260503831d35Sstevel return (DDI_SUCCESS); 260603831d35Sstevel } 260703831d35Sstevel 260803831d35Sstevel 260903831d35Sstevel static void 261003831d35Sstevel iosram_remove_map(struct iosramsoft *softp) 261103831d35Sstevel { 261203831d35Sstevel mutex_enter(&iosram_mutex); 261303831d35Sstevel 261403831d35Sstevel ASSERT((softp->state & IOSRAM_STATE_MASTER) == 0); 261503831d35Sstevel 261603831d35Sstevel if (softp->handle) { 261703831d35Sstevel ddi_regs_map_free(&softp->handle); 261803831d35Sstevel softp->handle = NULL; 261903831d35Sstevel } 262003831d35Sstevel softp->iosramp = NULL; 262103831d35Sstevel 262203831d35Sstevel /* 262303831d35Sstevel * Umap SBBC registers region. Shared with handler for SBBC 262403831d35Sstevel * interrupts, take intr_mutex. 262503831d35Sstevel */ 262603831d35Sstevel mutex_enter(&softp->intr_mutex); 262703831d35Sstevel if (softp->sbbc_region) { 262803831d35Sstevel ddi_regs_map_free(&softp->sbbc_handle); 262903831d35Sstevel softp->sbbc_region = NULL; 263003831d35Sstevel } 263103831d35Sstevel mutex_exit(&softp->intr_mutex); 263203831d35Sstevel 263303831d35Sstevel softp->state &= ~IOSRAM_STATE_MAPPED; 263403831d35Sstevel 263503831d35Sstevel mutex_exit(&iosram_mutex); 263603831d35Sstevel } 263703831d35Sstevel 263803831d35Sstevel 263903831d35Sstevel /* 264003831d35Sstevel * iosram_is_chosen(struct iosramsoft *softp) 264103831d35Sstevel * 264203831d35Sstevel * Looks up "chosen" node property to 264303831d35Sstevel * determine if it is the chosen IOSRAM. 264403831d35Sstevel */ 264503831d35Sstevel static int 264603831d35Sstevel iosram_is_chosen(struct iosramsoft *softp) 264703831d35Sstevel { 264803831d35Sstevel char chosen_iosram[MAXNAMELEN]; 264903831d35Sstevel char pn[MAXNAMELEN]; 265003831d35Sstevel int nodeid; 265103831d35Sstevel int chosen; 265203831d35Sstevel pnode_t dnode; 265303831d35Sstevel 265403831d35Sstevel /* 265503831d35Sstevel * Get /chosen node info. prom interface will handle errors. 265603831d35Sstevel */ 265703831d35Sstevel dnode = prom_chosennode(); 265803831d35Sstevel 265903831d35Sstevel /* 266003831d35Sstevel * Look for the "iosram" property on the chosen node with a prom 266103831d35Sstevel * interface as ddi_find_devinfo() couldn't be used (calls 266203831d35Sstevel * ddi_walk_devs() that creates one extra lock on the device tree). 266303831d35Sstevel */ 266403831d35Sstevel if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) { 266503831d35Sstevel /* 266603831d35Sstevel * Can't find IOSRAM_CHOSEN_PROP property under chosen node 266703831d35Sstevel */ 266803831d35Sstevel cmn_err(CE_WARN, 266903831d35Sstevel "iosram(%d): can't find chosen iosram property\n", 267003831d35Sstevel softp->instance); 267103831d35Sstevel return (0); 267203831d35Sstevel } 267303831d35Sstevel 267403831d35Sstevel DPRINTF(1, ("iosram(%d): Got '%x' for chosen '%s' property\n", 267503831d35Sstevel softp->instance, nodeid, IOSRAM_CHOSEN_PROP)); 267603831d35Sstevel 267703831d35Sstevel /* 267803831d35Sstevel * get the full OBP pathname of this node 267903831d35Sstevel */ 268003831d35Sstevel if (prom_phandle_to_path((phandle_t)nodeid, chosen_iosram, 268103831d35Sstevel sizeof (chosen_iosram)) < 0) { 268203831d35Sstevel cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", nodeid); 268303831d35Sstevel return (0); 268403831d35Sstevel } 268503831d35Sstevel DPRINTF(1, ("iosram(%d): prom_phandle_to_path(%x) is '%s'\n", 268603831d35Sstevel softp->instance, nodeid, chosen_iosram)); 268703831d35Sstevel 268803831d35Sstevel (void) ddi_pathname(softp->dip, pn); 268903831d35Sstevel DPRINTF(1, ("iosram(%d): ddi_pathname(%p) is '%s'\n", 2690*07d06da5SSurya Prakki softp->instance, (void *)softp->dip, pn)); 269103831d35Sstevel 269203831d35Sstevel chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0; 269303831d35Sstevel DPRINTF(1, ("iosram(%d): ... %s\n", softp->instance, 269403831d35Sstevel chosen ? "MASTER" : "SLAVE")); 269503831d35Sstevel IOSRAMLOG(1, "iosram(%d): ... %s\n", softp->instance, 269603831d35Sstevel (chosen ? "MASTER" : "SLAVE"), NULL, NULL); 269703831d35Sstevel 269803831d35Sstevel return (chosen); 269903831d35Sstevel } 270003831d35Sstevel 270103831d35Sstevel 270203831d35Sstevel /* 270303831d35Sstevel * iosram_set_master(struct iosramsoft *softp) 270403831d35Sstevel * 270503831d35Sstevel * Set master tunnel to the specified IOSRAM 270603831d35Sstevel * Must be called while holding iosram_mutex. 270703831d35Sstevel */ 270803831d35Sstevel static void 270903831d35Sstevel iosram_set_master(struct iosramsoft *softp) 271003831d35Sstevel { 271103831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 271203831d35Sstevel ASSERT(softp != NULL); 271303831d35Sstevel ASSERT(softp->state & IOSRAM_STATE_MAPPED); 271403831d35Sstevel ASSERT(IOSRAM_GET_HDRFIELD32(softp, status) == IOSRAM_VALID); 271503831d35Sstevel 271603831d35Sstevel /* 271703831d35Sstevel * Clear MASTER flag on any previous IOSRAM master, if any 271803831d35Sstevel */ 271903831d35Sstevel if (iosram_master && (iosram_master != softp)) { 272003831d35Sstevel iosram_master->state &= ~IOSRAM_STATE_MASTER; 272103831d35Sstevel } 272203831d35Sstevel 272303831d35Sstevel /* 272403831d35Sstevel * Setup new IOSRAM master 272503831d35Sstevel */ 272603831d35Sstevel iosram_update_addrs(softp); 272703831d35Sstevel iosram_handle = softp->handle; 272803831d35Sstevel softp->state |= IOSRAM_STATE_MASTER; 272903831d35Sstevel softp->tswitch_ok++; 273003831d35Sstevel iosram_master = softp; 273103831d35Sstevel 273203831d35Sstevel IOSRAMLOG(1, "SETMASTER: softp:%p instance:%d\n", softp, 273303831d35Sstevel softp->instance, NULL, NULL); 273403831d35Sstevel } 273503831d35Sstevel 273603831d35Sstevel 273703831d35Sstevel /* 273803831d35Sstevel * iosram_read_toc() 273903831d35Sstevel * 274003831d35Sstevel * Read the TOC from an IOSRAM instance that has been mapped in. 274103831d35Sstevel * If the TOC is flawed or the IOSRAM isn't valid, return an error. 274203831d35Sstevel */ 274303831d35Sstevel static int 274403831d35Sstevel iosram_read_toc(struct iosramsoft *softp) 274503831d35Sstevel { 274603831d35Sstevel int i; 274703831d35Sstevel int instance = softp->instance; 274803831d35Sstevel uint8_t *toc_entryp; 274903831d35Sstevel iosram_flags_t *flagsp = NULL; 275003831d35Sstevel int new_nchunks; 275103831d35Sstevel iosram_chunk_t *new_chunks; 275203831d35Sstevel iosram_chunk_t *chunkp; 275303831d35Sstevel iosram_chunk_t *old_chunkp; 275403831d35Sstevel iosram_toc_entry_t index; 275503831d35Sstevel 275603831d35Sstevel /* 275703831d35Sstevel * Never try to read the TOC out of an unmapped IOSRAM. 275803831d35Sstevel */ 275903831d35Sstevel ASSERT(softp->state & IOSRAM_STATE_MAPPED); 276003831d35Sstevel 276103831d35Sstevel mutex_enter(&iosram_mutex); 276203831d35Sstevel 276303831d35Sstevel /* 276403831d35Sstevel * Check to make sure this IOSRAM is marked valid. Return 276503831d35Sstevel * an error if it isn't. 276603831d35Sstevel */ 276703831d35Sstevel if (IOSRAM_GET_HDRFIELD32(softp, status) != IOSRAM_VALID) { 276803831d35Sstevel DPRINTF(1, ("iosram_read_toc(%d): IOSRAM not flagged valid\n", 276903831d35Sstevel instance)); 277003831d35Sstevel mutex_exit(&iosram_mutex); 277103831d35Sstevel return (EINVAL); 277203831d35Sstevel } 277303831d35Sstevel 277403831d35Sstevel /* 277503831d35Sstevel * Get the location of the TOC. 277603831d35Sstevel */ 277703831d35Sstevel toc_entryp = softp->iosramp + IOSRAM_GET_HDRFIELD32(softp, toc_offset); 277803831d35Sstevel 277903831d35Sstevel /* 278003831d35Sstevel * Read the index entry from the TOC and make sure it looks correct. 278103831d35Sstevel */ 278203831d35Sstevel ddi_rep_get8(softp->handle, (uint8_t *)&index, toc_entryp, 278303831d35Sstevel sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR); 278403831d35Sstevel if ((index.key != IOSRAM_INDEX_KEY) || 278503831d35Sstevel (index.off != IOSRAM_INDEX_OFF)) { 278603831d35Sstevel cmn_err(CE_WARN, "iosram(%d): invalid TOC index.\n", instance); 278703831d35Sstevel mutex_exit(&iosram_mutex); 278803831d35Sstevel return (EINVAL); 278903831d35Sstevel } 279003831d35Sstevel 279103831d35Sstevel /* 279203831d35Sstevel * Allocate storage for the new chunks array and initialize it with data 279303831d35Sstevel * from the TOC and callback data from the corresponding old chunk, if 279403831d35Sstevel * it exists. 279503831d35Sstevel */ 279603831d35Sstevel new_nchunks = index.len - 1; 279703831d35Sstevel new_chunks = (iosram_chunk_t *)kmem_zalloc(new_nchunks * 279803831d35Sstevel sizeof (iosram_chunk_t), KM_SLEEP); 279903831d35Sstevel for (i = 0, chunkp = new_chunks; i < new_nchunks; i++, chunkp++) { 280003831d35Sstevel toc_entryp += sizeof (iosram_toc_entry_t); 280103831d35Sstevel ddi_rep_get8(softp->handle, (uint8_t *)&(chunkp->toc_data), 280203831d35Sstevel toc_entryp, sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR); 280303831d35Sstevel chunkp->hash = NULL; 280403831d35Sstevel if ((chunkp->toc_data.off < softp->iosramlen) && 280503831d35Sstevel (chunkp->toc_data.len <= softp->iosramlen) && 280603831d35Sstevel ((chunkp->toc_data.off + chunkp->toc_data.len) <= 280703831d35Sstevel softp->iosramlen)) { 280803831d35Sstevel chunkp->basep = softp->iosramp + chunkp->toc_data.off; 280903831d35Sstevel DPRINTF(1, 2810*07d06da5SSurya Prakki ("iosram_read_toc(%d): k:%x o:%x l:%x p:%p\n", 281103831d35Sstevel instance, chunkp->toc_data.key, 281203831d35Sstevel chunkp->toc_data.off, chunkp->toc_data.len, 2813*07d06da5SSurya Prakki (void *)chunkp->basep)); 281403831d35Sstevel } else { 281503831d35Sstevel cmn_err(CE_WARN, "iosram(%d): TOC entry %d" 281603831d35Sstevel "out of range... off:%x len:%x\n", 281703831d35Sstevel instance, i + 1, chunkp->toc_data.off, 281803831d35Sstevel chunkp->toc_data.len); 281903831d35Sstevel kmem_free(new_chunks, new_nchunks * 282003831d35Sstevel sizeof (iosram_chunk_t)); 282103831d35Sstevel mutex_exit(&iosram_mutex); 282203831d35Sstevel return (EINVAL); 282303831d35Sstevel } 282403831d35Sstevel 282503831d35Sstevel /* 282603831d35Sstevel * Note the existence of the flags chunk, which is required in 282703831d35Sstevel * a correct TOC. 282803831d35Sstevel */ 282903831d35Sstevel if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) { 283003831d35Sstevel flagsp = (iosram_flags_t *)chunkp->basep; 283103831d35Sstevel } 283203831d35Sstevel 283303831d35Sstevel /* 283403831d35Sstevel * If there was an entry for this chunk in the old list, copy 283503831d35Sstevel * the callback data from old to new storage. 283603831d35Sstevel */ 283703831d35Sstevel if ((nchunks > 0) && 283803831d35Sstevel ((old_chunkp = iosram_find_chunk(chunkp->toc_data.key)) != 283903831d35Sstevel NULL)) { 284003831d35Sstevel bcopy(&(old_chunkp->cback), &(chunkp->cback), 284103831d35Sstevel sizeof (iosram_cback_t)); 284203831d35Sstevel } 284303831d35Sstevel } 284403831d35Sstevel /* 284503831d35Sstevel * The TOC is malformed if there is no entry for the flags chunk. 284603831d35Sstevel */ 284703831d35Sstevel if (flagsp == NULL) { 284803831d35Sstevel kmem_free(new_chunks, new_nchunks * sizeof (iosram_chunk_t)); 284903831d35Sstevel mutex_exit(&iosram_mutex); 285003831d35Sstevel return (EINVAL); 285103831d35Sstevel } 285203831d35Sstevel 285303831d35Sstevel /* 285403831d35Sstevel * Free any memory that is no longer needed and install the new data 285503831d35Sstevel * as current data. 285603831d35Sstevel */ 285703831d35Sstevel if (chunks != NULL) { 285803831d35Sstevel kmem_free(chunks, nchunks * sizeof (iosram_chunk_t)); 285903831d35Sstevel } 286003831d35Sstevel chunks = new_chunks; 286103831d35Sstevel nchunks = new_nchunks; 286203831d35Sstevel iosram_init_hashtab(); 286303831d35Sstevel 286403831d35Sstevel mutex_exit(&iosram_mutex); 286503831d35Sstevel return (0); 286603831d35Sstevel } 286703831d35Sstevel 286803831d35Sstevel 286903831d35Sstevel /* 287003831d35Sstevel * iosram_init_hashtab() 287103831d35Sstevel * 287203831d35Sstevel * Initialize the hash table and populate it with the IOSRAM 287303831d35Sstevel * chunks previously read from the TOC. The caller must hold the 287403831d35Sstevel * ioram_mutex lock. 287503831d35Sstevel */ 287603831d35Sstevel static void 287703831d35Sstevel iosram_init_hashtab(void) 287803831d35Sstevel { 287903831d35Sstevel int i, bucket; 288003831d35Sstevel iosram_chunk_t *chunkp; 288103831d35Sstevel 288203831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 288303831d35Sstevel 288403831d35Sstevel for (i = 0; i < IOSRAM_HASHSZ; i++) { 288503831d35Sstevel iosram_hashtab[i] = NULL; 288603831d35Sstevel } 288703831d35Sstevel 288803831d35Sstevel if (chunks) { 288903831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) { 289003831d35Sstevel /* 289103831d35Sstevel * Hide the flags chunk by leaving it out of the hash 289203831d35Sstevel * table. 289303831d35Sstevel */ 289403831d35Sstevel if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) { 289503831d35Sstevel continue; 289603831d35Sstevel } 289703831d35Sstevel 289803831d35Sstevel /* 289903831d35Sstevel * Add the current chunk to the hash table. 290003831d35Sstevel */ 290103831d35Sstevel bucket = IOSRAM_HASH(chunkp->toc_data.key); 290203831d35Sstevel chunkp->hash = iosram_hashtab[bucket]; 290303831d35Sstevel iosram_hashtab[bucket] = chunkp; 290403831d35Sstevel } 290503831d35Sstevel } 290603831d35Sstevel } 290703831d35Sstevel 290803831d35Sstevel 290903831d35Sstevel /* 291003831d35Sstevel * iosram_update_addrs() 291103831d35Sstevel * 291203831d35Sstevel * Process the chunk list, updating each chunk's basep, which is a pointer 291303831d35Sstevel * to the beginning of the chunk's memory in kvaddr space. Record the 291403831d35Sstevel * basep value of the flags chunk to speed up flag access. The caller 291503831d35Sstevel * must hold the iosram_mutex lock. 291603831d35Sstevel */ 291703831d35Sstevel static void 291803831d35Sstevel iosram_update_addrs(struct iosramsoft *softp) 291903831d35Sstevel { 292003831d35Sstevel int i; 292103831d35Sstevel iosram_flags_t *flagsp; 292203831d35Sstevel iosram_chunk_t *chunkp; 292303831d35Sstevel 292403831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 292503831d35Sstevel 292603831d35Sstevel /* 292703831d35Sstevel * First go through all of the chunks updating their base pointers and 292803831d35Sstevel * looking for the flags chunk. 292903831d35Sstevel */ 293003831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) { 293103831d35Sstevel chunkp->basep = softp->iosramp + chunkp->toc_data.off; 293203831d35Sstevel if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) { 293303831d35Sstevel flagsp = (iosram_flags_t *)(chunkp->basep); 293403831d35Sstevel DPRINTF(1, 293503831d35Sstevel ("iosram_update_addrs flags: o:0x%08x p:%p", 2936*07d06da5SSurya Prakki chunkp->toc_data.off, (void *)flagsp)); 293703831d35Sstevel } 293803831d35Sstevel } 293903831d35Sstevel 294003831d35Sstevel /* 294103831d35Sstevel * Now, go through and update each chunk's flags pointer. This can't be 294203831d35Sstevel * done in the first loop because we don't have the address of the flags 294303831d35Sstevel * chunk yet. 294403831d35Sstevel */ 294503831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) { 294603831d35Sstevel chunkp->flagsp = flagsp++; 294703831d35Sstevel DPRINTF(1, ("iosram_update_addrs: k:0x%x f:%p\n", 2948*07d06da5SSurya Prakki chunkp->toc_data.key, (void *)chunkp->flagsp)); 294903831d35Sstevel } 295003831d35Sstevel } 295103831d35Sstevel 295203831d35Sstevel /* 295303831d35Sstevel * iosram_find_chunk(key) 295403831d35Sstevel * 295503831d35Sstevel * Return a pointer to iosram_chunk structure corresponding to the 295603831d35Sstevel * "key" IOSRAM chunk. The caller must hold the iosram_mutex lock. 295703831d35Sstevel */ 295803831d35Sstevel static iosram_chunk_t * 295903831d35Sstevel iosram_find_chunk(uint32_t key) 296003831d35Sstevel { 296103831d35Sstevel iosram_chunk_t *chunkp; 296203831d35Sstevel int index = IOSRAM_HASH(key); 296303831d35Sstevel 296403831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 296503831d35Sstevel 296603831d35Sstevel for (chunkp = iosram_hashtab[index]; chunkp; chunkp = chunkp->hash) { 296703831d35Sstevel if (chunkp->toc_data.key == key) { 296803831d35Sstevel break; 296903831d35Sstevel } 297003831d35Sstevel } 297103831d35Sstevel 297203831d35Sstevel return (chunkp); 297303831d35Sstevel } 297403831d35Sstevel 297503831d35Sstevel 297603831d35Sstevel /* 297703831d35Sstevel * iosram_add_intr(iosramsoft_t *) 297803831d35Sstevel */ 297903831d35Sstevel static int 298003831d35Sstevel iosram_add_intr(iosramsoft_t *softp) 298103831d35Sstevel { 298203831d35Sstevel IOSRAMLOG(2, "ADDINTR: softp:%p instance:%d\n", 298303831d35Sstevel softp, softp->instance, NULL, NULL); 298403831d35Sstevel 298503831d35Sstevel if (ddi_add_softintr(softp->dip, DDI_SOFTINT_MED, 298603831d35Sstevel &softp->softintr_id, &softp->soft_iblk, NULL, 298703831d35Sstevel iosram_softintr, (caddr_t)softp) != DDI_SUCCESS) { 298803831d35Sstevel cmn_err(CE_WARN, 298903831d35Sstevel "iosram(%d): Can't register softintr.\n", 299003831d35Sstevel softp->instance); 299103831d35Sstevel return (DDI_FAILURE); 299203831d35Sstevel } 299303831d35Sstevel 299403831d35Sstevel if (ddi_add_intr(softp->dip, 0, &softp->real_iblk, NULL, 299503831d35Sstevel iosram_intr, (caddr_t)softp) != DDI_SUCCESS) { 299603831d35Sstevel cmn_err(CE_WARN, 299703831d35Sstevel "iosram(%d): Can't register intr" 299803831d35Sstevel " handler.\n", softp->instance); 299903831d35Sstevel ddi_remove_softintr(softp->softintr_id); 300003831d35Sstevel return (DDI_FAILURE); 300103831d35Sstevel } 300203831d35Sstevel 300303831d35Sstevel /* 300403831d35Sstevel * Enable SBBC interrupts 300503831d35Sstevel */ 300603831d35Sstevel ddi_put32(softp->sbbc_handle, &(softp->sbbc_region->int_enable.reg), 300703831d35Sstevel IOSRAM_SBBC_INT0|IOSRAM_SBBC_INT1); 300803831d35Sstevel 300903831d35Sstevel return (DDI_SUCCESS); 301003831d35Sstevel } 301103831d35Sstevel 301203831d35Sstevel 301303831d35Sstevel /* 301403831d35Sstevel * iosram_remove_intr(iosramsoft_t *) 301503831d35Sstevel */ 301603831d35Sstevel static int 301703831d35Sstevel iosram_remove_intr(iosramsoft_t *softp) 301803831d35Sstevel { 301903831d35Sstevel IOSRAMLOG(2, "REMINTR: softp:%p instance:%d\n", 302003831d35Sstevel softp, softp->instance, NULL, NULL); 302103831d35Sstevel 302203831d35Sstevel /* 302303831d35Sstevel * Disable SBBC interrupts if SBBC is mapped in 302403831d35Sstevel */ 302503831d35Sstevel if (softp->sbbc_region) { 302603831d35Sstevel ddi_put32(softp->sbbc_handle, 302703831d35Sstevel &(softp->sbbc_region->int_enable.reg), 0); 302803831d35Sstevel } 302903831d35Sstevel 303003831d35Sstevel /* 303103831d35Sstevel * Remove SBBC interrupt handler 303203831d35Sstevel */ 303303831d35Sstevel ddi_remove_intr(softp->dip, 0, softp->real_iblk); 303403831d35Sstevel 303503831d35Sstevel /* 303603831d35Sstevel * Remove soft interrupt handler 303703831d35Sstevel */ 303803831d35Sstevel mutex_enter(&iosram_mutex); 303903831d35Sstevel if (softp->softintr_id != NULL) { 304003831d35Sstevel ddi_remove_softintr(softp->softintr_id); 304103831d35Sstevel softp->softintr_id = NULL; 304203831d35Sstevel } 304303831d35Sstevel mutex_exit(&iosram_mutex); 304403831d35Sstevel 304503831d35Sstevel return (0); 304603831d35Sstevel } 304703831d35Sstevel 304803831d35Sstevel 304903831d35Sstevel /* 305003831d35Sstevel * iosram_add_instance(iosramsoft_t *) 305103831d35Sstevel * Must be called while holding iosram_mutex 305203831d35Sstevel */ 305303831d35Sstevel static void 305403831d35Sstevel iosram_add_instance(iosramsoft_t *new_softp) 305503831d35Sstevel { 305603831d35Sstevel #ifdef DEBUG 305703831d35Sstevel int instance = new_softp->instance; 305803831d35Sstevel iosramsoft_t *softp; 305903831d35Sstevel #endif 306003831d35Sstevel 306103831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 306203831d35Sstevel 306303831d35Sstevel #if defined(DEBUG) 306403831d35Sstevel /* Verify that this instance is not in the list */ 306503831d35Sstevel for (softp = iosram_instances; softp != NULL; softp = softp->next) { 306603831d35Sstevel ASSERT(softp->instance != instance); 306703831d35Sstevel } 306803831d35Sstevel #endif 306903831d35Sstevel 307003831d35Sstevel /* 307103831d35Sstevel * Add this instance to the list 307203831d35Sstevel */ 307303831d35Sstevel if (iosram_instances != NULL) { 307403831d35Sstevel iosram_instances->prev = new_softp; 307503831d35Sstevel } 307603831d35Sstevel new_softp->next = iosram_instances; 307703831d35Sstevel new_softp->prev = NULL; 307803831d35Sstevel iosram_instances = new_softp; 307903831d35Sstevel } 308003831d35Sstevel 308103831d35Sstevel 308203831d35Sstevel /* 308303831d35Sstevel * iosram_remove_instance(int instance) 308403831d35Sstevel * Must be called while holding iosram_mutex 308503831d35Sstevel */ 308603831d35Sstevel static void 308703831d35Sstevel iosram_remove_instance(int instance) 308803831d35Sstevel { 308903831d35Sstevel iosramsoft_t *softp; 309003831d35Sstevel 309103831d35Sstevel /* 309203831d35Sstevel * Remove specified instance from the iosram_instances list so that 309303831d35Sstevel * it can't be chosen for tunnel in future. 309403831d35Sstevel */ 309503831d35Sstevel ASSERT(mutex_owned(&iosram_mutex)); 309603831d35Sstevel 309703831d35Sstevel for (softp = iosram_instances; softp != NULL; softp = softp->next) { 309803831d35Sstevel if (softp->instance == instance) { 309903831d35Sstevel if (softp->next != NULL) { 310003831d35Sstevel softp->next->prev = softp->prev; 310103831d35Sstevel } 310203831d35Sstevel if (softp->prev != NULL) { 310303831d35Sstevel softp->prev->next = softp->next; 310403831d35Sstevel } 310503831d35Sstevel if (iosram_instances == softp) { 310603831d35Sstevel iosram_instances = softp->next; 310703831d35Sstevel } 310803831d35Sstevel 310903831d35Sstevel return; 311003831d35Sstevel } 311103831d35Sstevel } 311203831d35Sstevel } 311303831d35Sstevel 311403831d35Sstevel 311503831d35Sstevel /* 311603831d35Sstevel * iosram_sema_acquire: Acquire hardware semaphore. 311703831d35Sstevel * Return 0 if the semaphore could be acquired, or one of the following 311803831d35Sstevel * possible values: 311903831d35Sstevel * EAGAIN: there is a tunnel switch in progress 312003831d35Sstevel * EBUSY: the semaphore was already "held" 312103831d35Sstevel * ENXIO: an IO error occured (e.g. SBBC not mapped) 312203831d35Sstevel * If old_value is not NULL, the location it points to will be updated 312303831d35Sstevel * with the semaphore value read when attempting to acquire it. 312403831d35Sstevel */ 312503831d35Sstevel int 312603831d35Sstevel iosram_sema_acquire(uint32_t *old_value) 312703831d35Sstevel { 312803831d35Sstevel struct iosramsoft *softp; 312903831d35Sstevel int rv; 313003831d35Sstevel uint32_t sema_val; 313103831d35Sstevel 313203831d35Sstevel DPRINTF(2, ("IOSRAM: in iosram_sema_acquire\n")); 313303831d35Sstevel 313403831d35Sstevel mutex_enter(&iosram_mutex); 313503831d35Sstevel 313603831d35Sstevel /* 313703831d35Sstevel * Disallow access if there is a tunnel switch in progress. 313803831d35Sstevel */ 313903831d35Sstevel if (iosram_tswitch_active) { 314003831d35Sstevel mutex_exit(&iosram_mutex); 314103831d35Sstevel return (EAGAIN); 314203831d35Sstevel } 314303831d35Sstevel 314403831d35Sstevel /* 314503831d35Sstevel * Use current master IOSRAM for operation, fail if none is 314603831d35Sstevel * currently active. 314703831d35Sstevel */ 314803831d35Sstevel if ((softp = iosram_master) == NULL) { 314903831d35Sstevel mutex_exit(&iosram_mutex); 315003831d35Sstevel DPRINTF(1, ("IOSRAM: iosram_sema_acquire: no master\n")); 315103831d35Sstevel return (ENXIO); 315203831d35Sstevel } 315303831d35Sstevel 315403831d35Sstevel mutex_enter(&softp->intr_mutex); 315503831d35Sstevel 315603831d35Sstevel /* 315703831d35Sstevel * Fail if SBBC region has not been mapped. This shouldn't 315803831d35Sstevel * happen if we have a master IOSRAM, but we double-check. 315903831d35Sstevel */ 316003831d35Sstevel if (softp->sbbc_region == NULL) { 316103831d35Sstevel mutex_exit(&softp->intr_mutex); 316203831d35Sstevel mutex_exit(&iosram_mutex); 316303831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: " 316403831d35Sstevel "SBBC not mapped\n", softp->instance)); 316503831d35Sstevel return (ENXIO); 316603831d35Sstevel } 316703831d35Sstevel 316803831d35Sstevel /* read semaphore value */ 316903831d35Sstevel sema_val = IOSRAM_SEMA_RD(softp); 317003831d35Sstevel if (old_value != NULL) 317103831d35Sstevel *old_value = sema_val; 317203831d35Sstevel 317303831d35Sstevel if (IOSRAM_SEMA_IS_HELD(sema_val)) { 317403831d35Sstevel /* semaphore was held by someone else */ 317503831d35Sstevel rv = EBUSY; 317603831d35Sstevel } else { 317703831d35Sstevel /* semaphore was not held, we just acquired it */ 317803831d35Sstevel rv = 0; 317903831d35Sstevel } 318003831d35Sstevel 318103831d35Sstevel mutex_exit(&softp->intr_mutex); 318203831d35Sstevel mutex_exit(&iosram_mutex); 318303831d35Sstevel 318403831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: " 318503831d35Sstevel "old value=0x%x rv=%d\n", softp->instance, sema_val, rv)); 318603831d35Sstevel 318703831d35Sstevel return (rv); 318803831d35Sstevel } 318903831d35Sstevel 319003831d35Sstevel 319103831d35Sstevel /* 319203831d35Sstevel * iosram_sema_release: Release hardware semaphore. 319303831d35Sstevel * This function will "release" the hardware semaphore, and return 0 on 319403831d35Sstevel * success. If an error occured, one of the following values will be 319503831d35Sstevel * returned: 319603831d35Sstevel * EAGAIN: there is a tunnel switch in progress 319703831d35Sstevel * ENXIO: an IO error occured (e.g. SBBC not mapped) 319803831d35Sstevel */ 319903831d35Sstevel int 320003831d35Sstevel iosram_sema_release(void) 320103831d35Sstevel { 320203831d35Sstevel struct iosramsoft *softp; 320303831d35Sstevel 320403831d35Sstevel DPRINTF(2, ("IOSRAM: in iosram_sema_release\n")); 320503831d35Sstevel 320603831d35Sstevel mutex_enter(&iosram_mutex); 320703831d35Sstevel 320803831d35Sstevel /* 320903831d35Sstevel * Disallow access if there is a tunnel switch in progress. 321003831d35Sstevel */ 321103831d35Sstevel if (iosram_tswitch_active) { 321203831d35Sstevel mutex_exit(&iosram_mutex); 321303831d35Sstevel return (EAGAIN); 321403831d35Sstevel } 321503831d35Sstevel 321603831d35Sstevel /* 321703831d35Sstevel * Use current master IOSRAM for operation, fail if none is 321803831d35Sstevel * currently active. 321903831d35Sstevel */ 322003831d35Sstevel if ((softp = iosram_master) == NULL) { 322103831d35Sstevel mutex_exit(&iosram_mutex); 322203831d35Sstevel DPRINTF(1, ("IOSRAM: iosram_sema_release: no master\n")); 322303831d35Sstevel return (ENXIO); 322403831d35Sstevel } 322503831d35Sstevel 322603831d35Sstevel mutex_enter(&softp->intr_mutex); 322703831d35Sstevel 322803831d35Sstevel /* 322903831d35Sstevel * Fail if SBBC region has not been mapped in. This shouldn't 323003831d35Sstevel * happen if we have a master IOSRAM, but we double-check. 323103831d35Sstevel */ 323203831d35Sstevel if (softp->sbbc_region == NULL) { 323303831d35Sstevel mutex_exit(&softp->intr_mutex); 323403831d35Sstevel mutex_exit(&iosram_mutex); 323503831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: " 323603831d35Sstevel "SBBC not mapped\n", softp->instance)); 323703831d35Sstevel return (ENXIO); 323803831d35Sstevel } 323903831d35Sstevel 324003831d35Sstevel /* Release semaphore by clearing our semaphore register */ 324103831d35Sstevel IOSRAM_SEMA_WR(softp, 0); 324203831d35Sstevel 324303831d35Sstevel mutex_exit(&softp->intr_mutex); 324403831d35Sstevel mutex_exit(&iosram_mutex); 324503831d35Sstevel 324603831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: success\n", 324703831d35Sstevel softp->instance)); 324803831d35Sstevel 324903831d35Sstevel return (0); 325003831d35Sstevel } 325103831d35Sstevel 325203831d35Sstevel 325303831d35Sstevel #if defined(IOSRAM_LOG) 325403831d35Sstevel void 325503831d35Sstevel iosram_log(caddr_t fmt, intptr_t a1, intptr_t a2, intptr_t a3, intptr_t a4) 325603831d35Sstevel { 325703831d35Sstevel uint32_t seq; 325803831d35Sstevel iosram_log_t *logp; 325903831d35Sstevel 326003831d35Sstevel mutex_enter(&iosram_log_mutex); 326103831d35Sstevel 326203831d35Sstevel seq = iosram_logseq++; 326303831d35Sstevel logp = &iosram_logbuf[seq % IOSRAM_MAXLOG]; 326403831d35Sstevel logp->seq = seq; 3265d3d50737SRafael Vanoni logp->tstamp = ddi_get_lbolt(); 326603831d35Sstevel logp->fmt = fmt; 326703831d35Sstevel logp->arg1 = a1; 326803831d35Sstevel logp->arg2 = a2; 326903831d35Sstevel logp->arg3 = a3; 327003831d35Sstevel logp->arg4 = a4; 327103831d35Sstevel 327203831d35Sstevel mutex_exit(&iosram_log_mutex); 327303831d35Sstevel 327403831d35Sstevel if (iosram_log_print) { 327503831d35Sstevel cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp); 327603831d35Sstevel if (logp->fmt) { 327703831d35Sstevel cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2, 327803831d35Sstevel logp->arg3, logp->arg4); 327903831d35Sstevel if (logp->fmt[strlen(logp->fmt)-1] != '\n') { 328003831d35Sstevel cmn_err(CE_CONT, "\n"); 328103831d35Sstevel } 328203831d35Sstevel } else { 328303831d35Sstevel cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n", 3284*07d06da5SSurya Prakki (void *)logp->fmt, logp->arg1, logp->arg2, 3285*07d06da5SSurya Prakki logp->arg3, logp->arg4); 328603831d35Sstevel } 328703831d35Sstevel } 328803831d35Sstevel } 328903831d35Sstevel #endif /* IOSRAM_LOG */ 329003831d35Sstevel 329103831d35Sstevel 329203831d35Sstevel #if defined(DEBUG) 329303831d35Sstevel /* 329403831d35Sstevel * iosram_get_keys(buf, len) 329503831d35Sstevel * Return IOSRAM TOC in the specified buffer 329603831d35Sstevel */ 329703831d35Sstevel static int 329803831d35Sstevel iosram_get_keys(iosram_toc_entry_t *bufp, uint32_t *len) 329903831d35Sstevel { 330003831d35Sstevel struct iosram_chunk *chunkp; 330103831d35Sstevel int error = 0; 330203831d35Sstevel int i; 330303831d35Sstevel int cnt = (*len) / sizeof (iosram_toc_entry_t); 330403831d35Sstevel 330503831d35Sstevel IOSRAMLOG(2, "iosram_get_keys(bufp:%p *len:%x)\n", bufp, *len, NULL, 330603831d35Sstevel NULL); 330703831d35Sstevel 330803831d35Sstevel /* 330903831d35Sstevel * Copy data while holding the lock to prevent any data 331003831d35Sstevel * corruption or invalid pointer dereferencing. 331103831d35Sstevel */ 331203831d35Sstevel mutex_enter(&iosram_mutex); 331303831d35Sstevel 331403831d35Sstevel if (iosram_master == NULL) { 331503831d35Sstevel error = EIO; 331603831d35Sstevel } else { 331703831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks && i < cnt; 331803831d35Sstevel i++, chunkp++) { 331903831d35Sstevel bufp[i].key = chunkp->toc_data.key; 332003831d35Sstevel bufp[i].off = chunkp->toc_data.off; 332103831d35Sstevel bufp[i].len = chunkp->toc_data.len; 332203831d35Sstevel bufp[i].unused = chunkp->toc_data.unused; 332303831d35Sstevel } 332403831d35Sstevel *len = i * sizeof (iosram_toc_entry_t); 332503831d35Sstevel } 332603831d35Sstevel 332703831d35Sstevel mutex_exit(&iosram_mutex); 332803831d35Sstevel return (error); 332903831d35Sstevel } 333003831d35Sstevel 333103831d35Sstevel 333203831d35Sstevel /* 333303831d35Sstevel * iosram_print_state(instance) 333403831d35Sstevel */ 333503831d35Sstevel static void 333603831d35Sstevel iosram_print_state(int instance) 333703831d35Sstevel { 333803831d35Sstevel struct iosramsoft *softp; 333903831d35Sstevel char pn[MAXNAMELEN]; 334003831d35Sstevel 334103831d35Sstevel if (instance < 0) { 334203831d35Sstevel softp = iosram_master; 334303831d35Sstevel } else { 334403831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance); 334503831d35Sstevel } 334603831d35Sstevel 334703831d35Sstevel if (softp == NULL) { 334803831d35Sstevel cmn_err(CE_CONT, "iosram_print_state: Can't find instance %d\n", 334903831d35Sstevel instance); 335003831d35Sstevel return; 335103831d35Sstevel } 335203831d35Sstevel instance = softp->instance; 335303831d35Sstevel 335403831d35Sstevel mutex_enter(&iosram_mutex); 335503831d35Sstevel mutex_enter(&softp->intr_mutex); 335603831d35Sstevel 335703831d35Sstevel cmn_err(CE_CONT, "iosram_print_state(%d): ... %s\n", instance, 335803831d35Sstevel ((softp == iosram_master) ? "MASTER" : "SLAVE")); 335903831d35Sstevel 336003831d35Sstevel (void) ddi_pathname(softp->dip, pn); 336103831d35Sstevel cmn_err(CE_CONT, " pathname:%s\n", pn); 336203831d35Sstevel cmn_err(CE_CONT, " instance:%d portid:%d iosramlen:0x%x\n", 336303831d35Sstevel softp->instance, softp->portid, softp->iosramlen); 3364*07d06da5SSurya Prakki cmn_err(CE_CONT, " softp:%p handle:%p iosramp:%p\n", (void *)softp, 3365*07d06da5SSurya Prakki (void *)softp->handle, (void *)softp->iosramp); 336603831d35Sstevel cmn_err(CE_CONT, " state:0x%x tswitch_ok:%x tswitch_fail:%x\n", 336703831d35Sstevel softp->state, softp->tswitch_ok, softp->tswitch_fail); 336803831d35Sstevel cmn_err(CE_CONT, " softintr_id:%p intr_busy:%x intr_pending:%x\n", 3369*07d06da5SSurya Prakki (void *)softp->softintr_id, softp->intr_busy, softp->intr_pending); 337003831d35Sstevel 337103831d35Sstevel mutex_exit(&softp->intr_mutex); 337203831d35Sstevel mutex_exit(&iosram_mutex); 337303831d35Sstevel } 337403831d35Sstevel 337503831d35Sstevel 337603831d35Sstevel /* 337703831d35Sstevel * iosram_print_stats() 337803831d35Sstevel */ 337903831d35Sstevel static void 338003831d35Sstevel iosram_print_stats() 338103831d35Sstevel { 338203831d35Sstevel uint32_t calls; 338303831d35Sstevel 338403831d35Sstevel cmn_err(CE_CONT, "iosram_stats:\n"); 338503831d35Sstevel calls = iosram_stats.read; 338603831d35Sstevel cmn_err(CE_CONT, " read ... calls:%x bytes:%lx avg_sz:%x\n", 338703831d35Sstevel calls, iosram_stats.bread, 338803831d35Sstevel (uint32_t)((calls != 0) ? (iosram_stats.bread/calls) : 0)); 338903831d35Sstevel 339003831d35Sstevel calls = iosram_stats.write; 339103831d35Sstevel cmn_err(CE_CONT, " write ... calls:%x bytes:%lx avg_sz:%x\n", 339203831d35Sstevel calls, iosram_stats.bwrite, 339303831d35Sstevel (uint32_t)((calls != 0) ? (iosram_stats.bwrite/calls) : 0)); 339403831d35Sstevel 339503831d35Sstevel cmn_err(CE_CONT, " intr recv (real:%x soft:%x) sent:%x cback:%x\n", 339603831d35Sstevel iosram_stats.intr_recv, iosram_stats.sintr_recv, 339703831d35Sstevel iosram_stats.intr_send, iosram_stats.callbacks); 339803831d35Sstevel 339903831d35Sstevel cmn_err(CE_CONT, " tswitch: %x getflag:%x setflag:%x\n", 340003831d35Sstevel iosram_stats.tswitch, iosram_stats.getflag, 340103831d35Sstevel iosram_stats.setflag); 340203831d35Sstevel 340303831d35Sstevel cmn_err(CE_CONT, " iosram_rw_active_max: %x\n", iosram_rw_active_max); 340403831d35Sstevel } 340503831d35Sstevel 340603831d35Sstevel 340703831d35Sstevel static void 340803831d35Sstevel iosram_print_cback() 340903831d35Sstevel { 341003831d35Sstevel iosram_chunk_t *chunkp; 341103831d35Sstevel int i; 341203831d35Sstevel 341303831d35Sstevel /* 341403831d35Sstevel * Print callback handlers 341503831d35Sstevel */ 341603831d35Sstevel mutex_enter(&iosram_mutex); 341703831d35Sstevel 341803831d35Sstevel cmn_err(CE_CONT, "IOSRAM callbacks:\n"); 341903831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) { 342003831d35Sstevel if (chunkp->cback.handler) { 342103831d35Sstevel cmn_err(CE_CONT, " %2d: key:0x%x hdlr:%p arg:%p " 342203831d35Sstevel "busy:%d unreg:%d\n", i, chunkp->toc_data.key, 3423*07d06da5SSurya Prakki (void *)chunkp->cback.handler, 3424*07d06da5SSurya Prakki (void *)chunkp->cback.arg, 342503831d35Sstevel chunkp->cback.busy, chunkp->cback.unregister); 342603831d35Sstevel } 342703831d35Sstevel } 342803831d35Sstevel mutex_exit(&iosram_mutex); 342903831d35Sstevel } 343003831d35Sstevel 343103831d35Sstevel 343203831d35Sstevel static void 343303831d35Sstevel iosram_print_flags() 343403831d35Sstevel { 343503831d35Sstevel int i; 343603831d35Sstevel uint32_t *keys; 343703831d35Sstevel iosram_flags_t *flags; 343803831d35Sstevel 343903831d35Sstevel mutex_enter(&iosram_mutex); 344003831d35Sstevel 344103831d35Sstevel if (iosram_master == NULL) { 344203831d35Sstevel mutex_exit(&iosram_mutex); 344303831d35Sstevel cmn_err(CE_CONT, "IOSRAM Flags: not accessible\n"); 344403831d35Sstevel return; 344503831d35Sstevel } 344603831d35Sstevel 344703831d35Sstevel keys = kmem_alloc(nchunks * sizeof (uint32_t), KM_SLEEP); 344803831d35Sstevel flags = kmem_alloc(nchunks * sizeof (iosram_flags_t), KM_SLEEP); 344903831d35Sstevel 345003831d35Sstevel for (i = 0; i < nchunks; i++) { 345103831d35Sstevel keys[i] = chunks[i].toc_data.key; 345203831d35Sstevel ddi_rep_get8(iosram_handle, (uint8_t *)&(flags[i]), 345303831d35Sstevel (uint8_t *)(chunks[i].flagsp), sizeof (iosram_flags_t), 345403831d35Sstevel DDI_DEV_AUTOINCR); 345503831d35Sstevel } 345603831d35Sstevel 345703831d35Sstevel mutex_exit(&iosram_mutex); 345803831d35Sstevel 345903831d35Sstevel cmn_err(CE_CONT, "IOSRAM Flags:\n"); 346003831d35Sstevel for (i = 0; i < nchunks; i++) { 346103831d35Sstevel cmn_err(CE_CONT, 346203831d35Sstevel " %2d: key: 0x%x data_valid:%x int_pending:%x\n", 346303831d35Sstevel i, keys[i], flags[i].data_valid, flags[i].int_pending); 346403831d35Sstevel } 346503831d35Sstevel 346603831d35Sstevel kmem_free(keys, nchunks * sizeof (uint32_t)); 346703831d35Sstevel kmem_free(flags, nchunks * sizeof (iosram_flags_t)); 346803831d35Sstevel } 346903831d35Sstevel 347003831d35Sstevel 347103831d35Sstevel /*PRINTFLIKE1*/ 347203831d35Sstevel static void 347303831d35Sstevel iosram_dprintf(const char *fmt, ...) 347403831d35Sstevel { 347503831d35Sstevel char msg_buf[256]; 347603831d35Sstevel va_list adx; 347703831d35Sstevel 347803831d35Sstevel va_start(adx, fmt); 3479*07d06da5SSurya Prakki (void) vsprintf(msg_buf, fmt, adx); 348003831d35Sstevel va_end(adx); 348103831d35Sstevel 348203831d35Sstevel cmn_err(CE_CONT, "%s", msg_buf); 348303831d35Sstevel } 348403831d35Sstevel #endif /* DEBUG */ 348503831d35Sstevel 348603831d35Sstevel 348703831d35Sstevel #if IOSRAM_LOG 348803831d35Sstevel /* 348903831d35Sstevel * iosram_print_log(int cnt) 349003831d35Sstevel * Print last few entries of the IOSRAM log in reverse order 349103831d35Sstevel */ 349203831d35Sstevel static void 349303831d35Sstevel iosram_print_log(int cnt) 349403831d35Sstevel { 349503831d35Sstevel int i; 349603831d35Sstevel 349703831d35Sstevel if (cnt <= 0) { 349803831d35Sstevel cnt = 20; 349903831d35Sstevel } else if (cnt > IOSRAM_MAXLOG) { 350003831d35Sstevel cnt = IOSRAM_MAXLOG; 350103831d35Sstevel } 350203831d35Sstevel 350303831d35Sstevel 350403831d35Sstevel cmn_err(CE_CONT, 350503831d35Sstevel "\niosram_logseq: 0x%x lbolt: %lx iosram_log_level:%x\n", 3506d3d50737SRafael Vanoni iosram_logseq, ddi_get_lbolt(), iosram_log_level); 350703831d35Sstevel cmn_err(CE_CONT, "iosram_logbuf: %p max entries:0x%x\n", 3508*07d06da5SSurya Prakki (void *)iosram_logbuf, IOSRAM_MAXLOG); 350903831d35Sstevel for (i = iosram_logseq; --i >= 0 && --cnt >= 0; ) { 351003831d35Sstevel iosram_log_t *logp; 351103831d35Sstevel 351203831d35Sstevel mutex_enter(&iosram_log_mutex); 351303831d35Sstevel 351403831d35Sstevel logp = &iosram_logbuf[i %IOSRAM_MAXLOG]; 351503831d35Sstevel cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp); 351603831d35Sstevel 351703831d35Sstevel if (logp->fmt) { 351803831d35Sstevel cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2, 351903831d35Sstevel logp->arg3, logp->arg4); 352003831d35Sstevel if (logp->fmt[strlen(logp->fmt)-1] != '\n') { 352103831d35Sstevel cmn_err(CE_CONT, "\n"); 352203831d35Sstevel } 352303831d35Sstevel } else { 352403831d35Sstevel cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n", 3525*07d06da5SSurya Prakki (void *)logp->fmt, logp->arg1, logp->arg2, 352603831d35Sstevel logp->arg3, logp->arg4); 352703831d35Sstevel } 352803831d35Sstevel 352903831d35Sstevel mutex_exit(&iosram_log_mutex); 353003831d35Sstevel } 353103831d35Sstevel } 353203831d35Sstevel #endif /* IOSRAM_LOG */ 3533