/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Silicon Image 3XXX controller specific processing
*
* This file may be expanded to take advantage of Silicon Image
* additional features (if applicable to specific controller model):
* 1. Virtual DMA operation
* 2. Concurrent all-channel DMA
* 3. Large Block Transfers
* 4. Watchdog Timer
* 5. Power Management
* 6. Hot Plug Support
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ata_common.h"
#include "sil3xxx.h"
#include <sys/pci.h>
int fifocntctl[] = {FIFO_CNTCTL_0, FIFO_CNTCTL_1, FIFO_CNTCTL_2, FIFO_CNTCTL_3};
int sfiscfg[] = {SFISCFG_0, SFISCFG_1, SFISCFG_2, SFISCFG_3};
/*
* Controller specific initialization
*/
uint_t
sil3xxx_init_controller(dev_info_t *dip,
/* LINTED */
ushort_t vendor_id, ushort_t device_id)
{
ddi_acc_handle_t pci_conf_handle; /* pci config space handle */
uint8_t cache_lnsz, frrc = 0;
uint32_t fifo_cnt_ctl;
int ports, i;
#ifdef DEBUG
/* LINTED */
ushort_t sfiscfg_val;
#endif
/*
* Sil3114, Sil3512, Sil3112
* We want to perform this initialization only once per entire
* pciide controller (all channels)
*/
if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_get_parent(dip),
DDI_PROP_DONTPASS, "sil3xxx-initialized")) {
return (TRUE);
}
if (pci_config_setup(ddi_get_parent(dip), &pci_conf_handle) !=
DDI_SUCCESS) {
cmn_err(CE_WARN,
"sil3xxx_init_controller: Can't do pci_config_setup\n");
return (FALSE);
}
/*
* Sil3114/3512/3112 incorrectly change between MR and back to
* MRM for same transaction, which violates the PCI spec and can
* lead to incorrect data reads. The workaround
* is to set bits 2:0 in the FIFO count and control register so
* that its value, a multiple of 32 bytes starting at 32, not 0,
* is greater or equal to the cacheline size, a multiple of 4
* bytes. This will prevent any reads until the FIFO free space
* is greater than a cacheline size, ensuring only MRM is issued.
*/
cache_lnsz = pci_config_get8(pci_conf_handle, PCI_CONF_CACHE_LINESZ);
/*
* The cache line is specified in 32-bit words, so multiply by 4
* to get bytes. Then divide by 32 bytes, the granularity of the
* FIFO control bits 2:0. Add 1 if there is any remainder to
* account for a partial 32-byte block, then subtract 1 since for
* FIFO controls bits 2:0, 0 corresponds to 32, 1 corresponds to
* 64, and so on. The calculation is expanded for clarity.
*/
if (cache_lnsz != 0) {
frrc = (cache_lnsz * 4 / 32) +
(((cache_lnsz * 4) % 32) ? 1 : 0) - 1;
}
if (device_id == SIL3114_DEVICE_ID) {
ports = 4;
} else {
ports = 2;
}
/*
* The following BAR5 registers are accessed via an indirect register
* in the PCI configuration space rather than mapping BAR5.
*/
for (i = 0; i < ports; i++) {
GET_BAR5_INDIRECT(pci_conf_handle, fifocntctl[i],
fifo_cnt_ctl);
fifo_cnt_ctl = (fifo_cnt_ctl & ~0x7) | (frrc & 0x7);
PUT_BAR5_INDIRECT(pci_conf_handle, fifocntctl[i],
fifo_cnt_ctl);
/*
* Correct default setting for FIS0cfg
*/
#ifdef DEBUG
GET_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
sfiscfg_val);
ADBG_WARN(("sil3xxx_init_controller: old val SFISCfg "
"ch%d: %x\n", i, sfiscfg_val));
#endif
PUT_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
SFISCFG_ERRATA);
#ifdef DEBUG
GET_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
sfiscfg_val);
ADBG_WARN(("sil3xxx_init_controller: new val SFISCfg "
"ch%d: %x\n", i, sfiscfg_val));
#endif
}
/* Now tear down the pci config setup */
pci_config_teardown(&pci_conf_handle);
/* Create property indicating that initialization was done */
(void) ddi_prop_update_int(DDI_DEV_T_NONE, ddi_get_parent(dip),
"sil3xxx-initialized", 1);
return (TRUE);
}