03831d35f7499c87d51205817c93e9a8d42c4baestevel * CDDL HEADER START
03831d35f7499c87d51205817c93e9a8d42c4baestevel * The contents of this file are subject to the terms of the
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Common Development and Distribution License (the "License").
03831d35f7499c87d51205817c93e9a8d42c4baestevel * You may not use this file except in compliance with the License.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
03831d35f7499c87d51205817c93e9a8d42c4baestevel * See the License for the specific language governing permissions
03831d35f7499c87d51205817c93e9a8d42c4baestevel * and limitations under the License.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * When distributing Covered Code, include this CDDL HEADER in each
03831d35f7499c87d51205817c93e9a8d42c4baestevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * If applicable, add the following below this CDDL HEADER, with the
03831d35f7499c87d51205817c93e9a8d42c4baestevel * fields enclosed by brackets "[]" replaced with your own identifying
03831d35f7499c87d51205817c93e9a8d42c4baestevel * information: Portions Copyright [yyyy] [name of copyright owner]
03831d35f7499c87d51205817c93e9a8d42c4baestevel * CDDL HEADER END
193974072f41a843678abf5f61979c748687e66bSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Use is subject to license terms.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * PCI SBBC Device Driver that provides interfaces into
03831d35f7499c87d51205817c93e9a8d42c4baestevel * EPLD and IO-SRAM
03831d35f7499c87d51205817c93e9a8d42c4baestevel#include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
03831d35f7499c87d51205817c93e9a8d42c4baestevel/* debug flag */
03831d35f7499c87d51205817c93e9a8d42c4baestevel#endif /* DEBUG */
03831d35f7499c87d51205817c93e9a8d42c4baestevel/* driver entry point fn definitions */
03831d35f7499c87d51205817c93e9a8d42c4baestevelstatic int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
03831d35f7499c87d51205817c93e9a8d42c4baestevelstatic int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * SBBC soft state hook
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Chosen IOSRAM
03831d35f7499c87d51205817c93e9a8d42c4baestevel * define new iosram's sbbc and liked list of sbbc.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * At attach time, check if the device is the 'chosen' node
03831d35f7499c87d51205817c93e9a8d42c4baestevel * if it is, set up the IOSRAM Solaris<->SC Comm tunnel
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Its like 'Highlander' - there can be only one !
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Local variable to save intr_in_enabled when the driver is suspended
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Local declarations
03831d35f7499c87d51205817c93e9a8d42c4baestevelstatic void softsp_init(sbbc_softstate_t *, dev_info_t *);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ops stuff.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Declare ops vectors for auto configuration.
03831d35f7499c87d51205817c93e9a8d42c4baestevel 0, /* devo_refcnt */
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Loadable module support.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Initialise the global 'chosen' IOSRAM mutex
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Initialise the iosram driver
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Initialize the mailbox
03831d35f7499c87d51205817c93e9a8d42c4baestevel * remove the mailbox
03831d35f7499c87d51205817c93e9a8d42c4baestevel * remove the iosram driver
03831d35f7499c87d51205817c93e9a8d42c4baestevel#endif /* DEBUG */
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Set the dip in the soft state
03831d35f7499c87d51205817c93e9a8d42c4baestevel * And get interrupt cookies and initialize the
03831d35f7499c87d51205817c93e9a8d42c4baestevel * per instance mutex.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Verify that an 'interrupts' property exists for
03831d35f7499c87d51205817c93e9a8d42c4baestevel * this device. If not, this instance will be ignored.
03831d35f7499c87d51205817c93e9a8d42c4baestevel SBBC_ERR1(CE_WARN, "No 'interrupts' property for the "
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Add this instance to the sbbc chosen iosram list
03831d35f7499c87d51205817c93e9a8d42c4baestevel * so that it can be used for tunnel switch.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * If this is the chosen IOSRAM and there is no master IOSRAM
03831d35f7499c87d51205817c93e9a8d42c4baestevel * yet, then let's set this instance as the master.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * if there is a master alreay due to the previous tunnel switch
03831d35f7499c87d51205817c93e9a8d42c4baestevel * then keep as is even though this is the chosen.
03831d35f7499c87d51205817c93e9a8d42c4baestevel /* Do 'chosen' init only */
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
03831d35f7499c87d51205817c93e9a8d42c4baestevel#endif /* DEBUG */
03831d35f7499c87d51205817c93e9a8d42c4baestevel if ((softsp->suspended == TRUE) && (softsp->chosen == TRUE)) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Enable Interrupts now, turn on both INT#A lines
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Reset intr_in_enabled to the original value
03831d35f7499c87d51205817c93e9a8d42c4baestevel * so the SC can send us interrupt.
03831d35f7499c87d51205817c93e9a8d42c4baestevel /* only tunnel switch the instance with iosram chosen */
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (sgsbbc_iosram_switchfrom(softsp) == DDI_FAILURE) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel "tunnel switch failed\n");
03831d35f7499c87d51205817c93e9a8d42c4baestevel /* Adjust linked list */
03831d35f7499c87d51205817c93e9a8d42c4baestevel if ((softsp->suspended == FALSE) && (softsp->chosen == TRUE)) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Disable Interrupts now, turn OFF both INT#A lines
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Set intr_in_enabled to 0 so the SC won't send
03831d35f7499c87d51205817c93e9a8d42c4baestevel * us interrupt.
03831d35f7499c87d51205817c93e9a8d42c4baestevelsoftsp_init(sbbc_softstate_t *softsp, dev_info_t *devi)
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ddi_get_iblock_cookie() here because we need
03831d35f7499c87d51205817c93e9a8d42c4baestevel * to initialise the mutex regardless of whether
03831d35f7499c87d51205817c93e9a8d42c4baestevel * or not this SBBC will eventually
03831d35f7499c87d51205817c93e9a8d42c4baestevel * register an interrupt handler
03831d35f7499c87d51205817c93e9a8d42c4baestevel (void) ddi_get_iblock_cookie(devi, 0, &softsp->iblock);
03831d35f7499c87d51205817c93e9a8d42c4baestevel sbbc_find_dip_t *dip_struct = (sbbc_find_dip_t *)arg;
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Need to find a node named "bootbus-controller" that is neither
03831d35f7499c87d51205817c93e9a8d42c4baestevel * disabled nor failed. If a node is not ok, there will be an
03831d35f7499c87d51205817c93e9a8d42c4baestevel * OBP status property. Therefore, we will look for a node
03831d35f7499c87d51205817c93e9a8d42c4baestevel * without the status property.
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (strcmp(node_name, "bootbus-controller") == 0 && DDI_CF2(dip) &&
193974072f41a843678abf5f61979c748687e66bSherry Moore (prom_getprop(ddi_get_nodeid(ddi_get_parent(dip)),
03831d35f7499c87d51205817c93e9a8d42c4baestevel * SBBC Interrupt Handler
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Check the SBBC Port Interrupt Status
03831d35f7499c87d51205817c93e9a8d42c4baestevel * register to verify that its our interrupt.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * If yes, clear the register.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Then read the 'interrupt reason' field from SRAM,
03831d35f7499c87d51205817c93e9a8d42c4baestevel * this triggers the appropriate soft_intr handler
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Normally if port_int_status is 0, we assume it is not
03831d35f7499c87d51205817c93e9a8d42c4baestevel * our interrupt. However, we don't want to miss the
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ones that come in during tunnel switch. Therefore,
03831d35f7499c87d51205817c93e9a8d42c4baestevel * we always check the interrupt reason bits in IOSRAM
03831d35f7499c87d51205817c93e9a8d42c4baestevel * to be sure.
03831d35f7499c87d51205817c93e9a8d42c4baestevel port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Generate a softint for each interrupt
03831d35f7499c87d51205817c93e9a8d42c4baestevel * bit set in the intr_in_reason field in SRAM
03831d35f7499c87d51205817c93e9a8d42c4baestevel * that has a corresponding bit set in the
03831d35f7499c87d51205817c93e9a8d42c4baestevel * intr_in_enabled field in SRAM
193974072f41a843678abf5f61979c748687e66bSherry Moore (caddr_t)&intr_enabled, sizeof (intr_enabled))) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
03831d35f7499c87d51205817c93e9a8d42c4baestevel intr_reason = ddi_get32(intr_in_handle, intr_in_reason);
03831d35f7499c87d51205817c93e9a8d42c4baestevel SGSBBC_DBG_INTR(CE_CONT, "intr_reason = %x\n", intr_reason);
03831d35f7499c87d51205817c93e9a8d42c4baestevel for (i = 0; i < SBBC_MAX_INTRS; i++) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * The model we agree with a handler
03831d35f7499c87d51205817c93e9a8d42c4baestevel * is that they run until they have
03831d35f7499c87d51205817c93e9a8d42c4baestevel * exhausted all work. To avoid
03831d35f7499c87d51205817c93e9a8d42c4baestevel * triggering them again, they pass
03831d35f7499c87d51205817c93e9a8d42c4baestevel * a state flag and lock when registering.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * We check the flag, if they are idle,
03831d35f7499c87d51205817c93e9a8d42c4baestevel * we trigger.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * The interrupt handler should so
03831d35f7499c87d51205817c93e9a8d42c4baestevel * intr_func()
03831d35f7499c87d51205817c93e9a8d42c4baestevel * mutex_enter(sbbc_intr_lock);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * sbbc_intr_state = RUNNING;
03831d35f7499c87d51205817c93e9a8d42c4baestevel * mutex_exit(sbbc_intr_lock);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ..........
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ..........
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ..........
03831d35f7499c87d51205817c93e9a8d42c4baestevel * mutex_enter(sbbc_intr_lock);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * sbbc_intr_state = IDLE;
03831d35f7499c87d51205817c93e9a8d42c4baestevel * mutex_exit(sbbc_intr_lock);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * The handler is running
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Clear the corresponding reason bit in SRAM
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Since there is no interlocking between
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Solaris and the SC when writing to SRAM,
03831d35f7499c87d51205817c93e9a8d42c4baestevel * it is possible for the SC to set another
03831d35f7499c87d51205817c93e9a8d42c4baestevel * bit in the interrupt reason field while
03831d35f7499c87d51205817c93e9a8d42c4baestevel * we are handling the current interrupt.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * To minimize the window in which an
03831d35f7499c87d51205817c93e9a8d42c4baestevel * additional bit can be set, reading
03831d35f7499c87d51205817c93e9a8d42c4baestevel * and writing the interrupt reason
03831d35f7499c87d51205817c93e9a8d42c4baestevel * in SRAM must be as close as possible.
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (intr_reason == 0) /* No more interrupts to be processed */
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Clear the Interrupt Status Register (RW1C)
03831d35f7499c87d51205817c93e9a8d42c4baestevel ddi_put32(softsp->sbbc_reg_handle1, port_int_reg, port_int_status);
03831d35f7499c87d51205817c93e9a8d42c4baestevel port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * If we don't already have a master SBBC selected,
03831d35f7499c87d51205817c93e9a8d42c4baestevel * get the <sbbc> property from the /chosen node. If
03831d35f7499c87d51205817c93e9a8d42c4baestevel * the pathname matches, this is the master SBBC and
03831d35f7499c87d51205817c93e9a8d42c4baestevel * we set up the console/TOD SRAM mapping here.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * We've got one already
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Get /chosen node info. prom interface will handle errors.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Look for the "iosram" property on the chosen node with a prom
03831d35f7499c87d51205817c93e9a8d42c4baestevel * interface as ddi_find_devinfo() couldn't be used (calls
03831d35f7499c87d51205817c93e9a8d42c4baestevel * ddi_walk_devs() that creates one extra lock on the device tree).
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * No I/O Board SBBC set up as console, what to do ?
03831d35f7499c87d51205817c93e9a8d42c4baestevel SBBC_ERR(CE_PANIC, "No SBBC found for Console/TOD \n");
03831d35f7499c87d51205817c93e9a8d42c4baestevel * SRAM TOC Offset defaults to 0
03831d35f7499c87d51205817c93e9a8d42c4baestevel * get the full OBP pathname of this node
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (prom_phandle_to_path((phandle_t)nodeid, master_sbbc,
193974072f41a843678abf5f61979c748687e66bSherry Moore sizeof (master_sbbc)) < 0) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
03831d35f7499c87d51205817c93e9a8d42c4baestevel SGSBBC_DBG_ALL("chosen pathname : %s\n", master_sbbc);
03831d35f7499c87d51205817c93e9a8d42c4baestevel SGSBBC_DBG_ALL("device pathname : %s\n", ddi_pathname(softsp->dip, pn));
03831d35f7499c87d51205817c93e9a8d42c4baestevel if (strcmp(master_sbbc, ddi_pathname(softsp->dip, pn)) == 0) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * map in the SBBC regs
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Only the 'chosen' node is used for iosram_read()/_write()
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Must initialise the tunnel before the console/tod
193974072f41a843678abf5f61979c748687e66bSherry Moore "comm. tunnel \n");
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Verify that an 'interrupts' property
03831d35f7499c87d51205817c93e9a8d42c4baestevel * exists for this device
03831d35f7499c87d51205817c93e9a8d42c4baestevel SBBC_ERR(CE_PANIC, "No 'interrupts' property for the "
193974072f41a843678abf5f61979c748687e66bSherry Moore "'chosen' SBBC \n");
03831d35f7499c87d51205817c93e9a8d42c4baestevel * add the interrupt handler
03831d35f7499c87d51205817c93e9a8d42c4baestevel * should this be a high-level interrupt ?
03831d35f7499c87d51205817c93e9a8d42c4baestevel SBBC_ERR(CE_PANIC, "Can't add interrupt handler for "
193974072f41a843678abf5f61979c748687e66bSherry Moore "'chosen' SBBC \n");
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Create the mailbox
03831d35f7499c87d51205817c93e9a8d42c4baestevel * sbbc_add_instance
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Must be called to hold chosen_lock.
03831d35f7499c87d51205817c93e9a8d42c4baestevel#if defined(DEBUG)
03831d35f7499c87d51205817c93e9a8d42c4baestevel /* Verify that this instance is not in the list yet */
03831d35f7499c87d51205817c93e9a8d42c4baestevel for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Add this instance to the front of the list.
03831d35f7499c87d51205817c93e9a8d42c4baestevel for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Generate an SBBC interrupt to the SC
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Called from iosram_send_intr()
03831d35f7499c87d51205817c93e9a8d42c4baestevel * send_intr == 0, check if EPLD register clear
03831d35f7499c87d51205817c93e9a8d42c4baestevel * for sync'ing SC/OS
03831d35f7499c87d51205817c93e9a8d42c4baestevel * send_intr == 1, send the interrupt
03831d35f7499c87d51205817c93e9a8d42c4baestevelsbbc_send_intr(sbbc_softstate_t *softsp, int send_intr)
193974072f41a843678abf5f61979c748687e66bSherry Moore (softsp->epld_regs == (struct sbbc_epld_regs *)NULL))
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Check the L1 EPLD Interrupt register. If the
03831d35f7499c87d51205817c93e9a8d42c4baestevel * interrupt bit is set, theres an interrupt outstanding
03831d35f7499c87d51205817c93e9a8d42c4baestevel * (we assume) so return (EBUSY).
03831d35f7499c87d51205817c93e9a8d42c4baestevel epld_int = &softsp->epld_regs->epld_reg[EPLD_INTERRUPT];
03831d35f7499c87d51205817c93e9a8d42c4baestevel epld_status = ddi_get8(softsp->sbbc_reg_handle2, epld_int);
03831d35f7499c87d51205817c93e9a8d42c4baestevel return (0);
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Map SBBC Internal registers
03831d35f7499c87d51205817c93e9a8d42c4baestevel * The call to function should be protected by
03831d35f7499c87d51205817c93e9a8d42c4baestevel * chosen_lock or master_iosram->iosram_lock
03831d35f7499c87d51205817c93e9a8d42c4baestevel * to make sure a tunnel switch will not occur
03831d35f7499c87d51205817c93e9a8d42c4baestevel * in a middle of mapping.
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Map in register set 1, Common Device Regs
03831d35f7499c87d51205817c93e9a8d42c4baestevel * SBCC offset 0x0
193974072f41a843678abf5f61979c748687e66bSherry Moore &attr, &softsp->sbbc_reg_handle1) != DDI_SUCCESS) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Map in using register set 1, EPLD
03831d35f7499c87d51205817c93e9a8d42c4baestevel * SBCC offset 0xe000
193974072f41a843678abf5f61979c748687e66bSherry Moore &attr, &softsp->sbbc_reg_handle2) != DDI_SUCCESS) {
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Set up pointers for registers
03831d35f7499c87d51205817c93e9a8d42c4baestevel softsp->port_int_regs = (uint32_t *)((char *)softsp->sbbc_regs +
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Unmap SBBC Internal registers
03831d35f7499c87d51205817c93e9a8d42c4baestevel * This is here to allow the IOSRAM driver get the softstate
03831d35f7499c87d51205817c93e9a8d42c4baestevel * for a chosen node when doing a tunnel switch. Just enables
03831d35f7499c87d51205817c93e9a8d42c4baestevel * us to avoid exporting the sbbcp softstate hook
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Add interrupt handlers
03831d35f7499c87d51205817c93e9a8d42c4baestevel * map in the SBBC interrupts
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Note that the iblock_cookie was initialised
03831d35f7499c87d51205817c93e9a8d42c4baestevel * in the 'attach' routine
193974072f41a843678abf5f61979c748687e66bSherry Moore " interrupt handler\n");
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Enable Interrupts now, turn on both INT#A lines
03831d35f7499c87d51205817c93e9a8d42c4baestevel pci_intr_enable_reg = (uint32_t *)((char *)softsp->sbbc_regs +
03831d35f7499c87d51205817c93e9a8d42c4baestevel ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg,
03831d35f7499c87d51205817c93e9a8d42c4baestevel * Disable Interrupts now, turn off both INT#A lines
03831d35f7499c87d51205817c93e9a8d42c4baestevel pci_intr_enable_reg = (uint32_t *)((char *)softsp->sbbc_regs +