diff options
Diffstat (limited to 'src')
31 files changed, 3939 insertions, 0 deletions
diff --git a/src/arch/ppc/boot/boot.c b/src/arch/ppc/boot/boot.c new file mode 100644 index 0000000000..79eea965b3 --- /dev/null +++ b/src/arch/ppc/boot/boot.c @@ -0,0 +1,83 @@ +#include <ip_checksum.h> +#include <boot/elf.h> +#include <boot/elf_boot.h> +#include <string.h> +#include <printk.h> + + +#ifndef CMD_LINE +#define CMD_LINE "" +#endif + + + +#define UPSZ(X) ((sizeof(X) + 3) &~3) + +static struct { + Elf_Bhdr hdr; + Elf_Nhdr ft_hdr; + unsigned char ft_desc[UPSZ(FIRMWARE_TYPE)]; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char cmd_desc[UPSZ(CMD_LINE)]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 4, + }, + .ft_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(FIRMWARE_TYPE), + .n_type = EBN_FIRMWARE_TYPE, + }, + .ft_desc = FIRMWARE_TYPE, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(CMD_LINE), + .n_type = EBN_COMMAND_LINE, + }, + .cmd_desc = CMD_LINE, +}; + + +int elf_check_arch(Elf_ehdr *ehdr) +{ + return ( + (ehdr->e_machine == EM_PPC) && + (ehdr->e_ident[EI_CLASS] == ELFCLASS32) && + (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + ); + +} + +void jmp_to_elf_entry(void *entry, unsigned long buffer) +{ + void (*kernel_entry)(void); + kernel_entry = entry; + + /* On ppc we don't currently support loading over LinuxBIOS. + * So ignore the buffer. + */ + + /* Jump to kernel */ + kernel_entry(); +} + + diff --git a/src/arch/ppc/boot/linuxbios_table.c b/src/arch/ppc/boot/linuxbios_table.c new file mode 100644 index 0000000000..253a5e5b7f --- /dev/null +++ b/src/arch/ppc/boot/linuxbios_table.c @@ -0,0 +1,285 @@ +#include <mem.h> +#include <ip_checksum.h> +#include <boot/linuxbios_tables.h> +#include <boot/linuxbios_table.h> +#include <printk.h> +#include <string.h> +#include <version.h> + + +struct lb_header *lb_table_init(unsigned long addr) +{ + struct lb_header *header; + + /* 16 byte align the address */ + addr += 15; + addr &= ~15; + + header = (void *)addr; + header->signature[0] = 'L'; + header->signature[1] = 'B'; + header->signature[2] = 'I'; + header->signature[3] = 'O'; + header->header_bytes = sizeof(*header); + header->header_checksum = 0; + header->table_bytes = 0; + header->table_checksum = 0; + header->table_entries = 0; + return header; +} + +struct lb_record *lb_first_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header)); + return rec; +} + +struct lb_record *lb_last_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header) + header->table_bytes); + return rec; +} + +struct lb_record *lb_next_record(struct lb_record *rec) +{ + rec = (void *)(((char *)rec) + rec->size); + return rec; +} + +struct lb_record *lb_new_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = lb_last_record(header); + if (header->table_entries) { + header->table_bytes += rec->size; + } + rec = lb_last_record(header); + header->table_entries++; + rec->tag = LB_TAG_UNUSED; + rec->size = sizeof(*rec); + return rec; +} + + +struct lb_memory *lb_memory(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_memory *mem; + rec = lb_new_record(header); + mem = (struct lb_memory *)rec; + mem->tag = LB_TAG_MEMORY; + mem->size = sizeof(*mem); + return mem; +} + +struct lb_mainboard *lb_mainboard(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_mainboard *mainboard; + rec = lb_new_record(header); + mainboard = (struct lb_mainboard *)rec; + mainboard->tag = LB_TAG_MAINBOARD; + + mainboard->size = (sizeof(*mainboard) + + strlen(mainboard_vendor) + 1 + + strlen(mainboard_part_number) + 1 + + 3) & ~3; + + mainboard->vendor_idx = 0; + mainboard->part_number_idx = strlen(mainboard_vendor) + 1; + + memcpy(mainboard->strings + mainboard->vendor_idx, + mainboard_vendor, strlen(mainboard_vendor) + 1); + memcpy(mainboard->strings + mainboard->part_number_idx, + mainboard_part_number, strlen(mainboard_part_number) + 1); + + return mainboard; +} + +void lb_strings(struct lb_header *header) +{ + static const struct { + uint32_t tag; + const uint8_t *string; + } strings[] = { + { LB_TAG_VERSION, linuxbios_version, }, + { LB_TAG_EXTRA_VERSION, linuxbios_extra_version, }, + { LB_TAG_BUILD, linuxbios_build, }, + { LB_TAG_COMPILE_TIME, linuxbios_compile_time, }, + { LB_TAG_COMPILE_BY, linuxbios_compile_by, }, + { LB_TAG_COMPILE_HOST, linuxbios_compile_host, }, + { LB_TAG_COMPILE_DOMAIN, linuxbios_compile_domain, }, + { LB_TAG_COMPILER, linuxbios_compiler, }, + { LB_TAG_LINKER, linuxbios_linker, }, + { LB_TAG_ASSEMBLER, linuxbios_assembler, }, + }; + int i; + for(i = 0; i < sizeof(strings)/sizeof(strings[0]); i++) { + struct lb_record *rec; + struct lb_string *str; + size_t len; + rec = lb_new_record(header); + str = (struct lb_string *)rec; + len = strlen(strings[i].string); + str->tag = strings[i].tag; + str->size = (sizeof(*rec) + len + 1 + 3) & ~3; + memcpy(str->string, strings[i].string, len+1); + } + +} + +/* Some version of gcc have problems with 64 bit types so + * take an unsigned long instead of a uint64_t for now. + */ +void lb_memory_range(struct lb_memory *mem, + uint32_t type, unsigned long start, unsigned long size) +{ + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + mem->map[entries].start = start; + mem->map[entries].size = size; + mem->map[entries].type = type; + mem->size += sizeof(mem->map[0]); +} + +static void lb_memory_rangek(struct lb_memory *mem, + uint32_t type, unsigned long startk, unsigned long endk) +{ + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + mem->map[entries].start = startk; + mem->map[entries].start <<= 10; + mem->map[entries].size = endk - startk; + mem->map[entries].size <<= 10; + mem->map[entries].type = type; + mem->size += sizeof(mem->map[0]); +} + +static void lb_reserve_table_memory(struct lb_header *head) +{ + struct lb_record *last_rec; + struct lb_memory *mem; + uint64_t start; + uint64_t end; + int i, entries; + + last_rec = lb_last_record(head); + mem = get_lb_mem(); + start = (unsigned long)head; + end = (unsigned long)last_rec; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + /* Resize the right two memory areas so this table is in + * a reserved area of memory. Everything has been carefully + * setup so that is all we need to do. + */ + for(i = 0; i < entries; i++ ) { + uint64_t map_start = mem->map[i].start; + uint64_t map_end = map_start + mem->map[i].size; + /* Does this area need to be expanded? */ + if (map_end == start) { + mem->map[i].size = end - map_start; + } + /* Does this area need to be contracted? */ + else if (map_start == start) { + mem->map[i].start = end; + mem->map[i].size = map_end - end; + } + } +} + + +unsigned long lb_table_fini(struct lb_header *head) +{ + struct lb_record *rec, *first_rec; + rec = lb_last_record(head); + if (head->table_entries) { + head->table_bytes += rec->size; + } + lb_reserve_table_memory(head); + first_rec = lb_first_record(head); + head->table_checksum = compute_ip_checksum(first_rec, head->table_bytes); + head->header_checksum = 0; + head->header_checksum = compute_ip_checksum(head, sizeof(*head)); + printk_debug("Wrote linuxbios table at: %p - %p checksum %lx\n", + head, rec, head->table_checksum); + return (unsigned long)rec; +} + + +/* Routines to extract part so the linuxBIOS table or + * information from the linuxBIOS table after we have written it. + * Currently get_lb_mem relies on a global we can change the + * implementaiton. + */ +static struct lb_memory *mem_ranges = 0; +struct lb_memory *get_lb_mem(void) +{ + return mem_ranges; +} + +unsigned long write_linuxbios_table( + unsigned long *processor_map, + struct mem_range *ram, + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_startk, unsigned long rom_table_endk) +{ + unsigned long table_size; + struct mem_range *ramp; + struct lb_header *head; + struct lb_memory *mem; +#if HAVE_OPTION_TABLE == 1 + struct lb_record *rec_dest, *rec_src; +#endif + + head = lb_table_init(low_table_end); + low_table_end = (unsigned long)head; +#if HAVE_OPTION_TABLE == 1 + /* Write the option config table... */ + rec_dest = lb_new_record(head); + rec_src = (struct lb_record *)&option_table; + memcpy(rec_dest, rec_src, rec_src->size); +#endif + mem = lb_memory(head); + mem_ranges = mem; + /* I assume there is always ram at address 0 */ + /* Reserve our tables in low memory */ + table_size = (low_table_end - low_table_start); + lb_memory_range(mem, LB_MEM_TABLE, 0, table_size); + lb_memory_range(mem, LB_MEM_RAM, table_size, (ram[0].sizek << 10) - table_size); + /* Reserving pci memory mapped space will keep the kernel from booting seeing + * any pci resources. + */ + for(ramp = &ram[1]; ramp->sizek; ramp++) { + unsigned long startk, endk; + startk = ramp->basek; + endk = startk + ramp->sizek; + if ((startk < rom_table_startk) && (endk > rom_table_startk)) { + lb_memory_rangek(mem, LB_MEM_RAM, startk, rom_table_startk); + startk = rom_table_startk; + } + if ((startk == rom_table_startk) && (endk > startk)) { + unsigned long tend; + tend = rom_table_endk; + if (tend > endk) { + tend = endk; + } + lb_memory_rangek(mem, LB_MEM_TABLE, rom_table_startk, tend); + startk = tend; + } + if (endk > startk) { + lb_memory_rangek(mem, LB_MEM_RAM, startk, endk); + } + } + + /* Record our motheboard */ + lb_mainboard(head); + /* Record our various random string information */ + lb_strings(head); + + low_table_end = lb_table_fini(head); + + /* Remember where my valid memory ranges are */ + return low_table_end; +} diff --git a/src/arch/ppc/include/arch/boot/boot.h b/src/arch/ppc/include/arch/boot/boot.h new file mode 100644 index 0000000000..b68455b557 --- /dev/null +++ b/src/arch/ppc/include/arch/boot/boot.h @@ -0,0 +1,8 @@ +#ifndef ASM_I386_BOOT_H +#define ASM_I386_BOOT_H + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_PPC + +#endif /* ASM_I386_BOOT_H */ diff --git a/src/arch/ppc/include/arch/io.h b/src/arch/ppc/include/arch/io.h new file mode 100644 index 0000000000..f51f14089e --- /dev/null +++ b/src/arch/ppc/include/arch/io.h @@ -0,0 +1,192 @@ +/* + * BK Id: SCCS/s.io.h 1.14 10/16/01 15:58:42 trini + */ +#ifndef _PPC_IO_H +#define _PPC_IO_H +#include <types.h> + +#define SIO_CONFIG_RA 0x398 +#define SIO_CONFIG_RD 0x399 + +#define SLOW_DOWN_IO + +#define PMAC_ISA_MEM_BASE 0 +#define PMAC_PCI_DRAM_OFFSET 0 +#define CHRP_ISA_IO_BASE 0xf8000000 +#define CHRP_ISA_MEM_BASE 0xf7000000 +#define CHRP_PCI_DRAM_OFFSET 0 +#define PREP_ISA_IO_BASE 0x80000000 +#define PREP_ISA_MEM_BASE 0xc0000000 +#define PREP_PCI_DRAM_OFFSET 0x80000000 + +#define _IO_BASE 0xfe000000 + +#define readb(addr) in_8((volatile u8 *)(addr)) +#define writeb(b,addr) out_8((volatile u8 *)(addr), (b)) +#define readw(addr) in_le16((volatile u16 *)(addr)) +#define readl(addr) in_le32((volatile u32 *)(addr)) +#define writew(b,addr) out_le16((volatile u16 *)(addr),(b)) +#define writel(b,addr) out_le32((volatile u32 *)(addr),(b)) + + +#define __raw_readb(addr) (*(volatile unsigned char *)(addr)) +#define __raw_readw(addr) (*(volatile unsigned short *)(addr)) +#define __raw_readl(addr) (*(volatile unsigned int *)(addr)) +#define __raw_writeb(v, addr) (*(volatile unsigned char *)(addr) = (v)) +#define __raw_writew(v, addr) (*(volatile unsigned short *)(addr) = (v)) +#define __raw_writel(v, addr) (*(volatile unsigned int *)(addr) = (v)) + +/* + * The insw/outsw/insl/outsl macros don't do byte-swapping. + * They are only used in practice for transferring buffers which + * are arrays of bytes, and byte-swapping is not appropriate in + * that case. - paulus + */ +#define insb(port, buf, ns) _insb((u8 *)((port)+_IO_BASE), (buf), (ns)) +#define outsb(port, buf, ns) _outsb((u8 *)((port)+_IO_BASE), (buf), (ns)) +#define insw(port, buf, ns) _insw_ns((u16 *)((port)+_IO_BASE), (buf), (ns)) +#define outsw(port, buf, ns) _outsw_ns((u16 *)((port)+_IO_BASE), (buf), (ns)) +#define insl(port, buf, nl) _insl_ns((u32 *)((port)+_IO_BASE), (buf), (nl)) +#define outsl(port, buf, nl) _outsl_ns((u32 *)((port)+_IO_BASE), (buf), (nl)) + +#define inb(port) in_8((u8 *)((port)+_IO_BASE)) +#define outb(val, port) out_8((u8 *)((port)+_IO_BASE), (val)) +#define inw(port) in_le16((u16 *)((port)+_IO_BASE)) +#define outw(val, port) out_le16((u16 *)((port)+_IO_BASE), (val)) +#define inl(port) in_le32((u32 *)((port)+_IO_BASE)) +#define outl(val, port) out_le32((u32 *)((port)+_IO_BASE), (val)) + +#define inb_p(port) inb((port)) +#define outb_p(val, port) outb((val), (port)) +#define inw_p(port) inw((port)) +#define outw_p(val, port) outw((val), (port)) +#define inl_p(port) inl((port)) +#define outl_p(val, port) outl((val), (port)) + +extern void _insb(volatile u8 *port, void *buf, int ns); +extern void _outsb(volatile u8 *port, const void *buf, int ns); +extern void _insw(volatile u16 *port, void *buf, int ns); +extern void _outsw(volatile u16 *port, const void *buf, int ns); +extern void _insl(volatile u32 *port, void *buf, int nl); +extern void _outsl(volatile u32 *port, const void *buf, int nl); +extern void _insw_ns(volatile u16 *port, void *buf, int ns); +extern void _outsw_ns(volatile u16 *port, const void *buf, int ns); +extern void _insl_ns(volatile u32 *port, void *buf, int nl); +extern void _outsl_ns(volatile u32 *port, const void *buf, int nl); + +/* + * The *_ns versions below don't do byte-swapping. + * Neither do the standard versions now, these are just here + * for older code. + */ +#define insw_ns(port, buf, ns) _insw_ns((u16 *)((port)+_IO_BASE), (buf), (ns)) +#define outsw_ns(port, buf, ns) _outsw_ns((u16 *)((port)+_IO_BASE), (buf), (ns)) +#define insl_ns(port, buf, nl) _insl_ns((u32 *)((port)+_IO_BASE), (buf), (nl)) +#define outsl_ns(port, buf, nl) _outsl_ns((u32 *)((port)+_IO_BASE), (buf), (nl)) + + +#define IO_SPACE_LIMIT ~0 + +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +/* + * Enforce In-order Execution of I/O: + * Acts as a barrier to ensure all previous I/O accesses have + * completed before any further ones are issued. + */ +extern inline void eieio(void) +{ + __asm__ __volatile__ ("eieio" : : : "memory"); +} + +/* Enforce in-order execution of data I/O. + * No distinction between read/write on PPC; use eieio for all three. + */ +#define iobarrier_rw() eieio() +#define iobarrier_r() eieio() +#define iobarrier_w() eieio() + +/* + * 8, 16 and 32 bit, big and little endian I/O operations, with barrier. + */ +extern inline int in_8(volatile unsigned char *addr) +{ + int ret; + + __asm__ __volatile__("lbz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr)); + return ret; +} + +extern inline void out_8(volatile unsigned char *addr, int val) +{ + __asm__ __volatile__("stb%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val)); +} + +extern inline int in_le16(volatile unsigned short *addr) +{ + int ret; + + __asm__ __volatile__("lhbrx %0,0,%1; eieio" : "=r" (ret) : + "r" (addr), "m" (*addr)); + return ret; +} + +extern inline int in_be16(volatile unsigned short *addr) +{ + int ret; + + __asm__ __volatile__("lhz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr)); + return ret; +} + +extern inline void out_le16(volatile unsigned short *addr, int val) +{ + __asm__ __volatile__("sthbrx %1,0,%2; eieio" : "=m" (*addr) : + "r" (val), "r" (addr)); +} + +extern inline void out_be16(volatile unsigned short *addr, int val) +{ + __asm__ __volatile__("sth%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val)); +} + +extern inline unsigned in_le32(volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwbrx %0,0,%1; eieio" : "=r" (ret) : + "r" (addr), "m" (*addr)); + return ret; +} + +extern inline unsigned in_be32(volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr)); + return ret; +} + +extern inline void out_le32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stwbrx %1,0,%2; eieio" : "=m" (*addr) : + "r" (val), "r" (addr)); +} + +extern inline void out_be32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stw%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val)); +} + +extern inline void _insw_ns(volatile u16 *port, void *buf, int ns) +{ + u16 * b = (u16 *)buf; + + while (ns > 0) { + *b++ = readw(port); + ns--; + } +} +#endif diff --git a/src/arch/ppc/include/arch/pirq_routing.h b/src/arch/ppc/include/arch/pirq_routing.h new file mode 100644 index 0000000000..dad8531eb5 --- /dev/null +++ b/src/arch/ppc/include/arch/pirq_routing.h @@ -0,0 +1,54 @@ +#ifndef ARCH_PIRQ_ROUTING_H +#define ARCH_PIRQ_ROUTING_H + +#include <types.h> + +#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) +#define PIRQ_VERSION 0x0100 + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +#if defined(IRQ_SLOT_COUNT) +#define IRQ_SLOTS_COUNT IRQ_SLOT_COUNT +#elif (__GNUC__ < 3) +#define IRQ_SLOTS_COUNT 1 +#else +#define IRQ_SLOTS_COUNT +#endif + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[IRQ_SLOTS_COUNT]; +} __attribute__((packed)); + +extern const struct irq_routing_table intel_irq_routing_table; + +#if defined(DEBUG) && defined(HAVE_PIRQ_TABLE) +void check_pirq_routing_table(void); +#else +#define check_pirq_routing_table() do {} while(0) +#endif + +#if defined(HAVE_PIRQ_TABLE) +unsigned long copy_pirq_routing_table(unsigned long start); +#else +#define copy_pirq_routing_table(start) (start) +#endif + +#endif /* ARCH_PIRQ_ROUTING_H */ diff --git a/src/arch/ppc/include/bitops.h b/src/arch/ppc/include/bitops.h new file mode 100644 index 0000000000..23cfa66077 --- /dev/null +++ b/src/arch/ppc/include/bitops.h @@ -0,0 +1,22 @@ +#ifndef I386_BITOPS_H +#define I386_BITOPS_H + +/** + * log2 - Find the truncated log base 2 of x + */ + +static inline unsigned long log2(unsigned long x) +{ + unsigned long r = 0; + /* + __asm__( + "bsrl %1, %0\n\t" + "jnz 1f\n\t" + "movl $-1, %0\n\t" + "1:\n\t" + : "=r" (r) : "r" (x)); + */ + return r; + +} +#endif /* I386_BITOPS_H */ diff --git a/src/arch/ppc/include/ppc.h b/src/arch/ppc/include/ppc.h new file mode 100644 index 0000000000..fe4aa1654b --- /dev/null +++ b/src/arch/ppc/include/ppc.h @@ -0,0 +1,21 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#ifndef _PPC_H +#define _PPC_H + +#define BIG_ENDIAN +#define RODATA __attribute__ ((__section__ (".rodata"))) + +/* Do CPU specific setup, with optional icache */ +void ppc_setup_cpu(int icache); + +void ppc_enable_dcache(void); +void ppc_disable_dcache(void); +void ppc_enable_mmu(void); + +/* Describe which sort of ppc CPU I am */ +void ppc_identify(void); + +#endif diff --git a/src/arch/ppc/include/ppc_asm.tmpl b/src/arch/ppc/include/ppc_asm.tmpl new file mode 100644 index 0000000000..da1f7f7782 --- /dev/null +++ b/src/arch/ppc/include/ppc_asm.tmpl @@ -0,0 +1,293 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This file contains all the macros and symbols which define + * a PowerPC assembly language environment. + */ +#ifndef __PPC_ASM_TMPL__ +#define __PPC_ASM_TMPL__ + +/*************************************************************************** + * + * These definitions simplify the ugly declarations necessary for GOT + * definitions. + * + * Stolen from prepboot/bootldr.h, (C) 1998 Gabriel Paubert, paubert@iram.es + * + * Uses r14 to access the GOT + */ + +#define START_GOT \ + .section ".got2","aw"; \ +.LCTOC1 = .+32768 + +#define END_GOT \ + .text + +#define GET_GOT \ + bl 1f ; \ + .text 2 ; \ +0: .long .LCTOC1-1f ; \ + .text ; \ +1: mflr r14 ; \ + lwz r0,0b-1b(r14) ; \ + add r14,r0,r14 ; + +#define GOT_ENTRY(NAME) .L_ ## NAME = . - .LCTOC1 ; .long NAME + +#define GOT(NAME) .L_ ## NAME (r14) + + +/*************************************************************************** + * Register names + */ +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +/* + * FP register names + */ +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +/* Some special registers */ + +#define TBRU 269 /* Time base Upper/Lower (Reading) */ +#define TBRL 268 +#define TBWU 284 /* Time base Upper/Lower (Writing) */ +#define TBWL 285 +#define XER 1 +#define LR 8 +#define CTR 9 +#define HID0 1008 /* Hardware Implementation */ +#define PVR 287 /* Processor Version */ +#define SDR1 25 /* MMU hash base register */ +#define DAR 19 /* Data Address Register */ +#define SPR0 272 /* Supervisor Private Registers */ +#define SPRG0 272 +#define SPR1 273 +#define SPRG1 273 +#define SPR2 274 +#define SPRG2 274 +#define SPR3 275 +#define SPRG3 275 +#define DSISR 18 +#define SRR0 26 /* Saved Registers (exception) */ +#define SRR1 27 +#define DEC 22 /* Decrementer */ +#define EAR 282 /* External Address Register */ +#define ICR 148 /* Interrupt Cause Register (37-44) */ +#define DER 149 +#define COUNTA 150 /* Breakpoint Counter (37-44) */ +#define COUNTB 151 /* Breakpoint Counter (37-44) */ +#define LCTRL1 156 /* Load/Store Support (37-40) */ +#define LCTRL2 157 /* Load/Store Support (37-41) */ +#define ICTRL 158 + +/* Registers in the processor's internal memory map that we use. +*/ +#define IMMR 0xff000000 + +#define SYPCR 0x00000004 +#define BR0 0x00000100 +#define OR0 0x00000104 +#define BR1 0x00000108 +#define OR1 0x0000010c +#define BR2 0x00000110 +#define OR2 0x00000114 +#define BR3 0x00000118 +#define OR3 0x0000011c +#define BR4 0x00000120 +#define OR4 0x00000124 + +#define MAR 0x00000164 +#define MCR 0x00000168 +#define MAMR 0x00000170 +#define MBMR 0x00000174 +#define MSTAT 0x00000178 +#define MPTPR 0x0000017a +#define MDR 0x0000017c + +#define TBSCR 0x00000200 +#define TBREFF0 0x00000204 + +#define PLPRCR 0x00000284 + +#define curptr r2 + +#define SYNC \ + sync; \ + isync + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +/* + * GCC sometimes accesses words at negative offsets from the stack + * pointer, although the SysV ABI says it shouldn't. To cope with + * this, we leave this much untouched space on the stack on exception + * entry. + */ +#define STACK_UNDERHEAD 64 + +#if 0 /* we don't use virtual addresses in PPCBOOT */ +#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h +#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h +#else +#define tophys(rd,rs,rt) mr rd,rs +#define tovirt(rd,rs,rt) mr rd,rs +#endif + +/* + * Exception entry code. This code runs with address translation + * turned off, i.e. using physical addresses. + * We assume sprg3 has the physical address of the current + * task's thread_struct. + */ +#define EXCEPTION_PROLOG \ + mtspr SPRG0,r20; \ + mtspr SPRG1,r21; \ + mfcr r20; \ + tophys(r21,r1,r21); /* use tophys(kernel sp) otherwise */ \ + subi r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD; /* alloc exc. frame */\ +1: stw r20,_CCR(r21); /* save registers */ \ + stw r22,GPR22(r21); \ + stw r23,GPR23(r21); \ + mfspr r20,SPRG0; \ + stw r20,GPR20(r21); \ + mfspr r22,SPRG1; \ + stw r22,GPR21(r21); \ + mflr r20; \ + stw r20,_LINK(r21); \ + mfctr r22; \ + stw r22,_CTR(r21); \ + mfspr r20,XER; \ + stw r20,_XER(r21); \ + mfspr r22,SRR0; \ + mfspr r23,SRR1; \ + stw r0,GPR0(r21); \ + stw r1,GPR1(r21); \ + stw r2,GPR2(r21); \ + stw r1,0(r21); \ + tovirt(r1,r21,r1); /* set new kernel sp */ \ + SAVE_4GPRS(3, r21); +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r21, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + * + * The data words for `hdlr' and `int_return' are initialized with + * OFFSET values only; they must be relocated first before they can + * be used! + */ +#define STD_EXCEPTION(n, label, hdlr) \ + . = n; \ +label: \ + EXCEPTION_PROLOG; \ + lwz r3,GOT(transfer_to_handler); \ + mtlr r3; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r20,MSR_KERNEL; \ + blrl ; \ +.L_ ## label : \ + .long hdlr - _start + EXC_OFF_SYS_RESET; \ + .long int_return - _start + EXC_OFF_SYS_RESET + + +#endif /* __PPC_ASM_TMPL__ */ diff --git a/src/arch/ppc/include/ppcreg.h b/src/arch/ppc/include/ppcreg.h new file mode 100644 index 0000000000..5ec0dd9417 --- /dev/null +++ b/src/arch/ppc/include/ppcreg.h @@ -0,0 +1,128 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +/* In the MSR, not all bits are interesting to us + 13 - POW - Power management + 14 - TGPR - temporary registers for page table routines + 15 - ILE - Exception little endian + 16 - EE - External interrupts + 17 - PR - Privilege level + 18 - FP - Floating Point available + 19 - ME - Machine check exception enable + 20 - FE0 - Floating exception mode 0 + 21 - SE - Single step trace mode + 22 - BE - Branch trace enable + 23 - FE1 - Floating exception mode 1 + 25 - IP - Exception prefix + 26 - IR - Instruction address translation + 27 - DR - Data address translation + 30 - RI - Recoverable exception + 31 - LE - Little endian mode + MSR_MASK is the bits we do not change. + */ + +#define MSR_MASK 0xfff8008c +#define MSR_POW 0x00040000 +#define MSR_TGPR 0x00020000 +#define MSR_ILE 0x00010000 +#define MSR_EE 0x00008000 +#define MSR_PR 0x00004000 +#define MSR_FP 0x00002000 +#define MSR_ME 0x00001000 +#define MSR_FE0 0x00000800 +#define MSR_SE 0x00000400 +#define MSR_BE 0x00000200 +#define MSR_FE1 0x00000100 +#define MSR_IP 0x00000040 +#define MSR_IR 0x00000020 +#define MSR_DR 0x00000010 +#define MSR_RI 0x00000002 +#define MSR_LE 0x00000001 + +#define MSR_DEFAULT (MSR_FP | MSR_IR | MSR_DR) + +/* We are interested in the following hid0 bits: + 6 - ECLK - Enable external test clock (603 only) + 11 - DPM - Turn on dynamic power management (603 only) + 15 - NHR - Not hard reset + 16 - ICE - Instruction cache enable + 17 - DCE - Data cache enable + 18 - ILOCK - Instruction cache lock + 19 - DLOCK - Data cache lock + 20 - ICFI - Instruction cache invalidate + 21 - DCFI - Data cache invalidate + 24 - NOSER - Serial execution disable (604 only - turbo mode) + 24 - SGE - Store gathering enable (7410 only) + 29 - BHT - Branch history table (604 only) + + I made up the tags for the 604 specific bits, as they aren't + named in the 604 book. The 603 book calls the invalidate bits + ICFI and DCI, and I have no idea why it isn't DCFI. Maybe IBM named + one, and Motorola named the other. */ + +#define HID0_ECLK 0x02000000 +#define HID0_DPM 0x00100000 +#define HID0_NHR 0x00010000 +#define HID0_ICE 0x00008000 +#define HID0_DCE 0x00004000 +#define HID0_ILOCK 0x00002000 +#define HID0_DLOCK 0x00001000 +#define HID0_ICFI 0x00000800 +#define HID0_DCFI 0x00000400 +#define HID0_NOSER 0x00000080 +#define HID0_SGE 0x00000080 +#define HID0_BTIC 0x00000020 +#define HID0_BHT 0x00000004 + +/* + * BAT defines + */ + +/* + * BL field in upper BAT register + */ +#define BAT_BL_128K 0x00000000 +#define BAT_BL_256K 0x00000004 +#define BAT_BL_512K 0x0000000C +#define BAT_BL_1M 0x0000001C +#define BAT_BL_2M 0x0000003C +#define BAT_BL_4M 0x0000007C +#define BAT_BL_8M 0x000000FC +#define BAT_BL_16M 0x000001FC +#define BAT_BL_32M 0x000003FC +#define BAT_BL_64M 0x000007FC +#define BAT_BL_128M 0x00000FFC +#define BAT_BL_256M 0x00001FFC + +/* + * Supervisor/user valid mode in upper BAT register + */ +#define BAT_VALID_SUPERVISOR 0x00000002 +#define BAT_VALID_USER 0x00000001 +#define BAT_INVALID 0x00000000 + +/* + * WIMG bit setting in lower BAT register + */ +#define BAT_WRITE_THROUGH 0x00000040 +#define BAT_CACHE_INHIBITED 0x00000020 +#define BAT_COHERENT 0x00000010 +#define BAT_GUARDED 0x00000008 + +/* + * Protection bits in lower BAT register + */ +#define BAT_NO_ACCESS 0x00000000 +#define BAT_READ_ONLY 0x00000001 +#define BAT_READ_WRITE 0x00000002 + +#ifndef ASM +unsigned __getmsr(void); +void __setmsr(unsigned value); +unsigned __gethid0(void); +unsigned __gethid1(void); +void __sethid0(unsigned value); +unsigned __getpvr(void); +#endif + diff --git a/src/arch/ppc/include/stddef.h b/src/arch/ppc/include/stddef.h new file mode 100644 index 0000000000..2ea8ebb13c --- /dev/null +++ b/src/arch/ppc/include/stddef.h @@ -0,0 +1,15 @@ +#ifndef PPC_STDDEF_H +#define PPC_STDDEF_H + +typedef long ptrdiff_t; +typedef unsigned long size_t; +typedef long ssize_t; + +typedef int wchar_t; +typedef unsigned int wint_t; + +#define NULL 0 + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif /* PPC_STDDEF_H */ diff --git a/src/arch/ppc/include/stdint.h b/src/arch/ppc/include/stdint.h new file mode 100644 index 0000000000..73da924b1e --- /dev/null +++ b/src/arch/ppc/include/stdint.h @@ -0,0 +1,52 @@ +#ifndef PPC_STDINT_H +#define PPC_STDINT_H + +/* Exact integral types */ +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef unsigned short uint16_t; +typedef signed short int16_t; + +typedef unsigned int uint32_t; +typedef signed int int32_t; + +typedef unsigned long long uint64_t; +typedef signed long long int64_t; + +/* Small types */ +typedef unsigned char uint_least8_t; +typedef signed char int_least8_t; + +typedef unsigned short uint_least16_t; +typedef signed short int_least16_t; + +typedef unsigned int uint_least32_t; +typedef signed int int_least32_t; + +typedef unsigned long long uint_least64_t; +typedef signed long long int_least64_t; + +/* Fast Types */ +typedef unsigned char uint_fast8_t; +typedef signed char int_fast8_t; + +typedef unsigned int uint_fast16_t; +typedef signed int int_fast16_t; + +typedef unsigned int uint_fast32_t; +typedef signed int int_fast32_t; + +typedef unsigned long long uint_fast64_t; +typedef signed long long int_fast64_t; + +/* Types for `void *' pointers. */ +typedef int intptr_t; +typedef unsigned int uintptr_t; + +/* Largest integral types */ +typedef long long int intmax_t; +typedef unsigned long long uintmax_t; + + +#endif /* PPC_STDINT_H */ diff --git a/src/arch/ppc/include/timer.h b/src/arch/ppc/include/timer.h new file mode 100644 index 0000000000..9a2328f7cd --- /dev/null +++ b/src/arch/ppc/include/timer.h @@ -0,0 +1,13 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#ifndef _TIMER_H +#define __TIMER_H + +unsigned get_hz(void); +unsigned ticks_since_boot(void); +void sleep_ticks(unsigned); +void udelay(int); + +#endif diff --git a/src/arch/ppc/lib/c_start.S b/src/arch/ppc/lib/c_start.S new file mode 100644 index 0000000000..1719fd7103 --- /dev/null +++ b/src/arch/ppc/lib/c_start.S @@ -0,0 +1,109 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +/* + * The assumption is that we're located in ROM and we have a fake stack + * located in cache. Our task is to turn on memory proper, the finish + * configuring the machine. + */ + +#define ASM +#include "ppcreg.h" +#include <ppc_asm.tmpl> + +.section ".text" +.globl _start + +_start: + /* + * init stack pointer to real ram now that memory is on + */ + lis r1, _estack@ha + addi r1, r1, _estack@l + stwu r0,-64(r1) + stwu r1,-24(r1) + + /* + * Clear stack + */ + lis r4, _stack@ha + addi r4, r4, _stack@l + lis r7, _estack@ha + addi r7, r7, _estack@l + lis r5, 0 +1: stwx r5, 0, r4 + addi r4, r4, 4 + cmp 0, 0, r4, r7 + ble 1b + sync + + /* + * Clear bss + */ + lis r4, _bss@ha + addi r4, r4, _bss@l + lis r7, _ebss@ha + addi r7, r7, _ebss@l + lis r5, 0 +1: stwx r5, 0, r4 + addi r4, r4, 4 + cmp 0, 0, r4, r7 + ble 1b + sync + + /* + * Set up the EABI pointers, before we enter any C code + */ + lis r13, _SDA_BASE_@ha + addi r13, r13, _SDA_BASE_@l + lis r2, _SDA2_BASE_@ha + addi r2, r2, _SDA2_BASE_@l + + /* + * load start address into SRR0 for rfi + */ + lis r3, hardwaremain@ha + addi r3, r3, hardwaremain@l + mtspr SRR0, r3 + + /* + * load the current MSR into SRR1 so that it will be copied + * back into MSR on rfi + */ + mfmsr r4 + mtspr SRR1, r4 // load SRR1 with r4 + + /* + * If something returns after rfi then die + */ + lis r3, dead@ha + addi r3, r3, dead@l + mtlr r3 + + /* + * Complete rest of initialization in C (hardwaremain) + */ + rfi + + /* + * Stop here if something goes wrong + */ +dead: + b dead + /*NOTREACHED*/ + +/* Remove need for ecrti.o and ectrn.o */ +.globl __init +__init: +.globl __fini +__fini: +.globl __CTOR_LIST__ +__CTOR_LIST__: +.globl __CTOR_END__ +__CTOR_END__: +.globl __DTOR_LIST__ +__DTOR_LIST__: +.globl __DTOR_END__ +__DTOR_END__: + blr diff --git a/src/arch/ppc/lib/floats.S b/src/arch/ppc/lib/floats.S new file mode 100644 index 0000000000..57b38cb9c3 --- /dev/null +++ b/src/arch/ppc/lib/floats.S @@ -0,0 +1,45 @@ +/* $Id$ */ +/* Copyright 1999-2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#include <ppc_asm.tmpl> + + .globl _init_float_registers + +_init_float_registers: + lfd fr0, 0(r3) + lfd fr1, 0(r3) + lfd fr2, 0(r3) + lfd fr3, 0(r3) + lfd fr4, 0(r3) + lfd fr5, 0(r3) + lfd fr6, 0(r3) + lfd fr7, 0(r3) + lfd fr8, 0(r3) + lfd fr9, 0(r3) + lfd fr10, 0(r3) + lfd fr11, 0(r3) + lfd fr12, 0(r3) + lfd fr13, 0(r3) + lfd fr14, 0(r3) + lfd fr15, 0(r3) + lfd fr16, 0(r3) + lfd fr17, 0(r3) + lfd fr18, 0(r3) + lfd fr19, 0(r3) + lfd fr20, 0(r3) + lfd fr21, 0(r3) + lfd fr22, 0(r3) + lfd fr23, 0(r3) + lfd fr24, 0(r3) + lfd fr25, 0(r3) + lfd fr26, 0(r3) + lfd fr27, 0(r3) + lfd fr28, 0(r3) + lfd fr29, 0(r3) + lfd fr30, 0(r3) + lfd fr31, 0(r3) + blr + + .end + diff --git a/src/arch/ppc/lib/floats.inc b/src/arch/ppc/lib/floats.inc new file mode 100644 index 0000000000..5c366af63f --- /dev/null +++ b/src/arch/ppc/lib/floats.inc @@ -0,0 +1,43 @@ +/* $Id$ */ +/* Copyright 1999-2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +/* .text*/ + .globl _init_float_registers + +_init_float_registers: + lfd 0, 0(3) + lfd 1, 0(3) + lfd 2, 0(3) + lfd 3, 0(3) + lfd 4, 0(3) + lfd 5, 0(3) + lfd 6, 0(3) + lfd 7, 0(3) + lfd 8, 0(3) + lfd 9, 0(3) + lfd 10, 0(3) + lfd 11, 0(3) + lfd 12, 0(3) + lfd 13, 0(3) + lfd 14, 0(3) + lfd 15, 0(3) + lfd 16, 0(3) + lfd 17, 0(3) + lfd 18, 0(3) + lfd 19, 0(3) + lfd 20, 0(3) + lfd 21, 0(3) + lfd 22, 0(3) + lfd 23, 0(3) + lfd 24, 0(3) + lfd 25, 0(3) + lfd 26, 0(3) + lfd 27, 0(3) + lfd 28, 0(3) + lfd 29, 0(3) + lfd 30, 0(3) + lfd 31, 0(3) + blr + .end + diff --git a/src/arch/ppc/lib/setup.c b/src/arch/ppc/lib/setup.c new file mode 100644 index 0000000000..000a4b15a9 --- /dev/null +++ b/src/arch/ppc/lib/setup.c @@ -0,0 +1,130 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#include "ppc.h" +#include "ppcreg.h" + +unsigned __getmsr(void) +{ + unsigned result; + __asm__ volatile ("mfmsr %0" : "=r" (result)); + return result; +} + +unsigned __gethid0(void) +{ + unsigned result; + __asm__ volatile ("mfspr %0,1008" : "=r" (result)); + return result; +} + +unsigned __gethid1(void) +{ + unsigned result; + __asm__ volatile ("mfspr %0,1009" : "=r" (result)); + return result; +} + +void __sethid0(unsigned value) +{ + __asm__ volatile ("mtspr 1008,%0" : : "r" (value)); +} + +unsigned __getpvr(void) +{ + int result; + __asm__("mfspr %0, 287" : "=r" (result)); + return result; +} + +void __setmsr(unsigned value) +{ + __asm__ volatile ("mtmsr %0; sync" :: "r" (value)); +} + +void __set1015(unsigned value) +{ + __asm__ volatile ("mtspr 1015,%0" : : "r" (value)); +} + +extern void _init_float_registers(const double *); +/*RODATA static const double dummy_float = 1.0;*/ +static const double dummy_float = 1.0; + +#define HID0_DCACHE HID0_DCE +#define MSR_DATA MSR_DR + +void ppc_setup_cpu(int icache) +{ + int type = __getpvr() >> 16; + int version = __getpvr() & 0xffff; + + if (type == 0xc) + { + if (version == 0x0200) + __set1015(0x19000004); + else if (((version & 0xff00) == 0x0200) && + (version != 0x0209)) + __set1015(0x01000000); + } + if (icache) + { + __sethid0(HID0_NHR | HID0_BHT | HID0_ICE | HID0_ICFI | HID0_BTIC + | HID0_DCACHE); + __sethid0(HID0_DPM | HID0_NHR | HID0_BHT | HID0_ICE | HID0_BTIC + | HID0_DCACHE); + } + else + { + __sethid0(HID0_DPM | HID0_NHR | HID0_BHT | HID0_BTIC | HID0_DCACHE); + } +#if 1 + /* if (type == 8 || type == 12) */ + { + __setmsr(MSR_FP | MSR_DATA); + _init_float_registers(&dummy_float); + } +#endif +} + +void ppc_enable_dcache(void) +{ + /* + * Already enabled in crt0.S + */ +#if 0 + unsigned hid0 = __gethid0(); + __sethid0(hid0 | HID0_DCFI | HID0_DCE); + __sethid0(hid0 | HID0_DCE); +#endif +} + +void ppc_disable_dcache(void) +{ + unsigned hid0 = __gethid0(); + __sethid0(hid0 & ~HID0_DCE); +} + +void ppc_enable_mmu(void) +{ + unsigned msr = __getmsr(); + __setmsr(msr | MSR_DR | MSR_IR); +} + +void make_coherent(void *base, unsigned length) +{ + unsigned hid0 = __gethid0(); + + if (hid0 & HID0_DCE) + { + unsigned i; + unsigned offset = 0x1f & (unsigned) base; + unsigned adjusted_base = (unsigned) base & ~0x1f; + for(i = 0; i < length + offset; i+= 32) + __asm__ volatile ("dcbf %1,%0" : : "r" (adjusted_base), "r" (i)); + if (hid0 & HID0_ICE) + for(i = 0; i < length + offset; i+= 32) + __asm__ volatile ("icbi %1,%0" : : "r" (adjusted_base), "r" (i)); + } +} diff --git a/src/arch/ppc/lib/timebase.S b/src/arch/ppc/lib/timebase.S new file mode 100644 index 0000000000..48171db498 --- /dev/null +++ b/src/arch/ppc/lib/timebase.S @@ -0,0 +1,13 @@ +/* $Id$ */ +/* Copyright 1999-2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + + .text + .globl _timebase +_timebase: + mftbu 3 + mftb 4 + mftbu 5 + cmpw 3, 5 + bne _timebase + blr diff --git a/src/arch/ppc/lib/timer.c b/src/arch/ppc/lib/timer.c new file mode 100644 index 0000000000..8f968f1a5f --- /dev/null +++ b/src/arch/ppc/lib/timer.c @@ -0,0 +1,31 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#include <timer.h> +#include <bsp.h> + +unsigned get_hz(void) +{ + return bsp_clock_speed(); +} + +unsigned ticks_since_boot(void) +{ + extern unsigned long long _timebase(void); + return (unsigned) (_timebase()); +} + +void sleep_ticks(unsigned ticks) +{ + unsigned then = ticks + ticks_since_boot(); + while(ticks_since_boot() < then) + ; +} + +void udelay(int usecs) +{ + unsigned ticksperusec = get_hz() / 1000000; + + sleep_ticks(ticksperusec * usecs); +} diff --git a/src/include/pc80/ide.h b/src/include/pc80/ide.h new file mode 100644 index 0000000000..dacaa292f5 --- /dev/null +++ b/src/include/pc80/ide.h @@ -0,0 +1,210 @@ +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +typedef uint64_t sector_t; + +struct controller { + uint16_t cmd_base; + uint16_t ctrl_base; +}; + +struct harddisk_info { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; /* am i lba (0x40) or chs (0x00) */ +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 + int drive_exists; + int slave_absent; + int basedrive; +}; + + +#define IDE_SECTOR_SIZE 0x200 + +#define IDE_BASE0 (0x1F0u) /* primary controller */ +#define IDE_BASE1 (0x170u) /* secondary */ +#define IDE_BASE2 (0x0F0u) /* third */ +#define IDE_BASE3 (0x070u) /* fourth */ + +#define IDE_REG_EXTENDED_OFFSET (0x204u) + +#define IDE_REG_DATA(ctrl) ((ctrl)->cmd_base + 0u) /* word register */ +#define IDE_REG_ERROR(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_PRECOMP(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_FEATURE(ctrl) ((ctrl)->cmd_base + 1u) +#define IDE_REG_SECTOR_COUNT(ctrl) ((ctrl)->cmd_base + 2u) +#define IDE_REG_SECTOR_NUMBER(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_LBA_LOW(ctrl) ((ctrl)->cmd_base + 3u) +#define IDE_REG_CYLINDER_LSB(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_LBA_MID(ctrl) ((ctrl)->cmd_base + 4u) +#define IDE_REG_CYLINDER_MSB(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_LBA_HIGH(ctrl) ((ctrl)->cmd_base + 5u) +#define IDE_REG_DRIVEHEAD(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_DEVICE(ctrl) ((ctrl)->cmd_base + 6u) +#define IDE_REG_STATUS(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_COMMAND(ctrl) ((ctrl)->cmd_base + 7u) +#define IDE_REG_ALTSTATUS(ctrl) ((ctrl)->ctrl_base + 2u) +#define IDE_REG_DEVICE_CONTROL(ctrl) ((ctrl)->ctrl_base + 2u) + +struct ide_pio_command +{ + uint8_t feature; + uint8_t sector_count; + uint8_t lba_low; + uint8_t lba_mid; + uint8_t lba_high; + uint8_t device; +# define IDE_DH_DEFAULT (0xA0) +# define IDE_DH_HEAD(x) ((x) & 0x0F) +# define IDE_DH_MASTER (0x00) +# define IDE_DH_SLAVE (0x10) +# define IDE_DH_LBA (0x40) +# define IDE_DH_CHS (0x00) + uint8_t command; + uint8_t sector_count2; + uint8_t lba_low2; + uint8_t lba_mid2; + uint8_t lba_high2; +}; + +#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT } + +#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */ +#define IDE_ERR_BBK 0x80 /* ATA bad block */ +#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */ +#define IDE_ERR_MC 0x20 /* ATA media change */ +#define IDE_ERR_IDNF 0x10 /* ATA id not found */ +#define IDE_ERR_MCR 0x08 /* ATA media change request */ +#define IDE_ERR_ABRT 0x04 /* ATA command aborted */ +#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */ +#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */ + +#define IDE_STATUS_BSY 0x80 /* busy */ +#define IDE_STATUS_RDY 0x40 /* ready */ +#define IDE_STATUS_DF 0x20 /* device fault */ +#define IDE_STATUS_WFT 0x20 /* write fault (old name) */ +#define IDE_STATUS_SKC 0x10 /* seek complete */ +#define IDE_STATUS_DRQ 0x08 /* data request */ +#define IDE_STATUS_CORR 0x04 /* corrected */ +#define IDE_STATUS_IDX 0x02 /* index */ +#define IDE_STATUS_ERR 0x01 /* error (ATA) */ +#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */ + +#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */ +#define IDE_CTRL_SRST 0x04 /* soft reset */ +#define IDE_CTRL_NIEN 0x02 /* disable interrupts */ + + +/* Most mandtory and optional ATA commands (from ATA-3), */ + +#define IDE_CMD_CFA_ERASE_SECTORS 0xC0 +#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define IDE_CMD_CHECK_POWER_MODE1 0xE5 +#define IDE_CMD_CHECK_POWER_MODE2 0x98 +#define IDE_CMD_DEVICE_RESET 0x08 +#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define IDE_CMD_FLUSH_CACHE 0xE7 +#define IDE_CMD_FORMAT_TRACK 0x50 +#define IDE_CMD_IDENTIFY_DEVICE 0xEC +#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define IDE_CMD_IDLE1 0xE3 +#define IDE_CMD_IDLE2 0x97 +#define IDE_CMD_IDLE_IMMEDIATE1 0xE1 +#define IDE_CMD_IDLE_IMMEDIATE2 0x95 +#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define IDE_CMD_NOP 0x00 +#define IDE_CMD_PACKET 0xA0 +#define IDE_CMD_READ_BUFFER 0xE4 +#define IDE_CMD_READ_DMA 0xC8 +#define IDE_CMD_READ_DMA_QUEUED 0xC7 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_READ_SECTORS 0x20 +#define IDE_CMD_READ_SECTORS_EXT 0x24 +#define IDE_CMD_READ_VERIFY_SECTORS 0x40 +#define IDE_CMD_RECALIBRATE 0x10 +#define IDE_CMD_SEEK 0x70 +#define IDE_CMD_SET_FEATURES 0xEF +#define IDE_CMD_SET_MAX_ADDR_EXT 0x24 +#define IDE_CMD_SET_MULTIPLE_MODE 0xC6 +#define IDE_CMD_SLEEP1 0xE6 +#define IDE_CMD_SLEEP2 0x99 +#define IDE_CMD_STANDBY1 0xE2 +#define IDE_CMD_STANDBY2 0x96 +#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0 +#define IDE_CMD_STANDBY_IMMEDIATE2 0x94 +#define IDE_CMD_WRITE_BUFFER 0xE8 +#define IDE_CMD_WRITE_DMA 0xCA +#define IDE_CMD_WRITE_DMA_QUEUED 0xCC +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_WRITE_SECTORS 0x30 +#define IDE_CMD_WRITE_VERIFY 0x3C + +/* IDE_CMD_SET_FEATURE sub commands */ +#define IDE_FEATURE_CFA_ENABLE_8BIT_PIO 0x01 +#define IDE_FEATURE_ENABLE_WRITE_CACHE 0x02 +#define IDE_FEATURE_SET_TRANSFER_MODE 0x03 +#define IDE_FEATURE_ENABLE_POWER_MANAGEMENT 0x05 +#define IDE_FEATURE_ENABLE_POWERUP_IN_STANDBY 0x06 +#define IDE_FEATURE_STANDBY_SPINUP_DRIVE 0x07 +#define IDE_FEATURE_CFA_ENABLE_POWER_MODE1 0x0A +#define IDE_FEATURE_DISABLE_MEDIA_STATUS_NOTIFICATION 0x31 +#define IDE_FEATURE_ENABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0x42 +#define IDE_FEATURE_SET_MAXIMUM_HOST_INTERFACE_SECTOR_TIMES 0x43 +#define IDE_FEATURE_DISABLE_READ_LOOKAHEAD 0x55 +#define IDE_FEATURE_ENABLE_RELEASE_INTERRUPT 0x5D +#define IDE_FEATURE_ENABLE_SERVICE_INTERRUPT 0x5E +#define IDE_FEATURE_DISABLE_REVERTING_TO_POWERON_DEFAULTS 0x66 +#define IDE_FEATURE_CFA_DISABLE_8BIT_PIO 0x81 +#define IDE_FEATURE_DISABLE_WRITE_CACHE 0x82 +#define IDE_FEATURE_DISABLE_POWER_MANAGEMENT 0x85 +#define IDE_FEATURE_DISABLE_POWERUP_IN_STANDBY 0x86 +#define IDE_FEATURE_CFA_DISABLE_POWER_MODE1 0x8A +#define IDE_FEATURE_ENABLE_MEDIA_STATUS_NOTIFICATION 0x95 +#define IDE_FEATURE_ENABLE_READ_LOOKAHEAD 0xAA +#define IDE_FEATURE_DISABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0xC2 +#define IDE_FEATURE_ENABLE_REVERTING_TO_POWERON_DEFAULTS 0xCC +#define IDE_FEATURE_DISABLE_SERVICE_INTERRUPT 0xDE + +#define NUM_HD (4) +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 + +/* Maximum block_size that may be set. */ +#define DISK_BUFFER_SIZE (18 * SECTOR_SIZE) + +extern struct harddisk_info harddisk_info[NUM_HD]; + +extern int ide_init(void); +extern int ide_read_sector(int driveno, void * buf, unsigned int sector, + int byte_offset, int n_bytes); diff --git a/src/mainboard/motorola/sandpoint/STATUS b/src/mainboard/motorola/sandpoint/STATUS new file mode 100644 index 0000000000..1f528e38da --- /dev/null +++ b/src/mainboard/motorola/sandpoint/STATUS @@ -0,0 +1,27 @@ +# These are keyword-value pairs. +# a : separates the keyword from the value +# the value is arbitrary text delimited by newline. +# continuation, if needed, will be via the \ at the end of a line +# comments are indicated by a '#' as the first character. +# the keywords are case-INSENSITIVE +owner: Greg Watson +email: gwatson@lanl.gov +#status: One of unsupported, unstable, stable +status: unstable +explanation: currently under development +flash-types: +payload-types: +# e.g. linux, plan 9, wince, etc. +OS-types: linux +# e.g. "Plan 9 interrupts don't work on this chipset" +OS-issues: +console-types: serial +# vga is unsupported, unstable, or stable +vga: unsupported +# Last-known-good follows the internationl date standard: day/month/year +last-known-good: 19/04/2003 +Comments: +Links: +Mainboard-revision: +# What other mainboards are like this one? List them here. +AKA: diff --git a/src/mainboard/motorola/sandpoint/flash.h b/src/mainboard/motorola/sandpoint/flash.h new file mode 100644 index 0000000000..5ef1c6e19a --- /dev/null +++ b/src/mainboard/motorola/sandpoint/flash.h @@ -0,0 +1,36 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#ifndef _FLASH_H +#define _FLASH_H + +struct flash_device; + +typedef struct flash_fn +{ + const char *(* identify)(struct flash_device *flash); + void *(* ptr)(void *data); + int (* erase_all)(void *data); + int (* erase)(void *data, unsigned offset, unsigned length); + int (* program)(void *data, unsigned offset, const void *source, unsigned length); + u8 ( *read_byte)(void *data, unsigned offset); +} flash_fn; + +typedef struct flash_device +{ + const flash_fn *fn; + char *tag; + void *data; + unsigned long base; + unsigned size; + unsigned erase_size; + unsigned store_size; + struct flash_device *next; +} flash_device; + +int register_flash_device(const flash_fn *fn, char *tag, void *data); +flash_device *find_flash_device(const char *tag); +int init_flash_amd800(char *tag, unsigned base, unsigned spacing); + +#endif diff --git a/src/mainboard/motorola/sandpoint/nvram.h b/src/mainboard/motorola/sandpoint/nvram.h new file mode 100644 index 0000000000..67b99f57f6 --- /dev/null +++ b/src/mainboard/motorola/sandpoint/nvram.h @@ -0,0 +1,37 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ +/* Definitions for nvram devices - these are flash or eeprom devices used to + store information across power cycles and resets. Though they are byte + addressable, writes must be committed to allow flash devices to write + complete sectors. */ + +#ifndef _NVRAM_H +#define _NVRAM_H + +typedef struct nvram_device +{ + unsigned (*size)(struct nvram_device *data); + int (*read_block)(struct nvram_device *dev, unsigned offset, + unsigned char *data, unsigned length); + int (*write_byte)(struct nvram_device *dev, unsigned offset, unsigned char byte); + void (*commit)(struct nvram_device *data); + void *data; +} nvram_device; + +int nvram_init (nvram_device *dev); +void nvram_clear(void); + +extern nvram_device pcrtc_nvram; +extern void nvram_putenv(const char *name, const char *value); +extern int nvram_getenv(const char *name, char *buffer, unsigned size); + +typedef const struct nvram_constant +{ + const char *name; + const char *value; +} nvram_constant; + +extern nvram_constant hardcoded_environment[]; + +#endif diff --git a/src/northbridge/motorola/mpc107/epic.c b/src/northbridge/motorola/mpc107/epic.c new file mode 100644 index 0000000000..5db7c107ce --- /dev/null +++ b/src/northbridge/motorola/mpc107/epic.c @@ -0,0 +1,517 @@ +/************************************************** + * + * copyright @ motorola, 1999 + * + *************************************************/ +#include <pci.h> +#include <printk.h> +#include <northbridge/motorola/mpc107/epic.h> + +extern struct pci_ops pci_direct_ppc; + +typedef void (*VOIDFUNCPTR) (void); /* ptr to function returning void */ +struct SrcVecTable SrcVecTable[MAXVEC] = /* Addr/Vector cross-reference tbl */ + { + { EPIC_EX_INT0_VEC_REG, "External Direct/Serial Source 0"}, + { EPIC_EX_INT1_VEC_REG, "External Direct/Serial Source 1"}, + { EPIC_EX_INT2_VEC_REG, "External Direct/Serial Source 2"}, + { EPIC_EX_INT3_VEC_REG, "External Direct/Serial Source 3"}, + { EPIC_EX_INT4_VEC_REG, "External Direct/Serial Source 4"}, + + { EPIC_SR_INT5_VEC_REG, "External Serial Source 5"}, + { EPIC_SR_INT6_VEC_REG, "External Serial Source 6"}, + { EPIC_SR_INT7_VEC_REG, "External Serial Source 7"}, + { EPIC_SR_INT8_VEC_REG, "External Serial Source 8"}, + { EPIC_SR_INT9_VEC_REG, "External Serial Source 9"}, + { EPIC_SR_INT10_VEC_REG, "External Serial Source 10"}, + { EPIC_SR_INT11_VEC_REG, "External Serial Source 11"}, + { EPIC_SR_INT12_VEC_REG, "External Serial Source 12"}, + { EPIC_SR_INT13_VEC_REG, "External Serial Source 13"}, + { EPIC_SR_INT14_VEC_REG, "External Serial Source 14"}, + { EPIC_SR_INT15_VEC_REG, "External Serial Source 15"}, + + { EPIC_I2C_INT_VEC_REG, "Internal I2C Source"}, + { EPIC_DMA0_INT_VEC_REG, "Internal DMA0 Source"}, + { EPIC_DMA1_INT_VEC_REG, "Internal DMA1 Source"}, + { EPIC_MSG_INT_VEC_REG, "Internal Message Source"}, + }; + +VOIDFUNCPTR intVecTbl[MAXVEC]; /* Interrupt vector table */ + + +/**************************************************************************** +* epicInit - Initialize the EPIC registers +* +* This routine resets the Global Configuration Register, thus it: +* - Disables all interrupts +* - Sets epic registers to reset values +* - Sets the value of the Processor Current Task Priority to the +* highest priority (0xF). +* epicInit then sets the EPIC operation mode to Mixed Mode (vs. Pass +* Through or 8259 compatible mode). +* +* If IRQType (input) is Direct IRQs: +* - IRQType is written to the SIE bit of the EPIC Interrupt +* Configuration register (ICR). +* - clkRatio is ignored. +* If IRQType is Serial IRQs: +* - both IRQType and clkRatio will be written to the ICR register +*/ + +void epicInit + ( + unsigned int IRQType, /* Direct or Serial */ + unsigned int clkRatio /* Clk Ratio for Serial IRQs */ + ) + { + ULONG tmp; + + tmp = sysEUMBBARRead(EPIC_GLOBAL_REG); + tmp |= 0xa0000000; /* Set the Global Conf. register */ + sysEUMBBARWrite(EPIC_GLOBAL_REG, tmp); + /* + * Wait for EPIC to reset - CLH + */ + while( (sysEUMBBARRead(EPIC_GLOBAL_REG) & 0x80000000) == 1); + sysEUMBBARWrite(EPIC_GLOBAL_REG, 0x20000000); + tmp = sysEUMBBARRead(EPIC_INT_CONF_REG); /* Read interrupt conf. reg */ + + if (IRQType == EPIC_DIRECT_IRQ) /* direct mode */ + sysEUMBBARWrite(EPIC_INT_CONF_REG, tmp & 0xf7ffffff); + else /* Serial mode */ + { + tmp = (clkRatio << 28) | 0x08000000; /* Set clock ratio */ + sysEUMBBARWrite(EPIC_INT_CONF_REG, tmp); + } + + while (epicIntAck() != 0xff) /* Clear all pending interrupts */ + epicEOI(); +} + +/**************************************************************************** + * epicIntEnable - Enable an interrupt source + * + * This routine clears the mask bit of an external, an internal or + * a Timer register to enable the interrupt. + * + * RETURNS: None + */ +void epicIntEnable(int intVec) +{ + ULONG tmp; + ULONG srAddr; + + srAddr = SrcVecTable[intVec].srcAddr; /* Retrieve src Vec/Prio register */ + tmp = sysEUMBBARRead(srAddr); + tmp &= ~EPIC_VEC_PRI_MASK; /* Clear the mask bit */ + tmp |= (EPIC_VEC_PRI_DFLT_PRI << 16); /* Set priority to Default - CLH */ + tmp |= intVec; /* Set Vector number */ + sysEUMBBARWrite(srAddr, tmp); + + return; + } + +/**************************************************************************** + * epicIntDisable - Disable an interrupt source + * + * This routine sets the mask bit of an external, an internal or + * a Timer register to disable the interrupt. + * + * RETURNS: OK or ERROR + * + */ + +void epicIntDisable + ( + int intVec /* Interrupt vector number */ + ) + { + + ULONG tmp, srAddr; + + srAddr = SrcVecTable[intVec].srcAddr; + tmp = sysEUMBBARRead(srAddr); + tmp |= 0x80000000; /* Set the mask bit */ + sysEUMBBARWrite(srAddr, tmp); + return; + } + +/**************************************************************************** + * epicIntSourceConfig - Set properties of an interrupt source + * + * This function sets interrupt properites (Polarity, Sense, Interrupt + * Prority, and Interrupt Vector) of an Interrupt Source. The properties + * can be set when the current source is not in-request or in-service, + * which is determined by the Activity bit. This routine return ERROR + * if the the Activity bit is 1 (in-request or in-service). + * + * This function assumes that the Source Vector/Priority register (input) + * is a valid address. + * + * RETURNS: OK or ERROR + */ + +int epicIntSourceConfig + ( + int Vect, /* interrupt source vector number */ + int Polarity, /* interrupt source polarity */ + int Sense, /* interrupt source Sense */ + int Prio /* interrupt source priority */ + ) + + { + ULONG tmp, newVal; + ULONG actBit, srAddr; + + srAddr = SrcVecTable[Vect].srcAddr; + tmp = sysEUMBBARRead(srAddr); + actBit = (tmp & 40000000) >> 30; /* retrieve activity bit - bit 30 */ + if (actBit == 1) + return ERROR; + + tmp &= 0xff30ff00; /* Erase previously set P,S,Prio,Vector bits */ + newVal = (Polarity << 23) | (Sense << 22) | (Prio << 16) | Vect; + sysEUMBBARWrite(srAddr, tmp | newVal ); + return (OK); + } + +/**************************************************************************** + * epicIntAck - acknowledge an interrupt + * + * This function reads the Interrupt acknowldge register and return + * the vector number of the highest pending interrupt. + * + * RETURNS: Interrupt Vector number. + */ + +unsigned int epicIntAck(void) +{ + return(sysEUMBBARRead( EPIC_PROC_INT_ACK_REG )); +} + +/**************************************************************************** + * epicEOI - signal an end of interrupt + * + * This function writes 0x0 to the EOI register to signal end of interrupt. + * It is usually called after an interrupt routine is served. + * + * RETURNS: None + */ + +void epicEOI(void) + { + sysEUMBBARWrite(EPIC_PROC_EOI_REG, 0x0); + } + +/**************************************************************************** + * epicCurTaskPrioSet - sets the priority of the Processor Current Task + * + * This function should be called after epicInit() to lower the priority + * of the processor current task. + * + * RETURNS: OK or ERROR + */ + +int epicCurTaskPrioSet + ( + int prioNum /* New priority value */ + ) + { + + if ( (prioNum < 0) || (prioNum > 0xF)) + return ERROR; + sysEUMBBARWrite(EPIC_PROC_CTASK_PRI_REG, prioNum); + return OK; + } + + +/************************************************************************ + * function: epicIntTaskGet + * + * description: Get value of processor current interrupt task priority register + * + * note: + ***********************************************************************/ +unsigned char epicIntTaskGet() +{ + /* get the interrupt task priority register */ + ULONG reg; + unsigned char rec; + + reg = sysEUMBBARRead( EPIC_PROC_CTASK_PRI_REG ); + rec = ( reg & 0x0F ); + return rec; +} + + +/************************************************************** + * function: epicISR + * + * description: EPIC service routine called by the core exception + * at 0x500 + * + * note: + **************************************************************/ +unsigned int epicISR(void) +{ + return 0; +} + + +/************************************************************ + * function: epicModeGet + * + * description: query EPIC mode, return 0 if pass through mode + * return 1 if mixed mode + * + * note: + *************************************************************/ +unsigned int epicModeGet(void) +{ + ULONG val; + + val = sysEUMBBARRead( EPIC_GLOBAL_REG ); + return (( val & 0x20000000 ) >> 29); +} + + +/********************************************* + * function: epicConfigGet + * + * description: Get the EPIC interrupt Configuration + * return 0 if not error, otherwise return 1 + * + * note: + ********************************************/ +void epicConfigGet( unsigned int *clkRatio, unsigned int *serEnable) +{ + ULONG val; + + val = sysEUMBBARRead( EPIC_INT_CONF_REG ); + *clkRatio = ( val & 0x70000000 ) >> 28; + *serEnable = ( val & 0x8000000 ) >> 27; +} + + +/******************************************************************* + * sysEUMBBARRead - Read a 32-bit EUMBBAR register + * + * This routine reads the content of a register in the Embedded + * Utilities Memory Block, and swaps to big endian before returning + * the value. + * + * RETURNS: The content of the specified EUMBBAR register. + */ + +ULONG sysEUMBBARRead + ( + ULONG regNum + ) + { + u32 temp; + + pci_direct_ppc.read_dword(0, 0, regNum, &temp); + return ( temp ); + } + +/******************************************************************* + * sysEUMBBARWrite - Write a 32-bit EUMBBAR register + * + * This routine swaps the value to little endian then writes it to + * a register in the Embedded Utilities Memory Block address space. + * + * RETURNS: N/A + */ + +void sysEUMBBARWrite + ( + ULONG regNum, /* EUMBBAR register address */ + u32 regVal /* Value to be written */ + ) + { + + pci_direct_ppc.read_dword(0, 0, regNum, ®Val); + return ; + } + + +/******************************************************** + * function: epicVendorId + * + * description: return the EPIC Vendor Identification + * register: + * + * siliccon version, device id, and vendor id + * + * note: + ********************************************************/ +void epicVendorId + ( + unsigned int *step, + unsigned int *devId, + unsigned int *venId + ) + { + ULONG val; + val = sysEUMBBARRead( EPIC_VENDOR_ID_REG ); + *step = ( val & 0x00FF0000 ) >> 16; + *devId = ( val & 0x0000FF00 ) >> 8; + *venId = ( val & 0x000000FF ); + } + +/************************************************** + * function: epicFeatures + * + * description: return the number of IRQ supported, + * number of CPU, and the version of the + * OpenEPIC + * + * note: + *************************************************/ +void epicFeatures + ( + unsigned int *noIRQs, + unsigned int *noCPUs, + unsigned int *verId + ) + { + ULONG val; + + val = sysEUMBBARRead( EPIC_FEATURES_REG ); + *noIRQs = ( val & 0x07FF0000 ) >> 16; + *noCPUs = ( val & 0x00001F00 ) >> 8; + *verId = ( val & 0x000000FF ); +} + + +/********************************************************* + * function: epciTmFrequncySet + * + * description: Set the timer frequency reporting register + ********************************************************/ +void epicTmFrequencySet( unsigned int frq ) +{ + sysEUMBBARWrite(EPIC_TM_FREQ_REG, frq); +} + +/******************************************************* + * function: epicTmFrequncyGet + * + * description: Get the current value of the Timer Frequency + * Reporting register + * + ******************************************************/ +unsigned int epicTmFrequencyGet(void) +{ + return( sysEUMBBARRead(EPIC_TM_FREQ_REG)) ; +} + + +/**************************************************** + * function: epicTmBaseSet + * + * description: Set the #n global timer base count register + * return 0 if no error, otherwise return 1. + * + * note: + ****************************************************/ +unsigned int epicTmBaseSet + ( + ULONG srcAddr, /* Address of the Timer Base register */ + unsigned int cnt, /* Base count */ + unsigned int inhibit /* 1 - count inhibit */ + ) +{ + + unsigned int val = 0x80000000; + /* First inhibit counting the timer */ + sysEUMBBARWrite(srcAddr, val) ; + + /* set the new value */ + val = (cnt & 0x7fffffff) | ((inhibit & 0x1) << 31); + sysEUMBBARWrite(srcAddr, val) ; + return 0; +} + +/*********************************************************************** + * function: epicTmBaseGet + * + * description: Get the current value of the global timer base count register + * return 0 if no error, otherwise return 1. + * + * note: + ***********************************************************************/ +unsigned int epicTmBaseGet( ULONG srcAddr, unsigned int *val ) +{ + *val = sysEUMBBARRead( srcAddr ); + *val = *val & 0x7fffffff; + return 0; +} + +/*********************************************************** + * function: epicTmCountGet + * + * description: Get the value of a given global timer + * current count register + * return 0 if no error, otherwise return 1 + * note: + **********************************************************/ +unsigned int epicTmCountGet( ULONG srcAddr, unsigned int *val ) +{ + *val = sysEUMBBARRead( srcAddr ); + *val = *val & 0x7fffffff; + return 0; +} + + + +/*********************************************************** + * function: epicTmInhibit + * + * description: Stop counting of a given global timer + * return 0 if no error, otherwise return 1 + * + * note: + ***********************************************************/ +unsigned int epicTmInhibit( unsigned int srcAddr ) +{ + ULONG val; + + val = sysEUMBBARRead( srcAddr ); + val |= 0x80000000; + sysEUMBBARWrite( srcAddr, val ); + return 0; +} + +/****************************************************************** + * function: epicTmEnable + * + * description: Enable counting of a given global timer + * return 0 if no error, otherwise return 1 + * + * note: + *****************************************************************/ +unsigned int epicTmEnable( ULONG srcAddr ) +{ + ULONG val; + + val = sysEUMBBARRead( srcAddr ); + val &= 0x7fffffff; + sysEUMBBARWrite( srcAddr, val ); + return 0; +} + +void epicSourcePrint(int Vect) + { + ULONG srcVal; + + srcVal = sysEUMBBARRead(SrcVecTable[Vect].srcAddr); + printk_info("%s\n", SrcVecTable[Vect].srcName); + printk_info("Address = 0x%lx\n", SrcVecTable[Vect].srcAddr); + printk_info("Vector = %ld\n", (srcVal & 0x000000FF) ); + printk_info("Mask = %ld\n", srcVal >> 31); + printk_info("Activitiy = %ld\n", (srcVal & 40000000) >> 30); + printk_info("Polarity = %ld\n", (srcVal & 0x00800000) >> 23); + printk_info("Sense = %ld\n", (srcVal & 0x00400000) >> 22); + printk_info("Priority = %ld\n", (srcVal & 0x000F0000) >> 16); + } diff --git a/src/northbridge/motorola/mpc107/i2c.c b/src/northbridge/motorola/mpc107/i2c.c new file mode 100644 index 0000000000..4ead02331b --- /dev/null +++ b/src/northbridge/motorola/mpc107/i2c.c @@ -0,0 +1,99 @@ +/* $Id$ + * (C) Copyright 2002 + * Humboldt Solutions Ltd, <adrian@humboldt.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <types.h> +#include <stdlib.h> +#include <string.h> +#include "i2c.h" + +static i2c_bus *first_i2c = NULL; + +#if 0 +int register_i2c_bus(const i2c_fn *fn, char *tag, void *data) +{ + i2c_bus *bus = malloc (sizeof (i2c_bus)); + + if (bus) + { + bus->fn = fn; + bus->tag = tag; + bus->data = data; + bus->next = first_i2c; + first_i2c = bus; + return 0; + } + return -1; +} +#endif + +i2c_bus *find_i2c_bus(const char *name) +{ + int len; + + if (! name) + return first_i2c; + + if (first_i2c) + { + i2c_bus *i2c; + + len = strlen(name); + + for (i2c = first_i2c; i2c; i2c = i2c->next) + if (strlen(i2c->tag) == len && memcmp (name, i2c->tag, len) == 0) + return i2c; + } + return NULL; +} + +void i2c_start(struct i2c_bus *bus) +{ + if (! bus) + bus = first_i2c; + + bus->fn->start(bus); +} + +void i2c_stop(struct i2c_bus *bus) +{ + if (! bus) + bus = first_i2c; + + bus->fn->stop(bus); +} + +int i2c_master_write(struct i2c_bus *bus, int target, int address, + const u8 *data, int length) +{ + if (! bus) + bus = first_i2c; + + return bus->fn->master_write(bus, target, address, data, length); +} + +int i2c_master_read(struct i2c_bus *bus, int target, int address, + u8 *data, int length) +{ + if (! bus) + bus = first_i2c; + + return bus->fn->master_read(bus, target, address, data, length); +} + diff --git a/src/northbridge/motorola/mpc107/i2c.h b/src/northbridge/motorola/mpc107/i2c.h new file mode 100644 index 0000000000..868c94a082 --- /dev/null +++ b/src/northbridge/motorola/mpc107/i2c.h @@ -0,0 +1,57 @@ +/* $Id$ + * (C) Copyright 2002 + * Humboldt Solutions Ltd, <adrian@humboldt.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _I2C_H +#define _I2C_H + +struct i2c_bus; + +typedef struct i2c_fn +{ + void (* start)(struct i2c_bus *bus); + void (* stop)(struct i2c_bus *bus); + int (* master_write)(struct i2c_bus *bus, int target, int address, + const u8 *data, int length); + int (* master_read)(struct i2c_bus *bus, int target, int address, + u8 *data, int length); +} i2c_fn; + +typedef struct i2c_bus +{ + const i2c_fn *fn; + char *tag; + void *data; + struct i2c_bus *next; +} i2c_bus; + +i2c_bus *find_i2c_bus(const char *name); +int register_i2c_bus(const i2c_fn *fn, char *tag, void *data); + +void i2c_start(struct i2c_bus *bus); +void i2c_stop(struct i2c_bus *bus); +int i2c_master_write(struct i2c_bus *bus, int target, int address, + const u8 *data, int length); +int i2c_master_read(struct i2c_bus *bus, int target, int address, + u8 *data, int length); +void init_i2c_nvram(const char *i2c_tag); + +extern i2c_fn mpc107_i2c_fn; + +#endif diff --git a/src/northbridge/motorola/mpc107/meminfo.c b/src/northbridge/motorola/mpc107/meminfo.c new file mode 100644 index 0000000000..7245fc419d --- /dev/null +++ b/src/northbridge/motorola/mpc107/meminfo.c @@ -0,0 +1,202 @@ +/* + * (C) Copyright 2001 + * Humboldt Solutions Ltd, adrian@humboldt.co.uk. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <printk.h> +#include "mpc107.h" + +void +sdram_dimm_to_bank_info(const char *data, sdram_dimm_info *dimm, int verbose) +{ + sdram_bank_info *bank1 = dimm->bank1; + sdram_bank_info *bank2 = dimm->bank2; + unsigned char csum = 0; + unsigned char x; + int i; + int no_cas_latencies = 0; + char latency[3]; + + /* Mark banks initially broken */ + bank1->size = 0; + bank2->size = 0; + + if (data[0] < 64) + { + if (verbose) + printk_info("SPD data too short\n"); + return; + } + + for(i = 0; i < 63; i++) + csum += data[i]; + + if (csum != data[63]) + { + if (verbose) + printk_info("Broken checksum\n"); + return; + } + + if (data[2] != 0x04) + { + if (verbose) + printk_info("SDRAM Only\n"); + return; + } + + bank1->row_bits = data[3] & 0x0f; + if (data[3] >> 4) + bank2->row_bits = data[3] >> 4; + else + bank2->row_bits = bank1->row_bits; + + bank1->internal_banks = bank2->internal_banks = data[17]; + + bank1->col_bits = data[4] & 0x0f; + if (data[4] >> 4) + bank2->col_bits = data[4] >> 4; + else + bank2->col_bits = bank1->col_bits; + + if (data[7] || (data[6] != 80 && data[6] != 72 && data[6] != 64)) + { + if (verbose) + printk_info("Data width incorrect\n"); + return; + } + + if (data[8] != 0x01) + { + if (verbose) + printk_info("3.3V TTL DIMMS only\n"); + return; + } + + /* Extract CAS latencies in reverse order, as we only get info on + the highest ones. */ + x = data[18]; + for(i = 7; i > 0; i--) + { + if (x & 0x40) + { + if (no_cas_latencies < 3) + latency[no_cas_latencies] = i; + no_cas_latencies++; + } + x <<= 1; + } + + /* Now fill in other timings - we're most interested in the lowest + CAS latency, so we shuffle data to put that first. */ + for(i = no_cas_latencies; i >= 0; i--) + bank1->cas_latency[no_cas_latencies - i - 1] = + bank2->cas_latency[no_cas_latencies - i - 1] = + latency[i]; + for(i = no_cas_latencies; i < 3; i++) + bank1->cas_latency[i] = bank2->cas_latency[i] = 0; + + /* Store values for the highest cas latency */ + bank1->cycle_time[no_cas_latencies - 1] = + bank2->cycle_time[no_cas_latencies- 1] = + 100 * (data[9] >> 4) + 10 * (data[9] & 0xf); + bank1->access_time[no_cas_latencies - 1] = + bank2->access_time[no_cas_latencies - 1] = + 100 * (data[10] >> 4) + 10 * (data[10] & 0xf); + /* Then the second highest */ + if (no_cas_latencies > 1) + { + bank1->cycle_time[no_cas_latencies - 2] = + bank2->cycle_time[no_cas_latencies- 2] = + 100 * (data[23] >> 4) + 10 * (data[23] & 0xf); + bank1->access_time[no_cas_latencies - 2] = + bank2->access_time[no_cas_latencies - 2] = + 100 * (data[24] >> 4) + 10 * (data[24] & 0xf); + } + /* Then the third highest */ + if (no_cas_latencies > 2) + { + bank1->cycle_time[no_cas_latencies - 3] = + bank2->cycle_time[no_cas_latencies- 3] = + 100 * (data[25] >> 2) + 25 * (data[25] & 0x3); + bank1->access_time[no_cas_latencies - 3] = + bank2->access_time[no_cas_latencies - 3] = + 100 * (data[26] >> 2) + 25 * (data[26] & 0x3); + } + if (verbose) + for(i = 0; i < no_cas_latencies; i++) + printk_info("CL %d: cycle %dns access %dns\n", + bank1->cas_latency[i], bank1->cycle_time[i] / 100, + bank1->access_time[i] / 100); + + /* Other timings */ + bank1->min_back_to_back = bank2->min_back_to_back = data[15]; + bank1->min_row_precharge = bank2->min_row_precharge = data[27]; + bank1->min_active_to_active = bank2->min_active_to_active = data[28]; + bank1->min_ras_to_cas = bank2->min_ras_to_cas = data[29]; + bank1->min_ras = bank2->min_ras = data[30]; + + /* Error detection type */ + bank1->error_detect = bank2->error_detect = data[11]; + + /* Crucial row sizes - these mark the data as valid */ + for(i = 7; i >= 0; i--) + { + if (data[31] & (1 << i)) + { + bank1->size = (4*1024*1024) << i; + break; + } + } + if (data[5] > 1) + { + for(i-- ; i >= 0; i--) + { + if (data[31] & (1 << i)) + { + bank2->size = (4*1024*1024) << i; + break; + } + } + if (! bank2->size) + bank2->size = bank1->size; + } + dimm->size = bank1->size + bank2->size; +} + +void +print_sdram_bank_info(const sdram_bank_info *bank) +{ + printk_info("Bank %d: %dMB\n", bank->number, bank->size / (1024*1024)); +} + +static const char *error_types[] = {"", "Parity ", "ECC "}; + +void +print_sdram_dimm_info(const sdram_dimm_info *dimm) +{ + printk_info("Dimm %d: ", dimm->number); + if (dimm->size) + printk_info("%dMB CL%d (%s): Running at CL%d %s\n", + dimm->size / (1024*1024), dimm->bank1->cas_latency[0], + dimm->part_number, + dimm->bank1->actual_cas, + error_types[dimm->bank1->actual_detect]); + else + printk_info("(none)\n"); +} diff --git a/src/northbridge/motorola/mpc107/mpc107.c b/src/northbridge/motorola/mpc107/mpc107.c new file mode 100644 index 0000000000..76f368a9a5 --- /dev/null +++ b/src/northbridge/motorola/mpc107/mpc107.c @@ -0,0 +1,487 @@ +/* + * (C) Copyright 2001 + * Humboldt Solutions Ltd, adrian@humboldt.co.uk. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <bsp.h> +#include <ppc.h> +#include <pci.h> +#include <mem.h> +#include <types.h> +#include <string.h> +#include <printk.h> +#include <arch/io.h> +#include "i2c.h" +#include "mpc107.h" +#include <timer.h> + +#define NUM_DIMMS 1 +#define NUM_BANKS 2 + +extern struct pci_ops pci_direct_ppc; + +struct mem_range * +getmeminfo(void) +{ + int i; + sdram_dimm_info dimm[NUM_DIMMS]; + sdram_bank_info bank[NUM_BANKS]; + static struct mem_range meminfo; + + hostbridge_probe_dimms(NUM_DIMMS, dimm, bank); + + meminfo.basek = 0; + meminfo.sizek = 0; + + for (i = 0; i < NUM_BANKS; i++) { + meminfo.sizek += bank[i].size; + } + + meminfo.sizek >>= 10; + + return &meminfo; +} + +/* + * Memory is already turned on, but with pessimistic settings. Now + * we optimize settings to the actual memory configuration. + */ +unsigned +mpc107_config_memory(void) +{ + sdram_dimm_info sdram_dimms[NUM_DIMMS]; + sdram_bank_info sdram_banks[NUM_BANKS]; + + hostbridge_probe_dimms(NUM_DIMMS, sdram_dimms, sdram_banks); + return hostbridge_config_memory(NUM_BANKS, sdram_banks, 2); +} + +/* + * Configure memory settings. + */ +unsigned long +hostbridge_config_memory(int no_banks, sdram_bank_info * bank, int for_real) +{ + int i, j; + char ignore[8]; + /* Convert bus clock to cycle time in 100ns units */ + unsigned cycle_time = 10 * (2500000000U / bsp_clock_speed()); + /* Approximate */ + unsigned access_time = cycle_time - 300; + unsigned cas_latency = 0; + unsigned rdlat; + unsigned refint; + unsigned refrec; + unsigned acttorw, acttopre; + unsigned pretoact, bstopre; + enum sdram_error_detect error_detect; + u32 mccr1; + u32 mccr2; + u32 mccr3; + u32 mccr4; + u8 bank_enable; + u32 memstart1, memstart2; + u32 extmemstart1, extmemstart2; + u32 memend1, memend2; + u32 extmemend1, extmemend2; + u32 address; + + /* Set up the ignore mask */ + for(i = 0; i < no_banks; i++) + ignore[i] = (bank[i].size == 0); + + /* Pick best CAS latency possible */ + for (i = 0; i < no_banks; i++) + { + if (! ignore[i]) + { + for (j = 0; j < 3; j++) + { + if (cycle_time >= bank[i].cycle_time[j] && + access_time >= bank[i].access_time[j]) + { + cas_latency = bank[i].cas_latency[j]; + break; + } + } + } + } + if (!cas_latency) + return 0; + + /* For various parameters there is a risk of clashing between banks */ + error_detect = (for_real > 1) ? ERRORS_ECC : ERRORS_NONE; + for (i = 0; i < no_banks; i++) + { + if (! ignore[i]) + { + { + for (j = 0; j < 3; j++) + if (bank[i].cas_latency[j] == cas_latency) + break; + if (j == 3) + { + ignore[i] = 1; + if (! for_real) + printk_info("Disabling memory bank %d (cas latency)\n", i); + } + if (bank[i].error_detect < error_detect) + error_detect = bank[i].error_detect; + } + } + } + + /* Read in configuration of port X */ + pci_direct_ppc.read_dword(0, 0, 0xf0, &mccr1); + pci_direct_ppc.read_dword(0, 0, 0xf4, &mccr2); + pci_direct_ppc.read_dword(0, 0, 0xfc, &mccr4); + mccr1 &= 0xfff00000; + mccr2 &= 0xffe00000; + mccr3 = 0; + mccr4 &= 0x00230000; + + pretoact = 0; + acttorw = 0; + acttopre = 0; + for (i = 0; i < no_banks; i++) + if (! ignore[i]) + { + int rowcode = -1; + if (for_real) + { + bank[i].actual_detect = error_detect; + bank[i].actual_cas = cas_latency; + } + + switch (bank[i].row_bits) { + case 13: + if (bank[i].internal_banks == 4) + rowcode = 2; + else if (bank[i].internal_banks == 2) + rowcode = 1; + break; + case 12: + if (bank[i].internal_banks == 4) + rowcode = 0; + else if (bank[i].internal_banks == 2) + rowcode = 1; + break; + case 11: + if (bank[i].internal_banks == 4) + rowcode = 0; + else if (bank[i].internal_banks == 2) + rowcode = 3; + break; + } + if (rowcode == -1) { + ignore[i] = 1; + if (! for_real) + printk_info("Memory bank %d disabled: row bits %d and banks %d not supported\n", i, bank[i].row_bits, bank[i].internal_banks); + } else + mccr1 |= rowcode << (2 * i); + + /* Update worst case settings */ + if (! ignore[i]) { + if (bank[i].min_row_precharge > pretoact) + pretoact = bank[i].min_row_precharge; + if (bank[i].min_ras_to_cas > acttorw) + acttorw = bank[i].min_ras_to_cas; + if (bank[i].min_ras > acttopre) + acttopre = bank[i].min_ras; + } + } + + /* Now convert to clock cycles, rounding up */ + pretoact = (100 * pretoact + cycle_time - 1) / cycle_time; + acttopre = (100 * acttopre + cycle_time - 1) / cycle_time; + acttorw = (100 * acttorw + cycle_time - 1) / cycle_time; + refrec = acttopre; + bstopre = 0x240; /* Set conservative values, because we can't derive */ + refint = 1000; + + if (error_detect == ERRORS_ECC) + { + rdlat = cas_latency + 2; + mccr4 |= 0x00400000; + mccr2 |= 0x000c0001; + } + else + { + rdlat = cas_latency + 1; + mccr4 |= 0x00100000; + } + + if (pretoact > 16 || acttopre > 16 || acttorw > 16) + if (! for_real) + printk_info("Timings out of range\n"); + mccr4 |= ((pretoact & 0x0f) << 28) | ((acttopre & 0xf) << 24) | + ((acttorw & 0x0f) << 4) | + ((bstopre & 0x003) << 18) | ((bstopre & 0x3c0) >> 6) | + (cas_latency << 12) | 0x00000200 /* burst length */ ; + mccr3 |= ((bstopre & 0x03c) << 26) | + ((refrec & 0x0f) << 24) | (rdlat << 20); + mccr2 |= refint << 2; + mccr1 |= 0x00080000; /* memgo */ + + address = 0; + memstart1 = memstart2 = 0; + extmemstart1 = extmemstart2 = 0; + memend1 = memend2 = 0; + extmemend1 = extmemend2 = 0; + bank_enable = 0; + for (i = 0; i < no_banks; i++) { + if (! ignore[i]) { + u32 end = address + bank[i].size - 1; + bank_enable |= 1 << i; + if (i < 4) { + memstart1 |= ((address >> 20) & 0xff) << (8 * i); + extmemstart1 |= ((address >> 28) & 0x03) << (8 * i); + memend1 |= ((end >> 20) & 0xff) << (8 * i); + extmemend1 |= ((end >> 28) & 0x03) << (8 * i); + } else { + int k = i - 4; + memstart2 |= ((address >> 20) & 0xff) << (8 * k); + extmemstart2 |= ((address >> 28) & 0x03) << (8 * k); + memend2 |= ((end >> 20) & 0xff) << (8 * k); + extmemend2 |= ((end >> 28) & 0x03) << (8 * k); + } + address += bank[i].size; + } + } + + if (for_real) + { + pci_direct_ppc.write_byte(0, 0, 0xa0, bank_enable); + pci_direct_ppc.write_dword(0, 0, 0x80, memstart1); + pci_direct_ppc.write_dword(0, 0, 0x84, memstart2); + pci_direct_ppc.write_dword(0, 0, 0x88, extmemstart1); + pci_direct_ppc.write_dword(0, 0, 0x8c, extmemstart2); + pci_direct_ppc.write_dword(0, 0, 0x90, memend1); + pci_direct_ppc.write_dword(0, 0, 0x94, memend2); + pci_direct_ppc.write_dword(0, 0, 0x98, extmemend1); + pci_direct_ppc.write_dword(0, 0, 0x9c, extmemend2); + + pci_direct_ppc.write_dword(0, 0, 0xfc, mccr4); + pci_direct_ppc.write_dword(0, 0, 0xf8, mccr3); + pci_direct_ppc.write_dword(0, 0, 0xf4, mccr2); + pci_direct_ppc.write_dword(0, 0, 0xf0, mccr1); + } + + return address; +} + +static int +i2c_wait(unsigned timeout, int writing) +{ + u32 x; + while (((x = readl(MPC107_BASE + MPC107_I2CSR)) & (MPC107_I2C_CSR_MCF | MPC107_I2C_CSR_MIF)) + != (MPC107_I2C_CSR_MCF | MPC107_I2C_CSR_MIF)) { + if (ticks_since_boot() > timeout) + return -1; + } + + if (x & MPC107_I2C_CSR_MAL) { + return -1; + } + if (writing && (x & MPC107_I2C_CSR_RXAK)) { + printk_info("No RXAK\n"); + /* generate stop */ + writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR); + return -1; + } + writel(0, MPC107_BASE + MPC107_I2CSR); + return 0; +} + +static void +mpc107_i2c_start(struct i2c_bus *bus) +{ + /* Set clock */ + writel(0x1031, MPC107_BASE + MPC107_I2CFDR); + /* Clear arbitration */ + writel(0, MPC107_BASE + MPC107_I2CSR); +} + +static void +mpc107_i2c_stop(struct i2c_bus *bus) +{ + /* After last DIMM shut down I2C */ + writel(0x0, MPC107_BASE + MPC107_I2CCR); +} + +static int +mpc107_i2c_byte_write(struct i2c_bus *bus, int target, int address, u8 data) +{ + unsigned timeout = ticks_since_boot() + 3 * get_hz(); + + /* Must wait here for clocks to start */ + sleep_ticks(get_hz() / 40); + /* Start with MEN */ + writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR); + /* Start as master */ + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_MTX, MPC107_BASE + MPC107_I2CCR); + /* Write target byte */ + writel(target, MPC107_BASE + MPC107_I2CDR); + + if (i2c_wait(timeout, 1) < 0) + return -1; + + /* Write data address byte */ + writel(address, MPC107_BASE + MPC107_I2CDR); + + if (i2c_wait(timeout, 1) < 0) + return -1; + + /* Write data byte */ + writel(data, MPC107_BASE + MPC107_I2CDR); + + if (i2c_wait(timeout, 1) < 0) + return -1; + + /* generate stop */ + writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR); + return 0; +} + +static int +mpc107_i2c_master_write(struct i2c_bus *bus, int target, int address, const u8 *data, int length) +{ + unsigned count; + for(count = 0; count < length; count++) + { + if (mpc107_i2c_byte_write(bus, target, address, data[count]) < 0) + return -1; + } + return count; +} + +#define DIMM_LENGTH 0xfff + +static int +mpc107_i2c_master_read(struct i2c_bus *bus, int target, int address, + u8 *data, int length) +{ + unsigned timeout = ticks_since_boot() + 3 * get_hz(); + unsigned count; + + /* Must wait here for clocks to start */ + sleep_ticks(get_hz() / 40); + /* Start with MEN */ + writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR); + /* Start as master */ + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_MTX, MPC107_BASE + MPC107_I2CCR); + /* Write target byte */ + writel(target, MPC107_BASE + MPC107_I2CDR); + + if (i2c_wait(timeout, 1) < 0) + return -1; + + /* Write data address byte */ + writel(address, MPC107_BASE + MPC107_I2CDR); + + if (i2c_wait(timeout, 1) < 0) + return -1; + + /* Switch to read - restart */ + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_MTX | MPC107_I2C_CCR_RSTA, MPC107_BASE + MPC107_I2CCR); + /* Write target address byte - this time with the read flag set */ + writel(target | 1, MPC107_BASE + MPC107_I2CDR); + + if (i2c_wait(timeout, 0) < 0) + return -1; + + if (length == 1) + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_TXAK, MPC107_BASE + MPC107_I2CCR); + else + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA, MPC107_BASE + MPC107_I2CCR); + /* Dummy read */ + readl(MPC107_BASE + MPC107_I2CDR); + + count = 0; + while (count < length) { + + if (i2c_wait(timeout, 0) < 0) + return -1; + + /* Generate txack on next to last byte */ + if (count == length - 2) + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_TXAK, MPC107_BASE + MPC107_I2CCR); + /* Generate stop on last byte */ + if (count == length - 1) + writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_TXAK, MPC107_BASE + MPC107_I2CCR); + data[count] = readl(MPC107_BASE + MPC107_I2CDR); + if (count == 0 && length == DIMM_LENGTH) { + if (data[0] == 0xff) { + printk_debug("I2C device not present\n"); + length = 3; + } else { + length = data[0]; + if (length < 3) + length = 3; + } + } + count++; + } + + /* Finish with disable master */ + writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR); + return length; +} + +i2c_fn mpc107_i2c_fn = { + mpc107_i2c_start, mpc107_i2c_stop, + mpc107_i2c_master_write, mpc107_i2c_master_read +}; + +/* + * Find dimm information. + */ +void +hostbridge_probe_dimms(int no_dimms, sdram_dimm_info *dimms, sdram_bank_info * bank) +{ + unsigned char data[256]; + unsigned dimm; + + printk_debug("i2c testing\n"); + mpc107_i2c_start(NULL); + + for(dimm = 0; dimm < no_dimms; dimm++) + { + dimms[dimm].number = dimm; + dimms[dimm].bank1 = bank + dimm*2; + dimms[dimm].bank2 = bank + dimm*2 + 1; + bank[dimm*2].size = 0; + bank[dimm*2+1].size = 0; + bank[dimm*2].number = 0; + bank[dimm*2+1].number = 1; + } + + + for (dimm = 0; dimm < no_dimms; dimm ++) { + unsigned limit = mpc107_i2c_master_read(NULL, 0xa0 + 2*dimm, 0, + data, DIMM_LENGTH); + + if (limit > 3) { + sdram_dimm_to_bank_info(data, dimms + dimm, 0); + memcpy(dimms[dimm].part_number, data + 73, 18); + dimms[dimm].part_number[18] = 0; + printk_debug("Part Number: %s\n", dimms[dimm].part_number); + } + } + + mpc107_i2c_stop(NULL); +} diff --git a/src/northbridge/motorola/mpc107/mpc107.h b/src/northbridge/motorola/mpc107/mpc107.h new file mode 100644 index 0000000000..89352342ff --- /dev/null +++ b/src/northbridge/motorola/mpc107/mpc107.h @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2001 + * Humboldt Solutions Ltd, adrian@humboldt.co.uk. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _MPC107_H +#define _MPC107_H + +#ifdef ASM +#define BMC_BASE 0x8000 /* Bridge memory controller base address */ +#else + +#define MPC107_BASE 0xfc000000 + +#define MPC107_EUMBBAR 0x78 + +#define MPC107_PIC1 0xa8 +#define MPC107_PIC1_CF_MP 0x000003 +#define MPC107_PIC1_SPEC_PCI 0x000004 +#define MPC107_PIC1_CF_APARK 0x000008 +#define MPC107_PIC1_CF_LOOP_SNOOP 0x000010 +#define MPC107_PIC1_LE_MODE 0x000020 +#define MPC107_PIC1_ST_GATH_EN 0x000040 +#define MPC107_PIC1_NO_BUS_WIDTH_CHECK 0x000080 +#define MPC107_PIC1_TEA_EN 0x000400 +#define MPC107_PIC1_MCP_EN 0x000800 +#define MPC107_PIC1_FLASH_WR_EN 0x001000 +#define MPC107_PIC1_CF_LBA_EN 0x002000 +#define MPC107_PIC1_CF_MP_ID 0x00c000 +#define MPC107_PIC1_ADDRESS_MAP 0x010000 +#define MPC107_PIC1_PROC_TYPE 0x050000 +#define MPC107_PIC1_RCS0 0x100000 +#define MPC107_PIC1_CF_BREAD_WS 0xc00000 + +#define MPC107_I2CADR 0x3000 +#define MPC107_I2CFDR 0x3004 +#define MPC107_I2CCR 0x3008 +#define MPC107_I2CSR 0x300c +#define MPC107_I2CDR 0x3010 + +#define MPC107_I2C_CCR_MEN 0x80 +#define MPC107_I2C_CCR_MIEN 0x40 +#define MPC107_I2C_CCR_MSTA 0x20 +#define MPC107_I2C_CCR_MTX 0x10 +#define MPC107_I2C_CCR_TXAK 0x08 +#define MPC107_I2C_CCR_RSTA 0x04 + +#define MPC107_I2C_CSR_MCF 0x80 +#define MPC107_I2C_CSR_MAAS 0x40 +#define MPC107_I2C_CSR_MBB 0x20 +#define MPC107_I2C_CSR_MAL 0x10 +#define MPC107_I2C_CSR_SRW 0x04 +#define MPC107_I2C_CSR_MIF 0x02 +#define MPC107_I2C_CSR_RXAK 0x01 + +enum sdram_error_detect { + ERRORS_NONE, ERRORS_PARITY, ERRORS_ECC +}; + +typedef struct sdram_dimm_info +{ + unsigned size; + unsigned number; + char part_number[20]; + struct sdram_bank_info *bank1; + struct sdram_bank_info *bank2; +} sdram_dimm_info; + +typedef struct sdram_bank_info +{ + unsigned number; + unsigned char row_bits; + unsigned char internal_banks; + unsigned char col_bits; + unsigned char data_width; + /* Cycle and access times are stored with lowest CAS latency first. Units + are 0.01ns */ + unsigned short cycle_time[3]; + unsigned short access_time[3]; + /* Best CAS latencies */ + unsigned char cas_latency[3]; + unsigned char cs_latency; + unsigned char we_latency; + unsigned char min_back_to_back; + unsigned char min_row_precharge; + unsigned char min_active_to_active; + unsigned char min_ras_to_cas; + unsigned char min_ras; + unsigned char burst_mask; + enum sdram_error_detect error_detect; + /* Bank size */ + unsigned size; + unsigned long start; + unsigned long end; + enum sdram_error_detect actual_detect; + unsigned char actual_cas; +} sdram_bank_info; + +void sdram_dimm_to_bank_info(const char *dimm_data, sdram_dimm_info *dimm, int verbose); +void print_sdram_dimm_info(const sdram_dimm_info *dimm); +void print_sdram_bank_info(const sdram_bank_info *bank); + +unsigned long hostbridge_config_memory(int no_banks, sdram_bank_info *bank, int for_real); +void hostbridge_probe_dimms(int no_dimms, sdram_dimm_info *dimm, sdram_bank_info * bank); +unsigned mpc107_config_memory(void); +#endif +#endif diff --git a/src/northbridge/motorola/mpc107/mpc107_smp.c b/src/northbridge/motorola/mpc107/mpc107_smp.c new file mode 100644 index 0000000000..408a87fddb --- /dev/null +++ b/src/northbridge/motorola/mpc107/mpc107_smp.c @@ -0,0 +1,27 @@ +#include <pci.h> +#include "mpc107.h" + +void +wait_for_other_cpus(void) +{ +} + +unsigned long +this_processors_id(void) +{ + u32 pic1; + + pcibios_read_config_dword(0, 0, MPC107_PIC1, &pic1); + return (pic1 & MPC107_PIC1_CF_MP_ID) >> 14; +} + +unsigned long +processor_index(unsigned long id) +{ + return id; +} + +void +startup_other_cpus(unsigned long *map) +{ +} diff --git a/src/pc80/ide/ide.c b/src/pc80/ide/ide.c new file mode 100644 index 0000000000..823c5b5b6d --- /dev/null +++ b/src/pc80/ide/ide.c @@ -0,0 +1,513 @@ +#define BSY_SET_DURING_SPINUP 1 +/* + * UBL, The Universal Talkware Boot Loader + * Copyright (C) 2000 Universal Talkware Inc. + * Copyright (C) 2002 Eric Biederman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ + +#include <arch/io.h> +#include <printk.h> +#include <string.h> +#include <delay.h> +#include <pci.h> +#include <pc80/ide.h> +#include <arch/io.h> + +struct controller controller; +struct harddisk_info harddisk_info[NUM_HD]; + +static int await_ide(int (*done)(struct controller *ctrl), + struct controller *ctrl, unsigned long timeout) +{ + int result; + for(;;) { + result = done(ctrl); + if (result) { + return 0; + } + if (timeout-- <= 0) { + break; + } + udelay(1000); /* Added to avoid spinning GRW */ + } + return -1; +} + +/* The maximum time any IDE command can last 31 seconds, + * So if any IDE commands takes this long we know we have problems. + */ +#define IDE_TIMEOUT (32*1000) + +static int not_bsy(struct controller *ctrl) +{ + return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY); +} +#if !BSY_SET_DURING_SPINUP +static int timeout(struct controller *ctrl) +{ + return 0; +} +#endif + +static int ide_software_reset(struct controller *ctrl) +{ + /* Wait a little bit in case this is immediately after + * hardware reset. + */ + udelay(2000); + /* A software reset should not be delivered while the bsy bit + * is set. If the bsy bit does not clear in a reasonable + * amount of time give up. + */ + if (await_ide(not_bsy, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } + + /* Disable Interrupts and reset the ide bus */ + outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, + IDE_REG_DEVICE_CONTROL(ctrl)); + udelay(5); + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + udelay(2000); + if (await_ide(not_bsy, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } + return 0; +} + +static void pio_set_registers( + struct controller *ctrl, const struct ide_pio_command *cmd) +{ + uint8_t device; + /* Disable Interrupts */ + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl)); + + /* Possibly switch selected device */ + device = inb(IDE_REG_DEVICE(ctrl)); + outb(cmd->device, IDE_REG_DEVICE(ctrl)); + if ((device & (1UL << 4)) != (cmd->device & (1UL << 4))) { + /* Allow time for the selected drive to switch, + * The linux ide code suggests 50ms is the right + * amount of time to use here. + */ + udelay(50000); + } + outb(cmd->feature, IDE_REG_FEATURE(ctrl)); + outb(cmd->sector_count2, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->sector_count, IDE_REG_SECTOR_COUNT(ctrl)); + outb(cmd->lba_low2, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_low, IDE_REG_LBA_LOW(ctrl)); + outb(cmd->lba_mid2, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_mid, IDE_REG_LBA_MID(ctrl)); + outb(cmd->lba_high2, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->lba_high, IDE_REG_LBA_HIGH(ctrl)); + outb(cmd->command, IDE_REG_COMMAND(ctrl)); +} + + +static int pio_non_data(struct controller *ctrl, const struct ide_pio_command *cmd) +{ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } + + pio_set_registers(ctrl, cmd); + if (await_ide(not_bsy, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } + /* FIXME is there more error checking I could do here? */ + return 0; +} + +static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd, + void *buffer, size_t bytes) +{ + unsigned int status; + + /* FIXME handle commands with multiple blocks */ + /* Wait until the busy bit is clear */ + if (await_ide(not_bsy, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } + + /* How do I tell if INTRQ is asserted? */ + pio_set_registers(ctrl, cmd); + if (await_ide(not_bsy, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } + status = inb(IDE_REG_STATUS(ctrl)); + if (!(status & IDE_STATUS_DRQ)) { + return -1; + } + insw(IDE_REG_DATA(ctrl), buffer, bytes/2); + status = inb(IDE_REG_STATUS(ctrl)); + if (status & IDE_STATUS_ERR) { + return -1; + } + return 0; +} + +static inline int ide_read_sector_chs( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + unsigned int track; + unsigned int offset; + unsigned int cylinder; + + memset(&cmd, 0, sizeof(cmd)); + cmd.sector_count = 1; + + track = sector / info->sectors_per_track; + /* Sector number */ + offset = 1 + (sector % info->sectors_per_track); + cylinder = track / info->heads; + cmd.lba_low = offset; + cmd.lba_mid = cylinder & 0xff; + cmd.lba_high = (cylinder >> 8) & 0xff; + cmd.device = IDE_DH_DEFAULT | + IDE_DH_HEAD(track % info->heads) | + info->slave | + IDE_DH_CHS; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba( + struct harddisk_info *info, void *buffer, unsigned long sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.device = IDE_DH_DEFAULT | + ((sector >> 24) & 0x0f) | + info->slave | + IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +static inline int ide_read_sector_lba48( + struct harddisk_info *info, void *buffer, sector_t sector) +{ + struct ide_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_count = 1; + cmd.lba_low = sector & 0xff; + cmd.lba_mid = (sector >> 8) & 0xff; + cmd.lba_high = (sector >> 16) & 0xff; + cmd.lba_low2 = (sector >> 24) & 0xff; + cmd.lba_mid2 = (sector >> 32) & 0xff; + cmd.lba_high2 = (sector >> 40) & 0xff; + cmd.device = info->slave | IDE_DH_LBA; + cmd.command = IDE_CMD_READ_SECTORS_EXT; + return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE); +} + +int ide_read_sector(int driveno, void * buf, unsigned int sector, + int byte_offset, int n_bytes) +{ + struct harddisk_info *info = &harddisk_info[driveno]; + int result; + + /* Report the buffer is empty */ + if (sector > info->sectors) { + return -1; + } + if (info->address_mode == ADDRESS_MODE_CHS) { + result = ide_read_sector_chs(info, buf, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA) { + result = ide_read_sector_lba(info, buf, sector); + } + else if (info->address_mode == ADDRESS_MODE_LBA48) { + result = ide_read_sector_lba48(info, buf, sector); + } + else { + result = -1; + } + return result; +} + +static int init_drive(struct harddisk_info *info, struct controller *ctrl, int slave, int basedrive) +{ + uint16_t* drive_info; + struct ide_pio_command cmd; + unsigned char disk_buffer[DISK_BUFFER_SIZE]; + int i; + + info->ctrl = ctrl; + info->heads = 0u; + info->cylinders = 0u; + info->sectors_per_track = 0u; + info->address_mode = IDE_DH_CHS; + info->sectors = 0ul; + info->drive_exists = 0; + info->slave_absent = 0; + info->slave = slave?IDE_DH_SLAVE: IDE_DH_MASTER; + info->basedrive = basedrive; + + printk_info("Testing for disk %d\n", info->basedrive); + + /* Select the drive that we are testing */ + outb(IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave, + IDE_REG_DEVICE(ctrl)); + udelay(50000); + + /* Test to see if the drive registers exist, + * In many cases this quickly rules out a missing drive. + */ + for(i = 0; i < 4; i++) { + outb(0xaa + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0xaa + i) { + return 1; + } + } + for(i = 0; i < 4; i++) { + outb(0x55 + i, (ctrl->cmd_base) + 2 + i); + } + for(i = 0; i < 4; i++) { + if (inb((ctrl->cmd_base) + 2 + i) != 0x55 + i) { + return 1; + } + } + + printk_info("Probing for disk %d\n", info->basedrive); + + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.command = IDE_CMD_IDENTIFY_DEVICE; + + + if (pio_data_in(ctrl, &cmd, disk_buffer, IDE_SECTOR_SIZE) < 0) { + /* Well, if that command didn't work, we probably don't have drive. */ + return 1; + } + + + /* Now suck the data out */ + drive_info = (uint16_t *)disk_buffer; + if (drive_info[2] == 0x37C8) { + /* If the response is incomplete spin up the drive... */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.feature = IDE_FEATURE_STANDBY_SPINUP_DRIVE; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If the command doesn't work give up on the drive */ + return 1; + } + + } + if ((drive_info[2] == 0x37C8) || (drive_info[2] == 0x8C73)) { + /* The response is incomplete retry the drive info command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | + info->slave; + cmd.command = IDE_CMD_IDENTIFY_DEVICE; + if(pio_data_in(ctrl, &cmd, disk_buffer, IDE_SECTOR_SIZE) < 0) { + /* If the command didn't work give up on the drive. */ + return 1; + } + } + if ((drive_info[2] != 0x37C8) && + (drive_info[2] != 0x738C) && + (drive_info[2] != 0x8C73) && + (drive_info[2] != 0xC837) && + (drive_info[2] != 0x0000)) { + printk_info("Invalid IDE Configuration: %hx\n", drive_info[2]); + return 1; + } + for(i = 27; i < 47; i++) { + info->model_number[((i-27)<< 1)] = (drive_info[i] >> 8) & 0xff; + info->model_number[((i-27)<< 1)+1] = drive_info[i] & 0xff; + } + info->model_number[40] = '\0'; + info->drive_exists = 1; + + /* See if LBA is supported */ + if (drive_info[49] & (1 << 9)) { + info->address_mode = ADDRESS_MODE_LBA; + info->sectors = (drive_info[61] << 16) | (drive_info[60]); + /* Enable LBA48 mode if it is present */ + if (drive_info[83] & (1 <<10)) { + /* Should LBA48 depend on LBA? */ + printk_info("LBA48 mode\n"); + info->address_mode = ADDRESS_MODE_LBA48; + info->sectors = + (((sector_t)drive_info[103]) << 48) | + (((sector_t)drive_info[102]) << 32) | + (((sector_t)drive_info[101]) << 16) | + (((sector_t)drive_info[100]) << 0); + } + } else { + info->address_mode = ADDRESS_MODE_CHS; + info->heads = drive_info[3]; + info->cylinders = drive_info[1]; + info->sectors_per_track = drive_info[6]; + info->sectors = + info->sectors_per_track * + info->heads * + info->cylinders; + printk_info("%s sectors_per_track=[%d], heads=[%d], cylinders=[%d]\n", + __FUNCTION__, + info->sectors_per_track, + info->heads, + info->cylinders); + } + /* See if we have a slave */ + if (!info->slave && (((drive_info[93] >> 14) & 3) == 1)) { + info->slave_absent = !(drive_info[93] & (1 << 5)); + } + /* See if we need to put the device in CFA power mode 1 */ + if ((drive_info[160] & ((1 << 15) | (1 << 13)| (1 << 12))) == + ((1 << 15) | (1 << 13)| (1 << 12))) { + memset(&cmd, 0, sizeof(cmd)); + cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave; + cmd.feature = IDE_FEATURE_CFA_ENABLE_POWER_MODE1; + if (pio_non_data(ctrl, &cmd) < 0) { + /* If I need to power up the drive, and I can't + * give up. + */ + printk_info("Cannot power up CFA device\n"); + return 1; + } + } + printk_info("disk%d %dk cap: %hx\n", + info->basedrive, + (unsigned long)(info->sectors >> 1), + drive_info[49]); + return 0; +} + +static int init_controller(struct controller *ctrl, int basedrive) +{ + struct harddisk_info *info; + + /* Put the drives ide channel in a know state and wait + * for the drives to spinup. + * + * In practice IDE disks tend not to respond to commands until + * they have spun up. This makes IDE hard to deal with + * immediately after power up, as the delays can be quite + * long, so we must be very careful here. + * + * There are two pathological cases that must be dealt with: + * + * - The BSY bit not being set while the IDE drives spin up. + * In this cases only a hard coded delay will work. As + * I have not reproduced it, and this is out of spec for + * IDE drives the work around can be enabled by setting + * BSY_SET_DURING_SPINUP to 0. + * + * - The BSY bit floats high when no drives are plugged in. + * This case will not be detected except by timing out but + * we avoid the problems by only probing devices we are + * supposed to boot from. If we don't do the probe we + * will not experience the problem. + * + * So speed wise I am only slow if the BSY bit is not set + * or not reported by the IDE controller during spinup, which + * is quite rare. + * + */ +#if !BSY_SET_DURING_SPINUP + if (await_ide(timeout, ctrl, IDE_TIMEOUT) < 0) { + return -1; + } +#endif + if (ide_software_reset(ctrl) < 0) { + return -1; + } + + /* Note: I have just done a software reset. It may be + * reasonable to just read the boot time signatures + * off of the drives to see if they are present. + * + * For now I will go with just sending commands to the drives + * and assuming filtering out missing drives by detecting registers + * that won't set and commands that fail to execute properly. + */ + + /* Now initialize the individual drives */ + info = &harddisk_info[basedrive]; + init_drive(info, ctrl, 0, basedrive & 1); + + /* at the moment this only works for the first drive */ +#if 0 + if (info->drive_exists && !info->slave_absent) { + basedrive++; + info++; + init_drive(info, ctrl, 1, basedrive & 1); + } +#endif + + return 0; +} + +int ide_init(void) +{ + int index; + int drives = 0; + + /* Intialize the harddisk_info structures */ + memset(harddisk_info, 0, sizeof(harddisk_info)); + + for(index = 0; index < 1; index++) { +#if 0 + /* IDE normal pci mode */ + unsigned cmd_reg, ctrl_reg; + uint32_t cmd_base, ctrl_base; + if (index < 2) { + cmd_reg = PCI_BASE_ADDRESS_0; + ctrl_reg = PCI_BASE_ADDRESS_1; + } else { + cmd_reg = PCI_BASE_ADDRESS_2; + ctrl_reg = PCI_BASE_ADDRESS_3; + } + pcibios_read_config_dword(0, 0, cmd_reg, &cmd_base); + pcibios_read_config_dword(0, 0, ctrl_reg, &ctrl_base); + controller.cmd_base = cmd_base & ~3; + controller.ctrl_base = ctrl_base & ~3; + +#endif + uint16_t base; + base = (index < 1)?IDE_BASE0:IDE_BASE1; + controller.cmd_base = base; + controller.ctrl_base = base + IDE_REG_EXTENDED_OFFSET; + + printk_info("init_controller %d at (%x, %x)\n", index, controller.cmd_base, controller.ctrl_base); + + if (init_controller(&controller, index << 1) < 0) { + /* nothing behind the controller */ + continue; + } + drives++; + } + + return drives > 0 ? 0 : -1; +} diff --git a/src/pmc/altimus/mpc7410/mpc7410.c b/src/pmc/altimus/mpc7410/mpc7410.c new file mode 100644 index 0000000000..ee1dc4b096 --- /dev/null +++ b/src/pmc/altimus/mpc7410/mpc7410.c @@ -0,0 +1,68 @@ +/* $Id$ */ +/* Copyright 2000 AG Electronics Ltd. */ +/* This code is distributed without warranty under the GPL v2 (see COPYING) */ + +#include <ppc.h> +#include <ppcreg.h> +#include <types.h> +#include <string.h> +#include <pci.h> +#include <printk.h> + +#define ONEMEG 0x00100000 +#define HALFMEG 0x00080000 + +unsigned long memory_base = 0; +unsigned long memory_top = 0; +unsigned long memory_size = 0; + +//extern char __heap_end[]; +extern unsigned mpc107_config_memory(void); + +unsigned config_memory(unsigned offset) +{ + //extern char __start[]; + //extern char __bss_start[]; + //unsigned rom_image = (unsigned) __start & 0xfff00000; + //unsigned physical = rom_image + offset; + //unsigned codesize = (unsigned) __bss_start - rom_image; + +#if 0 + /* At this point, DBAT 0 is memory, 1 is variable, 2 is the rom image, + and 3 is IO. */ + ppc_set_io_dbat_reloc(2, rom_image, physical, ONEMEG); + ppc_set_io_dbat (3, 0xf0000000, 0x10000000); + if ( rom_image != physical ) + ppc_set_ibats_reloc(rom_image, physical, ONEMEG); + else + ppc_set_ibats(physical, ONEMEG); + + printk_debug("bsp_init_memory...\n"); +#endif + + ppc_setup_cpu(1); /* icache enable = 1 */ + //ppc_enable_mmu(); + + memory_size = mpc107_config_memory(); + + /* If we have some working RAM, we copy the code and rodata into it. + * This allows us to reprogram the flash later. */ +#if 0 + if (memory_size) + { + unsigned onemeg = memory_size - ONEMEG; + ppc_set_mem_dbat_reloc(1, onemeg, onemeg, ONEMEG); + memcpy((void *)onemeg, (void *)rom_image, codesize); + memset((void *)(onemeg + codesize), 0, ONEMEG - codesize); + ppc_set_ibats_reloc2(rom_image, physical, onemeg, ONEMEG); + ppc_set_mem_dbat_reloc(2, rom_image, onemeg, ONEMEG); + make_coherent((void *)onemeg, ONEMEG); + } + + ppc_set_memory_dbat (memory_size); +#endif + + //ppc_enable_dcache (); + + return memory_size; +} |