/*
* Product specific probe and attach routines for:
* 3940, 2940, aic7895, aic7890, aic7880,
* aic7870, aic7860 and aic7850 SCSI controllers
*
* Copyright (c) 1994-2001 Justin T. Gibbs.
* Copyright (c) 2000-2001 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: ahc_pci.c,v 1.76 2025/06/27 21:36:22 andvar Exp $
*
* //depot/aic7xxx/aic7xxx/aic7xxx_pci.c#57 $
*
* $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.22 2003/01/20 20:44:55 gibbs Exp $
*/
/*
* Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
*/
/*
* If the second function is not hooked up, ignore it.
* Unfortunately, not all MB vendors implement the
* subdevice ID as per the Adaptec spec, so do our best
* to sanity check it prior to accepting the subdevice
* ID as valid.
*/
if (func > 0
&& ahc_9005_subdevinfo_valid(PCI_VENDOR(id), PCI_PRODUCT(id),
PCI_VENDOR(subid), PCI_PRODUCT(subid))
&& SUBID_9005_MFUNCENB(PCI_PRODUCT(subid)) == 0)
return (NULL);
for (i = 0; i < ahc_num_pci_devs; i++) {
entry = &ahc_pci_ident_table[i];
if (entry->full_id == (full_id & entry->id_mask))
return (entry);
}
return (NULL);
}
#ifdef AHC_ALLOW_MEMIO
if (memh_valid) {
st = memt;
sh = memh;
} else
#endif
if (ioh_valid) {
st = iot;
sh = ioh;
} else {
aprint_error(": unable to map registers\n");
return;
}
ahc->tag = st;
ahc->bsh = sh;
ahc->chip |= AHC_PCI;
/*
* Before we continue probing the card, ensure that
* its interrupts are *disabled*. We don't want
* a misstep to hang the machine in an interrupt
* storm.
*/
ahc_intr_enable(ahc, FALSE);
/*
* XXX somehow reading this once fails on some sparc64 systems.
* This may be a problem in the sparc64 PCI code. Doing it
* twice works around it.
*/
devconfig = pci_conf_read(pa->pa_pc, pa->pa_tag, DEVCONFIG);
devconfig = pci_conf_read(pa->pa_pc, pa->pa_tag, DEVCONFIG);
/*
* If we need to support high memory, enable dual
* address cycles. This bit must be set to enable
* high address bit generation even if we are on a
* 64bit bus (PCI64BIT set in devconfig).
*/
if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
/*
* Disable PCI parity error reporting. Users typically
* do this to work around broken PCI chipsets that get
* the parity timing wrong and thus generate lots of spurious
* errors.
*/
if ((ahc->flags & AHC_DISABLE_PCI_PERR) != 0)
command &= ~PCI_COMMAND_PARITY_ENABLE;
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command);
/* On all PCI adapters, we allow SCB paging */
ahc->flags |= AHC_PAGESCBS;
error = ahc_softc_init(ahc);
if (error != 0)
goto error_out;
ahc->bus_intr = ahc_pci_intr;
/* Remember how the card was setup in case there is no SEEPROM */
if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) {
ahc_pause(ahc);
if ((ahc->features & AHC_ULTRA2) != 0)
our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
else
our_id = ahc_inb(ahc, SCSIID) & OID;
sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
scsiseq = ahc_inb(ahc, SCSISEQ);
} else {
sxfrctl1 = STPWEN;
our_id = 7;
scsiseq = 0;
}
error = ahc_reset(ahc);
if (error != 0)
goto error_out;
if ((ahc->features & AHC_DT) != 0) {
u_int sfunct;
/*
* We cannot perform ULTRA speeds without the presence
* of the external precision resistor.
* Allow override for the SGI O2 though, which has two onboard ahc
* that fail here but are perfectly capable of ultra speeds.
*/
override_ultra = FALSE;
prop_dictionary_get_bool(device_properties(self),
"aic7xxx-override-ultra", &override_ultra);
if (ahc->flags & AHC_USEDEFAULTS) {
/*
* PCI Adapter default setup
* Should only be used if the adapter does not have
* a SEEPROM.
*/
/* See if someone else set us up already */
if ((ahc->flags & AHC_NO_BIOS_INIT) == 0
&& scsiseq != 0) {
prop_bool_t usetd;
printf("%s: Using left over BIOS settings\n",
ahc_name(ahc));
ahc->flags &= ~AHC_USEDEFAULTS;
/*
* Ignore target device settings and use default
* if BIOS initializes chip's SRAM with some
* conservative settings (async, no tagged
* queuing etc.) and machine dependent device
* property is set.
*/
usetd = prop_dictionary_get(
device_properties(ahc->sc_dev),
"aic7xxx-use-target-defaults");
if (usetd != NULL) {
KASSERT(prop_object_type(usetd) ==
PROP_TYPE_BOOL);
if (prop_bool_true(usetd))
ahc->flags |= AHC_USETARGETDEFAULTS;
}
ahc->flags |= AHC_BIOS_ENABLED;
} else {
/*
* Assume only one connector and always turn
* on termination.
*/
our_id = 0x07;
sxfrctl1 = STPWEN;
}
ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
ahc->our_id = our_id;
}
/*
* Take a look to see if we have external SRAM.
* We currently do not attempt to use SRAM that is
* shared among multiple controllers.
*/
ahc_probe_ext_scbram(ahc);
/*
* Record our termination setting for the
* generic initialization routine.
*/
if ((sxfrctl1 & STPWEN) != 0)
ahc->flags |= AHC_TERM_ENB_A;
static int
ahc_9005_subdevinfo_valid(uint16_t device, uint16_t vendor,
uint16_t subdevice, uint16_t subvendor)
{
int result;
/* Default to invalid. */
result = 0;
if (vendor == 0x9005
&& subvendor == 0x9005
&& subdevice != device
&& SUBID_9005_TYPE_KNOWN(subdevice) != 0) {
switch (SUBID_9005_TYPE(subdevice)) {
case SUBID_9005_TYPE_MB:
break;
case SUBID_9005_TYPE_CARD:
case SUBID_9005_TYPE_LCCARD:
/*
* Currently only trust Adaptec cards to
* get the sub device info correct.
*/
if (DEVID_9005_TYPE(device) == DEVID_9005_TYPE_HBA)
result = 1;
break;
case SUBID_9005_TYPE_RAID:
break;
default:
break;
}
}
return (result);
}
/*
* Test for the presence of external sram in an
* "unshared" configuration.
*/
static int
ahc_ext_scbram_present(struct ahc_softc *ahc)
{
u_int chip;
int ramps;
int single_user;
uint32_t devconfig;
if ((ahc->features & AHC_ULTRA2) != 0)
ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
else if (chip == AHC_AIC7895 || chip == AHC_AIC7895C)
/*
* External SCBRAM arbitration is flakey
* on these chips. Unfortunately this means
* we don't use the extra SCB ram space on the
* 3940AUW.
*/
ramps = 0;
else if (chip >= AHC_AIC7870)
ramps = (devconfig & RAMPSM) != 0;
else
ramps = 0;
if (ramps && single_user)
return (1);
return (0);
}
/*
* Enable external scbram.
*/
static void
ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
int fast, int large)
{
uint32_t devconfig;
if (ahc->features & AHC_MULTI_FUNC) {
/*
* Set the SCB Base addr (highest address bit)
* depending on which channel we are.
*/
ahc_outb(ahc, SCBBADDR, ahc->bd->func);
}
/*
* Take a look to see if we have external SRAM.
* We currently do not attempt to use SRAM that is
* shared among multiple controllers.
*/
static void
ahc_probe_ext_scbram(struct ahc_softc *ahc)
{
int num_scbs;
int test_num_scbs;
int enable;
int pcheck;
int fast;
int large;
enable = FALSE;
pcheck = FALSE;
fast = FALSE;
large = FALSE;
num_scbs = 0;
if (ahc_ext_scbram_present(ahc) == 0)
goto done;
/*
* Probe for the best parameters to use.
*/
ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
num_scbs = ahc_probe_scbs(ahc);
if (num_scbs == 0) {
/* The SRAM wasn't really present. */
goto done;
}
enable = TRUE;
/*
* Clear any outstanding parity error
* and ensure that parity error reporting
* is enabled.
*/
ahc_outb(ahc, SEQCTL, 0);
ahc_outb(ahc, CLRINT, CLRPARERR);
ahc_outb(ahc, CLRINT, CLRBRKADRINT);
/* Now see if we can do parity */
ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
num_scbs = ahc_probe_scbs(ahc);
if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
|| (ahc_inb(ahc, ERROR) & MPARERR) == 0)
pcheck = TRUE;
/* Now see if we can do fast timing */
ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
test_num_scbs = ahc_probe_scbs(ahc);
if (test_num_scbs == num_scbs
&& ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
|| (ahc_inb(ahc, ERROR) & MPARERR) == 0))
fast = TRUE;
/*
* See if we can use large SCBs and still maintain
* the same overall count of SCBs.
*/
if ((ahc->features & AHC_LARGE_SCBS) != 0) {
ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
test_num_scbs = ahc_probe_scbs(ahc);
if (test_num_scbs >= num_scbs) {
large = TRUE;
num_scbs = test_num_scbs;
if (num_scbs >= 64) {
/*
* We have enough space to move the
* "busy targets table" into SCB space
* and make it qualify all the way to the
* lun level.
*/
ahc->flags |= AHC_SCB_BTT;
}
}
}
done:
/*
* Disable parity error reporting until we
* can load instruction ram.
*/
ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
/* Clear any latched parity error */
ahc_outb(ahc, CLRINT, CLRPARERR);
ahc_outb(ahc, CLRINT, CLRBRKADRINT);
if (1/*bootverbose*/ && enable) {
printf("%s: External SRAM, %s access%s, %dbytes/SCB\n",
ahc_name(ahc), fast ? "fast" : "slow",
pcheck ? ", parity checking enabled" : "",
large ? 64 : 32);
}
ahc_scbram_config(ahc, enable, pcheck, fast, large);
}
#if 0
/*
* Perform some simple tests that should catch situations where
* our registers are invalidly mapped.
*/
static int
ahc_pci_test_register_access(struct ahc_softc *ahc)
{
int error;
u_int status1;
uint32_t cmd;
uint8_t hcntrl;
error = EIO;
/*
* Enable PCI error interrupt status, but suppress NMIs
* generated by SERR raised due to target aborts.
*/
cmd = pci_conf_read(ahc->bd->pc, ahc->bd->tag, PCIR_COMMAND);
pci_conf_write(ahc->bd->pc, ahc->bd->tag, PCIR_COMMAND,
cmd & ~PCIM_CMD_SERRESPEN);
/*
* First a simple test to see if any
* registers can be read. Reading
* HCNTRL has no side effects and has
* at least one bit that is guaranteed to
* be zero so it is a good register to
* use for this test.
*/
hcntrl = ahc_inb(ahc, HCNTRL);
if (hcntrl == 0xFF)
goto fail;
/*
* Next create a situation where write combining
* or read prefetching could be initiated by the
* CPU or host bridge. Our device does not support
* either, so look for data corruption and/or flagged
* PCI errors.
*/
ahc_outb(ahc, HCNTRL, hcntrl|PAUSE);
while (ahc_is_paused(ahc) == 0)
;
ahc_outb(ahc, SEQCTL, PERRORDIS);
ahc_outb(ahc, SCBPTR, 0);
ahc_outl(ahc, SCB_BASE, 0x5aa555aa);
if (ahc_inl(ahc, SCB_BASE) != 0x5aa555aa)
goto fail;
if (status1 & DPE) {
printf("%s: Data Parity Error Detected during address "
"or write data phase\n", ahc_name(ahc));
}
if (status1 & SSE) {
printf("%s: Signal System Error Detected\n", ahc_name(ahc));
}
if (status1 & RMA) {
printf("%s: Received a Master Abort\n", ahc_name(ahc));
}
if (status1 & RTA) {
printf("%s: Received a Target Abort\n", ahc_name(ahc));
}
if (status1 & STA) {
printf("%s: Signaled a Target Abort\n", ahc_name(ahc));
}
if (status1 & DPR) {
printf("%s: Data Parity Error has been reported via PERR#\n",
ahc_name(ahc));
}
/*
* The BIOS disables the use of MWI transactions
* since it does not have the MWI bug work around
* we have. Disabling MWI reduces performance, so
* turn it on again.
*/
command = pci_conf_read(ahc->bd->pc, ahc->bd->tag,
PCI_COMMAND_STATUS_REG);
command |= PCI_COMMAND_INVALIDATE_ENABLE;
pci_conf_write(ahc->bd->pc, ahc->bd->tag,
PCI_COMMAND_STATUS_REG, command);
ahc->bugs |= AHC_PCI_MWI_BUG;
}
/*
* XXX Does CACHETHEN really not work??? What about PCI retry?
* on C level chips. Need to test, but for now, play it safe.
*/
ahc->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
| AHC_CACHETHEN_BUG;
#if 0
uint32_t devconfig;
/*
* Cachesize must also be zero due to stray DAC
* problem when sitting behind some bridges.
*/
pci_conf_write(ahc->bd->pc, ahc->bd->tag, CSIZE_LATTIME, 0);
devconfig = pci_conf_read(ahc->bd->pc, ahc->bd->tag, DEVCONFIG);
devconfig |= MRDCEN;
pci_conf_write(ahc->bd->pc, ahc->bd->tag, DEVCONFIG, devconfig);
#endif
ahc->flags |= AHC_NEWEEPROM_FMT;
return (0);
}