rccide.c revision 1.2 1 /* $NetBSD: rccide.c,v 1.2 2003/11/07 10:44:42 enami Exp $ */
2
3 /*
4 * Copyright (c) 2003 By Noon Software, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The names of the authors may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: rccide.c,v 1.2 2003/11/07 10:44:42 enami Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34
35 #include <dev/pci/pcivar.h>
36 #include <dev/pci/pcidevs.h>
37 #include <dev/pci/pciidereg.h>
38 #include <dev/pci/pciidevar.h>
39
40 void serverworks_chip_map(struct pciide_softc *, struct pci_attach_args *);
41 void serverworks_setup_channel(struct channel_softc *);
42 int serverworks_pci_intr(void *);
43
44 int rccide_match(struct device *, struct cfdata *, void *);
45 void rccide_attach(struct device *, struct device *, void *);
46
47 CFATTACH_DECL(rccide, sizeof(struct pciide_softc),
48 rccide_match, rccide_attach, NULL, NULL);
49
50 const struct pciide_product_desc pciide_serverworks_products[] = {
51 { PCI_PRODUCT_SERVERWORKS_OSB4_IDE,
52 0,
53 "ServerWorks OSB4 IDE Controller",
54 serverworks_chip_map,
55 },
56 { PCI_PRODUCT_SERVERWORKS_CSB5_IDE,
57 0,
58 "ServerWorks CSB5 IDE Controller",
59 serverworks_chip_map,
60 },
61 { PCI_PRODUCT_SERVERWORKS_CSB6_IDE,
62 0,
63 "ServerWorks CSB6 RAID/IDE Controller",
64 serverworks_chip_map,
65 },
66 { PCI_PRODUCT_SERVERWORKS_CSB6_RAID,
67 0,
68 "ServerWorks CSB6 RAID/IDE Controller",
69 serverworks_chip_map,
70 },
71 { 0,
72 0,
73 NULL,
74 NULL,
75 }
76 };
77
78 int
79 rccide_match(struct device *parent, struct cfdata *match, void *aux)
80 {
81 struct pci_attach_args *pa = aux;
82
83 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SERVERWORKS &&
84 PCI_CLASS(pa->pa_class) == PCI_CLASS_MASS_STORAGE &&
85 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_MASS_STORAGE_IDE) {
86 if (pciide_lookup_product(pa->pa_id,
87 pciide_serverworks_products))
88 return (2);
89 }
90 return (0);
91 }
92
93 void
94 rccide_attach(struct device *parent, struct device *self, void *aux)
95 {
96 struct pci_attach_args *pa = aux;
97 struct pciide_softc *sc = (void *)self;
98
99 pciide_common_attach(sc, pa,
100 pciide_lookup_product(pa->pa_id, pciide_serverworks_products));
101 }
102
103 void
104 serverworks_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
105 {
106 struct pciide_channel *cp;
107 pcireg_t interface = PCI_INTERFACE(pa->pa_class);
108 pcitag_t pcib_tag;
109 int channel;
110 bus_size_t cmdsize, ctlsize;
111
112 if (pciide_chipen(sc, pa) == 0)
113 return;
114
115 aprint_normal("%s: bus-master DMA support present",
116 sc->sc_wdcdev.sc_dev.dv_xname);
117 pciide_mapreg_dma(sc, pa);
118 aprint_normal("\n");
119 sc->sc_wdcdev.cap = WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32 |
120 WDC_CAPABILITY_MODE;
121
122 if (sc->sc_dma_ok) {
123 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA | WDC_CAPABILITY_UDMA;
124 sc->sc_wdcdev.cap |= WDC_CAPABILITY_IRQACK;
125 sc->sc_wdcdev.irqack = pciide_irqack;
126 }
127 sc->sc_wdcdev.PIO_cap = 4;
128 sc->sc_wdcdev.DMA_cap = 2;
129 switch (sc->sc_pp->ide_product) {
130 case PCI_PRODUCT_SERVERWORKS_OSB4_IDE:
131 sc->sc_wdcdev.UDMA_cap = 2;
132 break;
133 case PCI_PRODUCT_SERVERWORKS_CSB5_IDE:
134 if (PCI_REVISION(pa->pa_class) < 0x92)
135 sc->sc_wdcdev.UDMA_cap = 4;
136 else
137 sc->sc_wdcdev.UDMA_cap = 5;
138 break;
139 case PCI_PRODUCT_SERVERWORKS_CSB6_IDE:
140 case PCI_PRODUCT_SERVERWORKS_CSB6_RAID:
141 sc->sc_wdcdev.UDMA_cap = 5;
142 break;
143 }
144
145 sc->sc_wdcdev.set_modes = serverworks_setup_channel;
146 sc->sc_wdcdev.channels = sc->wdc_chanarray;
147 sc->sc_wdcdev.nchannels = 2;
148
149 for (channel = 0; channel < sc->sc_wdcdev.nchannels; channel++) {
150 cp = &sc->pciide_channels[channel];
151 if (pciide_chansetup(sc, channel, interface) == 0)
152 continue;
153 pciide_mapchan(pa, cp, interface, &cmdsize, &ctlsize,
154 serverworks_pci_intr);
155 }
156
157 pcib_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, pa->pa_device, 0);
158 pci_conf_write(pa->pa_pc, pcib_tag, 0x64,
159 (pci_conf_read(pa->pa_pc, pcib_tag, 0x64) & ~0x2000) | 0x4000);
160 }
161
162 void
163 serverworks_setup_channel(struct channel_softc *chp)
164 {
165 struct ata_drive_datas *drvp;
166 struct pciide_channel *cp = (struct pciide_channel*)chp;
167 struct pciide_softc *sc = (struct pciide_softc *)cp->wdc_channel.wdc;
168 int channel = chp->channel;
169 int drive, unit;
170 u_int32_t pio_time, dma_time, pio_mode, udma_mode;
171 u_int32_t idedma_ctl;
172 static const u_int8_t pio_modes[5] = {0x5d, 0x47, 0x34, 0x22, 0x20};
173 static const u_int8_t dma_modes[3] = {0x77, 0x21, 0x20};
174
175 /* setup DMA if needed */
176 pciide_channel_dma_setup(cp);
177
178 pio_time = pci_conf_read(sc->sc_pc, sc->sc_tag, 0x40);
179 dma_time = pci_conf_read(sc->sc_pc, sc->sc_tag, 0x44);
180 pio_mode = pci_conf_read(sc->sc_pc, sc->sc_tag, 0x48);
181 udma_mode = pci_conf_read(sc->sc_pc, sc->sc_tag, 0x54);
182
183 pio_time &= ~(0xffff << (16 * channel));
184 dma_time &= ~(0xffff << (16 * channel));
185 pio_mode &= ~(0xff << (8 * channel + 16));
186 udma_mode &= ~(0xff << (8 * channel + 16));
187 udma_mode &= ~(3 << (2 * channel));
188
189 idedma_ctl = 0;
190
191 /* Per drive settings */
192 for (drive = 0; drive < 2; drive++) {
193 drvp = &chp->ch_drive[drive];
194 /* If no drive, skip */
195 if ((drvp->drive_flags & DRIVE) == 0)
196 continue;
197 unit = drive + 2 * channel;
198 /* add timing values, setup DMA if needed */
199 pio_time |= pio_modes[drvp->PIO_mode] << (8 * (unit^1));
200 pio_mode |= drvp->PIO_mode << (4 * unit + 16);
201 if ((chp->wdc->cap & WDC_CAPABILITY_UDMA) &&
202 (drvp->drive_flags & DRIVE_UDMA)) {
203 /* use Ultra/DMA, check for 80-pin cable */
204 if (drvp->UDMA_mode > 2 &&
205 (PCI_PRODUCT(pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_SUBSYS_ID_REG)) & (1 << (14 + channel))) == 0)
206 drvp->UDMA_mode = 2;
207 dma_time |= dma_modes[drvp->DMA_mode] << (8 * (unit^1));
208 udma_mode |= drvp->UDMA_mode << (4 * unit + 16);
209 udma_mode |= 1 << unit;
210 idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive);
211 } else if ((chp->wdc->cap & WDC_CAPABILITY_DMA) &&
212 (drvp->drive_flags & DRIVE_DMA)) {
213 /* use Multiword DMA */
214 drvp->drive_flags &= ~DRIVE_UDMA;
215 dma_time |= dma_modes[drvp->DMA_mode] << (8 * (unit^1));
216 idedma_ctl |= IDEDMA_CTL_DRV_DMA(drive);
217 } else {
218 /* PIO only */
219 drvp->drive_flags &= ~(DRIVE_UDMA | DRIVE_DMA);
220 }
221 }
222
223 pci_conf_write(sc->sc_pc, sc->sc_tag, 0x40, pio_time);
224 pci_conf_write(sc->sc_pc, sc->sc_tag, 0x44, dma_time);
225 if (sc->sc_pp->ide_product != PCI_PRODUCT_SERVERWORKS_OSB4_IDE)
226 pci_conf_write(sc->sc_pc, sc->sc_tag, 0x48, pio_mode);
227 pci_conf_write(sc->sc_pc, sc->sc_tag, 0x54, udma_mode);
228
229 if (idedma_ctl != 0) {
230 /* Add software bits in status register */
231 bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh,
232 IDEDMA_CTL + IDEDMA_SCH_OFFSET * channel, idedma_ctl);
233 }
234 }
235
236 int
237 serverworks_pci_intr(arg)
238 void *arg;
239 {
240 struct pciide_softc *sc = arg;
241 struct pciide_channel *cp;
242 struct channel_softc *wdc_cp;
243 int rv = 0;
244 int dmastat, i, crv;
245
246 for (i = 0; i < sc->sc_wdcdev.nchannels; i++) {
247 dmastat = bus_space_read_1(sc->sc_dma_iot, sc->sc_dma_ioh,
248 IDEDMA_CTL + IDEDMA_SCH_OFFSET * i);
249 if ((dmastat & (IDEDMA_CTL_ACT | IDEDMA_CTL_INTR)) !=
250 IDEDMA_CTL_INTR)
251 continue;
252 cp = &sc->pciide_channels[i];
253 wdc_cp = &cp->wdc_channel;
254 crv = wdcintr(wdc_cp);
255 if (crv == 0) {
256 printf("%s:%d: bogus intr\n",
257 sc->sc_wdcdev.sc_dev.dv_xname, i);
258 bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh,
259 IDEDMA_CTL + IDEDMA_SCH_OFFSET * i, dmastat);
260 } else
261 rv = 1;
262 }
263 return rv;
264 }
265