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 * Copyright 2018 RackTop Systems.
27 */
28 /*
29 * Silicon Image 3XXX controller specific processing
30 *
31 * This file may be expanded to take advantage of Silicon Image
32 * additional features (if applicable to specific controller model):
33 * 1. Virtual DMA operation
34 * 2. Concurrent all-channel DMA
35 * 3. Large Block Transfers
36 * 4. Watchdog Timer
37 * 5. Power Management
38 * 6. Hot Plug Support
39 */
40
41 #include "ata_common.h"
42 #include "sil3xxx.h"
43 #include <sys/pci.h>
44
45 int fifocntctl[] = {FIFO_CNTCTL_0, FIFO_CNTCTL_1, FIFO_CNTCTL_2, FIFO_CNTCTL_3};
46 int sfiscfg[] = {SFISCFG_0, SFISCFG_1, SFISCFG_2, SFISCFG_3};
47
48 /*
49 * Controller specific initialization
50 */
51 /* ARGSUSED */
52 uint_t
sil3xxx_init_controller(dev_info_t * dip,ushort_t vendor_id,ushort_t device_id)53 sil3xxx_init_controller(dev_info_t *dip, ushort_t vendor_id, ushort_t device_id)
54 {
55 ddi_acc_handle_t pci_conf_handle; /* pci config space handle */
56 uint8_t cache_lnsz, frrc = 0;
57 uint32_t fifo_cnt_ctl;
58 int ports, i;
59
60 #ifdef ATA_DEBUG
61 ushort_t sfiscfg_val __unused;
62 #endif
63
64 /*
65 * Sil3114, Sil3512, Sil3112
66 * We want to perform this initialization only once per entire
67 * pciide controller (all channels)
68 */
69 if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_get_parent(dip),
70 DDI_PROP_DONTPASS, "sil3xxx-initialized")) {
71 return (TRUE);
72 }
73
74 if (pci_config_setup(ddi_get_parent(dip), &pci_conf_handle) !=
75 DDI_SUCCESS) {
76 cmn_err(CE_WARN,
77 "sil3xxx_init_controller: Can't do pci_config_setup\n");
78 return (FALSE);
79 }
80
81 /*
82 * Sil3114/3512/3112 incorrectly change between MR and back to
83 * MRM for same transaction, which violates the PCI spec and can
84 * lead to incorrect data reads. The workaround
85 * is to set bits 2:0 in the FIFO count and control register so
86 * that its value, a multiple of 32 bytes starting at 32, not 0,
87 * is greater or equal to the cacheline size, a multiple of 4
88 * bytes. This will prevent any reads until the FIFO free space
89 * is greater than a cacheline size, ensuring only MRM is issued.
90 */
91
92 cache_lnsz = pci_config_get8(pci_conf_handle, PCI_CONF_CACHE_LINESZ);
93
94 /*
95 * The cache line is specified in 32-bit words, so multiply by 4
96 * to get bytes. Then divide by 32 bytes, the granularity of the
97 * FIFO control bits 2:0. Add 1 if there is any remainder to
98 * account for a partial 32-byte block, then subtract 1 since for
99 * FIFO controls bits 2:0, 0 corresponds to 32, 1 corresponds to
100 * 64, and so on. The calculation is expanded for clarity.
101 */
102 if (cache_lnsz != 0) {
103 frrc = (cache_lnsz * 4 / 32) +
104 (((cache_lnsz * 4) % 32) ? 1 : 0) - 1;
105 }
106
107 if (device_id == SIL3114_DEVICE_ID) {
108 ports = 4;
109 } else {
110 ports = 2;
111 }
112
113 /*
114 * The following BAR5 registers are accessed via an indirect register
115 * in the PCI configuration space rather than mapping BAR5.
116 */
117 for (i = 0; i < ports; i++) {
118 GET_BAR5_INDIRECT(pci_conf_handle, fifocntctl[i],
119 fifo_cnt_ctl);
120 fifo_cnt_ctl = (fifo_cnt_ctl & ~0x7) | (frrc & 0x7);
121 PUT_BAR5_INDIRECT(pci_conf_handle, fifocntctl[i],
122 fifo_cnt_ctl);
123 /*
124 * Correct default setting for FIS0cfg
125 */
126 #ifdef ATA_DEBUG
127 GET_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
128 sfiscfg_val);
129 ADBG_WARN(("sil3xxx_init_controller: old val SFISCfg "
130 "ch%d: %x\n", i, sfiscfg_val));
131 #endif
132 PUT_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
133 SFISCFG_ERRATA);
134 #ifdef ATA_DEBUG
135 GET_BAR5_INDIRECT(pci_conf_handle, sfiscfg[i],
136 sfiscfg_val);
137 ADBG_WARN(("sil3xxx_init_controller: new val SFISCfg "
138 "ch%d: %x\n", i, sfiscfg_val));
139 #endif
140 }
141
142 /* Now tear down the pci config setup */
143 pci_config_teardown(&pci_conf_handle);
144
145 /* Create property indicating that initialization was done */
146 (void) ddi_prop_update_int(DDI_DEV_T_NONE, ddi_get_parent(dip),
147 "sil3xxx-initialized", 1);
148
149 return (TRUE);
150 }
151