xref: /netbsd-src/sys/arch/cobalt/stand/boot/wdc.c (revision 27527e67bbdf8d9ec84fd58803048ed6d181ece2)
1 /*	$NetBSD: wdc.c,v 1.7 2005/12/11 12:17:06 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Manuel Bouyer.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/types.h>
40 #include <sys/disklabel.h>
41 #include <sys/bootblock.h>
42 
43 #include <lib/libsa/stand.h>
44 #include <machine/param.h>
45 
46 #include "boot.h"
47 #include "wdvar.h"
48 
49 #define WDCDELAY	100
50 #define WDCNDELAY_RST	31000 * 10
51 
52 static int  wdcprobe(struct wdc_channel *chp);
53 static int  wdc_wait_for_ready(struct wdc_channel *chp);
54 static int  wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c);
55 static int  __wdcwait_reset(struct wdc_channel *chp, int drv_mask);
56 
57 /*
58  * Reset the controller.
59  */
60 static int
61 __wdcwait_reset(chp, drv_mask)
62 	struct wdc_channel *chp;
63 	int drv_mask;
64 {
65 	int timeout;
66 	u_int8_t st0, st1;
67 
68 	/* wait for BSY to deassert */
69 	for (timeout = 0; timeout < WDCNDELAY_RST; timeout++) {
70 		WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); /* master */
71 		delay(10);
72 		st0 = WDC_READ_REG(chp, wd_status);
73 		WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); /* slave */
74 		delay(10);
75 		st1 = WDC_READ_REG(chp, wd_status);
76 
77 		if ((drv_mask & 0x01) == 0) {
78 			/* no master */
79 			if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) {
80 				/* No master, slave is ready, it's done */
81 				goto end;
82 			}
83 		} else if ((drv_mask & 0x02) == 0) {
84 			/* no slave */
85 			if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) {
86 				/* No slave, master is ready, it's done */
87 				goto end;
88 			}
89 		} else {
90 			/* Wait for both master and slave to be ready */
91 			if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) {
92 				goto end;
93 			}
94 		}
95 
96 		delay(WDCDELAY);
97 	}
98 
99 	/* Reset timed out. Maybe it's because drv_mask was not right */
100 	if (st0 & WDCS_BSY)
101 		drv_mask &= ~0x01;
102 	if (st1 & WDCS_BSY)
103 		drv_mask &= ~0x02;
104 
105 end:
106 	return (drv_mask);
107 }
108 
109 /* Test to see controller with at last one attached drive is there.
110  * Returns a bit for each possible drive found (0x01 for drive 0,
111  * 0x02 for drive 1).
112  * Logic:
113  * - If a status register is at 0xff, assume there is no drive here
114  *   (ISA has pull-up resistors).  Similarly if the status register has
115  *   the value we last wrote to the bus (for IDE interfaces without pullups).
116  *   If no drive at all -> return.
117  * - reset the controller, wait for it to complete (may take up to 31s !).
118  *   If timeout -> return.
119  */
120 static int
121 wdcprobe(chp)
122 	struct wdc_channel *chp;
123 {
124 	u_int8_t st0, st1, sc, sn, cl, ch;
125 	u_int8_t ret_value = 0x03;
126 	u_int8_t drive;
127 	int found;
128 
129 	/*
130 	 * Sanity check to see if the wdc channel responds at all.
131 	 */
132 	WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM);
133 	delay(10);
134 	st0 = WDC_READ_REG(chp, wd_status);
135 	WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10);
136 	delay(10);
137 	st1 = WDC_READ_REG(chp, wd_status);
138 
139 	if (st0 == 0xff || st0 == WDSD_IBM)
140 		ret_value &= ~0x01;
141 	if (st1 == 0xff || st1 == (WDSD_IBM | 0x10))
142 		ret_value &= ~0x02;
143 	if (ret_value == 0)
144 		return (ENXIO);
145 
146 	/* assert SRST, wait for reset to complete */
147 	WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM);
148 	delay(10);
149 	WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_RST | WDCTL_IDS);
150 	delay(1000);
151 	WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_IDS);
152 	delay(1000);
153 	(void) WDC_READ_REG(chp, wd_error);
154 	WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_4BIT);
155 	delay(10);
156 
157 	ret_value = __wdcwait_reset(chp, ret_value);
158 
159 	/* if reset failed, there's nothing here */
160 	if (ret_value == 0)
161 		return (ENXIO);
162 
163 	/*
164 	 * Test presence of drives. First test register signatures looking for
165 	 * ATAPI devices. If it's not an ATAPI and reset said there may be
166 	 * something here assume it's ATA or OLD. Ghost will be killed later in
167 	 * attach routine.
168 	 */
169 	found = 0;
170 	for (drive = 0; drive < 2; drive++) {
171 		if ((ret_value & (0x01 << drive)) == 0)
172 			continue;
173 		return (0);
174 	}
175 	return (ENXIO);
176 }
177 
178 /*
179  * Initialize the device.
180  */
181 int
182 wdc_init(sc, unit)
183 	struct wd_softc *sc;
184 	u_int *unit;
185 {
186 	if (pciide_init(&sc->sc_channel, unit) != 0)
187 		return (ENXIO);
188 	if (wdcprobe(&sc->sc_channel) != 0)
189 		return (ENXIO);
190 	return (0);
191 }
192 
193 /*
194  * Wait until the device is ready.
195  */
196 int
197 wdc_wait_for_ready(chp)
198 	struct wdc_channel *chp;
199 {
200 	u_int timeout;
201 	for (timeout = WDC_TIMEOUT; timeout > 0; --timeout) {
202 		if ((WDC_READ_REG(chp, wd_status) & (WDCS_BSY | WDCS_DRDY))
203 				== WDCS_DRDY)
204 			return (0);
205 	}
206 	return (ENXIO);
207 }
208 
209 /*
210  * Read one block off the device.
211  */
212 int
213 wdc_read_block(sc, wd_c)
214 	struct wd_softc *sc;
215 	struct wdc_command *wd_c;
216 {
217 	int i;
218 	struct wdc_channel *chp = &sc->sc_channel;
219 	u_int16_t *ptr = (u_int16_t*)wd_c->data;
220 
221 	if (ptr == NULL)
222 		return (0);
223 
224 	for (i = wd_c->bcount; i > 0; i -= sizeof(u_int16_t))
225 		*ptr++ = WDC_READ_DATA(chp);
226 
227 	return (0);
228 }
229 
230 /*
231  * Send a command to the device (CHS and LBA addressing).
232  */
233 int
234 wdccommand(sc, wd_c)
235 	struct wd_softc *sc;
236 	struct wdc_command *wd_c;
237 {
238 	u_int8_t err;
239 	struct wdc_channel *chp = &sc->sc_channel;
240 
241 #if 0
242 	DPRINTF(("wdccommand(%d, %d, %d, %d, %d, %d, %d)\n",
243 				wd_c->drive, wd_c->r_command, wd_c->r_cyl,
244 				wd_c->r_head, wd_c->r_sector, wd_c->bcount,
245 				wd_c->r_precomp));
246 #endif
247 
248 	WDC_WRITE_REG(chp, wd_precomp, wd_c->r_precomp);
249 	WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count);
250 	WDC_WRITE_REG(chp, wd_sector, wd_c->r_sector);
251 	WDC_WRITE_REG(chp, wd_cyl_lo, wd_c->r_cyl);
252 	WDC_WRITE_REG(chp, wd_cyl_hi, wd_c->r_cyl >> 8);
253 	WDC_WRITE_REG(chp, wd_sdh,
254 	    WDSD_IBM | (wd_c->drive << 4) | wd_c->r_head);
255 	WDC_WRITE_REG(chp, wd_command, wd_c->r_command);
256 
257 	if (wdc_wait_for_ready(chp) != 0)
258 		return (ENXIO);
259 
260 	if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) {
261 		printf("wd%d: error %x\n", chp->compatchan,
262 				WDC_READ_REG(chp, wd_error));
263 		return (ENXIO);
264 	}
265 
266 	return (0);
267 }
268 
269 /*
270  * Send a command to the device (LBA48 addressing).
271  */
272 int
273 wdccommandext(wd, wd_c)
274 	struct wd_softc *wd;
275 	struct wdc_command *wd_c;
276 {
277 	u_int8_t err;
278 	struct wdc_channel *chp = &wd->sc_channel;
279 
280 	/* Select drive, head, and addressing mode. */
281 	WDC_WRITE_REG(chp, wd_sdh, (wd_c->drive << 4) | WDSD_LBA);
282 
283 	/* previous */
284 	WDC_WRITE_REG(chp, wd_features, 0);
285 	WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count >> 8);
286 	WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 40);
287 	WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 32);
288 	WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno >> 24);
289 
290 	/* current */
291 	WDC_WRITE_REG(chp, wd_features, 0);
292 	WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count);
293 	WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 16);
294 	WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 8);
295 	WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno);
296 
297 	/* Send command. */
298 	WDC_WRITE_REG(chp, wd_command, wd_c->r_command);
299 
300 	if (wdc_wait_for_ready(chp) != 0)
301 		return (ENXIO);
302 
303 	if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) {
304 		printf("wd%d: error %x\n", chp->compatchan,
305 				WDC_READ_REG(chp, wd_error));
306 		return (ENXIO);
307 	}
308 
309 	return (0);
310 }
311 
312 /*
313  * Issue 'device identify' command.
314  */
315 int
316 wdc_exec_identify(wd, data)
317 	struct wd_softc *wd;
318 	void *data;
319 {
320 	int error;
321 	struct wdc_command wd_c;
322 
323 	memset(&wd_c, 0, sizeof(wd_c));
324 
325 	wd_c.drive = wd->sc_unit;
326 	wd_c.r_command = WDCC_IDENTIFY;
327 	wd_c.bcount = DEV_BSIZE;
328 	wd_c.data = data;
329 
330 	if ( (error = wdccommand(wd, &wd_c)) != 0)
331 		return (error);
332 
333 	return wdc_read_block(wd, &wd_c);
334 }
335 
336 /*
337  * Issue 'read' command.
338  */
339 int
340 wdc_exec_read(wd, cmd, blkno, data)
341 	struct wd_softc *wd;
342 	u_int8_t cmd;
343 	daddr_t blkno;
344 	void *data;
345 {
346 	int error;
347 	struct wdc_command wd_c;
348 
349 	memset(&wd_c, 0, sizeof(wd_c));
350 
351 	if (wd->sc_flags & WDF_LBA48) {
352 		/* LBA48 */
353 		wd_c.r_blkno = blkno;
354 	} else if (wd->sc_flags & WDF_LBA) {
355 		/* LBA */
356 		wd_c.r_sector = (blkno >> 0) & 0xff;
357 		wd_c.r_cyl = (blkno >> 8) & 0xffff;
358 		wd_c.r_head = (blkno >> 24) & 0x0f;
359 		wd_c.r_head |= WDSD_LBA;
360 	} else {
361 		/* LHS */
362 		wd_c.r_sector = blkno % wd->sc_label.d_nsectors;
363 		wd_c.r_sector++;    /* Sectors begin with 1, not 0. */
364 		blkno /= wd->sc_label.d_nsectors;
365 		wd_c.r_head = blkno % wd->sc_label.d_ntracks;
366 		blkno /= wd->sc_label.d_ntracks;
367 		wd_c.r_cyl = blkno;
368 		wd_c.r_head |= WDSD_CHS;
369 	}
370 
371 	wd_c.data = data;
372 	wd_c.r_count = 1;
373 	wd_c.drive = wd->sc_unit;
374 	wd_c.r_command = cmd;
375 	wd_c.bcount = wd->sc_label.d_secsize;
376 
377 	if (wd->sc_flags & WDF_LBA48)
378 		error = wdccommandext(wd, &wd_c);
379 	else
380 		error = wdccommand(wd, &wd_c);
381 
382 	if (error != 0)
383 		return error;
384 
385 	return wdc_read_block(wd, &wd_c);
386 }
387