11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel
221708Sstevel /*
23*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel
281708Sstevel /*
291708Sstevel * PCI SBBC Device Driver that provides interfaces into
301708Sstevel * EPLD and IO-SRAM
311708Sstevel *
321708Sstevel */
331708Sstevel #include <sys/types.h>
341708Sstevel #include <sys/param.h>
351708Sstevel #include <sys/errno.h>
361708Sstevel #include <sys/file.h>
371708Sstevel #include <sys/cmn_err.h>
381708Sstevel #include <sys/stropts.h>
391708Sstevel #include <sys/kmem.h>
401708Sstevel #include <sys/sunndi.h>
411708Sstevel #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
421708Sstevel #include <sys/modctl.h> /* for modldrv */
431708Sstevel #include <sys/promif.h>
441708Sstevel #include <sys/stat.h>
451708Sstevel #include <sys/ddi.h>
461708Sstevel
471708Sstevel #include <sys/serengeti.h>
481708Sstevel #include <sys/sgsbbc_priv.h>
491708Sstevel #include <sys/sgsbbc_iosram_priv.h>
501708Sstevel #include <sys/sgsbbc_mailbox_priv.h>
511708Sstevel
521708Sstevel #ifdef DEBUG
531708Sstevel /* debug flag */
541708Sstevel uint_t sgsbbc_debug = 0;
551708Sstevel #endif /* DEBUG */
561708Sstevel
571708Sstevel /* driver entry point fn definitions */
581708Sstevel static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
591708Sstevel static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
601708Sstevel
611708Sstevel /*
621708Sstevel * SBBC soft state hook
631708Sstevel */
641708Sstevel static void *sbbcp;
651708Sstevel
661708Sstevel /*
671708Sstevel * Chosen IOSRAM
681708Sstevel */
691708Sstevel struct chosen_iosram *master_iosram = NULL;
701708Sstevel
711708Sstevel /*
721708Sstevel * define new iosram's sbbc and liked list of sbbc.
731708Sstevel */
741708Sstevel struct sbbc_softstate *sgsbbc_instances = NULL;
751708Sstevel
761708Sstevel /*
771708Sstevel * At attach time, check if the device is the 'chosen' node
781708Sstevel * if it is, set up the IOSRAM Solaris<->SC Comm tunnel
791708Sstevel * Its like 'Highlander' - there can be only one !
801708Sstevel */
811708Sstevel static int master_chosen = FALSE;
821708Sstevel kmutex_t chosen_lock;
831708Sstevel
841708Sstevel /*
851708Sstevel * Local variable to save intr_in_enabled when the driver is suspended
861708Sstevel */
871708Sstevel static uint32_t intr_in_enabled;
881708Sstevel
891708Sstevel /*
901708Sstevel * Local declarations
911708Sstevel */
921708Sstevel static void softsp_init(sbbc_softstate_t *, dev_info_t *);
931708Sstevel static void sbbc_chosen_init(sbbc_softstate_t *);
941708Sstevel static void sbbc_add_instance(sbbc_softstate_t *);
951708Sstevel static void sbbc_remove_instance(sbbc_softstate_t *);
961708Sstevel static int sbbc_find_dip(dev_info_t *, void *);
971708Sstevel static void sbbc_unmap_regs(sbbc_softstate_t *);
981708Sstevel
991708Sstevel /*
1001708Sstevel * ops stuff.
1011708Sstevel */
1021708Sstevel static struct cb_ops sbbc_cb_ops = {
1031708Sstevel nodev, /* cb_open */
1041708Sstevel nodev, /* cb_close */
1051708Sstevel nodev, /* cb_strategy */
1061708Sstevel nodev, /* cb_print */
1071708Sstevel nodev, /* cb_dump */
1081708Sstevel nodev, /* cb_read */
1091708Sstevel nodev, /* cb_write */
1101708Sstevel nodev, /* cb_ioctl */
1111708Sstevel nodev, /* cb_devmap */
1121708Sstevel nodev, /* cb_mmap */
1131708Sstevel nodev, /* cb_segmap */
1141708Sstevel nochpoll, /* cb_chpoll */
1151708Sstevel ddi_prop_op, /* cb_prop_op */
1161708Sstevel NULL, /* cb_stream */
1171708Sstevel D_NEW | D_MP /* cb_flag */
1181708Sstevel };
1191708Sstevel
1201708Sstevel /*
1211708Sstevel * Declare ops vectors for auto configuration.
1221708Sstevel */
1231708Sstevel struct dev_ops sbbc_ops = {
1241708Sstevel DEVO_REV, /* devo_rev */
1251708Sstevel 0, /* devo_refcnt */
1261708Sstevel ddi_getinfo_1to1, /* devo_getinfo */
1271708Sstevel nulldev, /* devo_identify */
1281708Sstevel nulldev, /* devo_probe */
1291708Sstevel sbbc_attach, /* devo_attach */
1301708Sstevel sbbc_detach, /* devo_detach */
1311708Sstevel nodev, /* devo_reset */
1321708Sstevel &sbbc_cb_ops, /* devo_cb_ops */
1331708Sstevel (struct bus_ops *)NULL, /* devo_bus_ops */
134*7656SSherry.Moore@Sun.COM nulldev, /* devo_power */
135*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1361708Sstevel };
1371708Sstevel
1381708Sstevel /*
1391708Sstevel * Loadable module support.
1401708Sstevel */
1411708Sstevel extern struct mod_ops mod_driverops;
1421708Sstevel
1431708Sstevel static struct modldrv modldrv = {
1441708Sstevel &mod_driverops, /* type of module - driver */
145*7656SSherry.Moore@Sun.COM "PCI SBBC",
1461708Sstevel &sbbc_ops,
1471708Sstevel };
1481708Sstevel
1491708Sstevel static struct modlinkage modlinkage = {
1501708Sstevel MODREV_1,
1511708Sstevel (void *)&modldrv,
1521708Sstevel NULL
1531708Sstevel };
1541708Sstevel
1551708Sstevel int
_init(void)1561708Sstevel _init(void)
1571708Sstevel {
1581708Sstevel int error;
1591708Sstevel
1601708Sstevel if ((error = ddi_soft_state_init(&sbbcp,
161*7656SSherry.Moore@Sun.COM sizeof (sbbc_softstate_t), 1)) != 0)
1621708Sstevel return (error);
1631708Sstevel
1641708Sstevel if ((error = mod_install(&modlinkage)) != 0) {
1651708Sstevel ddi_soft_state_fini(&sbbcp);
1661708Sstevel return (error);
1671708Sstevel }
1681708Sstevel
1691708Sstevel /*
1701708Sstevel * Initialise the global 'chosen' IOSRAM mutex
1711708Sstevel */
1721708Sstevel mutex_init(&chosen_lock, NULL, MUTEX_DEFAULT, NULL);
1731708Sstevel
1741708Sstevel /*
1751708Sstevel * Initialise the iosram driver
1761708Sstevel */
1771708Sstevel iosram_init();
1781708Sstevel
1791708Sstevel /*
1801708Sstevel * Initialize the mailbox
1811708Sstevel */
1821708Sstevel sbbc_mbox_init();
1831708Sstevel
1841708Sstevel return (error);
1851708Sstevel
1861708Sstevel }
1871708Sstevel
1881708Sstevel int
_fini(void)1891708Sstevel _fini(void)
1901708Sstevel {
1911708Sstevel int error;
1921708Sstevel
1931708Sstevel if ((error = mod_remove(&modlinkage)) == 0)
1941708Sstevel ddi_soft_state_fini(&sbbcp);
1951708Sstevel
1961708Sstevel master_chosen = FALSE;
1971708Sstevel
1981708Sstevel mutex_destroy(&chosen_lock);
1991708Sstevel
2001708Sstevel /*
2011708Sstevel * remove the mailbox
2021708Sstevel */
2031708Sstevel sbbc_mbox_fini();
2041708Sstevel
2051708Sstevel /*
2061708Sstevel * remove the iosram driver
2071708Sstevel */
2081708Sstevel iosram_fini();
2091708Sstevel
2101708Sstevel return (error);
2111708Sstevel }
2121708Sstevel
2131708Sstevel int
_info(struct modinfo * modinfop)2141708Sstevel _info(struct modinfo *modinfop)
2151708Sstevel {
2161708Sstevel return (mod_info(&modlinkage, modinfop));
2171708Sstevel }
2181708Sstevel
2191708Sstevel static int
sbbc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2201708Sstevel sbbc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2211708Sstevel {
2221708Sstevel int instance;
2231708Sstevel sbbc_softstate_t *softsp;
2241708Sstevel uint32_t *pci_intr_enable_reg;
2251708Sstevel int len;
2261708Sstevel #ifdef DEBUG
2271708Sstevel char name[8];
2281708Sstevel #endif /* DEBUG */
2291708Sstevel
2301708Sstevel instance = ddi_get_instance(devi);
2311708Sstevel
2321708Sstevel switch (cmd) {
2331708Sstevel case DDI_ATTACH:
2341708Sstevel
2351708Sstevel if (ddi_soft_state_zalloc(sbbcp, instance) != 0)
2361708Sstevel return (DDI_FAILURE);
2371708Sstevel
2381708Sstevel softsp = ddi_get_soft_state(sbbcp, instance);
2391708Sstevel softsp->sbbc_instance = instance;
2401708Sstevel
2411708Sstevel /*
2421708Sstevel * Set the dip in the soft state
2431708Sstevel * And get interrupt cookies and initialize the
2441708Sstevel * per instance mutex.
2451708Sstevel */
2461708Sstevel softsp_init(softsp, devi);
2471708Sstevel
2481708Sstevel
2491708Sstevel /*
2501708Sstevel * Verify that an 'interrupts' property exists for
2511708Sstevel * this device. If not, this instance will be ignored.
2521708Sstevel */
2531708Sstevel if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
254*7656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "interrupts",
255*7656SSherry.Moore@Sun.COM &len) != DDI_PROP_SUCCESS) {
2561708Sstevel SBBC_ERR1(CE_WARN, "No 'interrupts' property for the "
257*7656SSherry.Moore@Sun.COM "SBBC instance %d\n", instance);
2581708Sstevel return (DDI_FAILURE);
2591708Sstevel }
2601708Sstevel /*
2611708Sstevel * Add this instance to the sbbc chosen iosram list
2621708Sstevel * so that it can be used for tunnel switch.
2631708Sstevel */
2641708Sstevel mutex_enter(&chosen_lock);
2651708Sstevel softsp->sbbc_state = SBBC_STATE_INIT;
2661708Sstevel sbbc_add_instance(softsp);
2671708Sstevel
2681708Sstevel /*
2691708Sstevel * If this is the chosen IOSRAM and there is no master IOSRAM
2701708Sstevel * yet, then let's set this instance as the master.
2711708Sstevel * if there is a master alreay due to the previous tunnel switch
2721708Sstevel * then keep as is even though this is the chosen.
2731708Sstevel */
2741708Sstevel if (sgsbbc_iosram_is_chosen(softsp)) {
2751708Sstevel ASSERT(master_iosram);
2761708Sstevel softsp->iosram = master_iosram;
2771708Sstevel master_iosram->sgsbbc = softsp;
2781708Sstevel
2791708Sstevel /* Do 'chosen' init only */
2801708Sstevel sbbc_chosen_init(softsp);
2811708Sstevel }
2821708Sstevel
2831708Sstevel mutex_exit(&chosen_lock);
2841708Sstevel #ifdef DEBUG
2851708Sstevel (void) sprintf(name, "sbbc%d", instance);
2861708Sstevel
2871708Sstevel if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
288*7656SSherry.Moore@Sun.COM NULL, NULL) == DDI_FAILURE) {
2891708Sstevel mutex_destroy(&softsp->sbbc_lock);
2901708Sstevel ddi_remove_minor_node(devi, NULL);
2911708Sstevel ddi_soft_state_free(sbbcp, instance);
2921708Sstevel return (DDI_FAILURE);
2931708Sstevel }
2941708Sstevel #endif /* DEBUG */
2951708Sstevel
2961708Sstevel ddi_report_dev(devi);
2971708Sstevel
2981708Sstevel return (DDI_SUCCESS);
2991708Sstevel
3001708Sstevel case DDI_RESUME:
3011708Sstevel
3021708Sstevel if (!(softsp = ddi_get_soft_state(sbbcp, instance)))
3031708Sstevel return (DDI_FAILURE);
3041708Sstevel
3051708Sstevel mutex_enter(&softsp->sbbc_lock);
3061708Sstevel if ((softsp->suspended == TRUE) && (softsp->chosen == TRUE)) {
3071708Sstevel /*
3081708Sstevel * Enable Interrupts now, turn on both INT#A lines
3091708Sstevel */
3101708Sstevel pci_intr_enable_reg = (uint32_t *)
311*7656SSherry.Moore@Sun.COM ((char *)softsp->sbbc_regs +
312*7656SSherry.Moore@Sun.COM SBBC_PCI_INT_ENABLE);
3131708Sstevel
3141708Sstevel ddi_put32(softsp->sbbc_reg_handle1,
315*7656SSherry.Moore@Sun.COM pci_intr_enable_reg,
316*7656SSherry.Moore@Sun.COM (uint32_t)SBBC_PCI_ENABLE_INT_A);
3171708Sstevel
3181708Sstevel /*
3191708Sstevel * Reset intr_in_enabled to the original value
3201708Sstevel * so the SC can send us interrupt.
3211708Sstevel */
3221708Sstevel if (iosram_write(SBBC_SC_INTR_ENABLED_KEY,
323*7656SSherry.Moore@Sun.COM 0, (caddr_t)&intr_in_enabled,
324*7656SSherry.Moore@Sun.COM sizeof (intr_in_enabled))) {
3251708Sstevel
3261708Sstevel mutex_exit(&softsp->sbbc_lock);
3271708Sstevel return (DDI_FAILURE);
3281708Sstevel }
3291708Sstevel }
3301708Sstevel softsp->suspended = FALSE;
3311708Sstevel
3321708Sstevel mutex_exit(&softsp->sbbc_lock);
3331708Sstevel
3341708Sstevel return (DDI_SUCCESS);
3351708Sstevel
3361708Sstevel default:
3371708Sstevel return (DDI_FAILURE);
3381708Sstevel }
3391708Sstevel }
3401708Sstevel
3411708Sstevel static int
sbbc_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)3421708Sstevel sbbc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3431708Sstevel {
3441708Sstevel sbbc_softstate_t *softsp;
3451708Sstevel int instance;
3461708Sstevel uint32_t *pci_intr_enable_reg;
3471708Sstevel int rc = DDI_SUCCESS;
3481708Sstevel
3491708Sstevel instance = ddi_get_instance(devi);
3501708Sstevel
3511708Sstevel if (!(softsp = ddi_get_soft_state(sbbcp, instance)))
3521708Sstevel return (DDI_FAILURE);
3531708Sstevel
3541708Sstevel switch (cmd) {
3551708Sstevel case DDI_DETACH:
3561708Sstevel mutex_enter(&chosen_lock);
3571708Sstevel softsp->sbbc_state |= SBBC_STATE_DETACH;
3581708Sstevel mutex_exit(&chosen_lock);
3591708Sstevel
3601708Sstevel /* only tunnel switch the instance with iosram chosen */
3611708Sstevel if (softsp->chosen == TRUE) {
3621708Sstevel if (sgsbbc_iosram_switchfrom(softsp) == DDI_FAILURE) {
3631708Sstevel SBBC_ERR(CE_WARN, "Cannot unconfigure: "
3641708Sstevel "tunnel switch failed\n");
3651708Sstevel return (DDI_FAILURE);
3661708Sstevel }
3671708Sstevel }
3681708Sstevel
3691708Sstevel /* Adjust linked list */
3701708Sstevel mutex_enter(&chosen_lock);
3711708Sstevel sbbc_remove_instance(softsp);
3721708Sstevel mutex_exit(&chosen_lock);
3731708Sstevel
3741708Sstevel sbbc_unmap_regs(softsp);
3751708Sstevel mutex_destroy(&softsp->sbbc_lock);
3761708Sstevel ddi_soft_state_free(sbbcp, instance);
3771708Sstevel
3781708Sstevel return (DDI_SUCCESS);
3791708Sstevel
3801708Sstevel case DDI_SUSPEND:
3811708Sstevel
3821708Sstevel mutex_enter(&softsp->sbbc_lock);
3831708Sstevel
3841708Sstevel if ((softsp->suspended == FALSE) && (softsp->chosen == TRUE)) {
3851708Sstevel uint32_t tmp_intr_enabled = 0;
3861708Sstevel
3871708Sstevel /*
3881708Sstevel * Disable Interrupts now, turn OFF both INT#A lines
3891708Sstevel */
3901708Sstevel pci_intr_enable_reg = (uint32_t *)
391*7656SSherry.Moore@Sun.COM ((char *)softsp->sbbc_regs +
392*7656SSherry.Moore@Sun.COM SBBC_PCI_INT_ENABLE);
3931708Sstevel
3941708Sstevel ddi_put32(softsp->sbbc_reg_handle1,
395*7656SSherry.Moore@Sun.COM pci_intr_enable_reg, 0);
3961708Sstevel
3971708Sstevel /*
3981708Sstevel * Set intr_in_enabled to 0 so the SC won't send
3991708Sstevel * us interrupt.
4001708Sstevel */
4011708Sstevel rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY,
402*7656SSherry.Moore@Sun.COM 0, (caddr_t)&intr_in_enabled,
403*7656SSherry.Moore@Sun.COM sizeof (intr_in_enabled));
4041708Sstevel
4051708Sstevel if (rc) {
4061708Sstevel mutex_exit(&softsp->sbbc_lock);
4071708Sstevel return (DDI_FAILURE);
4081708Sstevel }
4091708Sstevel
4101708Sstevel rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY,
411*7656SSherry.Moore@Sun.COM 0, (caddr_t)&tmp_intr_enabled,
412*7656SSherry.Moore@Sun.COM sizeof (tmp_intr_enabled));
4131708Sstevel
4141708Sstevel if (rc) {
4151708Sstevel mutex_exit(&softsp->sbbc_lock);
4161708Sstevel return (DDI_FAILURE);
4171708Sstevel }
4181708Sstevel }
4191708Sstevel softsp->suspended = TRUE;
4201708Sstevel
4211708Sstevel mutex_exit(&softsp->sbbc_lock);
4221708Sstevel
4231708Sstevel return (DDI_SUCCESS);
4241708Sstevel
4251708Sstevel default:
4261708Sstevel return (DDI_FAILURE);
4271708Sstevel }
4281708Sstevel
4291708Sstevel }
4301708Sstevel
4311708Sstevel static void
softsp_init(sbbc_softstate_t * softsp,dev_info_t * devi)4321708Sstevel softsp_init(sbbc_softstate_t *softsp, dev_info_t *devi)
4331708Sstevel {
4341708Sstevel softsp->dip = devi;
4351708Sstevel
4361708Sstevel /*
4371708Sstevel * XXXX
4381708Sstevel * ddi_get_iblock_cookie() here because we need
4391708Sstevel * to initialise the mutex regardless of whether
4401708Sstevel * or not this SBBC will eventually
4411708Sstevel * register an interrupt handler
4421708Sstevel */
4431708Sstevel
4441708Sstevel (void) ddi_get_iblock_cookie(devi, 0, &softsp->iblock);
4451708Sstevel
4461708Sstevel mutex_init(&softsp->sbbc_lock, NULL, MUTEX_DRIVER,
447*7656SSherry.Moore@Sun.COM (void *)softsp->iblock);
4481708Sstevel
4491708Sstevel softsp->suspended = FALSE;
4501708Sstevel softsp->chosen = FALSE;
4511708Sstevel }
4521708Sstevel
4531708Sstevel static int
sbbc_find_dip(dev_info_t * dip,void * arg)4541708Sstevel sbbc_find_dip(dev_info_t *dip, void *arg)
4551708Sstevel {
4561708Sstevel char *node_name;
4571708Sstevel sbbc_find_dip_t *dip_struct = (sbbc_find_dip_t *)arg;
4581708Sstevel char status[OBP_MAXPROPNAME];
4591708Sstevel
4601708Sstevel /*
4611708Sstevel * Need to find a node named "bootbus-controller" that is neither
4621708Sstevel * disabled nor failed. If a node is not ok, there will be an
4631708Sstevel * OBP status property. Therefore, we will look for a node
4641708Sstevel * without the status property.
4651708Sstevel */
4661708Sstevel node_name = ddi_node_name(dip);
4671708Sstevel if (strcmp(node_name, "bootbus-controller") == 0 && DDI_CF2(dip) &&
468*7656SSherry.Moore@Sun.COM (prom_getprop(ddi_get_nodeid(dip),
469*7656SSherry.Moore@Sun.COM "status", (caddr_t)status) == -1) &&
470*7656SSherry.Moore@Sun.COM (prom_getprop(ddi_get_nodeid(ddi_get_parent(dip)),
471*7656SSherry.Moore@Sun.COM "status", (caddr_t)status) == -1)) {
4721708Sstevel
4731708Sstevel if (dip != dip_struct->cur_dip) {
4741708Sstevel dip_struct->new_dip = (void *)dip;
4751708Sstevel return (DDI_WALK_TERMINATE);
4761708Sstevel }
4771708Sstevel }
4781708Sstevel
4791708Sstevel return (DDI_WALK_CONTINUE);
4801708Sstevel }
4811708Sstevel
4821708Sstevel /*
4831708Sstevel * SBBC Interrupt Handler
4841708Sstevel *
4851708Sstevel * Check the SBBC Port Interrupt Status
4861708Sstevel * register to verify that its our interrupt.
4871708Sstevel * If yes, clear the register.
4881708Sstevel *
4891708Sstevel * Then read the 'interrupt reason' field from SRAM,
4901708Sstevel * this triggers the appropriate soft_intr handler
4911708Sstevel */
4921708Sstevel uint_t
sbbc_intr_handler(caddr_t arg)4931708Sstevel sbbc_intr_handler(caddr_t arg)
4941708Sstevel {
4951708Sstevel sbbc_softstate_t *softsp = (sbbc_softstate_t *)arg;
4961708Sstevel uint32_t *port_int_reg;
4971708Sstevel volatile uint32_t port_int_status;
4981708Sstevel volatile uint32_t intr_reason;
4991708Sstevel uint32_t intr_enabled;
5001708Sstevel sbbc_intrs_t *intr;
5011708Sstevel int i, intr_mask;
5021708Sstevel struct tunnel_key tunnel_key;
5031708Sstevel ddi_acc_handle_t intr_in_handle;
5041708Sstevel uint32_t *intr_in_reason;
5051708Sstevel
5061708Sstevel if (softsp == (sbbc_softstate_t *)NULL) {
5071708Sstevel
5081708Sstevel return (DDI_INTR_UNCLAIMED);
5091708Sstevel }
5101708Sstevel
5111708Sstevel mutex_enter(&softsp->sbbc_lock);
5121708Sstevel
5131708Sstevel if (softsp->port_int_regs == NULL) {
5141708Sstevel mutex_exit(&softsp->sbbc_lock);
5151708Sstevel return (DDI_INTR_UNCLAIMED);
5161708Sstevel }
5171708Sstevel
5181708Sstevel /*
5191708Sstevel * Normally if port_int_status is 0, we assume it is not
5201708Sstevel * our interrupt. However, we don't want to miss the
5211708Sstevel * ones that come in during tunnel switch. Therefore,
5221708Sstevel * we always check the interrupt reason bits in IOSRAM
5231708Sstevel * to be sure.
5241708Sstevel */
5251708Sstevel port_int_reg = softsp->port_int_regs;
5261708Sstevel
5271708Sstevel port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
5281708Sstevel
5291708Sstevel /*
5301708Sstevel * Generate a softint for each interrupt
5311708Sstevel * bit set in the intr_in_reason field in SRAM
5321708Sstevel * that has a corresponding bit set in the
5331708Sstevel * intr_in_enabled field in SRAM
5341708Sstevel */
5351708Sstevel
5361708Sstevel if (iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
537*7656SSherry.Moore@Sun.COM (caddr_t)&intr_enabled, sizeof (intr_enabled))) {
5381708Sstevel
5391708Sstevel goto intr_handler_exit;
5401708Sstevel }
5411708Sstevel
5421708Sstevel tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
5431708Sstevel intr_in_reason = (uint32_t *)tunnel_key.base;
5441708Sstevel intr_in_handle = tunnel_key.reg_handle;
5451708Sstevel
5461708Sstevel intr_reason = ddi_get32(intr_in_handle, intr_in_reason);
5471708Sstevel
5481708Sstevel SGSBBC_DBG_INTR(CE_CONT, "intr_reason = %x\n", intr_reason);
5491708Sstevel
5501708Sstevel intr_reason &= intr_enabled;
5511708Sstevel
5521708Sstevel for (i = 0; i < SBBC_MAX_INTRS; i++) {
5531708Sstevel intr_mask = (1 << i);
5541708Sstevel if (intr_reason & intr_mask) {
5551708Sstevel intr = &softsp->intr_hdlrs[i];
5561708Sstevel if ((intr != NULL) &&
557*7656SSherry.Moore@Sun.COM (intr->sbbc_intr_id != 0)) {
5581708Sstevel /*
5591708Sstevel * XXXX
5601708Sstevel * The model we agree with a handler
5611708Sstevel * is that they run until they have
5621708Sstevel * exhausted all work. To avoid
5631708Sstevel * triggering them again, they pass
5641708Sstevel * a state flag and lock when registering.
5651708Sstevel * We check the flag, if they are idle,
5661708Sstevel * we trigger.
5671708Sstevel * The interrupt handler should so
5681708Sstevel * intr_func()
5691708Sstevel * mutex_enter(sbbc_intr_lock);
5701708Sstevel * sbbc_intr_state = RUNNING;
5711708Sstevel * mutex_exit(sbbc_intr_lock);
5721708Sstevel * ..........
5731708Sstevel * ..........
5741708Sstevel * ..........
5751708Sstevel * mutex_enter(sbbc_intr_lock);
5761708Sstevel * sbbc_intr_state = IDLE;
5771708Sstevel * mutex_exit(sbbc_intr_lock);
5781708Sstevel *
5791708Sstevel * XXXX
5801708Sstevel */
5811708Sstevel mutex_enter(intr->sbbc_intr_lock);
5821708Sstevel if (*(intr->sbbc_intr_state) ==
583*7656SSherry.Moore@Sun.COM SBBC_INTR_IDLE) {
5841708Sstevel mutex_exit(intr->sbbc_intr_lock);
5851708Sstevel ddi_trigger_softintr(
586*7656SSherry.Moore@Sun.COM intr->sbbc_intr_id);
5871708Sstevel } else {
5881708Sstevel /*
5891708Sstevel * The handler is running
5901708Sstevel */
5911708Sstevel mutex_exit(intr->sbbc_intr_lock);
5921708Sstevel }
5931708Sstevel intr_reason &= ~intr_mask;
5941708Sstevel /*
5951708Sstevel * Clear the corresponding reason bit in SRAM
5961708Sstevel *
5971708Sstevel * Since there is no interlocking between
5981708Sstevel * Solaris and the SC when writing to SRAM,
5991708Sstevel * it is possible for the SC to set another
6001708Sstevel * bit in the interrupt reason field while
6011708Sstevel * we are handling the current interrupt.
6021708Sstevel * To minimize the window in which an
6031708Sstevel * additional bit can be set, reading
6041708Sstevel * and writing the interrupt reason
6051708Sstevel * in SRAM must be as close as possible.
6061708Sstevel */
6071708Sstevel ddi_put32(intr_in_handle, intr_in_reason,
608*7656SSherry.Moore@Sun.COM ddi_get32(intr_in_handle,
609*7656SSherry.Moore@Sun.COM intr_in_reason) & ~intr_mask);
6101708Sstevel }
6111708Sstevel }
6121708Sstevel if (intr_reason == 0) /* No more interrupts to be processed */
6131708Sstevel break;
6141708Sstevel }
6151708Sstevel
6161708Sstevel /*
6171708Sstevel * Clear the Interrupt Status Register (RW1C)
6181708Sstevel */
6191708Sstevel ddi_put32(softsp->sbbc_reg_handle1, port_int_reg, port_int_status);
6201708Sstevel
6211708Sstevel port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
6221708Sstevel
6231708Sstevel intr_handler_exit:
6241708Sstevel
6251708Sstevel mutex_exit(&softsp->sbbc_lock);
6261708Sstevel
6271708Sstevel return (DDI_INTR_CLAIMED);
6281708Sstevel
6291708Sstevel }
6301708Sstevel
6311708Sstevel /*
6321708Sstevel * If we don't already have a master SBBC selected,
6331708Sstevel * get the <sbbc> property from the /chosen node. If
6341708Sstevel * the pathname matches, this is the master SBBC and
6351708Sstevel * we set up the console/TOD SRAM mapping here.
6361708Sstevel */
6371708Sstevel static void
sbbc_chosen_init(sbbc_softstate_t * softsp)6381708Sstevel sbbc_chosen_init(sbbc_softstate_t *softsp)
6391708Sstevel {
6401708Sstevel char master_sbbc[MAXNAMELEN];
6411708Sstevel char pn[MAXNAMELEN];
6421708Sstevel int nodeid, len;
6431708Sstevel pnode_t dnode;
6441708Sstevel
6451708Sstevel if (master_chosen != FALSE) {
6461708Sstevel /*
6471708Sstevel * We've got one already
6481708Sstevel */
6491708Sstevel return;
6501708Sstevel }
6511708Sstevel
6521708Sstevel /*
6531708Sstevel * Get /chosen node info. prom interface will handle errors.
6541708Sstevel */
6551708Sstevel dnode = prom_chosennode();
6561708Sstevel
6571708Sstevel /*
6581708Sstevel * Look for the "iosram" property on the chosen node with a prom
6591708Sstevel * interface as ddi_find_devinfo() couldn't be used (calls
6601708Sstevel * ddi_walk_devs() that creates one extra lock on the device tree).
6611708Sstevel */
6621708Sstevel if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
6631708Sstevel /*
6641708Sstevel * No I/O Board SBBC set up as console, what to do ?
6651708Sstevel */
6661708Sstevel SBBC_ERR(CE_PANIC, "No SBBC found for Console/TOD \n");
6671708Sstevel }
6681708Sstevel
6691708Sstevel if (prom_getprop(dnode, IOSRAM_TOC_PROP,
6701708Sstevel (caddr_t)&softsp->sram_toc) <= 0) {
6711708Sstevel /*
6721708Sstevel * SRAM TOC Offset defaults to 0
6731708Sstevel */
6741708Sstevel SBBC_ERR(CE_WARN, "No SBBC TOC Offset found\n");
6751708Sstevel softsp->sram_toc = 0;
6761708Sstevel }
6771708Sstevel
6781708Sstevel /*
6791708Sstevel * get the full OBP pathname of this node
6801708Sstevel */
6811708Sstevel if (prom_phandle_to_path((phandle_t)nodeid, master_sbbc,
682*7656SSherry.Moore@Sun.COM sizeof (master_sbbc)) < 0) {
6831708Sstevel
6841708Sstevel SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
6851708Sstevel nodeid);
6861708Sstevel }
6871708Sstevel SGSBBC_DBG_ALL("chosen pathname : %s\n", master_sbbc);
6881708Sstevel SGSBBC_DBG_ALL("device pathname : %s\n", ddi_pathname(softsp->dip, pn));
6891708Sstevel if (strcmp(master_sbbc, ddi_pathname(softsp->dip, pn)) == 0) {
6901708Sstevel
6911708Sstevel /*
6921708Sstevel * map in the SBBC regs
6931708Sstevel */
6941708Sstevel
6951708Sstevel if (sbbc_map_regs(softsp) != DDI_SUCCESS) {
6961708Sstevel SBBC_ERR(CE_PANIC, "Can't map the SBBC regs \n");
6971708Sstevel }
6981708Sstevel /*
6991708Sstevel * Only the 'chosen' node is used for iosram_read()/_write()
7001708Sstevel * Must initialise the tunnel before the console/tod
7011708Sstevel *
7021708Sstevel */
7031708Sstevel if (iosram_tunnel_init(softsp) == DDI_FAILURE) {
7041708Sstevel SBBC_ERR(CE_PANIC, "Can't create the SRAM <-> SC "
705*7656SSherry.Moore@Sun.COM "comm. tunnel \n");
7061708Sstevel }
7071708Sstevel
7081708Sstevel master_chosen = TRUE;
7091708Sstevel
7101708Sstevel /*
7111708Sstevel * Verify that an 'interrupts' property
7121708Sstevel * exists for this device
7131708Sstevel */
7141708Sstevel
7151708Sstevel if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
716*7656SSherry.Moore@Sun.COM DDI_PROP_DONTPASS, "interrupts",
717*7656SSherry.Moore@Sun.COM &len) != DDI_PROP_SUCCESS) {
7181708Sstevel
7191708Sstevel SBBC_ERR(CE_PANIC, "No 'interrupts' property for the "
720*7656SSherry.Moore@Sun.COM "'chosen' SBBC \n");
7211708Sstevel }
7221708Sstevel
7231708Sstevel /*
7241708Sstevel * add the interrupt handler
7251708Sstevel * NB
7261708Sstevel * should this be a high-level interrupt ?
7271708Sstevel * NB
7281708Sstevel */
7291708Sstevel if (sbbc_add_intr(softsp) == DDI_FAILURE) {
7301708Sstevel SBBC_ERR(CE_PANIC, "Can't add interrupt handler for "
731*7656SSherry.Moore@Sun.COM "'chosen' SBBC \n");
7321708Sstevel }
7331708Sstevel
7341708Sstevel sbbc_enable_intr(softsp);
7351708Sstevel
7361708Sstevel /*
7371708Sstevel * Create the mailbox
7381708Sstevel */
7391708Sstevel if (sbbc_mbox_create(softsp) != 0) {
7401708Sstevel cmn_err(CE_WARN, "No IOSRAM MailBox created!\n");
7411708Sstevel }
7421708Sstevel
7431708Sstevel }
7441708Sstevel }
7451708Sstevel /*
7461708Sstevel * sbbc_add_instance
7471708Sstevel * Must be called to hold chosen_lock.
7481708Sstevel */
7491708Sstevel static void
sbbc_add_instance(sbbc_softstate_t * softsp)7501708Sstevel sbbc_add_instance(sbbc_softstate_t *softsp)
7511708Sstevel {
7521708Sstevel #ifdef DEBUG
7531708Sstevel struct sbbc_softstate *sp;
7541708Sstevel #endif
7551708Sstevel
7561708Sstevel ASSERT(mutex_owned(&chosen_lock));
7571708Sstevel
7581708Sstevel #if defined(DEBUG)
7591708Sstevel /* Verify that this instance is not in the list yet */
7601708Sstevel for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
7611708Sstevel ASSERT(sp != softsp);
7621708Sstevel }
7631708Sstevel #endif
7641708Sstevel
7651708Sstevel /*
7661708Sstevel * Add this instance to the front of the list.
7671708Sstevel */
7681708Sstevel if (sgsbbc_instances != NULL) {
7691708Sstevel sgsbbc_instances->prev = softsp;
7701708Sstevel }
7711708Sstevel
7721708Sstevel softsp->next = sgsbbc_instances;
7731708Sstevel softsp->prev = NULL;
7741708Sstevel sgsbbc_instances = softsp;
7751708Sstevel }
7761708Sstevel
7771708Sstevel static void
sbbc_remove_instance(sbbc_softstate_t * softsp)7781708Sstevel sbbc_remove_instance(sbbc_softstate_t *softsp)
7791708Sstevel {
7801708Sstevel struct sbbc_softstate *sp;
7811708Sstevel
7821708Sstevel for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
7831708Sstevel if (sp == softsp) {
7841708Sstevel if (sp->next != NULL) {
7851708Sstevel sp->next->prev = sp->prev;
7861708Sstevel }
7871708Sstevel if (sp->prev != NULL) {
7881708Sstevel sp->prev->next = sp->next;
7891708Sstevel }
7901708Sstevel if (sgsbbc_instances == softsp) {
7911708Sstevel sgsbbc_instances = sp->next;
7921708Sstevel }
7931708Sstevel break;
7941708Sstevel }
7951708Sstevel }
7961708Sstevel }
7971708Sstevel
7981708Sstevel /*
7991708Sstevel * Generate an SBBC interrupt to the SC
8001708Sstevel * Called from iosram_send_intr()
8011708Sstevel *
8021708Sstevel * send_intr == 0, check if EPLD register clear
8031708Sstevel * for sync'ing SC/OS
8041708Sstevel * send_intr == 1, send the interrupt
8051708Sstevel */
8061708Sstevel int
sbbc_send_intr(sbbc_softstate_t * softsp,int send_intr)8071708Sstevel sbbc_send_intr(sbbc_softstate_t *softsp, int send_intr)
8081708Sstevel {
8091708Sstevel
8101708Sstevel uchar_t *epld_int;
8111708Sstevel volatile uchar_t epld_status;
8121708Sstevel
8131708Sstevel ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
8141708Sstevel
8151708Sstevel if ((softsp == (sbbc_softstate_t *)NULL) ||
816*7656SSherry.Moore@Sun.COM (softsp->epld_regs == (struct sbbc_epld_regs *)NULL))
8171708Sstevel return (ENXIO);
8181708Sstevel
8191708Sstevel /*
8201708Sstevel * Check the L1 EPLD Interrupt register. If the
8211708Sstevel * interrupt bit is set, theres an interrupt outstanding
8221708Sstevel * (we assume) so return (EBUSY).
8231708Sstevel */
8241708Sstevel
8251708Sstevel epld_int = &softsp->epld_regs->epld_reg[EPLD_INTERRUPT];
8261708Sstevel
8271708Sstevel epld_status = ddi_get8(softsp->sbbc_reg_handle2, epld_int);
8281708Sstevel
8291708Sstevel if (epld_status & INTERRUPT_ON)
8301708Sstevel return (EBUSY);
8311708Sstevel
8321708Sstevel if (send_intr == TRUE)
8331708Sstevel ddi_put8(softsp->sbbc_reg_handle2, epld_int,
834*7656SSherry.Moore@Sun.COM (epld_status | INTERRUPT_ON));
8351708Sstevel
8361708Sstevel return (0);
8371708Sstevel }
8381708Sstevel
8391708Sstevel /*
8401708Sstevel * Map SBBC Internal registers
8411708Sstevel *
8421708Sstevel * The call to function should be protected by
8431708Sstevel * chosen_lock or master_iosram->iosram_lock
8441708Sstevel * to make sure a tunnel switch will not occur
8451708Sstevel * in a middle of mapping.
8461708Sstevel */
8471708Sstevel int
sbbc_map_regs(sbbc_softstate_t * softsp)8481708Sstevel sbbc_map_regs(sbbc_softstate_t *softsp)
8491708Sstevel {
8501708Sstevel struct ddi_device_acc_attr attr;
8511708Sstevel
8521708Sstevel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
8531708Sstevel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
8541708Sstevel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
8551708Sstevel
8561708Sstevel /*
8571708Sstevel * Map in register set 1, Common Device Regs
8581708Sstevel * SBCC offset 0x0
8591708Sstevel */
8601708Sstevel if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
861*7656SSherry.Moore@Sun.COM (caddr_t *)&softsp->sbbc_regs,
862*7656SSherry.Moore@Sun.COM SBBC_REGS_OFFSET, SBBC_REGS_SIZE,
863*7656SSherry.Moore@Sun.COM &attr, &softsp->sbbc_reg_handle1) != DDI_SUCCESS) {
8641708Sstevel
8651708Sstevel cmn_err(CE_WARN, "sbbc%d: unable to map interrupt "
866*7656SSherry.Moore@Sun.COM "registers", ddi_get_instance(softsp->dip));
8671708Sstevel return (DDI_FAILURE);
8681708Sstevel }
8691708Sstevel /*
8701708Sstevel * Map in using register set 1, EPLD
8711708Sstevel * SBCC offset 0xe000
8721708Sstevel */
8731708Sstevel if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
874*7656SSherry.Moore@Sun.COM (caddr_t *)&softsp->epld_regs,
875*7656SSherry.Moore@Sun.COM SBBC_EPLD_OFFSET, SBBC_EPLD_SIZE,
876*7656SSherry.Moore@Sun.COM &attr, &softsp->sbbc_reg_handle2) != DDI_SUCCESS) {
8771708Sstevel
8781708Sstevel cmn_err(CE_WARN, "sbbc%d: unable to map EPLD "
879*7656SSherry.Moore@Sun.COM "registers", ddi_get_instance(softsp->dip));
8801708Sstevel return (DDI_FAILURE);
8811708Sstevel }
8821708Sstevel
8831708Sstevel /*
8841708Sstevel * Set up pointers for registers
8851708Sstevel */
8861708Sstevel softsp->port_int_regs = (uint32_t *)((char *)softsp->sbbc_regs +
887*7656SSherry.Moore@Sun.COM SBBC_PCI_INT_STATUS);
8881708Sstevel
8891708Sstevel map_regs_exit:
8901708Sstevel return (DDI_SUCCESS);
8911708Sstevel }
8921708Sstevel
8931708Sstevel
8941708Sstevel /*
8951708Sstevel * Unmap SBBC Internal registers
8961708Sstevel */
8971708Sstevel static void
sbbc_unmap_regs(sbbc_softstate_t * softsp)8981708Sstevel sbbc_unmap_regs(sbbc_softstate_t *softsp)
8991708Sstevel {
9001708Sstevel if (softsp == NULL)
9011708Sstevel return;
9021708Sstevel
9031708Sstevel mutex_enter(&master_iosram->iosram_lock);
9041708Sstevel
9051708Sstevel if (softsp->sbbc_regs) {
9061708Sstevel ddi_regs_map_free(&softsp->sbbc_reg_handle1);
9071708Sstevel softsp->sbbc_regs = NULL;
9081708Sstevel softsp->port_int_regs = NULL;
9091708Sstevel }
9101708Sstevel
9111708Sstevel if (softsp->epld_regs) {
9121708Sstevel ddi_regs_map_free(&softsp->sbbc_reg_handle2);
9131708Sstevel softsp->epld_regs = NULL;
9141708Sstevel }
9151708Sstevel
9161708Sstevel mutex_exit(&master_iosram->iosram_lock);
9171708Sstevel
9181708Sstevel return;
9191708Sstevel
9201708Sstevel }
9211708Sstevel /*
9221708Sstevel * This is here to allow the IOSRAM driver get the softstate
9231708Sstevel * for a chosen node when doing a tunnel switch. Just enables
9241708Sstevel * us to avoid exporting the sbbcp softstate hook
9251708Sstevel */
9261708Sstevel sbbc_softstate_t *
sbbc_get_soft_state(int instance)9271708Sstevel sbbc_get_soft_state(int instance)
9281708Sstevel {
9291708Sstevel return (ddi_get_soft_state(sbbcp, instance));
9301708Sstevel }
9311708Sstevel
9321708Sstevel /*
9331708Sstevel * Add interrupt handlers
9341708Sstevel */
9351708Sstevel int
sbbc_add_intr(sbbc_softstate_t * softsp)9361708Sstevel sbbc_add_intr(sbbc_softstate_t *softsp)
9371708Sstevel {
9381708Sstevel int rc = DDI_SUCCESS;
9391708Sstevel
9401708Sstevel /*
9411708Sstevel * map in the SBBC interrupts
9421708Sstevel * Note that the iblock_cookie was initialised
9431708Sstevel * in the 'attach' routine
9441708Sstevel */
9451708Sstevel
9461708Sstevel if (ddi_add_intr(softsp->dip, 0, &softsp->iblock,
947*7656SSherry.Moore@Sun.COM &softsp->idevice, sbbc_intr_handler,
948*7656SSherry.Moore@Sun.COM (caddr_t)softsp) != DDI_SUCCESS) {
9491708Sstevel
9501708Sstevel cmn_err(CE_WARN, "Can't register SBBC "
951*7656SSherry.Moore@Sun.COM " interrupt handler\n");
9521708Sstevel rc = DDI_FAILURE;
9531708Sstevel }
9541708Sstevel
9551708Sstevel return (rc);
9561708Sstevel }
9571708Sstevel
9581708Sstevel void
sbbc_enable_intr(sbbc_softstate_t * softsp)9591708Sstevel sbbc_enable_intr(sbbc_softstate_t *softsp)
9601708Sstevel {
9611708Sstevel uint32_t *pci_intr_enable_reg;
9621708Sstevel
9631708Sstevel /*
9641708Sstevel * Enable Interrupts now, turn on both INT#A lines
9651708Sstevel */
9661708Sstevel pci_intr_enable_reg = (uint32_t *)((char *)softsp->sbbc_regs +
967*7656SSherry.Moore@Sun.COM SBBC_PCI_INT_ENABLE);
9681708Sstevel ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg,
969*7656SSherry.Moore@Sun.COM (uint32_t)SBBC_PCI_ENABLE_INT_A);
9701708Sstevel }
9711708Sstevel
9721708Sstevel void
sbbc_disable_intr(sbbc_softstate_t * softsp)9731708Sstevel sbbc_disable_intr(sbbc_softstate_t *softsp)
9741708Sstevel {
9751708Sstevel uint32_t *pci_intr_enable_reg;
9761708Sstevel
9771708Sstevel /*
9781708Sstevel * Disable Interrupts now, turn off both INT#A lines
9791708Sstevel */
9801708Sstevel pci_intr_enable_reg = (uint32_t *)((char *)softsp->sbbc_regs +
981*7656SSherry.Moore@Sun.COM SBBC_PCI_INT_ENABLE);
9821708Sstevel ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg, 0);
9831708Sstevel }
984