xref: /illumos-gate/usr/src/uts/intel/io/dktp/controller/ata/sil3xxx.c (revision d7dc20313645bcb9c9960a816baebe924d60bcf3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Silicon Image 3XXX controller specific processing
28  *
29  * This file may be expanded to take advantage of Silicon Image
30  * additional features (if applicable to specific controller model):
31  * 1. Virtual DMA operation
32  * 2. Concurrent all-channel DMA
33  * 3. Large Block Transfers
34  * 4. Watchdog Timer
35  * 5. Power Management
36  * 6. Hot Plug Support
37  */
38 
39 #include "ata_common.h"
40 #include "sil3xxx.h"
41 #include <sys/pci.h>
42 
43 int fifocntctl[] = {FIFO_CNTCTL_0, FIFO_CNTCTL_1, FIFO_CNTCTL_2, FIFO_CNTCTL_3};
44 int sfiscfg[] = {SFISCFG_0, SFISCFG_1, SFISCFG_2, SFISCFG_3};
45 
46 /*
47  * Controller specific initialization
48  */
49 /* ARGSUSED */
50 uint_t
51 sil3xxx_init_controller(dev_info_t *dip, ushort_t vendor_id, ushort_t device_id)
52 {
53 	ddi_acc_handle_t  pci_conf_handle; /* pci config space handle */
54 	uint8_t cache_lnsz, frrc = 0;
55 	uint32_t fifo_cnt_ctl;
56 	int ports, i;
57 
58 #ifdef	DEBUG
59 	/* LINTED */
60 	ushort_t sfiscfg_val __unused;
61 #endif
62 
63 	/*
64 	 * Sil3114, Sil3512, Sil3112
65 	 * We want to perform this initialization only once per entire
66 	 * pciide controller (all channels)
67 	 */
68 	if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_get_parent(dip),
69 	    DDI_PROP_DONTPASS, "sil3xxx-initialized")) {
70 		return (TRUE);
71 	}
72 
73 	if (pci_config_setup(ddi_get_parent(dip), &pci_conf_handle) !=
74 	    DDI_SUCCESS) {
75 		cmn_err(CE_WARN,
76 		    "sil3xxx_init_controller: Can't do pci_config_setup\n");
77 		return (FALSE);
78 	}
79 
80 	/*
81 	 * Sil3114/3512/3112 incorrectly change between MR and back to
82 	 * MRM for same transaction, which violates the PCI spec and can
83 	 * lead to incorrect data reads.  The workaround
84 	 * is to set bits 2:0 in the FIFO count and control register so
85 	 * that its value, a multiple of 32 bytes starting at 32, not 0,
86 	 * is greater or equal to the cacheline size, a multiple of 4
87 	 * bytes.  This will prevent any reads until the FIFO free space
88 	 * is greater than a cacheline size, ensuring only MRM is issued.
89 	 */
90 
91 	cache_lnsz = pci_config_get8(pci_conf_handle, PCI_CONF_CACHE_LINESZ);
92 
93 	/*
94 	 * The cache line is specified in 32-bit words, so multiply by 4
95 	 * to get bytes.  Then divide by 32 bytes, the granularity of the
96 	 * FIFO control bits 2:0.  Add 1 if there is any remainder to
97 	 * account for a partial 32-byte block, then subtract 1 since for
98 	 * FIFO controls bits 2:0, 0 corresponds to 32, 1 corresponds to
99 	 * 64, and so on.  The calculation is expanded for clarity.
100 	 */
101 	if (cache_lnsz != 0) {
102 		frrc = (cache_lnsz * 4 / 32) +
103 		    (((cache_lnsz * 4) % 32) ? 1 : 0) - 1;
104 	}
105 
106 	if (device_id == SIL3114_DEVICE_ID) {
107 		ports = 4;
108 	} else {
109 		ports = 2;
110 	}
111 
112 	/*
113 	 * The following BAR5 registers are accessed via an indirect register
114 	 * in the PCI configuration space rather than mapping BAR5.
115 	 */
116 	for (i = 0; i < ports; i++) {
117 		GET_BAR5_INDIRECT(pci_conf_handle, fifocntctl[i],
118 		    fifo_cnt_ctl);
119 		fifo_cnt_ctl = (fifo_cnt_ctl & ~0x7) | (frrc & 0x7);
120 		PUT_BAR5_INDIRECT(pci_conf_handle, fifocntctl[i],
121 		    fifo_cnt_ctl);
122 		/*
123 		 * Correct default setting for FIS0cfg
124 		 */
125 #ifdef	DEBUG
126 		GET_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
127 		    sfiscfg_val);
128 		ADBG_WARN(("sil3xxx_init_controller: old val SFISCfg "
129 		    "ch%d: %x\n", i, sfiscfg_val));
130 #endif
131 		PUT_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
132 		    SFISCFG_ERRATA);
133 #ifdef	DEBUG
134 		GET_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
135 		    sfiscfg_val);
136 		ADBG_WARN(("sil3xxx_init_controller: new val SFISCfg "
137 		    "ch%d: %x\n", i, sfiscfg_val));
138 #endif
139 	}
140 
141 	/* Now tear down the pci config setup */
142 	pci_config_teardown(&pci_conf_handle);
143 
144 	/* Create property indicating that initialization was done */
145 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, ddi_get_parent(dip),
146 	    "sil3xxx-initialized", 1);
147 
148 	return (TRUE);
149 }
150