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