1/* $NetBSD: radeonfb_bios.c,v 1.7 2022/09/25 17:52:25 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2006 Itronix Inc.
5 * All rights reserved.
6 *
7 * Written by Garrett D'Amore for Itronix Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of Itronix Inc. may not be used to endorse
18 *    or promote products derived from this software without specific
19 *    prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * ATI Technologies Inc. ("ATI") has not assisted in the creation of, and
36 * does not endorse, this software.  ATI will not be responsible or liable
37 * for any actual or alleged damage or loss caused by or in connection with
38 * the use of or reliance on this software.
39 */
40
41#include <sys/cdefs.h>
42__KERNEL_RCSID(0, "$NetBSD: radeonfb_bios.c,v 1.7 2022/09/25 17:52:25 thorpej Exp $");
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/device.h>
47#include <sys/bus.h>
48
49#include <dev/pci/pcidevs.h>
50#include <dev/pci/pcireg.h>
51#include <dev/pci/pcivar.h>
52#include <dev/pci/radeonfbreg.h>
53#include <dev/pci/radeonfbvar.h>
54
55#include "opt_radeonfb.h"
56
57#ifdef RADEONFB_BIOS_INIT
58
59/*
60 * Globals for the entire BIOS.
61 */
62#define	ROM_HEADER_OFFSET		0x48
63#define	MAX_REVISION			0x10
64#define	SINGLE_TABLE_REVISION		0x09
65#define	MIN_OFFSET			0x60
66
67/*
68 * Offsets of specific tables.
69 */
70#define	RAGE_REGS1_OFFSET		0x0c
71#define	RAGE_REGS2_OFFSET		0x4e
72#define	DYN_CLOCK_OFFSET		0x52
73#define	PLL_INIT_OFFSET			0x46
74#define	MEM_CONFIG_OFFSET		0x48
75
76/*
77 * Values related to generic initialization tables.
78 */
79#define	TABLE_ENTRY_FLAG_MASK		0xe000
80#define	TABLE_ENTRY_INDEX_MASK		0x1fff
81#define	TABLE_ENTRY_COMMAND_MASK	0x00ff
82
83#define	TABLE_FLAG_WRITE_INDEXED	0x0000
84#define	TABLE_FLAG_WRITE_DIRECT		0x2000
85#define	TABLE_FLAG_MASK_INDEXED		0x4000
86#define	TABLE_FLAG_MASK_DIRECT		0x6000
87#define	TABLE_FLAG_DELAY		0x8000
88#define	TABLE_FLAG_SCOMMAND		0xa000
89
90#define	TABLE_SCOMMAND_WAIT_MC_BUSY_MASK	0x03
91#define	TABLE_SCOMMAND_WAIT_MEM_PWRUP_COMPLETE	0x08
92
93/*
94 * PLL initialization block values.
95 */
96#define	PLL_FLAG_MASK			0xc0
97#define	PLL_INDEX_MASK			0x3f
98
99#define	PLL_FLAG_WRITE			0x00
100#define	PLL_FLAG_MASK_BYTE		0x40
101#define	PLL_FLAG_WAIT			0x80
102
103#define	PLL_WAIT_150MKS				1
104#define	PLL_WAIT_5MS				2
105#define	PLL_WAIT_MC_BUSY_MASK			3
106#define	PLL_WAIT_DLL_READY_MASK			4
107#define	PLL_WAIT_CHK_SET_CLK_PWRMGT_CNTL24	5
108
109
110#ifdef	RADEONFB_BIOS_DEBUG
111#define	DPRINTF(x)	printf x
112#else
113#define	DPRINTF(x)
114#endif
115
116struct rb_table;
117
118static void rb_validate(struct radeonfb_softc *, struct rb_table *);
119static uint16_t rb_find_asic_table(struct radeonfb_softc *, struct rb_table *);
120static uint16_t rb_find_mem_reset_table(struct radeonfb_softc *,
121    struct rb_table *);
122static uint16_t rb_find_short_mem_reset_table(struct radeonfb_softc *,
123    struct rb_table *);
124static int rb_load_init_block(struct radeonfb_softc *, struct rb_table *);
125static int rb_load_pll_block(struct radeonfb_softc *, struct rb_table *);
126static int rb_reset_sdram(struct radeonfb_softc *, struct rb_table *);
127
128static void rb_wait_mc_busy_mask(struct radeonfb_softc *, uint16_t);
129static void rb_wait_mem_pwrup_complete(struct radeonfb_softc *, uint16_t);
130static void rb_wait_dll_ready_mask(struct radeonfb_softc *, uint16_t);
131static void rb_wait_chk_set_clk_pwrmgt_cntl24(struct radeonfb_softc *);
132
133/*
134 * Generic structure describing the tables.
135 */
136struct rb_table {
137	const unsigned char	*name;
138	uint16_t		offset;
139	struct rb_table 	*parent;
140
141	/* validate that the table looks sane */
142	void	(*validate)(struct radeonfb_softc *, struct rb_table *);
143
144	/* find looks for the table relative to its "parent" */
145	uint16_t	(*find)(struct radeonfb_softc *, struct rb_table *);
146};
147
148/*
149 * Instances of specific tables.
150 */
151static struct rb_table rb_rage_regs1_table = {
152	"rage_regs_1",			/* name */
153	RAGE_REGS1_OFFSET,		/* offset */
154	NULL,				/* parent */
155	rb_validate,			/* validate */
156	NULL,				/* find */
157};
158
159static struct rb_table rb_rage_regs2_table = {
160	"rage_regs_2",			/* name */
161	RAGE_REGS2_OFFSET,		/* offset */
162	NULL,				/* parent */
163	rb_validate,			/* validate */
164	NULL,				/* find */
165};
166
167static struct rb_table rb_dyn_clock_table = {
168	"dyn_clock",			/* name */
169	DYN_CLOCK_OFFSET,		/* offset */
170	NULL,				/* parent */
171	rb_validate,			/* validate */
172	NULL,				/* find */
173};
174
175static struct rb_table rb_pll_init_table = {
176	"pll_init",			/* name */
177	PLL_INIT_OFFSET,		/* offset */
178	NULL,				/* parent */
179	rb_validate,			/* validate */
180	NULL,				/* find */
181};
182
183static struct rb_table rb_mem_config_table = {
184	"mem_config",			/* name */
185	MEM_CONFIG_OFFSET,		/* offset */
186	NULL,				/* parent */
187	rb_validate,			/* validate */
188	NULL,				/* find */
189};
190
191static struct rb_table rb_mem_reset_table = {
192	"mem_reset",			/* name */
193	0,				/* offset */
194	&rb_mem_config_table,		/* parent */
195	NULL,				/* validate */
196	rb_find_mem_reset_table,	/* find */
197};
198
199static struct rb_table rb_short_mem_reset_table = {
200	"short_mem_reset",		/* name */
201	0,				/* offset */
202	&rb_mem_config_table,		/* parent */
203	NULL,				/* validate */
204	rb_find_short_mem_reset_table,	/* find */
205};
206
207static struct rb_table rb_rage_regs3_table = {
208	"rage_regs_3",			/* name */
209	0,				/* offset */
210	&rb_rage_regs2_table,		/* parent */
211	NULL,				/* validate */
212	rb_find_asic_table,		/* find */
213};
214
215static struct rb_table rb_rage_regs4_table = {
216	"rage_regs_4",			/* name */
217	0,				/* offset */
218	&rb_rage_regs3_table,		/* parent */
219	NULL,				/* validate */
220	rb_find_asic_table,		/* find */
221};
222
223static struct rb_table *rb_tables[] = {
224	&rb_rage_regs1_table,
225	&rb_rage_regs2_table,
226	&rb_dyn_clock_table,
227	&rb_pll_init_table,
228	&rb_mem_config_table,
229	&rb_mem_reset_table,
230	&rb_short_mem_reset_table,
231	&rb_rage_regs3_table,
232	&rb_rage_regs4_table,
233	NULL
234};
235
236void
237rb_validate(struct radeonfb_softc *sc, struct rb_table *tp)
238{
239	uint8_t	rev;
240
241	rev = GETBIOS8(sc, tp->offset - 1);
242
243	if (rev > MAX_REVISION) {
244		DPRINTF(("%s: bad rev %x of %s\n", XNAME(sc), rev, tp->name));
245		tp->offset = 0;
246		return;
247	}
248
249	if (tp->offset < MIN_OFFSET) {
250		DPRINTF(("%s: wrong pointer to %s!\n", XNAME(sc), tp->name));
251		tp->offset = 0;
252		return;
253	}
254}
255
256uint16_t
257rb_find_asic_table(struct radeonfb_softc *sc, struct rb_table *tp)
258{
259	uint16_t		offset;
260	uint8_t			c;
261
262	if ((offset = tp->offset) != 0) {
263		while ((c = GETBIOS8(sc, offset + 1)) != 0) {
264			if (c & 0x40)
265				offset += 10;
266			else if (c & 0x80)
267				offset += 4;
268			else
269				offset += 6;
270		}
271		return offset + 2;
272	}
273	return 0;
274}
275
276uint16_t
277rb_find_mem_reset_table(struct radeonfb_softc *sc, struct rb_table *tp)
278{
279	uint16_t		offset;
280
281	if ((offset = tp->offset) != 0) {
282		while (GETBIOS8(sc, offset))
283			offset++;
284		offset++;
285		return offset + 2;	/* skip table revision and mask */
286	}
287	return 0;
288}
289
290uint16_t
291rb_find_short_mem_reset_table(struct radeonfb_softc *sc, struct rb_table *tp)
292{
293
294	if ((tp->offset != 0) && (GETBIOS8(sc, tp->offset - 2) <= 64))
295		return (tp->offset + GETBIOS8(sc, tp->offset - 3));
296
297	return 0;
298}
299
300/* helper commands */
301void
302rb_wait_mc_busy_mask(struct radeonfb_softc *sc, uint16_t count)
303{
304	DPRINTF(("WAIT_MC_BUSY_MASK: %d ", count));
305	while (count--) {
306		if (!(radeonfb_getpll(sc, RADEON_CLK_PWRMGT_CNTL) &
307			RADEON_MC_BUSY))
308			break;
309	}
310	DPRINTF(("%d\n", count));
311}
312
313void
314rb_wait_mem_pwrup_complete(struct radeonfb_softc *sc, uint16_t count)
315{
316	DPRINTF(("WAIT_MEM_PWRUP_COMPLETE: %d ", count));
317	while (count--) {
318		if ((radeonfb_getindex(sc, RADEON_MEM_STR_CNTL) &
319			RADEON_MEM_PWRUP_COMPLETE) ==
320		    RADEON_MEM_PWRUP_COMPLETE)
321			break;
322	}
323	DPRINTF(("%d\n", count));
324}
325
326void
327rb_wait_dll_ready_mask(struct radeonfb_softc *sc, uint16_t count)
328{
329	DPRINTF(("WAIT_DLL_READY_MASK: %d ", count));
330	while (count--) {
331		if (radeonfb_getpll(sc, RADEON_CLK_PWRMGT_CNTL) &
332		    RADEON_DLL_READY)
333			break;
334	}
335	DPRINTF(("%d\n", count));
336}
337
338void
339rb_wait_chk_set_clk_pwrmgt_cntl24(struct radeonfb_softc *sc)
340{
341	uint32_t	pmc;
342	DPRINTF(("WAIT CHK_SET_CLK_PWRMGT_CNTL24\n"));
343	pmc = radeonfb_getpll(sc, RADEON_CLK_PWRMGT_CNTL);
344
345	if (pmc & RADEON_CLK_PWRMGT_CNTL24) {
346		radeonfb_maskpll(sc, RADEON_MCLK_CNTL, 0xFFFF0000,
347		    RADEON_SET_ALL_SRCS_TO_PCI);
348		delay(10000);
349		radeonfb_putpll(sc, RADEON_CLK_PWRMGT_CNTL,
350		    pmc & ~RADEON_CLK_PWRMGT_CNTL24);
351		delay(10000);
352	}
353}
354
355/*
356 * Block initialization routines.  These take action based on data in
357 * the tables.
358 */
359int
360rb_load_init_block(struct radeonfb_softc *sc, struct rb_table *tp)
361{
362	uint16_t	offset;
363	uint16_t	value;
364
365	if ((tp == NULL) || ((offset = tp->offset) == 0))
366		return 1;
367
368	DPRINTF(("%s: load_init_block processing %s\n", XNAME(sc), tp->name));
369	while ((value = GETBIOS16(sc, offset)) != 0) {
370		uint16_t	flag = value & TABLE_ENTRY_FLAG_MASK;
371		uint16_t	index = value & TABLE_ENTRY_INDEX_MASK;
372		uint8_t		command = value & TABLE_ENTRY_COMMAND_MASK;
373		uint32_t	ormask;
374		uint32_t	andmask;
375		uint16_t	count;
376
377		offset += 2;
378
379		switch (flag) {
380		case TABLE_FLAG_WRITE_INDEXED:
381			DPRINTF(("WRITE INDEXED: %x %x\n",
382				    index, (uint32_t)GETBIOS32(sc, offset)));
383			radeonfb_putindex(sc, index, GETBIOS32(sc, offset));
384			offset += 4;
385			break;
386
387		case TABLE_FLAG_WRITE_DIRECT:
388			DPRINTF(("WRITE DIRECT: %x %x\n",
389				    index, (uint32_t)GETBIOS32(sc, offset)));
390			radeonfb_put32(sc, index, GETBIOS32(sc, offset));
391			offset += 4;
392			break;
393
394		case TABLE_FLAG_MASK_INDEXED:
395			andmask = GETBIOS32(sc, offset);
396			offset += 4;
397			ormask = GETBIOS32(sc, offset);
398			offset += 4;
399			DPRINTF(("MASK INDEXED: %x %x %x\n",
400				    index, andmask, ormask));
401			radeonfb_maskindex(sc, index, andmask, ormask);
402			break;
403
404		case TABLE_FLAG_MASK_DIRECT:
405			andmask = GETBIOS32(sc, offset);
406			offset += 4;
407			ormask = GETBIOS32(sc, offset);
408			offset += 4;
409			DPRINTF(("MASK DIRECT: %x %x %x\n",
410				    index, andmask, ormask));
411			radeonfb_mask32(sc, index,  andmask, ormask);
412			break;
413
414		case TABLE_FLAG_DELAY:
415			/* in the worst case, this would be 16msec */
416			count = GETBIOS16(sc, offset);
417			DPRINTF(("DELAY: %d\n", count));
418			delay(count);
419			offset += 2;
420			break;
421
422		case TABLE_FLAG_SCOMMAND:
423			DPRINTF(("SCOMMAND %x\n", command));
424			switch (command) {
425
426			case TABLE_SCOMMAND_WAIT_MC_BUSY_MASK:
427				count = GETBIOS16(sc, offset);
428				rb_wait_mc_busy_mask(sc, count);
429				break;
430
431			case TABLE_SCOMMAND_WAIT_MEM_PWRUP_COMPLETE:
432				count = GETBIOS16(sc, offset);
433				rb_wait_mem_pwrup_complete(sc, count);
434				break;
435
436			}
437			offset += 2;
438			break;
439		}
440	}
441	return 0;
442}
443
444int
445rb_load_pll_block(struct radeonfb_softc *sc, struct rb_table *tp)
446{
447	uint16_t	offset;
448	uint8_t		index;
449	uint8_t		shift;
450	uint32_t	andmask;
451	uint32_t	ormask;
452
453	if ((tp == NULL) || ((offset = tp->offset) == 0))
454		return 1;
455
456	DPRINTF(("%s: load_pll_block processing %s\n", XNAME(sc), tp->name));
457	while ((index = GETBIOS8(sc, offset)) != 0) {
458		offset++;
459
460		switch (index & PLL_FLAG_MASK) {
461		case PLL_FLAG_WAIT:
462			switch (index & PLL_INDEX_MASK) {
463			case PLL_WAIT_150MKS:
464				delay(150);
465				break;
466			case PLL_WAIT_5MS:
467				/* perhaps this should be tsleep? */
468				delay(5000);
469				break;
470
471			case PLL_WAIT_MC_BUSY_MASK:
472				rb_wait_mc_busy_mask(sc, 1000);
473				break;
474
475			case PLL_WAIT_DLL_READY_MASK:
476				rb_wait_dll_ready_mask(sc, 1000);
477				break;
478
479			case PLL_WAIT_CHK_SET_CLK_PWRMGT_CNTL24:
480				rb_wait_chk_set_clk_pwrmgt_cntl24(sc);
481				break;
482			}
483			break;
484
485		case PLL_FLAG_MASK_BYTE:
486			shift = GETBIOS8(sc, offset) * 8;
487			offset++;
488
489			andmask =
490			    (((uint32_t)GETBIOS8(sc, offset)) << shift) |
491			    ~((uint32_t)0xff << shift);
492			offset++;
493
494			ormask = ((uint32_t)GETBIOS8(sc, offset)) << shift;
495			offset++;
496
497			DPRINTF(("PLL_MASK_BYTE %u %u %x %x\n", index,
498				    shift, andmask, ormask));
499			radeonfb_maskpll(sc, index, andmask, ormask);
500			break;
501
502		case PLL_FLAG_WRITE:
503			DPRINTF(("PLL_WRITE %u %x\n", index,
504				    GETBIOS32(sc, offset)));
505			radeonfb_putpll(sc, index, GETBIOS32(sc, offset));
506			offset += 4;
507			break;
508		}
509	}
510
511	return 0;
512}
513
514int
515rb_reset_sdram(struct radeonfb_softc *sc, struct rb_table *tp)
516{
517	uint16_t offset;
518	uint8_t	index;
519
520	if ((tp == NULL) || ((offset = tp->offset) == 0))
521		return 1;
522
523	DPRINTF(("%s: reset_sdram processing %s\n", XNAME(sc), tp->name));
524
525	while ((index = GETBIOS8(sc, offset)) != 0xff) {
526		offset++;
527		if (index == 0x0f) {
528			rb_wait_mem_pwrup_complete(sc, 20000);
529		} else {
530			uint32_t	ormask;
531
532			ormask = GETBIOS16(sc, offset);
533			offset += 2;
534
535			DPRINTF(("INDEX reg RADEON_MEM_SDRAM_MODE_REG %x %x\n",
536				    RADEON_SDRAM_MODE_MASK, ormask));
537			radeonfb_maskindex(sc, RADEON_MEM_SDRAM_MODE_REG,
538			    RADEON_SDRAM_MODE_MASK, ormask);
539
540			ormask = (uint32_t)index << 24;
541			DPRINTF(("INDEX reg RADEON_MEM_SDRAM_MODE_REG %x %x\n",
542				    RADEON_B3MEM_RESET_MASK, ormask));
543			radeonfb_maskindex(sc, RADEON_MEM_SDRAM_MODE_REG,
544			    RADEON_B3MEM_RESET_MASK, ormask);
545		}
546	}
547	return 0;
548}
549
550/*
551 * Master entry point to parse and act on table data.
552 */
553int
554radeonfb_bios_init(struct radeonfb_softc *sc)
555{
556	uint16_t		revision;
557	uint16_t		scratch;
558	int			i;
559	struct rb_table		*tp;
560
561	if (!sc->sc_biossz)
562		return 1;
563
564	scratch = GETBIOS16(sc, ROM_HEADER_OFFSET);
565	revision = GETBIOS8(sc, scratch);
566	DPRINTF(("%s: Bios Rev: %d\n", XNAME(sc), revision));
567
568
569	/* First parse pass -- locate tables  */
570	for (i = 0; (tp = rb_tables[i]) != NULL; i++) {
571
572		DPRINTF(("%s: parsing table %s\n", XNAME(sc), tp->name));
573
574		if (tp->offset != 0) {
575			uint16_t	temp, offset;
576
577			temp = GETBIOS16(sc, ROM_HEADER_OFFSET);
578			offset = GETBIOS16(sc, temp + tp->offset);
579			if (offset)
580				tp->offset = offset;
581
582		} else {
583			tp->offset = tp->find(sc, tp->parent);
584		}
585
586		if (tp->validate)
587			tp->validate(sc, tp);
588
589		if (revision > SINGLE_TABLE_REVISION)
590			break;
591	}
592
593	if (rb_rage_regs3_table.offset + 1 == rb_pll_init_table.offset) {
594		rb_rage_regs3_table.offset = 0;
595		rb_rage_regs4_table.offset = 0;
596	}
597
598	if (rb_rage_regs1_table.offset)
599		rb_load_init_block(sc, &rb_rage_regs1_table);
600
601	if (revision < SINGLE_TABLE_REVISION) {
602		if (rb_pll_init_table.offset)
603			rb_load_pll_block(sc, &rb_pll_init_table);
604		if (rb_rage_regs2_table.offset)
605			rb_load_init_block(sc, &rb_rage_regs2_table);
606		if (rb_rage_regs4_table.offset)
607			rb_load_init_block(sc, &rb_rage_regs4_table);
608		if (rb_mem_reset_table.offset)
609			rb_reset_sdram(sc, &rb_mem_reset_table);
610		if (rb_rage_regs3_table.offset)
611			rb_load_init_block(sc, &rb_rage_regs3_table);
612		if (rb_dyn_clock_table.offset)
613			rb_load_pll_block(sc, &rb_dyn_clock_table);
614	}
615
616	DPRINTF(("%s: BIOS parse done\n", XNAME(sc)));
617	return 0;
618}
619
620#endif
621