[LinuxBIOS] CAR for Geode GX1 / GX2

Juergen Beisert juergen127 at kreuzholzen.de
Mon May 7 17:15:05 CEST 2007


Hi Peter,

On Monday 07 May 2007 15:41, Peter Stuge wrote:
> On Mon, May 07, 2007 at 09:43:06AM +0200, Juergen Beisert wrote:
> > I could provide some code to map the Geode GX1 cache anywhere you
> > like. It simply uses the tr3, tr4 and tr5 registers to map each
> > cache line to a physical address. I'm currently using it to map the
> > scratch pad RAM to enable and use the video acceleration. Its
> > written in C, but its very easy to rewrite it in assembler.
> > Maybe it achieves LinuxBIOS's requirements, but I'm not sure.
>
> I would love to see the code.
>
> If we can keep it in C that's a lot better than rewriting it in
> assembly, too!

Hmmm, I think we must rewrite it in Assembler as I think it should
prepare some RAM to be used to setup the SDRAM in C. Or am I wrong?

Here is the code. Runs on my Geode GX1 based system to map a part of
the cache to a specific address. It maps always the set 0 of the cache
due to this part can be used as Scratch Pad RAM. But it should be not
a big problem to expand it, to map all sets of the cache (16 kiB in
this case).

/* ---------------------------------------------------------- */

static inline void write_tr3(u32 tr3)
{
	asm volatile ("movl %0, %%tr3" : : "r" (tr3));
}

static inline void write_tr4(u32 tr4)
{
	asm volatile ("movl %0, %%tr4" : : "r" (tr4));
}

static inline void write_tr5(u32 tr5)
{
	asm volatile ("movl %0, %%tr5" : : "r" (tr5));
}

#define invalidate_cache() __asm__ __volatile__ ("invd": : :"memory")

/*
 * Disable and write back the cache
 *
 * Call this function with interrupts disabled!
 */
static void disable_cache(void)
{
	unsigned long cr0;

	wbinvd();
	cr0 = read_cr0();
	cr0 |= 0x40000000;
	write_cr0(cr0);
	wbinvd();
}

/*
 * Enable the cache
 *
 * Call this function with interrupts disabled!
 */
static void enable_cache(void)
{
	unsigned long cr0;

	cr0 = read_cr0();
	cr0 &= 0x9fffffff;
	write_cr0(cr0);
}

/*
 * write 16 bytes into cache' fill buffer
 *
 * This data will be cacheline's content after mapping
 */
static void setup_fill_buffer(u32 value)
{
	write_tr5(0x0);
	write_tr3(value);
	write_tr5(0x4);
	write_tr3(value);
	write_tr5(0x8);
	write_tr3(value);
	write_tr5(0xc);
	write_tr3(value);
}

/*
 * map_cache_line - map a cachline of set 0 to a specific address
 * @mapping_address: physical address to map this line to
 *
 * Always set 0 will be mapped due to this set is used
 * as a scratch pad ram only
 *
 * Note: Call this function only when cache is disabled
 */
static void map_cache_line(u32 mapping_address)
{
	write_tr4((mapping_address & 0xFFFFF000) | 0x400);
	write_tr5((mapping_address & 0xFF0) | 0x00 << 2 | 0x1);
}

/*
 * init_scratch_pad_size
 * @info hardware info
 * @size: new size of the scratch pad ram
 *
 * Note: Called with size = 0 will disable the scratch pad ram.
 */
static int geode_gx1_init_scratch_pad_size(struct geode_gx1_info * info,u32 size)
{
	int i;
	u32 base;
	unsigned long flags;

	if (size > 4096)
		return -1;

	if ((size != 0) && (size < 2048))
		return -1;

	if (size < 3072)
		size=2048;
	else if (size < 4096)
		size=3072;
	/*
	 * The mapping uses always the base
	 * address of the internal chipset
	 * registers
	 */
	info->scratch_pad_phys = info->physical_base;
	base = info->physical_base+(4096-size);

	spin_lock_irqsave(&geode_gx1_memory_lock, flags);
	/*
	 * first disable and invalidate the remaining cache
	 */
	disable_cache();
	invalidate_cache();
	/*
	 * now free the _whole_ RAM for cache and invalidate
	 * it again to unmap currently used scratch pad ram
	 */
	set_scratch_pad_size(info,0);
	invalidate_cache();
	/*
	 * remap the cache lines again
	 */
	for (i=0; i<size; i+=16,base+=16) {
		setup_fill_buffer(0xdeadbeef);
		map_cache_line(base);
	}
	/*
	 * freeze the mapped cache lines
	 */
	set_scratch_pad_size(info,size);
	/*
	 * and again use the remaining
	 * cache lines as usual
	 */
	enable_cache();

	spin_unlock_irqrestore(&geode_gx1_memory_lock, flags);

	return 0;
}

/* ---------------------------------------------------------- */

setup_fill_buffer() and map_cache_line() are the most important functions
and map_cache_line() must be extended to also support the other three cache
sets -> ...| 0x00 << 2 |... stands for set 0, ...| 0x01 << 2 |... would be
set 1 and so on.

The functions above do their work in my LinuxBIOS based Geode GX1 system,
with a special Xorg driver and graphics acceleration.

Hope it helps,

Juergen




More information about the coreboot mailing list