1/*-
2 * Copyright (c) 2008 John Hay.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD$");
28#include <sys/param.h>
29#include <sys/ata.h>
30#include <sys/linker_set.h>
31
32#include <stdarg.h>
33
34#include "lib.h"
35#include "cf_ata.h"
36
37#include <machine/armreg.h>
38#include <arm/xscale/ixp425/ixp425reg.h>
39#include <dev/ic/ns16550.h>
40
41struct board_config {
42	const char *desc;
43	int	(*probe)(int boardtype_hint);
44	void	(*init)(void);
45};
46/* set of registered boards */
47SET_DECLARE(boards, struct board_config);
48#define	BOARD_CONFIG(name, _desc)			\
49static struct board_config name##config = {		\
50	.desc	= _desc,				\
51	.probe	= name##_probe,				\
52	.init	= name##_init,				\
53};							\
54DATA_SET(boards, name##config)
55
56static u_int cputype;
57#define	cpu_is_ixp43x()	(cputype == CPU_ID_IXP435)
58static u_int8_t *ubase;
59
60static u_int8_t uart_getreg(u_int8_t *, int);
61static void uart_setreg(u_int8_t *, int, u_int8_t);
62
63static void cf_init(void);
64static void cf_clr(void);
65
66#ifdef DEBUG
67#define	DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
68#else
69#define	DPRINTF(fmt, ...)
70#endif
71
72const char *
73board_init(void)
74{
75	struct board_config **pbp;
76
77	cputype = cpu_id() & CPU_ID_CPU_MASK;
78
79	SET_FOREACH(pbp, boards)
80		/* XXX pass down redboot board type */
81		if ((*pbp)->probe(0)) {
82			(*pbp)->init();
83			return (*pbp)->desc;
84		}
85	/* XXX panic, unknown board type */
86	return "???";
87}
88
89/*
90 * This should be called just before starting the kernel. This is so
91 * that one can undo incompatible hardware settings.
92 */
93void
94clr_board(void)
95{
96	cf_clr();
97}
98
99/*
100 * General support functions.
101 */
102
103/*
104 * DELAY should delay for the number of microseconds.
105 * The idea is that the inner loop should take 1us, so val is the
106 * number of usecs to delay.
107 */
108void
109DELAY(int val)
110{
111	volatile int sub;
112	volatile int subsub;
113
114	sub = val;
115	while(sub) {
116		subsub = 3;
117		while(subsub)
118			subsub--;
119		sub--;
120	}
121}
122
123u_int32_t
124swap32(u_int32_t a)
125{
126	return (((a & 0xff) << 24) | ((a & 0xff00) << 8) |
127	    ((a & 0xff0000) >> 8) | ((a & 0xff000000) >> 24));
128}
129
130u_int16_t
131swap16(u_int16_t val)
132{
133	return (val << 8) | (val >> 8);
134}
135
136/*
137 * uart related funcs
138 */
139static u_int8_t
140uart_getreg(u_int8_t *bas, int off)
141{
142	return *((volatile u_int32_t *)(bas + (off << 2))) & 0xff;
143}
144
145static void
146uart_setreg(u_int8_t *bas, int off, u_int8_t val)
147{
148	*((volatile u_int32_t *)(bas + (off << 2))) = (u_int32_t)val;
149}
150
151int
152getc(int seconds)
153{
154	int c, delay, limit;
155
156	c = 0;
157	delay = 10000;
158	limit = seconds * 1000000/10000;
159	while ((uart_getreg(ubase, REG_LSR) & LSR_RXRDY) == 0 && --limit)
160		DELAY(delay);
161
162	if ((uart_getreg(ubase, REG_LSR) & LSR_RXRDY) == LSR_RXRDY)
163		c = uart_getreg(ubase, REG_DATA);
164
165	return c;
166}
167
168void
169putchar(int ch)
170{
171	int delay, limit;
172
173	delay = 500;
174	limit = 20;
175	while ((uart_getreg(ubase, REG_LSR) & LSR_THRE) == 0 && --limit)
176		DELAY(delay);
177	uart_setreg(ubase, REG_DATA, ch);
178
179	limit = 40;
180	while ((uart_getreg(ubase, REG_LSR) & LSR_TEMT) == 0 && --limit)
181		DELAY(delay);
182}
183
184void
185xputchar(int ch)
186{
187	if (ch == '\n')
188		putchar('\r');
189	putchar(ch);
190}
191
192void
193putstr(const char *str)
194{
195	while(*str)
196		xputchar(*str++);
197}
198
199void
200puthex8(u_int8_t ch)
201{
202	const char *hex = "0123456789abcdef";
203
204	putchar(hex[ch >> 4]);
205	putchar(hex[ch & 0xf]);
206}
207
208void
209puthexlist(const u_int8_t *str, int length)
210{
211	while(length) {
212		puthex8(*str);
213		putchar(' ');
214		str++;
215		length--;
216	}
217}
218
219/*
220 *
221 * CF/IDE functions.
222 *
223 */
224
225struct {
226	u_int64_t dsize;
227	u_int64_t total_secs;
228	u_int8_t heads;
229	u_int8_t sectors;
230	u_int32_t cylinders;
231
232	u_int32_t *cs1to;
233	u_int32_t *cs2to;
234
235	u_int8_t *cs1;
236	u_int8_t *cs2;
237
238	u_int32_t use_lba;
239	u_int32_t use_stream8;
240	u_int32_t debug;
241
242	u_int8_t status;
243	u_int8_t error;
244} dskinf;
245
246static void cfenable16(void);
247static void cfdisable16(void);
248static u_int8_t cfread8(u_int32_t off);
249static u_int16_t cfread16(u_int32_t off);
250static void cfreadstream8(void *buf, int length);
251static void cfreadstream16(void *buf, int length);
252static void cfwrite8(u_int32_t off, u_int8_t val);
253static u_int8_t cfaltread8(u_int32_t off);
254static void cfaltwrite8(u_int32_t off, u_int8_t val);
255static int cfwait(u_int8_t mask);
256static int cfaltwait(u_int8_t mask);
257static int cfcmd(u_int32_t cmd, u_int32_t cylinder, u_int32_t head,
258    u_int32_t sector, u_int32_t count, u_int32_t feature);
259static void cfreset(void);
260#ifdef DEBUG
261static int cfgetparams(void);
262#endif
263static void cfprintregs(void);
264
265static void
266cf_init(void)
267{
268	u_int8_t status;
269#ifdef DEBUG
270	int rval;
271#endif
272
273	/* NB: board init routines setup other parts of dskinf */
274	dskinf.use_stream8 = 0;
275	dskinf.use_lba = 0;
276	dskinf.debug = 1;
277
278	DPRINTF("cs1 %x, cs2 %x\n", dskinf.cs1, dskinf.cs2);
279
280	/* Setup the CF window */
281	*dskinf.cs1to |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
282	DPRINTF("t1 %x, ", *dskinf.cs1to);
283
284	*dskinf.cs2to |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
285	DPRINTF("t2 %x\n", *dskinf.cs2to);
286
287	/* Detect if there is a disk. */
288	cfwrite8(CF_DRV_HEAD, CF_D_IBM);
289	DELAY(1000);
290	status = cfread8(CF_STATUS);
291	if (status != 0x50)
292		printf("cf-ata0 %x\n", (u_int32_t)status);
293	if (status == 0xff) {
294		printf("cf_ata0: No disk!\n");
295		return;
296	}
297
298	cfreset();
299
300	if (dskinf.use_stream8) {
301		DPRINTF("setting %d bit mode.\n", 8);
302		cfwrite8(CF_FEATURE, 0x01); /* Enable 8 bit transfers */
303		cfwrite8(CF_COMMAND, ATA_SETFEATURES);
304		cfaltwait(CF_S_READY);
305	}
306
307#ifdef DEBUG
308	rval = cfgetparams();
309	if (rval)
310		return;
311#endif
312	dskinf.use_lba = 1;
313	dskinf.debug = 0;
314}
315
316static void
317cf_clr(void)
318{
319	cfwrite8(CF_DRV_HEAD, CF_D_IBM);
320	cfaltwait(CF_S_READY);
321	cfwrite8(CF_FEATURE, 0x81); /* Enable 8 bit transfers */
322	cfwrite8(CF_COMMAND, ATA_SETFEATURES);
323	cfaltwait(CF_S_READY);
324}
325
326static void
327cfenable16(void)
328{
329	u_int32_t val;
330
331	val = *dskinf.cs1to;
332	*dskinf.cs1to = val &~ EXP_BYTE_EN;
333	DELAY(100);
334#if 0
335	DPRINTF("%s: cs1 timing reg %x\n", *dskinf.cs1to, __func__);
336#endif
337}
338
339static void
340cfdisable16(void)
341{
342	u_int32_t val;
343
344	DELAY(100);
345	val = *dskinf.cs1to;
346	*dskinf.cs1to = val | EXP_BYTE_EN;
347#if 0
348	DPRINTF("%s: cs1 timing reg %x\n", *dskinf.cs1to, __func__);
349#endif
350}
351
352static u_int8_t
353cfread8(u_int32_t off)
354{
355	volatile u_int8_t *vp;
356
357	vp = (volatile u_int8_t *)(dskinf.cs1 + off);
358	return *vp;
359}
360
361static void
362cfreadstream8(void *buf, int length)
363{
364	u_int8_t *lbuf;
365	u_int8_t tmp;
366
367	lbuf = buf;
368	while (length) {
369		tmp = cfread8(CF_DATA);
370		*lbuf = tmp;
371#ifdef DEBUG
372		if (dskinf.debug && (length > (512 - 32))) {
373			if ((length % 16) == 0)
374				xputchar('\n');
375			puthex8(tmp);
376			putchar(' ');
377		}
378#endif
379		lbuf++;
380		length--;
381	}
382#ifdef DEBUG
383	if (dskinf.debug)
384		xputchar('\n');
385#endif
386}
387
388static u_int16_t
389cfread16(u_int32_t off)
390{
391	volatile u_int16_t *vp;
392
393	vp = (volatile u_int16_t *)(dskinf.cs1 + off);
394	return swap16(*vp);
395}
396
397static void
398cfreadstream16(void *buf, int length)
399{
400	u_int16_t *lbuf;
401
402	length = length / 2;
403	cfenable16();
404	lbuf = buf;
405	while (length--) {
406		*lbuf = cfread16(CF_DATA);
407		lbuf++;
408	}
409	cfdisable16();
410}
411
412static void
413cfwrite8(u_int32_t off, u_int8_t val)
414{
415	volatile u_int8_t *vp;
416
417	vp = (volatile u_int8_t *)(dskinf.cs1 + off);
418	*vp = val;
419}
420
421#if 0
422static void
423cfwrite16(u_int32_t off, u_int16_t val)
424{
425	volatile u_int16_t *vp;
426
427	vp = (volatile u_int16_t *)(dskinf.cs1 + off);
428	*vp = val;
429}
430#endif
431
432static u_int8_t
433cfaltread8(u_int32_t off)
434{
435	volatile u_int8_t *vp;
436
437	off &= 0x0f;
438	vp = (volatile u_int8_t *)(dskinf.cs2 + off);
439	return *vp;
440}
441
442static void
443cfaltwrite8(u_int32_t off, u_int8_t val)
444{
445	volatile u_int8_t *vp;
446
447	/*
448	 * This is documented in the Intel appnote 302456.
449	 */
450	off &= 0x0f;
451	vp = (volatile u_int8_t *)(dskinf.cs2 + off);
452	*vp = val;
453}
454
455static int
456cfwait(u_int8_t mask)
457{
458	u_int8_t status;
459	u_int32_t tout;
460
461	tout = 0;
462	while (tout <= 5000000) {
463		status = cfread8(CF_STATUS);
464		if (status == 0xff) {
465			printf("%s: master: no status, reselecting\n",
466			    __func__);
467			cfwrite8(CF_DRV_HEAD, CF_D_IBM);
468			DELAY(1);
469			status = cfread8(CF_STATUS);
470		}
471		if (status == 0xff)
472			return -1;
473		dskinf.status = status;
474		if (!(status & CF_S_BUSY)) {
475			if (status & CF_S_ERROR) {
476				dskinf.error = cfread8(CF_ERROR);
477				printf("%s: error, status 0x%x error 0x%x\n",
478				    __func__, status, dskinf.error);
479			}
480			if ((status & mask) == mask) {
481				DPRINTF("%s: status 0x%x mask 0x%x tout %u\n",
482				    __func__, status, mask, tout);
483				return (status & CF_S_ERROR);
484			}
485		}
486		if (tout > 1000) {
487			tout += 1000;
488			DELAY(1000);
489		} else {
490			tout += 10;
491			DELAY(10);
492		}
493	}
494	return -1;
495}
496
497static int
498cfaltwait(u_int8_t mask)
499{
500	u_int8_t status;
501	u_int32_t tout;
502
503	tout = 0;
504	while (tout <= 5000000) {
505		status = cfaltread8(CF_ALT_STATUS);
506		if (status == 0xff) {
507			printf("cfaltwait: master: no status, reselecting\n");
508			cfwrite8(CF_DRV_HEAD, CF_D_IBM);
509			DELAY(1);
510			status = cfread8(CF_STATUS);
511		}
512		if (status == 0xff)
513			return -1;
514		dskinf.status = status;
515		if (!(status & CF_S_BUSY)) {
516			if (status & CF_S_ERROR)
517				dskinf.error = cfread8(CF_ERROR);
518			if ((status & mask) == mask) {
519				DPRINTF("cfaltwait: tout %u\n", tout);
520				return (status & CF_S_ERROR);
521			}
522		}
523		if (tout > 1000) {
524			tout += 1000;
525			DELAY(1000);
526		} else {
527			tout += 10;
528			DELAY(10);
529		}
530	}
531	return -1;
532}
533
534static int
535cfcmd(u_int32_t cmd, u_int32_t cylinder, u_int32_t head, u_int32_t sector,
536    u_int32_t count, u_int32_t feature)
537{
538	if (cfwait(0) < 0) {
539		printf("cfcmd: timeout\n");
540		return -1;
541	}
542	cfwrite8(CF_FEATURE, feature);
543	cfwrite8(CF_CYL_L, cylinder);
544	cfwrite8(CF_CYL_H, cylinder >> 8);
545	if (dskinf.use_lba)
546		cfwrite8(CF_DRV_HEAD, CF_D_IBM | CF_D_LBA | head);
547	else
548		cfwrite8(CF_DRV_HEAD, CF_D_IBM | head);
549	cfwrite8(CF_SECT_NUM, sector);
550	cfwrite8(CF_SECT_CNT, count);
551	cfwrite8(CF_COMMAND, cmd);
552	return 0;
553}
554
555static void
556cfreset(void)
557{
558	u_int8_t status;
559	u_int32_t tout;
560
561	cfwrite8(CF_DRV_HEAD, CF_D_IBM);
562	DELAY(1);
563#ifdef DEBUG
564	cfprintregs();
565#endif
566	cfread8(CF_STATUS);
567	cfaltwrite8(CF_ALT_DEV_CTR, CF_A_IDS | CF_A_RESET);
568	DELAY(10000);
569	cfaltwrite8(CF_ALT_DEV_CTR, CF_A_IDS);
570	DELAY(10000);
571	cfread8(CF_ERROR);
572	DELAY(3000);
573
574	for (tout = 0; tout < 310000; tout++) {
575		cfwrite8(CF_DRV_HEAD, CF_D_IBM);
576		DELAY(1);
577		status = cfread8(CF_STATUS);
578		if (!(status & CF_S_BUSY))
579			break;
580		DELAY(100);
581	}
582	DELAY(1);
583	if (status & CF_S_BUSY) {
584		cfprintregs();
585		printf("cfreset: Status stayed busy after reset.\n");
586	}
587	DPRINTF("cfreset: finished, tout %u\n", tout);
588}
589
590#ifdef DEBUG
591static int
592cfgetparams(void)
593{
594	u_int8_t *buf;
595
596	buf = (u_int8_t *)(0x170000);
597	p_memset((char *)buf, 0, 1024);
598	/* Select the drive. */
599	cfwrite8(CF_DRV_HEAD, CF_D_IBM);
600	DELAY(1);
601	cfcmd(ATA_ATA_IDENTIFY, 0, 0, 0, 0, 0);
602	if (cfaltwait(CF_S_READY | CF_S_DSC | CF_S_DRQ)) {
603		printf("cfgetparams: ATA_IDENTIFY failed.\n");
604		return -1;
605	}
606	if (dskinf.use_stream8)
607		cfreadstream8(buf, 512);
608	else
609		cfreadstream16(buf, 512);
610	if (dskinf.debug)
611		cfprintregs();
612#if 0
613	memcpy(&dskinf.ata_params, buf, sizeof(struct ata_params));
614	dskinf.cylinders = dskinf.ata_params.cylinders;
615	dskinf.heads = dskinf.ata_params.heads;
616	dskinf.sectors = dskinf.ata_params.sectors;
617	printf("dsk0: sec %x, hd %x, cyl %x, stat %x, err %x\n",
618	    (u_int32_t)dskinf.ata_params.sectors,
619	    (u_int32_t)dskinf.ata_params.heads,
620	    (u_int32_t)dskinf.ata_params.cylinders,
621	    (u_int32_t)dskinf.status,
622	    (u_int32_t)dskinf.error);
623#endif
624	dskinf.status = cfread8(CF_STATUS);
625	if (dskinf.debug)
626		printf("cfgetparams: ata_params * %x, stat %x\n",
627		    (u_int32_t)buf, (u_int32_t)dskinf.status);
628	return 0;
629}
630#endif /* DEBUG */
631
632static void
633cfprintregs(void)
634{
635	u_int8_t rv;
636
637	putstr("cfprintregs: regs error ");
638	rv = cfread8(CF_ERROR);
639	puthex8(rv);
640	putstr(", count ");
641	rv = cfread8(CF_SECT_CNT);
642	puthex8(rv);
643	putstr(", sect ");
644	rv = cfread8(CF_SECT_NUM);
645	puthex8(rv);
646	putstr(", cyl low ");
647	rv = cfread8(CF_CYL_L);
648	puthex8(rv);
649	putstr(", cyl high ");
650	rv = cfread8(CF_CYL_H);
651	puthex8(rv);
652	putstr(", drv head ");
653	rv = cfread8(CF_DRV_HEAD);
654	puthex8(rv);
655	putstr(", status ");
656	rv = cfread8(CF_STATUS);
657	puthex8(rv);
658	putstr("\n");
659}
660
661int
662avila_read(char *dest, unsigned source, unsigned length)
663{
664	if (dskinf.use_lba == 0 && source == 0)
665		source++;
666	if (dskinf.debug)
667		printf("avila_read: 0x%x, sect %d num secs %d\n",
668		    (u_int32_t)dest, source, length);
669	while (length) {
670		cfwait(CF_S_READY);
671		/* cmd, cyl, head, sect, count, feature */
672		cfcmd(ATA_READ, (source >> 8) & 0xffff, source >> 24,
673		    source & 0xff, 1, 0);
674
675		cfwait(CF_S_READY | CF_S_DRQ | CF_S_DSC);
676		if (dskinf.use_stream8)
677			cfreadstream8(dest, 512);
678		else
679			cfreadstream16(dest, 512);
680		length--;
681		source++;
682		dest += 512;
683	}
684	return 0;
685}
686
687/*
688 * Gateworks Avila Support.
689 */
690static int
691avila_probe(int boardtype_hint)
692{
693	volatile u_int32_t *cs;
694	/*
695	 * Redboot only configure the chip selects that are needed, so
696	 * use that to figure out if it is an Avila or ADI board. The
697	 * Avila boards use CS2 and ADI does not.
698	 */
699	cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET);
700	return (*cs != 0);
701}
702
703static void
704avila_init(void)
705{
706	/* Config the serial port. RedBoot should do the rest. */
707	ubase = (u_int8_t *)(IXP425_UART0_HWBASE);
708
709	dskinf.cs1to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS1_OFFSET);
710	dskinf.cs2to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET);
711	dskinf.cs1 = (u_int8_t *)IXP425_EXP_BUS_CS1_HWBASE;
712	dskinf.cs2 = (u_int8_t *)IXP425_EXP_BUS_CS2_HWBASE;
713
714	cf_init();
715}
716BOARD_CONFIG(avila, "Gateworks Avila");
717
718/*
719 * Gateworks Cambria Support.
720 */
721static int
722cambria_probe(int boardtype_hint)
723{
724	return cpu_is_ixp43x();
725}
726
727static void
728cambria_init(void)
729{
730	/* Config the serial port. RedBoot should do the rest. */
731	ubase = (u_int8_t *)(IXP425_UART0_HWBASE);
732
733	dskinf.cs1to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET);
734	dskinf.cs2to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS4_OFFSET);
735	dskinf.cs1 = (u_int8_t *)CAMBRIA_CFSEL0_HWBASE;
736	dskinf.cs2 = (u_int8_t *)CAMBRIA_CFSEL1_HWBASE;
737
738	cf_init();
739}
740BOARD_CONFIG(cambria, "Gateworks Cambria");
741
742/*
743 * Pronghorn Metro Support.
744 */
745static int
746pronghorn_probe(int boardtype_hint)
747{
748	volatile u_int32_t *cs;
749	/*
750	 * Redboot only configure the chip selects that are needed, so
751	 * use that to figure out if it is an Avila or ADI board. The
752	 * Avila boards use CS2 and ADI does not.
753	 */
754	cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET);
755	return (*cs == 0);
756}
757
758static void
759pronghorn_init(void)
760{
761	/* Config the serial port. RedBoot should do the rest. */
762	ubase = (u_int8_t *)(IXP425_UART1_HWBASE);
763
764	dskinf.cs1to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET);
765	dskinf.cs2to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS4_OFFSET);
766	dskinf.cs1 = (u_int8_t *)IXP425_EXP_BUS_CS3_HWBASE;
767	dskinf.cs2 = (u_int8_t *)IXP425_EXP_BUS_CS4_HWBASE;
768
769	cf_init();
770}
771BOARD_CONFIG(pronghorn, "Pronghorn Metro");
772