xref: /netbsd-src/sys/arch/mmeye/stand/boot/wdc.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: wdc.c,v 1.2 2015/07/11 10:32:46 kamil 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 <lib/libkern/libkern.h>
38 #include <machine/param.h>
39 
40 #include "boot.h"
41 #include "wdvar.h"
42 
43 #if defined(SH3)
44 #define MMEYE_WDC0_IOBASE	0xb6000000
45 #define MMEYE_WDC1_IOBASE	0xb7000000
46 #elif defined(SH4)
47 #define MMEYE_WDC0_IOBASE	0xb6000000
48 #define MMEYE_WDC1_IOBASE	0xb7000000
49 #endif
50 #define MMEYE_WDC_CTLBASE	0x206
51 
52 #define WDCDELAY	100
53 #define WDCNDELAY_RST	31000 * 10
54 
55 static int  wdcprobe(struct wdc_channel *chp);
56 static int  wdc_wait_for_ready(struct wdc_channel *chp);
57 static int  wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c);
58 static int  __wdcwait_reset(struct wdc_channel *chp, int drv_mask);
59 
60 /*
61  * Reset the controller.
62  */
63 static int
64 __wdcwait_reset(struct wdc_channel *chp, int drv_mask)
65 {
66 	int timeout;
67 	uint8_t st0, st1;
68 
69 	/* wait for BSY to deassert */
70 	for (timeout = 0; timeout < WDCNDELAY_RST; timeout++) {
71 		WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM); /* master */
72 		delay(10);
73 		st0 = WDC_READ_REG(chp, wd_status);
74 		WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10); /* slave */
75 		delay(10);
76 		st1 = WDC_READ_REG(chp, wd_status);
77 
78 		if ((drv_mask & 0x01) == 0) {
79 			/* no master */
80 			if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) {
81 				/* No master, slave is ready, it's done */
82 				goto end;
83 			}
84 		} else if ((drv_mask & 0x02) == 0) {
85 			/* no slave */
86 			if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) {
87 				/* No slave, master is ready, it's done */
88 				goto end;
89 			}
90 		} else {
91 			/* Wait for both master and slave to be ready */
92 			if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) {
93 				goto end;
94 			}
95 		}
96 
97 		delay(WDCDELAY);
98 	}
99 
100 	/* Reset timed out. Maybe it's because drv_mask was not right */
101 	if (st0 & WDCS_BSY)
102 		drv_mask &= ~0x01;
103 	if (st1 & WDCS_BSY)
104 		drv_mask &= ~0x02;
105 
106  end:
107 	return drv_mask;
108 }
109 
110 /* Test to see controller with at last one attached drive is there.
111  * Returns a bit for each possible drive found (0x01 for drive 0,
112  * 0x02 for drive 1).
113  * Logic:
114  * - If a status register is at 0xff, assume there is no drive here
115  *   (ISA has pull-up resistors).  Similarly if the status register has
116  *   the value we last wrote to the bus (for IDE interfaces without pullups).
117  *   If no drive at all -> return.
118  * - reset the controller, wait for it to complete (may take up to 31s !).
119  *   If timeout -> return.
120  */
121 static int
122 wdcprobe(struct wdc_channel *chp)
123 {
124 	uint8_t st0, st1;
125 	uint8_t ret_value = 0x03;
126 	uint8_t drive;
127 
128 	/*
129 	 * Sanity check to see if the wdc channel responds at all.
130 	 */
131 	WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM);
132 	delay(10);
133 	st0 = WDC_READ_REG(chp, wd_status);
134 	WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM | 0x10);
135 	delay(10);
136 	st1 = WDC_READ_REG(chp, wd_status);
137 
138 	if (st0 == 0xff || st0 == WDSD_IBM)
139 		ret_value &= ~0x01;
140 	if (st1 == 0xff || st1 == (WDSD_IBM | 0x10))
141 		ret_value &= ~0x02;
142 	if (ret_value == 0)
143 		return ENXIO;
144 
145 	/* assert SRST, wait for reset to complete */
146 	WDC_WRITE_REG(chp, wd_sdh, WDSD_IBM);
147 	delay(10);
148 	WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_RST | WDCTL_IDS);
149 	delay(1000);
150 	WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_IDS);
151 	delay(1000);
152 	(void) WDC_READ_REG(chp, wd_error);
153 	WDC_WRITE_CTLREG(chp, wd_aux_ctlr, WDCTL_4BIT);
154 	delay(10);
155 
156 	ret_value = __wdcwait_reset(chp, ret_value);
157 
158 	/* if reset failed, there's nothing here */
159 	if (ret_value == 0)
160 		return ENXIO;
161 
162 	/*
163 	 * Test presence of drives. First test register signatures looking for
164 	 * ATAPI devices. If it's not an ATAPI and reset said there may be
165 	 * something here assume it's ATA or OLD. Ghost will be killed later in
166 	 * attach routine.
167 	 */
168 	for (drive = 0; drive < 2; drive++) {
169 		if ((ret_value & (0x01 << drive)) == 0)
170 			continue;
171 		return 0;
172 	}
173 	return ENXIO;
174 }
175 
176 /*
177  * Initialize the device.
178  */
179 int
180 wdc_init(struct wd_softc *sc, u_int *unit)
181 {
182 	struct wdc_channel *chp = &sc->sc_channel;
183 	uint32_t cmdreg, ctlreg;
184 	int i;
185 
186 	/* XXXX: Shuld reset CF COR here? */
187 
188 	switch (*unit) {
189 	case 0: cmdreg = MMEYE_WDC0_IOBASE; break;
190 	case 1: cmdreg = MMEYE_WDC1_IOBASE; break;
191 
192 	default:
193 		return ENXIO;
194 	}
195 	ctlreg = cmdreg + MMEYE_WDC_CTLBASE;
196 
197 	/* set up cmd registers */
198 	chp->c_cmdbase = (uint8_t *)cmdreg;
199 	chp->c_data = (uint16_t *)(cmdreg + wd_data);
200 	for (i = 0; i < WDC_NPORTS; i++)
201 		chp->c_cmdreg[i] = chp->c_cmdbase + i;
202 	/* set up shadow registers */
203 	chp->c_cmdreg[wd_status]   = chp->c_cmdreg[wd_command];
204 	chp->c_cmdreg[wd_features] = chp->c_cmdreg[wd_precomp];
205 	/* set up ctl registers */
206 	chp->c_ctlbase = (uint8_t *)ctlreg;
207 
208 	if (wdcprobe(&sc->sc_channel) != 0)
209 		return ENXIO;
210 	return 0;
211 }
212 
213 /*
214  * Wait until the device is ready.
215  */
216 int
217 wdc_wait_for_ready(struct wdc_channel *chp)
218 {
219 	u_int timeout;
220 
221 	for (timeout = WDC_TIMEOUT; timeout > 0; --timeout) {
222 		if ((WDC_READ_REG(chp, wd_status) & (WDCS_BSY | WDCS_DRDY))
223 				== WDCS_DRDY)
224 			return 0;
225 		delay(1);
226 	}
227 	return ENXIO;
228 }
229 
230 /*
231  * Read one block off the device.
232  */
233 int
234 wdc_read_block(struct wd_softc *sc, struct wdc_command *wd_c)
235 {
236 	int i;
237 	struct wdc_channel *chp = &sc->sc_channel;
238 	uint16_t *ptr = (uint16_t *)wd_c->data;
239 
240 	if (ptr == NULL)
241 		return 0;
242 
243 	if (wd_c->r_command == WDCC_IDENTIFY)
244 		for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t))
245 			*ptr++ = WDC_READ_DATA(chp);
246 	else
247 		for (i = wd_c->bcount; i > 0; i -= sizeof(uint16_t))
248 			*ptr++ = WDC_READ_DATA_STREAM(chp);
249 
250 	return 0;
251 }
252 
253 /*
254  * Send a command to the device (CHS and LBA addressing).
255  */
256 int
257 wdccommand(struct wd_softc *sc, struct wdc_command *wd_c)
258 {
259 	struct wdc_channel *chp = &sc->sc_channel;
260 
261 #if 0
262 	DPRINTF(("wdccommand(%d, %d, %d, %d, %d, %d, %d)\n",
263 	    wd_c->drive, wd_c->r_command, wd_c->r_cyl,
264 	    wd_c->r_head, wd_c->r_sector, wd_c->bcount,
265 	    wd_c->r_precomp));
266 #endif
267 
268 	WDC_WRITE_REG(chp, wd_features, wd_c->r_features);
269 	WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count);
270 	WDC_WRITE_REG(chp, wd_sector, wd_c->r_sector);
271 	WDC_WRITE_REG(chp, wd_cyl_lo, wd_c->r_cyl);
272 	WDC_WRITE_REG(chp, wd_cyl_hi, wd_c->r_cyl >> 8);
273 	WDC_WRITE_REG(chp, wd_sdh,
274 	    WDSD_IBM | (wd_c->drive << 4) | wd_c->r_head);
275 	WDC_WRITE_REG(chp, wd_command, wd_c->r_command);
276 
277 	if (wdc_wait_for_ready(chp) != 0)
278 		return ENXIO;
279 
280 	if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) {
281 		printf("wd%d: error %x\n", chp->compatchan,
282 		    WDC_READ_REG(chp, wd_error));
283 		return ENXIO;
284 	}
285 
286 	return 0;
287 }
288 
289 /*
290  * Send a command to the device (LBA48 addressing).
291  */
292 int
293 wdccommandext(struct wd_softc *wd, struct wdc_command *wd_c)
294 {
295 	struct wdc_channel *chp = &wd->sc_channel;
296 
297 #if 0
298 	DPRINTF(("%s(%d, %x, %" PRId64 ", %d)\n", __func__,
299 	    wd_c->drive, wd_c->r_command,
300 	    wd_c->r_blkno, wd_c->r_count));
301 #endif
302 
303 	/* Select drive, head, and addressing mode. */
304 	WDC_WRITE_REG(chp, wd_sdh, (wd_c->drive << 4) | WDSD_LBA);
305 
306 	/* previous */
307 	WDC_WRITE_REG(chp, wd_features, 0);
308 	WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count >> 8);
309 	WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 40);
310 	WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 32);
311 	WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno >> 24);
312 
313 	/* current */
314 	WDC_WRITE_REG(chp, wd_features, 0);
315 	WDC_WRITE_REG(chp, wd_seccnt, wd_c->r_count);
316 	WDC_WRITE_REG(chp, wd_lba_hi, wd_c->r_blkno >> 16);
317 	WDC_WRITE_REG(chp, wd_lba_mi, wd_c->r_blkno >> 8);
318 	WDC_WRITE_REG(chp, wd_lba_lo, wd_c->r_blkno);
319 
320 	/* Send command. */
321 	WDC_WRITE_REG(chp, wd_command, wd_c->r_command);
322 
323 	if (wdc_wait_for_ready(chp) != 0)
324 		return ENXIO;
325 
326 	if (WDC_READ_REG(chp, wd_status) & WDCS_ERR) {
327 		printf("wd%d: error %x\n", chp->compatchan,
328 		    WDC_READ_REG(chp, wd_error));
329 		return ENXIO;
330 	}
331 
332 	return 0;
333 }
334 
335 /*
336  * Issue 'device identify' command.
337  */
338 int
339 wdc_exec_identify(struct wd_softc *wd, void *data)
340 {
341 	int error;
342 	struct wdc_command wd_c;
343 
344 	memset(&wd_c, 0, sizeof(wd_c));
345 
346 	wd_c.drive = 0;
347 	wd_c.r_command = WDCC_IDENTIFY;
348 	wd_c.bcount = DEV_BSIZE;
349 	wd_c.data = data;
350 
351 	if ((error = wdccommand(wd, &wd_c)) != 0)
352 		return error;
353 
354 	return wdc_read_block(wd, &wd_c);
355 }
356 
357 /*
358  * Issue 'read' command.
359  */
360 int
361 wdc_exec_read(struct wd_softc *wd, uint8_t cmd, daddr_t blkno, void *data)
362 {
363 	int error;
364 	struct wdc_command wd_c;
365 	bool lba, lba48;
366 
367 	memset(&wd_c, 0, sizeof(wd_c));
368 	lba   = false;
369 	lba48 = false;
370 
371 	wd_c.data = data;
372 	wd_c.r_count = 1;
373 	wd_c.r_features = 0;
374 	wd_c.drive = 0;
375 	wd_c.bcount = wd->sc_label.d_secsize;
376 
377 	if ((wd->sc_flags & WDF_LBA48) != 0 && blkno > wd->sc_capacity28)
378 		lba48 = true;
379 	else if ((wd->sc_flags & WDF_LBA) != 0)
380 		lba = true;
381 
382 	if (lba48) {
383 		/* LBA48 */
384 		wd_c.r_command = atacmd_to48(cmd);
385 		wd_c.r_blkno = blkno;
386 	} else if (lba) {
387 		/* LBA */
388 		wd_c.r_command = cmd;
389 		wd_c.r_sector = (blkno >> 0) & 0xff;
390 		wd_c.r_cyl = (blkno >> 8) & 0xffff;
391 		wd_c.r_head = (blkno >> 24) & 0x0f;
392 		wd_c.r_head |= WDSD_LBA;
393 	} else {
394 		/* CHS */
395 		wd_c.r_command = cmd;
396 		wd_c.r_sector = blkno % wd->sc_label.d_nsectors;
397 		wd_c.r_sector++;    /* Sectors begin with 1, not 0. */
398 		blkno /= wd->sc_label.d_nsectors;
399 		wd_c.r_head = blkno % wd->sc_label.d_ntracks;
400 		blkno /= wd->sc_label.d_ntracks;
401 		wd_c.r_cyl = blkno;
402 		wd_c.r_head |= WDSD_CHS;
403 	}
404 
405 	if (lba48)
406 		error = wdccommandext(wd, &wd_c);
407 	else
408 		error = wdccommand(wd, &wd_c);
409 
410 	if (error != 0)
411 		return error;
412 
413 	return wdc_read_block(wd, &wd_c);
414 }
415