pci-ide.c revision 42f87ea29774769e90a36caa2a9075c07dacac50
1N/A/*
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License (the "License").
1N/A * You may not use this file except in compliance with the License.
1N/A *
1N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * When distributing Covered Code, include this CDDL HEADER in each
1N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A * If applicable, add the following below this CDDL HEADER, with the
1N/A * fields enclosed by brackets "[]" replaced with your own identifying
1N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1N/A *
1N/A * CDDL HEADER END
1N/A */
1N/A/*
1N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A
1N/A/*
1N/A * PCI-IDE bus nexus driver
1N/A */
1N/A
1N/A#include <sys/types.h>
1N/A#include <sys/cmn_err.h>
1N/A#include <sys/conf.h>
1N/A#include <sys/errno.h>
1N/A#include <sys/debug.h>
1N/A#include <sys/ddidmareq.h>
1N/A#include <sys/ddi_impldefs.h>
1N/A#include <sys/dma_engine.h>
1N/A#include <sys/modctl.h>
1N/A#include <sys/ddi.h>
1N/A#include <sys/sunddi.h>
1N/A#include <sys/sunndi.h>
1N/A#include <sys/mach_intr.h>
1N/A#include <sys/kmem.h>
1N/A#include <sys/pci.h>
1N/A#include <sys/promif.h>
1N/A#include <sys/pci_intr_lib.h>
1N/A
1N/Aint pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1N/Aint pciide_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1N/A
1N/A#define PCIIDE_NATIVE_MODE(dip) \
1N/A (!ddi_prop_exists(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
1N/A "compatibility-mode"))
1N/A
1N/A#define PCIIDE_PRE26(dip) \
1N/A ddi_prop_exists(DDI_DEV_T_ANY, (dip), 0, "ignore-hardware-nodes")
1N/A
1N/A#define PCI_IDE_IF_BM_CAP_MASK 0x80
1N/A
1N/A#define PCIIDE_PDSIZE (sizeof (struct ddi_parent_private_data) + \
1N/A sizeof (struct intrspec))
1N/A
1N/A#ifdef DEBUG
1N/Astatic int pci_ide_debug = 0;
1N/A#define PDBG(fmt) \
1N/A if (pci_ide_debug) { \
1N/A prom_printf fmt; \
1N/A }
1N/A#else
1N/A#define PDBG(fmt)
1N/A#endif
1N/A
1N/A#ifndef TRUE
1N/A#define TRUE 1
1N/A#endif
1N/A#ifndef FALSE
1N/A#define FALSE 0
1N/A#endif
1N/A
1N/A/*
1N/A * bus_ops functions
1N/A */
1N/A
1N/Astatic int pciide_bus_map(dev_info_t *dip, dev_info_t *rdip,
1N/A ddi_map_req_t *mp, off_t offset, off_t len,
1N/A caddr_t *vaddrp);
1N/A
1N/Astatic int pciide_ddi_ctlops(dev_info_t *dip, dev_info_t *rdip,
1N/A ddi_ctl_enum_t ctlop, void *arg,
1N/A void *result);
1N/A
1N/Astatic int pciide_get_pri(dev_info_t *dip, dev_info_t *rdip,
1N/A ddi_intr_handle_impl_t *hdlp, int *pri);
1N/A
1N/Astatic int pciide_intr_ops(dev_info_t *dip, dev_info_t *rdip,
1N/A ddi_intr_op_t intr_op,
1N/A ddi_intr_handle_impl_t *hdlp, void *result);
1N/A
1N/Astatic struct intrspec *pciide_get_ispec(dev_info_t *dip, dev_info_t *rdip,
1N/A int inum);
1N/A
1N/A/*
1N/A * Local Functions
1N/A */
1N/Astatic int pciide_initchild(dev_info_t *mydip, dev_info_t *cdip);
1N/A
1N/Astatic void pciide_compat_setup(dev_info_t *mydip, dev_info_t *cdip,
1N/A int dev);
1N/Astatic int pciide_pre26_rnumber_map(dev_info_t *mydip, int rnumber);
1N/Astatic int pciide_map_rnumber(int canonical_rnumber, int pri_native,
1N/A int sec_native);
1N/A
1N/A
1N/A/*
1N/A * Config information
1N/A */
1N/A
1N/Astruct bus_ops pciide_bus_ops = {
1N/A BUSO_REV,
1N/A pciide_bus_map,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A i_ddi_map_fault,
1N/A ddi_dma_map,
1N/A ddi_dma_allochdl,
1N/A ddi_dma_freehdl,
1N/A ddi_dma_bindhdl,
1N/A ddi_dma_unbindhdl,
1N/A ddi_dma_flush,
1N/A ddi_dma_win,
1N/A ddi_dma_mctl,
1N/A pciide_ddi_ctlops,
1N/A ddi_bus_prop_op,
1N/A 0, /* (*bus_get_eventcookie)(); */
1N/A 0, /* (*bus_add_eventcall)(); */
1N/A 0, /* (*bus_remove_eventcall)(); */
1N/A 0, /* (*bus_post_event)(); */
1N/A 0,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A pciide_intr_ops
1N/A};
1N/A
1N/Astruct dev_ops pciide_ops = {
1N/A DEVO_REV, /* devo_rev, */
1N/A 0, /* refcnt */
1N/A ddi_no_info, /* info */
1N/A nulldev, /* identify */
1N/A nulldev, /* probe */
1N/A pciide_attach, /* attach */
1N/A pciide_detach, /* detach */
1N/A nodev, /* reset */
1N/A (struct cb_ops *)0, /* driver operations */
1N/A &pciide_bus_ops, /* bus operations */
1N/A NULL, /* power */
1N/A ddi_quiesce_not_needed, /* quiesce */
1N/A};
1N/A
1N/A/*
1N/A * Module linkage information for the kernel.
1N/A */
1N/A
1N/Astatic struct modldrv modldrv = {
1N/A &mod_driverops, /* Type of module. This is PCI-IDE bus driver */
1N/A "pciide nexus driver for 'PCI-IDE' 1.26",
1N/A &pciide_ops, /* driver ops */
1N/A};
1N/A
1N/Astatic struct modlinkage modlinkage = {
1N/A MODREV_1,
1N/A &modldrv,
1N/A NULL
1N/A};
1N/A
1N/A
1N/Aint
1N/A_init(void)
1N/A{
1N/A return (mod_install(&modlinkage));
1N/A}
1N/A
1N/Aint
1N/A_fini(void)
1N/A{
1N/A return (mod_remove(&modlinkage));
1N/A}
1N/A
1N/Aint
1N/A_info(struct modinfo *modinfop)
1N/A{
1N/A return (mod_info(&modlinkage, modinfop));
1N/A}
1N/A
1N/Aint
1N/Apciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1N/A{
1N/A uint16_t cmdreg;
1N/A ddi_acc_handle_t conf_hdl = NULL;
1N/A int rc;
1N/A
1N/A switch (cmd) {
1N/A case DDI_ATTACH:
1N/A /*
1N/A * Make sure bus-mastering is enabled, even if
1N/A * BIOS didn't.
1N/A */
1N/A rc = pci_config_setup(dip, &conf_hdl);
1N/A
1N/A /*
1N/A * In case of error, return SUCCESS. This is because
1N/A * bus-mastering could be already enabled by BIOS.
1N/A */
1N/A if (rc != DDI_SUCCESS)
1N/A return (DDI_SUCCESS);
1N/A
1N/A cmdreg = pci_config_get16(conf_hdl, PCI_CONF_COMM);
1N/A if ((cmdreg & PCI_COMM_ME) == 0) {
1N/A pci_config_put16(conf_hdl, PCI_CONF_COMM,
1N/A cmdreg | PCI_COMM_ME);
1N/A }
1N/A pci_config_teardown(&conf_hdl);
1N/A return (DDI_SUCCESS);
1N/A
1N/A case DDI_RESUME:
1N/A /* Restore our PCI configuration header */
1N/A if (pci_restore_config_regs(dip) != DDI_SUCCESS) {
1N/A /*
1N/A * XXXX
1N/A * This is a pretty bad thing. However, for some
1N/A * reason it always happens. To further complicate
1N/A * things, it appears if we just ignore this, we
1N/A * properly resume. For now, all I want to do is
1N/A * to generate this message so that it doesn't get
1N/A * forgotten.
1N/A */
1N/A cmn_err(CE_WARN,
1N/A "Couldn't restore PCI config regs for %s(%p)",
1N/A ddi_node_name(dip), (void *) dip);
1N/A }
1N/A#ifdef DEBUG
1N/A /* Bus mastering should still be enabled */
1N/A if (pci_config_setup(dip, &conf_hdl) != DDI_SUCCESS)
1N/A return (DDI_FAILURE);
1N/A cmdreg = pci_config_get16(conf_hdl, PCI_CONF_COMM);
1N/A ASSERT((cmdreg & PCI_COMM_ME) != 0);
1N/A pci_config_teardown(&conf_hdl);
1N/A#endif
1N/A return (DDI_SUCCESS);
1N/A }
1N/A
1N/A return (DDI_FAILURE);
1N/A}
1N/A
1N/A/*ARGSUSED*/
1N/Aint
1N/Apciide_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1N/A{
1N/A switch (cmd) {
1N/A case DDI_DETACH:
1N/A return (DDI_SUCCESS);
1N/A case DDI_SUSPEND:
1N/A /* Save our PCI configuration header */
1N/A if (pci_save_config_regs(dip) != DDI_SUCCESS) {
1N/A /* Don't suspend if we cannot save config regs */
1N/A return (DDI_FAILURE);
1N/A }
1N/A return (DDI_SUCCESS);
1N/A }
1N/A return (DDI_FAILURE);
1N/A}
1N/A
1N/A/*ARGSUSED*/
1N/Astatic int
1N/Apciide_ddi_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
1N/A void *arg, void *result)
1N/A{
1N/A dev_info_t *cdip;
1N/A int controller;
1N/A void *pdptr;
1N/A int rnumber;
1N/A off_t tmp;
1N/A int rc;
1N/A
1N/A PDBG(("pciide_bus_ctl\n"));
1N/A
1N/A switch (ctlop) {
1N/A case DDI_CTLOPS_INITCHILD:
1N/A cdip = (dev_info_t *)arg;
1N/A return (pciide_initchild(dip, cdip));
1N/A
1N/A case DDI_CTLOPS_UNINITCHILD:
1N/A cdip = (dev_info_t *)arg;
1N/A pdptr = ddi_get_parent_data(cdip);
1N/A ddi_set_parent_data(cdip, NULL);
1N/A ddi_set_name_addr(cdip, NULL);
1N/A kmem_free(pdptr, PCIIDE_PDSIZE);
1N/A return (DDI_SUCCESS);
1N/A
1N/A case DDI_CTLOPS_NREGS:
1N/A *(int *)result = 3;
1N/A return (DDI_SUCCESS);
1N/A
1N/A case DDI_CTLOPS_REGSIZE:
1N/A /*
1N/A * Adjust the rnumbers based on which controller instance
1N/A * is requested; adjust for the 2 tuples per controller.
1N/A */
1N/A if (strcmp("0", ddi_get_name_addr(rdip)) == 0)
1N/A controller = 0;
1N/A else
1N/A controller = 1;
1N/A
1N/A
1N/A switch (rnumber = *(int *)arg) {
1N/A case 0:
1N/A case 1:
1N/A rnumber += (2 * controller);
1N/A break;
1N/A case 2:
1N/A rnumber = 4;
1N/A break;
1N/A default:
1N/A PDBG(("pciide_ctlops invalid rnumber\n"));
1N/A return (DDI_FAILURE);
1N/A }
1N/A
1N/A
1N/A if (PCIIDE_PRE26(dip)) {
1N/A int old_rnumber;
1N/A int new_rnumber;
1N/A
1N/A old_rnumber = rnumber;
1N/A new_rnumber
= pciide_pre26_rnumber_map(dip, old_rnumber);
PDBG(("pciide rnumber old %d new %d\n",
old_rnumber, new_rnumber));
rnumber = new_rnumber;
}
/*
* Add 1 to skip over the PCI config space tuple
*/
rnumber++;
/*
* If it's not tuple #2 pass the adjusted request to my parent
*/
if (*(int *)arg != 2) {
return (ddi_ctlops(dip, dip, ctlop, &rnumber, result));
}
/*
* Handle my child's reg-tuple #2 here by splitting my 16 byte
* reg-tuple #4 into two 8 byte ranges based on the
* the child's controller #.
*/
tmp = 8;
rc = ddi_ctlops(dip, dip, ctlop, &rnumber, &tmp);
/*
* Allow for the possibility of less than 16 bytes by
* by checking what's actually returned for my reg-tuple #4.
*/
if (controller == 1) {
if (tmp < 8)
tmp = 0;
else
tmp -= 8;
}
if (tmp > 8)
tmp = 8;
*(off_t *)result = tmp;
return (rc);
default:
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
}
}
/*
* IEEE 1275 Working Group Proposal #414 says that the Primary
* controller is "ata@0" and the Secondary controller "ata@1".
*
* By the time we get here, boot Bootconf (2.6+) has created devinfo
* nodes with the appropriate "reg", "assigned-addresses" and "interrupts"
* properites on the pci-ide node and both ide child nodes.
*
* In compatibility mode the "reg" and "assigned-addresses" properties
* of the pci-ide node are set up like this:
*
* 1. PCI-IDE Nexus
*
* interrupts=0
* (addr-hi addr-mid addr-low size-hi size-low)
* reg= assigned-addresses=00000000.00000000.00000000.00000000.00000000
* 81000000.00000000.000001f0.00000000.00000008
* 81000000.00000000.000003f4.00000000.00000004
* 81000000.00000000,00000170.00000000.00000008
* 81000000.00000000,00000374.00000000.00000004
* 01000020.00000000,-[BAR4]-.00000000.00000010
*
* In native PCI mode the "reg" and "assigned-addresses" properties
* would be set up like this:
*
* 2. PCI-IDE Nexus
*
* interrupts=0
* reg= assigned-addresses=00000000.00000000.00000000.00000000.00000000
* 01000010.00000000.-[BAR0]-.00000000.00000008
* 01000014,00000000.-[BAR1]-.00000000.00000004
* 01000018.00000000.-[BAR2]-.00000000.00000008
* 0100001c.00000000.-[BAR3]-.00000000.00000004
* 01000020.00000000.-[BAR4]-.00000000.00000010
*
*
* In both modes the child nodes simply have the following:
*
* 2. primary controller (compatibility mode)
*
* interrupts=14
* reg=00000000
*
* 3. secondary controller
*
* interrupts=15
* reg=00000001
*
* The pciide_bus_map() function is responsible for turning requests
* to map primary or secondary controller rnumbers into mapping requests
* of the appropriate regspec on the pci-ide node.
*
*/
static int
pciide_initchild(dev_info_t *mydip, dev_info_t *cdip)
{
struct ddi_parent_private_data *pdptr;
struct intrspec *ispecp;
int vec;
int *rp;
uint_t proplen;
char name[80];
int dev;
PDBG(("pciide_initchild\n"));
/*
* Set the address portion of the node name based on
* the controller number (0 or 1) from the 'reg' property.
*/
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
"reg", &rp, (uint_t *)&proplen) != DDI_PROP_SUCCESS) {
PDBG(("pciide_intchild prop error\n"));
return (DDI_NOT_WELL_FORMED);
}
/*
* copy the controller number and
* free the memory allocated by ddi_prop_lookup_int_array
*/
dev = *rp;
ddi_prop_free(rp);
/*
* I only support two controllers per device, determine
* which this one is and set its unit address.
*/
if (dev > 1) {
PDBG(("pciide_initchild bad dev\n"));
return (DDI_NOT_WELL_FORMED);
}
(void) sprintf(name, "%d", dev);
ddi_set_name_addr(cdip, name);
/*
* determine if this instance is running in native or compat mode
*/
pciide_compat_setup(mydip, cdip, dev);
/* interrupts property is required */
if (PCIIDE_NATIVE_MODE(cdip)) {
vec = 1;
} else {
/*
* In compatibility mode, dev 0 should always be
* IRQ 14 and dev 1 is IRQ 15. If for some reason
* this needs to be changed, do it via the interrupts
* property in the ata.conf file.
*/
vec = ddi_prop_get_int(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
"interrupts", -1);
if (vec == -1) {
/* setup compatibility mode interrupts */
if (dev == 0) {
vec = 14;
} else if (dev == 1) {
vec = 15;
} else {
PDBG(("pciide_initchild bad intr\n"));
return (DDI_NOT_WELL_FORMED);
}
}
}
pdptr = kmem_zalloc(PCIIDE_PDSIZE, KM_SLEEP);
ispecp = (struct intrspec *)(pdptr + 1);
pdptr->par_nintr = 1;
pdptr->par_intr = ispecp;
ispecp->intrspec_vec = vec;
ddi_set_parent_data(cdip, pdptr);
PDBG(("pciide_initchild okay\n"));
return (DDI_SUCCESS);
}
static int
pciide_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
off_t offset, off_t len, caddr_t *vaddrp)
{
dev_info_t *pdip;
int rnumber = mp->map_obj.rnumber;
int controller;
int rc;
PDBG(("pciide_bus_map\n"));
if (strcmp("0", ddi_get_name_addr(rdip)) == 0)
controller = 0;
else
controller = 1;
/*
* Adjust the rnumbers based on which controller instance
* is being mapped; adjust for the 2 tuples per controller.
*/
switch (rnumber) {
case 0:
case 1:
mp->map_obj.rnumber += (controller * 2);
break;
case 2:
/*
* split the 16 I/O ports into two 8 port ranges
*/
mp->map_obj.rnumber = 4;
if (offset + len > 8) {
PDBG(("pciide_bus_map offset\n"));
return (DDI_FAILURE);
}
if (len == 0)
len = 8 - offset;
offset += 8 * controller;
break;
default:
PDBG(("pciide_bus_map default\n"));
return (DDI_FAILURE);
}
if (PCIIDE_PRE26(dip)) {
int old_rnumber;
int new_rnumber;
old_rnumber = mp->map_obj.rnumber;
new_rnumber = pciide_pre26_rnumber_map(dip, old_rnumber);
PDBG(("pciide rnumber old %d new %d\n",
old_rnumber, new_rnumber));
mp->map_obj.rnumber = new_rnumber;
}
/*
* Add 1 to skip over the PCI config space tuple
*/
mp->map_obj.rnumber++;
/*
* pass the adjusted request to my parent
*/
pdip = ddi_get_parent(dip);
rc = ((*(DEVI(pdip)->devi_ops->devo_bus_ops->bus_map))
(pdip, dip, mp, offset, len, vaddrp));
PDBG(("pciide_bus_map %s\n", rc == DDI_SUCCESS ? "okay" : "!ok"));
return (rc);
}
static struct intrspec *
pciide_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inumber)
{
struct ddi_parent_private_data *ppdptr;
PDBG(("pciide_get_ispec\n"));
/*
* Native mode PCI-IDE controllers share the parent's
* PCI interrupt line.
*
* Compatibility mode PCI-IDE controllers have their
* own intrspec which specifies ISA IRQ 14 or 15.
*
*/
if (PCIIDE_NATIVE_MODE(rdip)) {
ddi_intrspec_t is;
is = pci_intx_get_ispec(dip, dip, inumber);
PDBG(("pciide_get_ispec okay\n"));
return ((struct intrspec *)is);
}
/* Else compatibility mode, use the ISA IRQ */
if ((ppdptr = ddi_get_parent_data(rdip)) == NULL) {
PDBG(("pciide_get_ispec null\n"));
return (NULL);
}
/* validate the interrupt number */
if (inumber >= ppdptr->par_nintr) {
PDBG(("pciide_get_inum\n"));
return (NULL);
}
PDBG(("pciide_get_ispec ok\n"));
return ((struct intrspec *)&ppdptr->par_intr[inumber]);
}
static int
pciide_get_pri(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp, int *pri)
{
struct intrspec *ispecp;
int *intpriorities;
uint_t num_intpriorities;
PDBG(("pciide_get_pri\n"));
if ((ispecp = pciide_get_ispec(dip, rdip, hdlp->ih_inum)) == NULL) {
PDBG(("pciide_get_pri null\n"));
return (DDI_FAILURE);
}
if (PCIIDE_NATIVE_MODE(rdip)) {
*pri = ispecp->intrspec_pri;
PDBG(("pciide_get_pri ok\n"));
return (DDI_SUCCESS);
}
/* check if the intrspec has been initialized */
if (ispecp->intrspec_pri != 0) {
*pri = ispecp->intrspec_pri;
PDBG(("pciide_get_pri ok2\n"));
return (DDI_SUCCESS);
}
/* Use a default of level 5 */
ispecp->intrspec_pri = 5;
/*
* If there's an interrupt-priorities property, use it to
* over-ride the default interrupt priority.
*/
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
"interrupt-priorities", &intpriorities, &num_intpriorities) ==
DDI_PROP_SUCCESS) {
if (hdlp->ih_inum < num_intpriorities)
ispecp->intrspec_pri = intpriorities[hdlp->ih_inum];
ddi_prop_free(intpriorities);
}
*pri = ispecp->intrspec_pri;
PDBG(("pciide_get_pri ok3\n"));
return (DDI_SUCCESS);
}
static int
pciide_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
struct intrspec *ispecp;
int rc;
int pri = 0;
PDBG(("pciide_intr_ops: dip %p rdip %p op %x hdlp %p\n",
(void *)dip, (void *)rdip, intr_op, (void *)hdlp));
switch (intr_op) {
case DDI_INTROP_SUPPORTED_TYPES:
*(int *)result = DDI_INTR_TYPE_FIXED;
break;
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_LEVEL;
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL:
*(int *)result = (!PCIIDE_NATIVE_MODE(rdip)) ?
i_ddi_get_intx_nintrs(rdip) : 1;
break;
case DDI_INTROP_ALLOC:
if ((ispecp = pciide_get_ispec(dip, rdip, hdlp->ih_inum)) ==
NULL)
return (DDI_FAILURE);
*(int *)result = hdlp->ih_scratch1;
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
if (pciide_get_pri(dip, rdip, hdlp, &pri) != DDI_SUCCESS) {
*(int *)result = 0;
return (DDI_FAILURE);
}
*(int *)result = pri;
break;
case DDI_INTROP_ADDISR:
if ((ispecp = pciide_get_ispec(dip, rdip, hdlp->ih_inum)) ==
NULL)
return (DDI_FAILURE);
((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispecp;
ispecp->intrspec_func = hdlp->ih_cb_func;
break;
case DDI_INTROP_REMISR:
if ((ispecp = pciide_get_ispec(dip, rdip, hdlp->ih_inum)) ==
NULL)
return (DDI_FAILURE);
ispecp->intrspec_func = (uint_t (*)()) 0;
break;
case DDI_INTROP_ENABLE:
/* FALLTHRU */
case DDI_INTROP_DISABLE:
if (PCIIDE_NATIVE_MODE(rdip)) {
rdip = dip;
dip = ddi_get_parent(dip);
} else { /* get ptr to the root node */
dip = ddi_root_node();
}
rc = (*(DEVI(dip)->devi_ops->devo_bus_ops->bus_intr_op))(dip,
rdip, intr_op, hdlp, result);
#ifdef DEBUG
if (intr_op == DDI_INTROP_ENABLE) {
PDBG(("pciide_enable rc=%d", rc));
} else
PDBG(("pciide_disable rc=%d", rc));
#endif /* DEBUG */
return (rc);
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* This is one of the places where controller specific setup needs to be
* considered.
* At this point the controller was already pre-qualified as a known and
* supported pciide controller.
* Some controllers do not provide PCI_MASS_IDE sub-class code and IDE
* programming interface code but rather PCI_MASS_OTHER sub-class code
* without any additional data.
* For those controllers IDE programming interface cannot be extracted
* from PCI class - we assume that they are pci-native type and we fix
* the programming interface used by other functions.
* The programming interface byte is set to indicate pci-native mode
* for both controllers and the Bus Master DMA capabilitiy of the controller.
*/
static void
pciide_compat_setup(dev_info_t *mydip, dev_info_t *cdip, int dev)
{
int class_code;
int rc = DDI_PROP_SUCCESS;
class_code = ddi_prop_get_int(DDI_DEV_T_ANY, mydip,
DDI_PROP_DONTPASS, "class-code", 0);
if (((class_code & 0x00FF00) >> 8) == PCI_MASS_IDE) {
/*
* Controller provides PCI_MASS_IDE sub-class code first
* (implied IDE programming interface)
*/
if ((dev == 0 && !(class_code & PCI_IDE_IF_NATIVE_PRI)) ||
(dev == 1 && !(class_code & PCI_IDE_IF_NATIVE_SEC))) {
rc = ndi_prop_update_int(DDI_DEV_T_NONE, cdip,
"compatibility-mode", 1);
if (rc != DDI_PROP_SUCCESS)
cmn_err(CE_WARN,
"pciide prop error %d compat-mode", rc);
}
} else {
/*
* Pci-ide controllers not providing PCI_MASS_IDE sub-class are
* assumed to be of pci-native type and bus master DMA capable.
* Programming interface part of the class-code property is
* fixed here.
*/
class_code &= 0x00ffff00;
class_code |= PCI_IDE_IF_BM_CAP_MASK |
PCI_IDE_IF_NATIVE_PRI | PCI_IDE_IF_NATIVE_SEC;
rc = ddi_prop_update_int(DDI_DEV_T_NONE, mydip,
"class-code", class_code);
if (rc != DDI_PROP_SUCCESS)
cmn_err(CE_WARN,
"pciide prop error %d class-code", rc);
}
}
static int
pciide_pre26_rnumber_map(dev_info_t *mydip, int rnumber)
{
int pri_native;
int sec_native;
int class_code;
class_code = ddi_prop_get_int(DDI_DEV_T_ANY, mydip, DDI_PROP_DONTPASS,
"class-code", 0);
pri_native = (class_code & PCI_IDE_IF_NATIVE_PRI) ? TRUE : FALSE;
sec_native = (class_code & PCI_IDE_IF_NATIVE_SEC) ? TRUE : FALSE;
return (pciide_map_rnumber(rnumber, pri_native, sec_native));
}
/*
* The canonical order of the reg property tuples for the
* Base Address Registers is supposed to be:
*
* primary controller (BAR 0)
* primary controller (BAR 1)
* secondary controller (BAR 2)
* secondary controller (BAR 3)
* bus mastering regs (BAR 4)
*
* For 2.6, bootconf has been fixed to always generate the
* reg property (and assigned-addresses property) tuples
* in the above order.
*
* But in releases prior to 2.6 the order varies depending
* on whether compatibility or native mode is being used for
* each controller. There ends up being four possible
* orders:
*
* BM, P0, P1, S0, S1 primary compatible, secondary compatible
* S0, S1, BM, P0, P1 primary compatible, secondary native
* P0, P1, BM, S0, S1 primary native, secondary compatible
* P0, P1, S0, S1, BM primary native, secondary native
*
* where: Px is the primary tuples, Sx the secondary tuples, and
* B the Bus Master tuple.
*
* Here's the results for each of the four states:
*
* 0, 1, 2, 3, 4
*
* CC 1, 2, 3, 4, 0
* CN 3, 4, 0, 1, 2
* NC 0, 1, 3, 4, 2
* NN 0, 1, 2, 3, 4
*
* C = compatible(!native) == 0
* N = native == 1
*
* Here's the transformation matrix:
*/
static int pciide_transform[2][2][5] = {
/* P S */
/* [C][C] */ +1, +1, +1, +1, -4,
/* [C][N] */ +3, +3, -2, -2, -2,
/* [N][C] */ +0, +0, +1, +1, -2,
/* [N][N] */ +0, +0, +0, +0, +0
};
static int
pciide_map_rnumber(int rnumber, int pri_native, int sec_native)
{
/* transform flags into indexes */
pri_native = pri_native ? 1 : 0;
sec_native = sec_native ? 1 : 0;
rnumber += pciide_transform[pri_native][sec_native][rnumber];
return (rnumber);
}