diff options
89 files changed, 34073 insertions, 0 deletions
diff --git a/board/c2mon/flash.c b/board/c2mon/flash.c new file mode 100644 index 00000000000..181f82af2f4 --- /dev/null +++ b/board/c2mon/flash.c @@ -0,0 +1,570 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +#ifndef CFG_ENV_ADDR +#define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +#endif + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & OR_AM_MSK); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SIZE-1, + &flash_info[0]); +#endif + + if (size_b1) { + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size_b1 & 0xFFFF8000); + memctl->memc_br1 = ((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SIZE-1, + &flash_info[1]); +#endif + } else { + memctl->memc_br1 = 0; /* invalidate bank */ + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; + + value = addr[0]; + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; + + *addr = 0x00F000F0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ((addr[0] & 0x00800080) != 0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/cogent/dipsw.c b/board/cogent/dipsw.c new file mode 100644 index 00000000000..d2027c97589 --- /dev/null +++ b/board/cogent/dipsw.c @@ -0,0 +1,50 @@ +#include <common.h> +#include <board/cogent/dipsw.h> + +unsigned char +dipsw_raw(void) +{ + return cma_mb_reg_read(&((cma_mb_dipsw *)CMA_MB_DIPSW_BASE)->dip_val); +} + +unsigned char +dipsw_cooked(void) +{ + unsigned char val1, val2, mask1, mask2; + + val1 = dipsw_raw(); + + /* + * we want to mirror the bits because the low bit is switch 1 and high + * bit is switch 8 and also invert them because 1=off and 0=on, according + * to manual. + * + * this makes the value more intuitive i.e. + * - left most, or high, or top, bit is left most switch (1); + * - right most, or low, or bottom, bit is right most switch (8) + * - a set bit means "on" and a clear bit means "off" + */ + + val2 = 0; + for (mask1 = 1 << 7, mask2 = 1; mask1 > 0; mask1 >>= 1, mask2 <<= 1) + if ((val1 & mask1) == 0) + val2 |= mask2; + + return (val2); +} + +void +dipsw_init(void) +{ + unsigned char val, mask; + + val = dipsw_cooked(); + + printf("|"); + for (mask = 1 << 7; mask > 0; mask >>= 1) + if (val & mask) + printf("on |"); + else + printf("off|"); + printf("\n"); +} diff --git a/board/cogent/flash.c b/board/cogent/flash.c new file mode 100644 index 00000000000..86da80edb81 --- /dev/null +++ b/board/cogent/flash.c @@ -0,0 +1,648 @@ +/* + * (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 + */ + +#include <common.h> +#include <board/cogent/flash.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*----------------------------------------------------------------------- + */ + + +#if defined(CONFIG_CMA302) + +/* + * probe for the existence of flash at address "addr" + * 0 = yes, 1 = bad Manufacturer's Id, 2 = bad Device Id + */ +static int +c302f_probe_word(c302f_addr_t addr) +{ + /* reset the flash */ + *addr = C302F_BNK_CMD_RST; + + /* check the manufacturer id */ + *addr = C302F_BNK_CMD_RD_ID; + if (*C302F_BNK_ADDR_MAN(addr) != C302F_BNK_RD_ID_MAN) + return 1; + + /* check the device id */ + *addr = C302F_BNK_CMD_RD_ID; + if (*C302F_BNK_ADDR_DEV(addr) != C302F_BNK_RD_ID_DEV) + return 2; + +#ifdef FLASH_DEBUG + { + int i; + + printf("\nMaster Lock Config = 0x%08lx\n", + *C302F_BNK_ADDR_CFGM(addr)); + for (i = 0; i < C302F_BNK_NBLOCKS; i++) + printf("Block %2d Lock Config = 0x%08lx\n", + i, *C302F_BNK_ADDR_CFG(i, addr)); + } +#endif + + /* reset the flash again */ + *addr = C302F_BNK_CMD_RST; + + return 0; +} + +/* + * probe for Cogent CMA302 flash module at address "base" and store + * info for any found into flash_info entry "fip". Must find at least + * one bank. + */ +static void +c302f_probe(flash_info_t *fip, c302f_addr_t base) +{ + c302f_addr_t addr, eaddr; + int nbanks; + + fip->size = 0L; + fip->sector_count = 0; + + addr = base; + eaddr = C302F_BNK_ADDR_BASE(addr, C302F_MAX_BANKS); + nbanks = 0; + + while (addr < eaddr) { + c302f_addr_t addrw, eaddrw, addrb; + int i, osc, nsc; + + addrw = addr; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) + if (c302f_probe_word(addrw++) != 0) + goto out; + + /* bank exists - append info for this bank to *fip */ + fip->flash_id = FLASH_MAN_INTEL|FLASH_28F008S5; + fip->size += C302F_BNK_SIZE; + osc = fip->sector_count; + fip->sector_count += C302F_BNK_NBLOCKS; + if ((nsc = fip->sector_count) >= CFG_MAX_FLASH_SECT) + panic("Too many sectors in flash at address 0x%08lx\n", + (unsigned long)base); + + addrb = addr; + for (i = osc; i < nsc; i++) { + fip->start[i] = (ulong)addrb; + fip->protect[i] = 0; + addrb = C302F_BNK_ADDR_NEXT_BLK(addrb); + } + + addr = C302F_BNK_ADDR_NEXT_BNK(addr); + nbanks++; + } + +out: + if (nbanks == 0) + panic("ERROR: no flash found at address 0x%08lx\n", + (unsigned long)base); +} + +static void +c302f_reset(flash_info_t *info, int sect) +{ + c302f_addr_t addrw, eaddrw; + + addrw = (c302f_addr_t)info->start[sect]; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) { +#ifdef FLASH_DEBUG + printf(" writing reset cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = C302F_BNK_CMD_RST; + addrw++; + } +} + +static void +c302f_erase_init(flash_info_t *info, int sect) +{ + c302f_addr_t addrw, saddrw, eaddrw; + int flag; + +#ifdef FLASH_DEBUG + printf("0x%08lx C302F_BNK_CMD_PROG\n", C302F_BNK_CMD_PROG); + printf("0x%08lx C302F_BNK_CMD_ERASE1\n", C302F_BNK_CMD_ERASE1); + printf("0x%08lx C302F_BNK_CMD_ERASE2\n", C302F_BNK_CMD_ERASE2); + printf("0x%08lx C302F_BNK_CMD_CLR_STAT\n", C302F_BNK_CMD_CLR_STAT); + printf("0x%08lx C302F_BNK_CMD_RST\n", C302F_BNK_CMD_RST); + printf("0x%08lx C302F_BNK_STAT_RDY\n", C302F_BNK_STAT_RDY); + printf("0x%08lx C302F_BNK_STAT_ERR\n", C302F_BNK_STAT_ERR); +#endif + + saddrw = (c302f_addr_t)info->start[sect]; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(saddrw); + +#ifdef FLASH_DEBUG + printf("erasing sector %d, start addr = 0x%08lx " + "(bank next word addr = 0x%08lx)\n", sect, + (unsigned long)saddrw, (unsigned long)eaddrw); +#endif + + /* Disable intrs which might cause a timeout here */ + flag = disable_interrupts(); + + for (addrw = saddrw; addrw < eaddrw; addrw++) { +#ifdef FLASH_DEBUG + printf(" writing erase cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = C302F_BNK_CMD_ERASE1; + *addrw = C302F_BNK_CMD_ERASE2; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); +} + +static int +c302f_erase_poll(flash_info_t *info, int sect) +{ + c302f_addr_t addrw, saddrw, eaddrw; + int sectdone, haderr; + + saddrw = (c302f_addr_t)info->start[sect]; + eaddrw = C302F_BNK_ADDR_NEXT_WORD(saddrw); + + sectdone = 1; + haderr = 0; + + for (addrw = saddrw; addrw < eaddrw; addrw++) { + c302f_word_t stat = *addrw; + +#ifdef FLASH_DEBUG + printf(" checking status at addr " + "0x%08lx [0x%08lx]\n", + (unsigned long)addrw, stat); +#endif + if ((stat & C302F_BNK_STAT_RDY) != C302F_BNK_STAT_RDY) + sectdone = 0; + else if ((stat & C302F_BNK_STAT_ERR) != 0) { + printf(" failed on sector %d " + "(stat = 0x%08lx) at " + "address 0x%08lx\n", + sect, stat, + (unsigned long)addrw); + *addrw = C302F_BNK_CMD_CLR_STAT; + haderr = 1; + } + } + + if (haderr) + return (-1); + else + return (sectdone); +} + +static int +c302f_write_word(c302f_addr_t addr, c302f_word_t value) +{ + c302f_word_t stat; + ulong start; + int flag, retval; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = C302F_BNK_CMD_PROG; + + *addr = value; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + retval = 0; + + /* data polling for D7 */ + start = get_timer (0); + do { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + retval = 1; + goto done; + } + stat = *addr; + } while ((stat & C302F_BNK_STAT_RDY) != C302F_BNK_STAT_RDY); + + if ((stat & C302F_BNK_STAT_ERR) != 0) { + printf("flash program failed (stat = 0x%08lx) " + "at address 0x%08lx\n", (ulong)stat, (ulong)addr); + *addr = C302F_BNK_CMD_CLR_STAT; + retval = 3; + } + +done: + /* reset to read mode */ + *addr = C302F_BNK_CMD_RST; + + return (retval); +} + +#endif /* CONFIG_CMA302 */ + +unsigned long +flash_init(void) +{ + unsigned long total; + int i; + flash_info_t *fip; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + fip = &flash_info[0]; + total = 0L; + +#if defined(CONFIG_CMA302) + c302f_probe(fip, (c302f_addr_t)CFG_FLASH_BASE); + total += fip->size; + fip++; +#endif + +#if (CMA_MB_CAPS & CMA_MB_CAP_FLASH) + /* not yet ... + cmbf_probe(fip, (cmbf_addr_t)CMA_MB_FLASH_BASE); + total += fip->size; + fip++; + */ +#endif + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE == CFG_FLASH_BASE + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + return total; +} + +/*----------------------------------------------------------------------- + */ +void +flash_print_info(flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F008S5: printf ("28F008S5\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 4) == 0) + printf ("\n "); + printf (" %2d - %08lX%s", i, + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +int +flash_erase(flash_info_t *info, int s_first, int s_last) +{ + int prot, sect, haderr; + ulong start, now, last; + void (*erase_init)(flash_info_t *, int); + int (*erase_poll)(flash_info_t *, int); + void (*reset)(flash_info_t *, int); + int rcode = 0; + +#ifdef FLASH_DEBUG + printf("\nflash_erase: erase %d sectors (%d to %d incl.) from\n" + " Bank # %d: ", s_last - s_first + 1, s_first, s_last, + (info - flash_info) + 1); + flash_print_info(info); +#endif + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + switch (info->flash_id) { + +#if defined(CONFIG_CMA302) + case FLASH_MAN_INTEL|FLASH_28F008S5: + erase_init = c302f_erase_init; + erase_poll = c302f_erase_poll; + reset = c302f_reset; + break; +#endif + +#if (CMA_MB_CAPS & CMA_MB_CAP_FLASH) + case FLASH_MAN_INTEL|FLASH_28F800_B: + case FLASH_MAN_AMD|FLASH_AM29F800B: + /* not yet ... + erase_init = cmbf_erase_init; + erase_poll = cmbf_erase_poll; + reset = cmbf_reset; + break; + */ +#endif + + default: + printf ("Flash type %08lx not supported - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf("- Warning: %d protected sector%s will not be erased!\n", + prot, (prot > 1 ? "s" : "")); + } + + start = get_timer (0); + last = 0; + haderr = 0; + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + ulong estart; + int sectdone; + + (*erase_init)(info, sect); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + estart = get_timer(start); + + do { + now = get_timer(start); + + if (now - estart > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout (sect %d)\n", sect); + haderr = 1; + break; + } + +#ifndef FLASH_DEBUG + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } +#endif + + sectdone = (*erase_poll)(info, sect); + + if (sectdone < 0) { + haderr = 1; + break; + } + + } while (!sectdone); + + if (haderr) + break; + } + } + + if (haderr > 0) { + printf (" failed\n"); + rcode = 1; + } + else + printf (" done\n"); + + /* reset to read mode */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + (*reset)(info, sect); + } + } + return rcode; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 3 - write error + */ + +int +write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + ulong start, now, last; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + start = get_timer (0); + last = 0; + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + + /* show that we're waiting */ + now = get_timer(start); + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 3 - write error + */ +static int +write_word(flash_info_t *info, ulong dest, ulong data) +{ + int retval; + + /* Check if Flash is (sufficiently) erased */ + if ((*(ulong *)dest & data) != data) { + return (2); + } + + switch (info->flash_id) { + +#if defined(CONFIG_CMA302) + case FLASH_MAN_INTEL|FLASH_28F008S5: + retval = c302f_write_word((c302f_addr_t)dest, (c302f_word_t)data); + break; +#endif + +#if (CMA_MB_CAPS & CMA_MB_CAP_FLASH) + case FLASH_MAN_INTEL|FLASH_28F800_B: + case FLASH_MAN_AMD|FLASH_AM29F800B: + /* not yet ... + retval = cmbf_write_word((cmbf_addr_t)dest, (cmbf_word_t)data); + */ + retval = 3; + break; +#endif + + default: + printf ("Flash type %08lx not supported - aborted\n", + info->flash_id); + retval = 3; + break; + } + + return (retval); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/cu824/flash.c b/board/cu824/flash.c new file mode 100644 index 00000000000..0cec41ebdbd --- /dev/null +++ b/board/cu824/flash.c @@ -0,0 +1,486 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc824x.h> +#include <asm/processor.h> + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +#define FLASH_BANK_SIZE 0x800000 +#define MAIN_SECT_SIZE 0x40000 +#define PARAM_SECT_SIZE 0x8000 + +#define BOARD_CTRL_REG 0xFE800013 + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + +static int write_data (flash_info_t *info, ulong dest, ulong *data); +static void write_via_fpu(vu_long *addr, ulong *data); +static __inline__ unsigned long get_msr(void); +static __inline__ void set_msr(unsigned long msr); + +/*---------------------------------------------------------------------*/ +#undef DEBUG_FLASH + +/*---------------------------------------------------------------------*/ +#ifdef DEBUG_FLASH +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init(void) +{ + int i, j; + ulong size = 0; + volatile unsigned char *bcr = (volatile unsigned char *)(BOARD_CTRL_REG); + + DEBUGF("Write protect was: 0x%02X\n", *bcr); + *bcr &= 0x1; /* FWPT must be 0 */ + *bcr |= 0x6; /* FWP0 = FWP1 = 1 */ + DEBUGF("Write protect is: 0x%02X\n", *bcr); + + for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) { + vu_long *addr = (vu_long *)(CFG_FLASH_BASE + i * FLASH_BANK_SIZE); + + addr[0] = 0x00900090; + + DEBUGF ("Flash bank # %d:\n" + "\tManuf. ID @ 0x%08lX: 0x%08lX\n" + "\tDevice ID @ 0x%08lX: 0x%08lX\n", + i, + (ulong)(&addr[0]), addr[0], + (ulong)(&addr[2]), addr[2]); + + if ((addr[0] == addr[1]) && (addr[0] == INTEL_MANUFACT) && + (addr[2] == addr[3]) && (addr[2] == INTEL_ID_28F160F3B)) + { + flash_info[i].flash_id = (FLASH_MAN_INTEL & FLASH_VENDMASK) | + (INTEL_ID_28F160F3B & FLASH_TYPEMASK); + } else { + flash_info[i].flash_id = FLASH_UNKNOWN; + addr[0] = 0xFFFFFFFF; + goto Done; + } + + DEBUGF ("flash_id = 0x%08lX\n", flash_info[i].flash_id); + + addr[0] = 0xFFFFFFFF; + + flash_info[i].size = FLASH_BANK_SIZE; + flash_info[i].sector_count = CFG_MAX_FLASH_SECT; + memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT); + for (j = 0; j < flash_info[i].sector_count; j++) { + if (j <= 7) { + flash_info[i].start[j] = CFG_FLASH_BASE + + i * FLASH_BANK_SIZE + + j * PARAM_SECT_SIZE; + } else { + flash_info[i].start[j] = CFG_FLASH_BASE + + i * FLASH_BANK_SIZE + + (j - 7)*MAIN_SECT_SIZE; + } + } + size += flash_info[i].size; + } + + /* Protect monitor and environment sectors + */ +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + FLASH_BANK_SIZE + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE + CFG_MONITOR_LEN - 1, + &flash_info[1]); +#else + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE + CFG_MONITOR_LEN - 1, + &flash_info[0]); +#endif +#endif + +#if (CFG_ENV_IS_IN_FLASH == 1) && defined(CFG_ENV_ADDR) +#if CFG_ENV_ADDR >= CFG_FLASH_BASE + FLASH_BANK_SIZE + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, + &flash_info[1]); +#else + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, + &flash_info[0]); +#endif +#endif + +Done: + return size; +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t * info) +{ + int i; + + switch ((i = info->flash_id & FLASH_VENDMASK)) { + case (FLASH_MAN_INTEL & FLASH_VENDMASK): + printf ("Intel: "); + break; + default: + printf ("Unknown Vendor 0x%04x ", i); + break; + } + + switch ((i = info->flash_id & FLASH_TYPEMASK)) { + case (INTEL_ID_28F160F3B & FLASH_TYPEMASK): + printf ("28F160F3B (16Mbit)\n"); + break; + default: + printf ("Unknown Chip Type 0x%04x\n", i); + goto Done; + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; i++) { + if ((i % 5) == 0) { + printf ("\n "); + } + printf (" %08lX%s", info->start[i], + info->protect[i] ? " (RO)" : " "); + } + printf ("\n"); + +Done: + return; +} + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong start, now, last; + + DEBUGF ("Erase flash bank %d sect %d ... %d\n", + info - &flash_info[0], s_first, s_last); + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id & FLASH_VENDMASK) != + (FLASH_MAN_INTEL & FLASH_VENDMASK)) { + printf ("Can erase only Intel flash types - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + vu_long *addr = (vu_long *)(info->start[sect]); + + DEBUGF ("Erase sect %d @ 0x%08lX\n", + sect, (ulong)addr); + + /* Disable interrupts which might cause a timeout + * here. + */ + flag = disable_interrupts(); + + addr[0] = 0x00500050; /* clear status register */ + addr[0] = 0x00200020; /* erase setup */ + addr[0] = 0x00D000D0; /* erase confirm */ + + addr[1] = 0x00500050; /* clear status register */ + addr[1] = 0x00200020; /* erase setup */ + addr[1] = 0x00D000D0; /* erase confirm */ + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + while (((addr[0] & 0x00800080) != 0x00800080) || + ((addr[1] & 0x00800080) != 0x00800080) ) { + if ((now=get_timer(start)) > + CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + addr[0] = 0x00B000B0; /* suspend erase */ + addr[0] = 0x00FF00FF; /* to read mode */ + return 1; + } + + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + addr[0] = 0x00FF00FF; + } + } + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 4 - Flash not identified + */ + +#define FLASH_WIDTH 8 /* flash bus width in bytes */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong wp, cp, msr; + int l, rc, i; + ulong data[2]; + ulong *datah = &data[0]; + ulong *datal = &data[1]; + + DEBUGF ("Flash write_buff: @ 0x%08lx, src 0x%08lx len %ld\n", + addr, (ulong)src, cnt); + + if (info->flash_id == FLASH_UNKNOWN) { + return 4; + } + + msr = get_msr(); + set_msr(msr | MSR_FP); + + wp = (addr & ~(FLASH_WIDTH-1)); /* get lower aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + *datah = *datal = 0; + + for (i = 0, cp = wp; i < l; i++, cp++) { + if (i >= 4) { + *datah = (*datah << 8) | + ((*datal & 0xFF000000) >> 24); + } + + *datal = (*datal << 8) | (*(uchar *)cp); + } + for (; i < FLASH_WIDTH && cnt > 0; ++i) { + char tmp; + + tmp = *src; + + src++; + + if (i >= 4) { + *datah = (*datah << 8) | + ((*datal & 0xFF000000) >> 24); + } + + *datal = (*datal << 8) | tmp; + + --cnt; ++cp; + } + + for (; cnt == 0 && i < FLASH_WIDTH; ++i, ++cp) { + if (i >= 4) { + *datah = (*datah << 8) | + ((*datal & 0xFF000000) >> 24); + } + + *datal = (*datah << 8) | (*(uchar *)cp); + } + + if ((rc = write_data(info, wp, data)) != 0) { + set_msr(msr); + return (rc); + } + + wp += FLASH_WIDTH; + } + + /* + * handle FLASH_WIDTH aligned part + */ + while (cnt >= FLASH_WIDTH) { + *datah = *(ulong *)src; + *datal = *(ulong *)(src + 4); + if ((rc = write_data(info, wp, data)) != 0) { + set_msr(msr); + return (rc); + } + wp += FLASH_WIDTH; + cnt -= FLASH_WIDTH; + src += FLASH_WIDTH; + } + + if (cnt == 0) { + set_msr(msr); + return (0); + } + + /* + * handle unaligned tail bytes + */ + *datah = *datal = 0; + for (i = 0, cp = wp; i < FLASH_WIDTH && cnt > 0; ++i, ++cp) { + char tmp; + + tmp = *src; + + src++; + + if (i >= 4) { + *datah = (*datah << 8) | ((*datal & 0xFF000000) >> 24); + } + + *datal = (*datal << 8) | tmp; + + --cnt; + } + + for (; i < FLASH_WIDTH; ++i, ++cp) { + if (i >= 4) { + *datah = (*datah << 8) | ((*datal & 0xFF000000) >> 24); + } + + *datal = (*datal << 8) | (*(uchar *)cp); + } + + rc = write_data(info, wp, data); + set_msr(msr); + + return (rc); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, ulong dest, ulong *data) +{ + vu_long *addr = (vu_long *)dest; + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if (((addr[0] & data[0]) != data[0]) || + ((addr[1] & data[1]) != data[1]) ) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0] = 0x00400040; /* write setup */ + write_via_fpu(addr, data); + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + + while (((addr[0] & 0x00800080) != 0x00800080) || + ((addr[1] & 0x00800080) != 0x00800080) ) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + addr[0] = 0x00FF00FF; /* restore read mode */ + return (1); + } + } + + addr[0] = 0x00FF00FF; /* restore read mode */ + + return (0); +} + +/*----------------------------------------------------------------------- + */ +static void write_via_fpu(vu_long *addr, ulong *data) +{ + __asm__ __volatile__ ("lfd 1, 0(%0)" : : "r" (data)); + __asm__ __volatile__ ("stfd 1, 0(%0)" : : "r" (addr)); +} +/*----------------------------------------------------------------------- + */ +static __inline__ unsigned long get_msr(void) +{ + unsigned long msr; + + __asm__ __volatile__ ("mfmsr %0" : "=r" (msr) :); + return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ + __asm__ __volatile__ ("mtmsr %0" : : "r" (msr)); +} diff --git a/board/eltec/mhpc/flash.c b/board/eltec/mhpc/flash.c new file mode 100644 index 00000000000..58cfd7047fd --- /dev/null +++ b/board/eltec/mhpc/flash.c @@ -0,0 +1,453 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> +#include <linux/byteorder/swab.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Protection Flags: + */ +#define FLAG_PROTECT_SET 0x01 +#define FLAG_PROTECT_CLEAR 0x02 + +/* Board support for 1 or 2 flash devices */ +#undef FLASH_PORT_WIDTH32 +#define FLASH_PORT_WIDTH16 + +#ifdef FLASH_PORT_WIDTH16 +#define FLASH_PORT_WIDTH ushort +#define FLASH_PORT_WIDTHV vu_short +#define SWAP(x) __swab16(x) +#else +#define FLASH_PORT_WIDTH ulong +#define FLASH_PORT_WIDTHV vu_long +#define SWAP(x) __swab32(x) +#endif + +#define FPW FLASH_PORT_WIDTH +#define FPWV FLASH_PORT_WIDTHV + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (FPW *addr, flash_info_t *info); +static int write_data (flash_info_t *info, ulong dest, FPW data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + size_b0 = flash_get_size((FPW *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_PS_16 | BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((FPW *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + CFG_FLASH_BASE, + CFG_FLASH_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); + + flash_info[0].size = size_b0; + + return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + return; + } + + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000); + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F640J5 : + printf ("28F640J5 \n"); break; + default: printf ("Unknown Chip Type=0x%lXh\n", + info->flash_id & FLASH_TYPEMASK); break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (FPW *addr, flash_info_t *info) +{ + FPW value; + + /* Write auto select command: read Manufacturer ID */ + addr[0x5555] = (FPW)0xAA00AA00; + addr[0x2AAA] = (FPW)0x55005500; + addr[0x5555] = (FPW)0x90009000; + + value = SWAP(addr[0]); + + switch (value) { + case (FPW)INTEL_MANUFACT: + info->flash_id = FLASH_MAN_INTEL; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + addr[0] = (FPW)0xFF00FF00; /* restore read mode */ + return (0); /* no or unknown flash */ + } + + value = SWAP(addr[1]); /* device ID no swap !*/ + + switch (value) { + case (FPW)INTEL_ID_28F640J5 : + info->flash_id += FLASH_28F640J5 ; + info->sector_count = 64; + info->size = 0x00800000; + break; /* => 8 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + break; + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + addr[0] = (FPW)0xFF00FF00; /* restore read mode */ + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong type, start, now, last; + int rc = 0; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + type = (info->flash_id & FLASH_VENDMASK); + if ((type != FLASH_MAN_INTEL)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + FPWV *addr = (FPWV *)(info->start[sect]); + FPW status; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = (FPW)0x50005000; /* clear status register */ + *addr = (FPW)0x20002000; /* erase setup */ + *addr = (FPW)0xD000D000; /* erase confirm */ + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + while (((status = SWAP(*addr)) & (FPW)0x00800080) != (FPW)0x00800080) { + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + *addr = (FPW)0xB000B000; /* suspend erase */ + *addr = (FPW)0xFF00FF00; /* reset to read mode */ + rc = 1; + break; + } + + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + *addr = (FPW)0xFF00FF00; /* reset to read mode */ + printf (" done\n"); + } + } + return rc; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 4 - Flash not identified + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp; + FPW data; + int count, i, l, rc, port_width; + + if (info->flash_id == FLASH_UNKNOWN) { + return 4; + } +/* get lower word aligned address */ +#ifdef FLASH_PORT_WIDTH16 + wp = (addr & ~1); + port_width = 2; +#else + wp = (addr & ~3); + port_width = 4; +#endif + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<port_width && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<port_width; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += port_width; + } + + /* + * handle word aligned part + */ + count = 0; + while (cnt >= port_width) { + data = 0; + for (i=0; i<port_width; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += port_width; + cnt -= port_width; + if ((wp & 0xfff) == 0) + { + printf("%08lX",wp); + printf("\x1b[8D"); + } + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<port_width && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<port_width; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_data(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word or halfword to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, ulong dest, FPW data) +{ + FPWV *addr = (FPWV *)dest; + ulong status; + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*addr & data) != data) { + printf("not erased at %08lx (%x)\n",(ulong)addr,*addr); + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = (FPW)0x40004000; /* write setup */ + *addr = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + + while (((status = SWAP(*addr)) & (FPW)0x00800080) != (FPW)0x00800080) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + *addr = (FPW)0xFF00FF00; /* restore read mode */ + return (1); + } + } + + *addr = (FPW)0xFF00FF00; /* restore read mode */ + + return (0); +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/board/ep8260/flash.c b/board/ep8260/flash.c new file mode 100644 index 00000000000..cae8a13dde0 --- /dev/null +++ b/board/ep8260/flash.c @@ -0,0 +1,396 @@ +/* + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2002 + * Frank Panno <fpanno@delphintech.com>, Delphin Technology AG + * + * Flash Routines for AMD device AM29DL323DB on the EP8260 board. + * + * This file is based on board/tqm8260/flash.c. + *-------------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +#define V_ULONG(a) (*(volatile unsigned long *)( a )) +#define V_BYTE(a) (*(volatile unsigned char *)( a )) + + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + + +/*----------------------------------------------------------------------- + */ +void flash_reset(void) +{ + if( flash_info[0].flash_id != FLASH_UNKNOWN ) { + V_ULONG( flash_info[0].start[0] ) = 0x00F000F0; + V_ULONG( flash_info[0].start[0] + 4 ) = 0x00F000F0; + } +} + +/*----------------------------------------------------------------------- + */ +ulong flash_get_size( ulong baseaddr, flash_info_t *info ) +{ + short i; + unsigned long flashtest_h, flashtest_l; + + /* Write auto select command sequence and test FLASH answer */ + V_ULONG(baseaddr + ((ulong)0x0555 << 3)) = 0x00AA00AA; + V_ULONG(baseaddr + ((ulong)0x02AA << 3)) = 0x00550055; + V_ULONG(baseaddr + ((ulong)0x0555 << 3)) = 0x00900090; + V_ULONG(baseaddr + 4 + ((ulong)0x0555 << 3)) = 0x00AA00AA; + V_ULONG(baseaddr + 4 + ((ulong)0x02AA << 3)) = 0x00550055; + V_ULONG(baseaddr + 4 + ((ulong)0x0555 << 3)) = 0x00900090; + + flashtest_h = V_ULONG(baseaddr); /* manufacturer ID */ + flashtest_l = V_ULONG(baseaddr + 4); + + if ((int)flashtest_h == AMD_MANUFACT) { + info->flash_id = FLASH_MAN_AMD; + } else { + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + flashtest_h = V_ULONG(baseaddr + 8); /* device ID */ + flashtest_l = V_ULONG(baseaddr + 12); + if (flashtest_h != flashtest_l) { + info->flash_id = FLASH_UNKNOWN; + return(0); + } + if (flashtest_h == AMD_ID_DL323B) { + info->flash_id += FLASH_AMDL323B; + info->sector_count = 71; + info->size = 0x01000000; /* 4 * 4 MB = 16 MB */ + } else { + info->flash_id = FLASH_UNKNOWN; + return(0); /* no or unknown flash */ + } + + /* set up sector start adress table (bottom sector type) */ + for (i = 0; i < 8; i++) { + info->start[i] = baseaddr + (i * 0x00008000); + } + for (i = 8; i < info->sector_count; i++) { + info->start[i] = baseaddr + (i * 0x00040000) - 0x001C0000; + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + if ((V_ULONG( info->start[i] + 16 ) & 0x00010001) || + (V_ULONG( info->start[i] + 20 ) & 0x00010001)) { + info->protect[i] = 1; /* D0 = 1 if protected */ + } else { + info->protect[i] = 0; + } + } + + flash_reset(); + return(info->size); +} + +/*----------------------------------------------------------------------- + */ +unsigned long flash_init (void) +{ + unsigned long size_b0 = 0; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here (only one bank) */ + + size_b0 = flash_get_size(CFG_FLASH0_BASE, &flash_info[0]); + if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b0 == 0) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0>>20); + } + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE >= CFG_FLASH0_BASE + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#if (CFG_ENV_IS_IN_FLASH == 1) && defined(CFG_ENV_ADDR) +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, + &flash_info[0]); +#endif + + return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch ((info->flash_id >> 16) & 0xff) { + case FLASH_MAN_AMD: printf ("AMD "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AMDL323B: printf ("29DL323B (32 M, bottom sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) + prot++; + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + V_ULONG( info->start[0] + (0x0555 << 3) ) = 0x00AA00AA; + V_ULONG( info->start[0] + (0x02AA << 3) ) = 0x00550055; + V_ULONG( info->start[0] + (0x0555 << 3) ) = 0x00800080; + V_ULONG( info->start[0] + (0x0555 << 3) ) = 0x00AA00AA; + V_ULONG( info->start[0] + (0x02AA << 3) ) = 0x00550055; + V_ULONG( info->start[0] + 4 + (0x0555 << 3) ) = 0x00AA00AA; + V_ULONG( info->start[0] + 4 + (0x02AA << 3) ) = 0x00550055; + V_ULONG( info->start[0] + 4 + (0x0555 << 3) ) = 0x00800080; + V_ULONG( info->start[0] + 4 + (0x0555 << 3) ) = 0x00AA00AA; + V_ULONG( info->start[0] + 4 + (0x02AA << 3) ) = 0x00550055; + udelay (1000); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + V_ULONG( info->start[sect] ) = 0x00300030; + V_ULONG( info->start[sect] + 4 ) = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + while ((V_ULONG( info->start[l_sect] ) & 0x00800080) != 0x00800080 || + (V_ULONG( info->start[l_sect] + 4 ) & 0x00800080) != 0x00800080) + { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + serial_putc ('.'); + last = now; + } + } + + DONE: + /* reset to read mode */ + flash_reset (); + + printf (" done\n"); + return 0; +} + +static int write_dword (flash_info_t *, ulong, unsigned char *); + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong dp; + static unsigned char bb[8]; + int i, l, rc, cc = cnt; + + dp = (addr & ~7); /* get lower dword aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - dp) != 0) { + for (i = 0; i < 8; i++) + bb[i] = (i < l || (i-l) >= cc) ? V_BYTE(dp+i) : *src++; + if ((rc = write_dword(info, dp, bb)) != 0) + { + return (rc); + } + dp += 8; + cc -= 8 - l; + } + + /* + * handle word aligned part + */ + while (cc >= 8) { + if ((rc = write_dword(info, dp, src)) != 0) { + return (rc); + } + dp += 8; + src += 8; + cc -= 8; + } + + if (cc <= 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + for (i = 0; i < 8; i++) { + bb[i] = (i < cc) ? *src++ : V_BYTE(dp+i); + } + return (write_dword(info, dp, bb)); +} + +/*----------------------------------------------------------------------- + * Write a dword to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_dword (flash_info_t *info, ulong dest, unsigned char * pdata) +{ + ulong start; + ulong cl = 0, ch =0; + int flag, i; + + for (ch=0, i=0; i < 4; i++) + ch = (ch << 8) + *pdata++; /* high word */ + for (cl=0, i=0; i < 4; i++) + cl = (cl << 8) + *pdata++; /* low word */ + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & ch) != ch + ||(*((vu_long *)(dest + 4)) & cl) != cl) + { + return (2); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + V_ULONG( info->start[0] + (0x0555 << 3) ) = 0x00AA00AA; + V_ULONG( info->start[0] + (0x02AA << 3) ) = 0x00550055; + V_ULONG( info->start[0] + (0x0555 << 3) ) = 0x00A000A0; + V_ULONG( dest ) = ch; + V_ULONG( info->start[0] + 4 + (0x0555 << 3) ) = 0x00AA00AA; + V_ULONG( info->start[0] + 4 + (0x02AA << 3) ) = 0x00550055; + V_ULONG( info->start[0] + 4 + (0x0555 << 3) ) = 0x00A000A0; + V_ULONG( dest + 4 ) = cl; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while (((V_ULONG( dest ) & 0x00800080) != (ch & 0x00800080)) || + ((V_ULONG( dest + 4 ) & 0x00800080) != (cl & 0x00800080))) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} diff --git a/board/ep8260/mii_phy.c b/board/ep8260/mii_phy.c new file mode 100644 index 00000000000..e3b7878aa1e --- /dev/null +++ b/board/ep8260/mii_phy.c @@ -0,0 +1,108 @@ +#include <common.h> +#include <mii_phy.h> +#include "ep8260.h" + +#define MII_MDIO 0x01 +#define MII_MDCK 0x02 +#define MII_MDIR 0x04 + +void +mii_discover_phy(void) +{ + int known; + unsigned short phy_reg; + unsigned long phy_id; + + known = 0; + printf("Discovering phy @ 0: "); + phy_id = mii_phy_read(2) << 16; + phy_id |= mii_phy_read(3); + if ((phy_id & 0xFFFFFC00) == 0x00137800) { + printf("Level One "); + if ((phy_id & 0x000003F0) == 0xE0) { + printf("LXT971A Revision %d\n", (int)(phy_id & 0xF)); + known = 1; + } + else printf("unknown type\n"); + } + else printf("unknown OUI = 0x%08lX\n", phy_id); + + phy_reg = mii_phy_read(1); + if (!(phy_reg & 0x0004)) printf("Link is down\n"); + if (!(phy_reg & 0x0020)) printf("Auto-negotiation not complete\n"); + if (phy_reg & 0x0002) printf("Jabber condition detected\n"); + if (phy_reg & 0x0010) printf("Remote fault condition detected \n"); + + if (known) { + phy_reg = mii_phy_read(17); + if (phy_reg & 0x0400) + printf("Phy operating at %d MBit/s in %s-duplex mode\n", + phy_reg & 0x4000 ? 100 : 10, + phy_reg & 0x0200 ? "full" : "half"); + else + printf("bad link!!\n"); +/* +left off: no link, green 100MBit, yellow 10MBit +right off: no activity, green full-duplex, yellow half-duplex +*/ + mii_phy_write(20, 0x0452); + } +} + +unsigned short +mii_phy_read(unsigned short reg) +{ + int i; + unsigned short tmp, val = 0, adr = 0; + t_ep_regs *regs = (t_ep_regs*)CFG_REGS_BASE; + + tmp = 0x6002 | (adr << 7) | (reg << 2); + regs->bcsr4 = 0xC3; + for (i = 0; i < 64; i++) { + regs->bcsr4 ^= MII_MDCK; + } + for (i = 0; i < 16; i++) { + regs->bcsr4 &= ~MII_MDCK; + if (tmp & 0x8000) regs->bcsr4 |= MII_MDIO; + else regs->bcsr4 &= ~MII_MDIO; + regs->bcsr4 |= MII_MDCK; + tmp <<= 1; + } + regs->bcsr4 |= MII_MDIR; + for (i = 0; i < 16; i++) { + val <<= 1; + regs->bcsr4 = MII_MDIO | (regs->bcsr4 | MII_MDCK); + if (regs->bcsr4 & MII_MDIO) val |= 1; + regs->bcsr4 = MII_MDIO | (regs->bcsr4 &= ~MII_MDCK); + } + return val; +} + +void +mii_phy_write(unsigned short reg, unsigned short val) +{ + int i; + unsigned short tmp, adr = 0; + t_ep_regs *regs = (t_ep_regs*)CFG_REGS_BASE; + + tmp = 0x5002 | (adr << 7) | (reg << 2); + regs->bcsr4 = 0xC3; + for (i = 0; i < 64; i++) { + regs->bcsr4 ^= MII_MDCK; + } + for (i = 0; i < 16; i++) { + regs->bcsr4 &= ~MII_MDCK; + if (tmp & 0x8000) regs->bcsr4 |= MII_MDIO; + else regs->bcsr4 &= ~MII_MDIO; + regs->bcsr4 |= MII_MDCK; + tmp <<= 1; + } + for (i = 0; i < 16; i++) { + regs->bcsr4 &= ~MII_MDCK; + if (val & 0x8000) regs->bcsr4 |= MII_MDIO; + else regs->bcsr4 &= ~MII_MDIO; + regs->bcsr4 |= MII_MDCK; + val <<= 1; + } +} + diff --git a/board/esd/adciop/flash.c b/board/esd/adciop/flash.c new file mode 100644 index 00000000000..46ae03b3c06 --- /dev/null +++ b/board/esd/adciop/flash.c @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* + * include common flash code (for esd boards) + */ +#include "../common/flash.c" + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + flash_get_offsets (FLASH_BASE0_PRELIM, &flash_info[0]); + + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + FLASH_BASE0_PRELIM+size_b0-CFG_MONITOR_LEN, + FLASH_BASE0_PRELIM+size_b0-1, + &flash_info[0]); + + if (size_b1) { + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(FLASH_BASE0_PRELIM + size_b0), + &flash_info[1]); + + flash_get_offsets (FLASH_BASE0_PRELIM + size_b0, &flash_info[1]); + + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + FLASH_BASE0_PRELIM+size_b0+size_b1-CFG_MONITOR_LEN, + FLASH_BASE0_PRELIM+size_b0+size_b1-1, + &flash_info[1]); + /* monitor protection OFF by default (one is enough) */ + flash_protect(FLAG_PROTECT_CLEAR, + FLASH_BASE0_PRELIM+size_b0-CFG_MONITOR_LEN, + FLASH_BASE0_PRELIM+size_b0-1, + &flash_info[0]); + } else { + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} diff --git a/board/esd/common/flash.c b/board/esd/common/flash.c new file mode 100644 index 00000000000..78a1e0f2987 --- /dev/null +++ b/board/esd/common/flash.c @@ -0,0 +1,646 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + short n; + + /* set up sector start address table */ + if (((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM640U)) { + for (i = 0; i < info->sector_count; i++) + info->start[i] = base + (i * 0x00010000); + } else if (((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL322B) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL323B) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM320B) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL324B)) { + /* set sector offsets for bottom boot block type */ + for (i=0; i<8; ++i) { /* 8 x 8k boot sectors */ + info->start[i] = base; + base += 8 << 10; + } + while (i < info->sector_count) { /* 64k regular sectors */ + info->start[i] = base; + base += 64 << 10; + ++i; + } + } else if (((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL322T) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL323T) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM320T) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL324T)) { + /* set sector offsets for top boot block type */ + base += info->size; + i = info->sector_count; + for (n=0; n<8; ++n) { /* 8 x 8k boot sectors */ + base -= 8 << 10; + --i; + info->start[i] = base; + } + while (i > 0) { /* 64k regular sectors */ + base -= 64 << 10; + --i; + info->start[i] = base; + } + } else { + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + int k; + int size; + int erased; + volatile unsigned long *flash; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_SST: printf ("SST "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 M, top sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 M, bottom sector)\n"); + break; + case FLASH_AMDL322T: printf ("AM29DL322T (32 M, top sector)\n"); + break; + case FLASH_AMDL322B: printf ("AM29DL322B (32 M, bottom sector)\n"); + break; + case FLASH_AMDL323T: printf ("AM29DL323T (32 M, top sector)\n"); + break; + case FLASH_AMDL323B: printf ("AM29DL323B (32 M, bottom sector)\n"); + break; + case FLASH_AM640U: printf ("AM29LV640D (64 M, uniform sector)\n"); + break; + case FLASH_SST800A: printf ("SST39LF/VF800 (8 Mbit, uniform sector size)\n"); + break; + case FLASH_SST160A: printf ("SST39LF/VF160 (16 Mbit, uniform sector size)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { +#ifdef CFG_FLASH_EMPTY_INFO + /* + * Check if whole sector is erased + */ + if (i != (info->sector_count-1)) + size = info->start[i+1] - info->start[i]; + else + size = info->start[0] + info->size - info->start[i]; + erased = 1; + flash = (volatile unsigned long *)info->start[i]; + size = size >> 2; /* divide by 4 for longword access */ + for (k=0; k<size; k++) + { + if (*flash++ != 0xffffffff) + { + erased = 0; + break; + } + } + + if ((i % 5) == 0) + printf ("\n "); + /* print empty and read-only info */ + printf (" %08lX%s%s", + info->start[i], + erased ? " E" : " ", + info->protect[i] ? "RO " : " "); +#else + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " "); +#endif + + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + short n; + CFG_FLASH_WORD_SIZE value; + ulong base = (ulong)addr; + volatile CFG_FLASH_WORD_SIZE *addr2 = (CFG_FLASH_WORD_SIZE *)addr; + + /* Write auto select command: read Manufacturer ID */ + addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA; + addr2[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055; + addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00900090; + + value = addr2[CFG_FLASH_READ0]; + + switch (value) { + case (CFG_FLASH_WORD_SIZE)AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case (CFG_FLASH_WORD_SIZE)FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + case (CFG_FLASH_WORD_SIZE)SST_MANUFACT: + info->flash_id = FLASH_MAN_SST; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr2[CFG_FLASH_READ1]; /* device ID */ + + switch (value) { + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00080000; + break; /* => 0.5 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00080000; + break; /* => 0.5 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 71; + info->size = 0x00400000; break; /* => 4 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 71; + info->size = 0x00400000; break; /* => 4 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_DL322T: + info->flash_id += FLASH_AMDL322T; + info->sector_count = 71; + info->size = 0x00400000; break; /* => 4 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_DL322B: + info->flash_id += FLASH_AMDL322B; + info->sector_count = 71; + info->size = 0x00400000; break; /* => 4 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_DL323T: + info->flash_id += FLASH_AMDL323T; + info->sector_count = 71; + info->size = 0x00400000; break; /* => 4 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_DL323B: + info->flash_id += FLASH_AMDL323B; + info->sector_count = 71; + info->size = 0x00400000; break; /* => 4 MB */ + + case (CFG_FLASH_WORD_SIZE)AMD_ID_LV640U: + info->flash_id += FLASH_AM640U; + info->sector_count = 128; + info->size = 0x00800000; break; /* => 8 MB */ + + case (CFG_FLASH_WORD_SIZE)SST_ID_xF800A: + info->flash_id += FLASH_SST800A; + info->sector_count = 16; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (CFG_FLASH_WORD_SIZE)SST_ID_xF160A: + info->flash_id += FLASH_SST160A; + info->sector_count = 32; + info->size = 0x00200000; + break; /* => 2 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + if (((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM640U)) { + for (i = 0; i < info->sector_count; i++) + info->start[i] = base + (i * 0x00010000); + } else if (((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL322B) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL323B) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM320B) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL324B)) { + /* set sector offsets for bottom boot block type */ + for (i=0; i<8; ++i) { /* 8 x 8k boot sectors */ + info->start[i] = base; + base += 8 << 10; + } + while (i < info->sector_count) { /* 64k regular sectors */ + info->start[i] = base; + base += 64 << 10; + ++i; + } + } else if (((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL322T) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL323T) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM320T) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AMDL324T)) { + /* set sector offsets for top boot block type */ + base += info->size; + i = info->sector_count; + for (n=0; n<8; ++n) { /* 8 x 8k boot sectors */ + base -= 8 << 10; + --i; + info->start[i] = base; + } + while (i > 0) { /* 64k regular sectors */ + base -= 64 << 10; + --i; + info->start[i] = base; + } + } else { + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr2 = (volatile CFG_FLASH_WORD_SIZE *)(info->start[i]); + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) + info->protect[i] = 0; + else + info->protect[i] = addr2[CFG_FLASH_READ2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr2 = (CFG_FLASH_WORD_SIZE *)info->start[0]; + *addr2 = (CFG_FLASH_WORD_SIZE)0x00F000F0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + volatile CFG_FLASH_WORD_SIZE *addr = (CFG_FLASH_WORD_SIZE *)(info->start[0]); + volatile CFG_FLASH_WORD_SIZE *addr2; + int flag, prot, sect, l_sect; + ulong start, now, last; + int i; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[sect]); + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) { + addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA; + addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055; + addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080; + addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA; + addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055; + addr2[0] = (CFG_FLASH_WORD_SIZE)0x00500050; /* block erase */ + for (i=0; i<50; i++) + udelay(1000); /* wait 1 ms */ + } else { + if (sect == s_first) { + addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA; + addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055; + addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080; + addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA; + addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055; + } + addr2[0] = (CFG_FLASH_WORD_SIZE)0x00300030; /* sector erase */ + } + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (CFG_FLASH_WORD_SIZE *)(info->start[l_sect]); + while ((addr[0] & (CFG_FLASH_WORD_SIZE)0x00800080) != (CFG_FLASH_WORD_SIZE)0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (CFG_FLASH_WORD_SIZE *)info->start[0]; + addr[0] = (CFG_FLASH_WORD_SIZE)0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + volatile CFG_FLASH_WORD_SIZE *addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[0]); + volatile CFG_FLASH_WORD_SIZE *dest2 = (CFG_FLASH_WORD_SIZE *)dest; + volatile CFG_FLASH_WORD_SIZE *data2 = (CFG_FLASH_WORD_SIZE *)&data; + ulong start; + int flag; + int i; + + /* Check if Flash is (sufficiently) erased */ + if ((*((volatile CFG_FLASH_WORD_SIZE *)dest) & + (CFG_FLASH_WORD_SIZE)data) != (CFG_FLASH_WORD_SIZE)data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + for (i=0; i<4/sizeof(CFG_FLASH_WORD_SIZE); i++) + { + addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA; + addr2[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055; + addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00A000A0; + + dest2[i] = data2[i]; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((dest2[i] & (CFG_FLASH_WORD_SIZE)0x00800080) != + (data2[i] & (CFG_FLASH_WORD_SIZE)0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + } + + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/esd/cpci405/flash.c b/board/esd/cpci405/flash.c new file mode 100644 index 00000000000..70f7de4f083 --- /dev/null +++ b/board/esd/cpci405/flash.c @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* + * include common flash code (for esd boards) + */ +#include "../common/flash.c" + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long * addr, flash_info_t * info); +static void flash_get_offsets (ulong base, flash_info_t * info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + uint pbcr; + unsigned long base_b0, base_b1; + int size_val = 0; + + /* Init: no FLASHes known */ + for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + base_b0 = FLASH_BASE0_PRELIM; + size_b0 = flash_get_size ((vu_long *) base_b0, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0 << 20); + } + + base_b1 = FLASH_BASE1_PRELIM; + size_b1 = flash_get_size ((vu_long *) base_b1, &flash_info[1]); + + /* Re-do sizing to get full correct info */ + + if (size_b1) { + mtdcr (ebccfga, pb0cr); + pbcr = mfdcr (ebccfgd); + mtdcr (ebccfga, pb0cr); + base_b1 = -size_b1; + switch (size_b1) { + case 1 << 20: + size_val = 0; + break; + case 2 << 20: + size_val = 1; + break; + case 4 << 20: + size_val = 2; + break; + case 8 << 20: + size_val = 3; + break; + case 16 << 20: + size_val = 4; + break; + default: + size_val = 0; + break; + + } + pbcr = (pbcr & 0x0001ffff) | base_b1 | (size_val << 17); + mtdcr (ebccfgd, pbcr); + /* printf("pb1cr = %x\n", pbcr); */ + } + + if (size_b0) { + mtdcr (ebccfga, pb1cr); + pbcr = mfdcr (ebccfgd); + mtdcr (ebccfga, pb1cr); + base_b0 = base_b1 - size_b0; + switch (size_b1) { + case 1 << 20: + size_val = 0; + break; + case 2 << 20: + size_val = 1; + break; + case 4 << 20: + size_val = 2; + break; + case 8 << 20: + size_val = 3; + break; + case 16 << 20: + size_val = 4; + break; + } + pbcr = (pbcr & 0x0001ffff) | base_b0 | (size_val << 17); + mtdcr (ebccfgd, pbcr); + /* printf("pb0cr = %x\n", pbcr); */ + } + + size_b0 = flash_get_size ((vu_long *) base_b0, &flash_info[0]); + + flash_get_offsets (base_b0, &flash_info[0]); + + /* monitor protection ON by default */ + flash_protect (FLAG_PROTECT_SET, + base_b0 + size_b0 - CFG_MONITOR_LEN, + base_b0 + size_b0 - 1, &flash_info[0]); + + if (size_b1) { + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size ((vu_long *) base_b1, &flash_info[1]); + + flash_get_offsets (base_b1, &flash_info[1]); + + /* monitor protection ON by default */ + flash_protect (FLAG_PROTECT_SET, + base_b1 + size_b1 - CFG_MONITOR_LEN, + base_b1 + size_b1 - 1, &flash_info[1]); + /* monitor protection OFF by default (one is enough) */ + flash_protect (FLAG_PROTECT_CLEAR, + base_b0 + size_b0 - CFG_MONITOR_LEN, + base_b0 + size_b0 - 1, &flash_info[0]); + } else { + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} diff --git a/board/esd/dasa_sim/cmd_dasa_sim.c b/board/esd/dasa_sim/cmd_dasa_sim.c new file mode 100644 index 00000000000..4608da71a0b --- /dev/null +++ b/board/esd/dasa_sim/cmd_dasa_sim.c @@ -0,0 +1,236 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + * + */ + +#include <common.h> +#include <command.h> +#include <pci.h> + +#define OK 0 +#define ERROR (-1) + +#define TRUE 1 +#define FALSE 0 + + +extern u_long pci9054_iobase; + + +/*************************************************************************** + * + * Routines for PLX PCI9054 eeprom access + * + */ + +static unsigned int PciEepromReadLongVPD(int offs) +{ + unsigned int value; + unsigned int ret; + int count; + + pci_write_config_dword(CFG_PCI9054_DEV_FN, 0x4c, (offs<<16) | 0x0003); + count = 0; + + for (;;) + { + udelay(10 * 1000); + pci_read_config_dword(CFG_PCI9054_DEV_FN, 0x4c, &ret); + if ((ret & 0x80000000) != 0) + { + break; + } + else + { + count++; + if (count > 10) + { + printf("\nTimeout: ret=%08x - Please try again!\n", ret); + break; + } + } + } + + pci_read_config_dword(CFG_PCI9054_DEV_FN, 0x50, &value); + + return value; +} + + +static int PciEepromWriteLongVPD(int offs, unsigned int value) +{ + unsigned int ret; + int count; + + pci_write_config_dword(CFG_PCI9054_DEV_FN, 0x50, value); + pci_write_config_dword(CFG_PCI9054_DEV_FN, 0x4c, (offs<<16) | 0x80000003); + count = 0; + + for (;;) + { + udelay(10 * 1000); + pci_read_config_dword(CFG_PCI9054_DEV_FN, 0x4c, &ret); + if ((ret & 0x80000000) == 0) + { + break; + } + else + { + count++; + if (count > 10) + { + printf("\nTimeout: ret=%08x - Please try again!\n", ret); + break; + } + } + } + + return TRUE; +} + + +static void showPci9054(void) +{ + int val; + int l, i; + + /* read 9054-values */ + for (l=0; l<6; l++) + { + printf("%02x: ", l*0x10); + for (i=0; i<4; i++) + { + pci_read_config_dword(CFG_PCI9054_DEV_FN, l*16+i*4, &val); + printf("%08x ", val); + } + printf("\n"); + } + printf("\n"); + + for (l=0; l<7; l++) + { + printf("%02x: ", l*0x10); + for (i=0; i<4; i++) + printf("%08x ", PciEepromReadLongVPD((i+l*4)*4)); + printf("\n"); + } + printf("\n"); +} + + +static void updatePci9054(void) +{ + int val; + + /* + * Set EEPROM write-protect register to 0 + */ + out32(pci9054_iobase+0x0c, in32(pci9054_iobase+0x0c) & 0xffff00ff); + + /* Long Serial EEPROM Load Registers... */ + val = PciEepromWriteLongVPD(0x00, 0x905410b5); + val = PciEepromWriteLongVPD(0x04, 0x09800001); /* other input controller */ + val = PciEepromWriteLongVPD(0x08, 0x28140100); + + val = PciEepromWriteLongVPD(0x0c, 0x00000000); /* MBOX0... */ + val = PciEepromWriteLongVPD(0x10, 0x00000000); + + /* las0: fpga access (0x0000.0000 ... 0x0003.ffff) */ + val = PciEepromWriteLongVPD(0x14, 0xfffc0000); /* LAS0RR... */ + val = PciEepromWriteLongVPD(0x18, 0x00000001); /* LAS0BA */ + + val = PciEepromWriteLongVPD(0x1c, 0x00200000); /* MARBR... */ + val = PciEepromWriteLongVPD(0x20, 0x00300500); /* LMISC/BIGEND */ + + val = PciEepromWriteLongVPD(0x24, 0x00000000); /* EROMRR... */ + val = PciEepromWriteLongVPD(0x28, 0x00000000); /* EROMBA */ + + val = PciEepromWriteLongVPD(0x2c, 0x43030000); /* LBRD0... */ + + val = PciEepromWriteLongVPD(0x30, 0x00000000); /* DMRR... */ + val = PciEepromWriteLongVPD(0x34, 0x00000000); + val = PciEepromWriteLongVPD(0x38, 0x00000000); + + val = PciEepromWriteLongVPD(0x3c, 0x00000000); /* DMPBAM... */ + val = PciEepromWriteLongVPD(0x40, 0x00000000); + + /* Extra Long Serial EEPROM Load Registers... */ + val = PciEepromWriteLongVPD(0x44, 0x010212fe); /* PCISID... */ + + /* las1: 505-sram access (0x0004.0000 ... 0x001f.ffff) */ + /* Offset to LAS1: Group 1: 0x00040000 */ + /* Group 2: 0x00080000 */ + /* Group 3: 0x000c0000 */ + val = PciEepromWriteLongVPD(0x48, 0xffe00000); /* LAS1RR */ + val = PciEepromWriteLongVPD(0x4c, 0x00040001); /* LAS1BA */ + val = PciEepromWriteLongVPD(0x50, 0x00000208); /* LBRD1 */ /* so wars bisher */ + + val = PciEepromWriteLongVPD(0x54, 0x00004c06); /* HotSwap... */ + + printf("Finished writing defaults into PLX PCI9054 EEPROM!\n"); +} + + +static void clearPci9054(void) +{ + int val; + + /* + * Set EEPROM write-protect register to 0 + */ + out32(pci9054_iobase+0x0c, in32(pci9054_iobase+0x0c) & 0xffff00ff); + + /* Long Serial EEPROM Load Registers... */ + val = PciEepromWriteLongVPD(0x00, 0xffffffff); + val = PciEepromWriteLongVPD(0x04, 0xffffffff); /* other input controller */ + + printf("Finished clearing PLX PCI9054 EEPROM!\n"); +} + + +/* ------------------------------------------------------------------------- */ +int do_pci9054(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[]) +{ + if (strcmp(argv[1], "info") == 0) + { + showPci9054(); + return 0; + } + + if (strcmp(argv[1], "update") == 0) + { + updatePci9054(); + return 0; + } + + if (strcmp(argv[1], "clear") == 0) + { + clearPci9054(); + return 0; + } + + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + +} + +/* ------------------------------------------------------------------------- */ diff --git a/board/esd/dasa_sim/flash.c b/board/esd/dasa_sim/flash.c new file mode 100644 index 00000000000..2574eac7fdc --- /dev/null +++ b/board/esd/dasa_sim/flash.c @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* + * include common flash code (for esd boards) + */ +#include "../common/flash.c" + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static void flash_get_offsets (ulong base, flash_info_t *info); + + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0; + int i; + unsigned long base_b0; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + /* Setup offsets */ + flash_get_offsets (-size_b0, &flash_info[0]); + + base_b0 = -size_b0; + + /* Monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + -CFG_MONITOR_LEN, + 0xffffffff, + &flash_info[0]); + + flash_info[0].size = size_b0; + + return (size_b0); +} diff --git a/board/esd/du405/flash.c b/board/esd/du405/flash.c new file mode 100644 index 00000000000..97d832228ea --- /dev/null +++ b/board/esd/du405/flash.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* + * include common flash code (for esd boards) + */ +#include "../common/flash.c" + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long * addr, flash_info_t * info); +static void flash_get_offsets (ulong base, flash_info_t * info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + uint pbcr; + unsigned long base_b0, base_b1; + + /* Init: no FLASHes known */ + for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + base_b0 = FLASH_BASE0_PRELIM; + size_b0 = flash_get_size ((vu_long *) base_b0, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0 << 20); + } + + base_b1 = FLASH_BASE1_PRELIM; + size_b1 = flash_get_size ((vu_long *) base_b1, &flash_info[1]); + + /* Re-do sizing to get full correct info */ + + if (size_b1) { + mtdcr (ebccfga, pb0cr); + pbcr = mfdcr (ebccfgd); + mtdcr (ebccfga, pb0cr); + base_b1 = -size_b1; + pbcr = (pbcr & 0x0001ffff) | base_b1 | + (((size_b1 / 1024 / 1024) - 1) << 17); + mtdcr (ebccfgd, pbcr); + /* printf("pb1cr = %x\n", pbcr); */ + } + + if (size_b0) { + mtdcr (ebccfga, pb1cr); + pbcr = mfdcr (ebccfgd); + mtdcr (ebccfga, pb1cr); + base_b0 = base_b1 - size_b0; + pbcr = (pbcr & 0x0001ffff) | base_b0 | + (((size_b0 / 1024 / 1024) - 1) << 17); + mtdcr (ebccfgd, pbcr); + /* printf("pb0cr = %x\n", pbcr); */ + } + + size_b0 = flash_get_size ((vu_long *) base_b0, &flash_info[0]); + + flash_get_offsets (base_b0, &flash_info[0]); + + /* monitor protection ON by default */ + flash_protect (FLAG_PROTECT_SET, + base_b0 + size_b0 - CFG_MONITOR_LEN, + base_b0 + size_b0 - 1, &flash_info[0]); + + if (size_b1) { + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size ((vu_long *) base_b1, &flash_info[1]); + + flash_get_offsets (base_b1, &flash_info[1]); + + /* monitor protection ON by default */ + flash_protect (FLAG_PROTECT_SET, + base_b1 + size_b1 - CFG_MONITOR_LEN, + base_b1 + size_b1 - 1, &flash_info[1]); + /* monitor protection OFF by default (one is enough) */ + flash_protect (FLAG_PROTECT_CLEAR, + base_b0 + size_b0 - CFG_MONITOR_LEN, + base_b0 + size_b0 - 1, &flash_info[0]); + } else { + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} diff --git a/board/esd/ocrtc/flash.c b/board/esd/ocrtc/flash.c new file mode 100644 index 00000000000..90965eadf5f --- /dev/null +++ b/board/esd/ocrtc/flash.c @@ -0,0 +1,156 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* + * include common flash code (for esd boards) + */ +#include "../common/flash.c" + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long * addr, flash_info_t * info); +static void flash_get_offsets (ulong base, flash_info_t * info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + uint pbcr; + unsigned long base_b0, base_b1; + int size_val = 0; + + /* Init: no FLASHes known */ + for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + base_b0 = FLASH_BASE0_PRELIM; + size_b0 = flash_get_size ((vu_long *) base_b0, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0 << 20); + } + + base_b1 = FLASH_BASE1_PRELIM; + size_b1 = flash_get_size ((vu_long *) base_b1, &flash_info[1]); + + /* Re-do sizing to get full correct info */ + + if (size_b1) { + mtdcr (ebccfga, pb0cr); + pbcr = mfdcr (ebccfgd); + mtdcr (ebccfga, pb0cr); + base_b1 = -size_b1; + switch (size_b1) { + case 1 << 20: + size_val = 0; + break; + case 2 << 20: + size_val = 1; + break; + case 4 << 20: + size_val = 2; + break; + case 8 << 20: + size_val = 3; + break; + case 16 << 20: + size_val = 4; + break; + } + pbcr = (pbcr & 0x0001ffff) | base_b1 | (size_val << 17); + mtdcr (ebccfgd, pbcr); + /* printf("pb1cr = %x\n", pbcr); */ + } + + if (size_b0) { + mtdcr (ebccfga, pb1cr); + pbcr = mfdcr (ebccfgd); + mtdcr (ebccfga, pb1cr); + base_b0 = base_b1 - size_b0; + switch (size_b1) { + case 1 << 20: + size_val = 0; + break; + case 2 << 20: + size_val = 1; + break; + case 4 << 20: + size_val = 2; + break; + case 8 << 20: + size_val = 3; + break; + case 16 << 20: + size_val = 4; + break; + } + pbcr = (pbcr & 0x0001ffff) | base_b0 | (size_val << 17); + mtdcr (ebccfgd, pbcr); + /* printf("pb0cr = %x\n", pbcr); */ + } + + size_b0 = flash_get_size ((vu_long *) base_b0, &flash_info[0]); + + flash_get_offsets (base_b0, &flash_info[0]); + + /* monitor protection ON by default */ + flash_protect (FLAG_PROTECT_SET, + base_b0 + size_b0 - CFG_MONITOR_LEN, + base_b0 + size_b0 - 1, &flash_info[0]); + + if (size_b1) { + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size ((vu_long *) base_b1, &flash_info[1]); + + flash_get_offsets (base_b1, &flash_info[1]); + + /* monitor protection ON by default */ + flash_protect (FLAG_PROTECT_SET, + base_b1 + size_b1 - CFG_MONITOR_LEN, + base_b1 + size_b1 - 1, &flash_info[1]); + /* monitor protection OFF by default (one is enough) */ + flash_protect (FLAG_PROTECT_CLEAR, + base_b0 + size_b0 - CFG_MONITOR_LEN, + base_b0 + size_b0 - 1, &flash_info[0]); + } else { + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} diff --git a/board/esd/pci405/flash.c b/board/esd/pci405/flash.c new file mode 100644 index 00000000000..f904affc4f6 --- /dev/null +++ b/board/esd/pci405/flash.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2001 + * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com + * + * 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 + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> + +/* + * include common flash code (for esd boards) + */ +#include "../common/flash.c" + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long * addr, flash_info_t * info); +static void flash_get_offsets (ulong base, flash_info_t * info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0; + int i; + uint pbcr; + unsigned long base_b0; + int size_val = 0; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + /* Setup offsets */ + flash_get_offsets (-size_b0, &flash_info[0]); + + /* Re-do sizing to get full correct info */ + mtdcr(ebccfga, pb0cr); + pbcr = mfdcr(ebccfgd); + mtdcr(ebccfga, pb0cr); + base_b0 = -size_b0; + switch (size_b0) { + case 1 << 20: + size_val = 0; + break; + case 2 << 20: + size_val = 1; + break; + case 4 << 20: + size_val = 2; + break; + case 8 << 20: + size_val = 3; + break; + case 16 << 20: + size_val = 4; + break; + } + pbcr = (pbcr & 0x0001ffff) | base_b0 | (size_val << 17); + mtdcr(ebccfgd, pbcr); + + /* Monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + -CFG_MONITOR_LEN, + 0xffffffff, + &flash_info[0]); + + flash_info[0].size = size_b0; + + return (size_b0); +} diff --git a/board/esteem192e/flash.c b/board/esteem192e/flash.c new file mode 100644 index 00000000000..55845fac111 --- /dev/null +++ b/board/esteem192e/flash.c @@ -0,0 +1,1096 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#ifdef CONFIG_FLASH_16BIT +#define FLASH_WORD_SIZE unsigned short +#define FLASH_ID_MASK 0xFFFF +#else +#define FLASH_WORD_SIZE unsigned long +#define FLASH_ID_MASK 0xFFFFFFFF +#endif + +/*----------------------------------------------------------------------- + * Functions + */ + +ulong flash_get_size (volatile FLASH_WORD_SIZE *addr, flash_info_t *info); +#ifndef CONFIG_FLASH_16BIT +static int write_word (flash_info_t *info, ulong dest, ulong data); +#else +static int write_short (flash_info_t *info, ulong dest, ushort data); +#endif +/*int flash_write (uchar *, ulong, ulong); */ +/*flash_info_t *addr2info (ulong); */ + +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((volatile FLASH_WORD_SIZE *)FLASH_BASE0_PRELIM, + &flash_info[0]); + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + size_b1 = flash_get_size((volatile FLASH_WORD_SIZE *)FLASH_BASE1_PRELIM, + &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = CFG_FLASH_BASE | 0x00000801; /* (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V;*/ + + /* Re-do sizing to get full correct info */ + + size_b0 = flash_get_size((volatile FLASH_WORD_SIZE *)CFG_FLASH_BASE, + &flash_info[0]); + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + if (size_b1) { + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size_b1 & 0xFFFF8000); + memctl->memc_br1 = (CFG_FLASH_BASE | 0x00000801) + (size_b0 & BR_BA_MSK); + /*((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_V;*/ + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((volatile FLASH_WORD_SIZE *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + } else { + memctl->memc_br1 = 0; /* invalidate bank */ + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start adress table */ + if (info->flash_id & FLASH_BTYPE) { + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { + +#ifndef CONFIG_FLASH_16BIT + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00008000; + info->start[3] = base + 0x0000C000; + info->start[4] = base + 0x00010000; + info->start[5] = base + 0x00014000; + info->start[6] = base + 0x00018000; + info->start[7] = base + 0x0001C000; + for (i = 8; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x000E0000; + } + } + else { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } +#else + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00002000; + info->start[2] = base + 0x00004000; + info->start[3] = base + 0x00006000; + info->start[4] = base + 0x00008000; + info->start[5] = base + 0x0000A000; + info->start[6] = base + 0x0000C000; + info->start[7] = base + 0x0000E000; + for (i = 8; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00070000; + } + } + else { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } +#endif + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { + +#ifndef CONFIG_FLASH_16BIT + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + info->start[i--] = base + info->size - 0x00014000; + info->start[i--] = base + info->size - 0x00018000; + info->start[i--] = base + info->size - 0x0001C000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + + } else { + + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +#else + info->start[i--] = base + info->size - 0x00002000; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000A000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x0000E000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + + } else { + + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } +#endif + } + + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + uchar *boottype; + uchar botboot[]=", bottom boot sect)\n"; + uchar topboot[]=", top boot sector)\n"; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_SST: printf ("SST "); break; + case FLASH_MAN_STM: printf ("STM "); break; + case FLASH_MAN_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + if (info->flash_id & 0x0001 ) { + boottype = botboot; + } else { + boottype = topboot; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit%s",boottype); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit%s",boottype); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit%s",boottype); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit%s",boottype); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit%s",boottype); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit%s",boottype); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit%s",boottype); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit%s",boottype); + break; + case FLASH_INTEL800B: printf ("INTEL28F800B (8 Mbit%s",boottype); + break; + case FLASH_INTEL800T: printf ("INTEL28F800T (8 Mbit%s",boottype); + break; + case FLASH_INTEL160B: printf ("INTEL28F160B (16 Mbit%s",boottype); + break; + case FLASH_INTEL160T: printf ("INTEL28F160T (16 Mbit%s",boottype); + break; + case FLASH_INTEL320B: printf ("INTEL28F320B (32 Mbit%s",boottype); + break; + case FLASH_INTEL320T: printf ("INTEL28F320T (32 Mbit%s",boottype); + break; + +#if 0 /* enable when devices are available */ + + case FLASH_INTEL640B: printf ("INTEL28F640B (64 Mbit%s",boottype); + break; + case FLASH_INTEL640T: printf ("INTEL28F640T (64 Mbit%s",boottype); + break; +#endif + + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ +ulong flash_get_size (volatile FLASH_WORD_SIZE *addr, flash_info_t *info) +{ + short i; + ulong base = (ulong)addr; + FLASH_WORD_SIZE value; + + /* Write auto select command: read Manufacturer ID */ + + +#ifndef CONFIG_FLASH_16BIT + + /* + * Note: if it is an AMD flash and the word at addr[0000] + * is 0x00890089 this routine will think it is an Intel + * flash device and may(most likely) cause trouble. + */ + + addr[0x0000] = 0x00900090; + if(addr[0x0000] != 0x00890089){ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; +#else + + /* + * Note: if it is an AMD flash and the word at addr[0000] + * is 0x0089 this routine will think it is an Intel + * flash device and may(most likely) cause trouble. + */ + + addr[0x0000] = 0x0090; + + if(addr[0x0000] != 0x0089){ + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; + addr[0x0555] = 0x0090; +#endif + } + value = addr[0]; + + switch (value) { + case (AMD_MANUFACT & FLASH_ID_MASK): + info->flash_id = FLASH_MAN_AMD; + break; + case (FUJ_MANUFACT & FLASH_ID_MASK): + info->flash_id = FLASH_MAN_FUJ; + break; + case (STM_MANUFACT & FLASH_ID_MASK): + info->flash_id = FLASH_MAN_STM; + break; + case (SST_MANUFACT & FLASH_ID_MASK): + info->flash_id = FLASH_MAN_SST; + break; + case (INTEL_MANUFACT & FLASH_ID_MASK): + info->flash_id = FLASH_MAN_INTEL; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + + } + + value = addr[1]; /* device ID */ + + switch (value) { + + case (AMD_ID_LV400T & FLASH_ID_MASK): + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (AMD_ID_LV400B & FLASH_ID_MASK): + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (AMD_ID_LV800T & FLASH_ID_MASK): + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (AMD_ID_LV800B & FLASH_ID_MASK): + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (AMD_ID_LV160T & FLASH_ID_MASK): + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (AMD_ID_LV160B & FLASH_ID_MASK): + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case (AMD_ID_LV320T & FLASH_ID_MASK): + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case (AMD_ID_LV320B & FLASH_ID_MASK): + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + + case (INTEL_ID_28F800B3T & FLASH_ID_MASK): + info->flash_id += FLASH_INTEL800T; + info->sector_count = 23; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (INTEL_ID_28F800B3B & FLASH_ID_MASK): + info->flash_id += FLASH_INTEL800B; + info->sector_count = 23; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (INTEL_ID_28F160B3T & FLASH_ID_MASK): + info->flash_id += FLASH_INTEL160T; + info->sector_count = 39; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (INTEL_ID_28F160B3B & FLASH_ID_MASK): + info->flash_id += FLASH_INTEL160B; + info->sector_count = 39; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (INTEL_ID_28F320B3T & FLASH_ID_MASK): + info->flash_id += FLASH_INTEL320T; + info->sector_count = 71; + info->size = 0x00800000; + break; /* => 8 MB */ + + case (INTEL_ID_28F320B3B & FLASH_ID_MASK): + info->flash_id += FLASH_AM320B; + info->sector_count = 71; + info->size = 0x00800000; + break; /* => 8 MB */ + +#if 0 /* enable when devices are available */ + case (INTEL_ID_28F320B3T & FLASH_ID_MASK): + info->flash_id += FLASH_INTEL320T; + info->sector_count = 135; + info->size = 0x01000000; + break; /* => 16 MB */ + + case (INTEL_ID_28F320B3B & FLASH_ID_MASK): + info->flash_id += FLASH_AM320B; + info->sector_count = 135; + info->size = 0x01000000; + break; /* => 16 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start adress table */ + if (info->flash_id & FLASH_BTYPE) { + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { + +#ifndef CONFIG_FLASH_16BIT + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00008000; + info->start[3] = base + 0x0000C000; + info->start[4] = base + 0x00010000; + info->start[5] = base + 0x00014000; + info->start[6] = base + 0x00018000; + info->start[7] = base + 0x0001C000; + for (i = 8; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x000E0000; + } + } + else { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } +#else + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00002000; + info->start[2] = base + 0x00004000; + info->start[3] = base + 0x00006000; + info->start[4] = base + 0x00008000; + info->start[5] = base + 0x0000A000; + info->start[6] = base + 0x0000C000; + info->start[7] = base + 0x0000E000; + for (i = 8; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00070000; + } + } + else { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } +#endif + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { + +#ifndef CONFIG_FLASH_16BIT + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + info->start[i--] = base + info->size - 0x00014000; + info->start[i--] = base + info->size - 0x00018000; + info->start[i--] = base + info->size - 0x0001C000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + + } else { + + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +#else + info->start[i--] = base + info->size - 0x00002000; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000A000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x0000E000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + + } else { + + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } +#endif + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile FLASH_WORD_SIZE *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile FLASH_WORD_SIZE *)info->start[0]; + if( (info->flash_id & 0xFF00) == FLASH_MAN_INTEL){ + *addr = (0x00F000F0 & FLASH_ID_MASK); /* reset bank */ + } else { + *addr = (0x00FF00FF & FLASH_ID_MASK); /* reset bank */ + } + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + + volatile FLASH_WORD_SIZE *addr=(volatile FLASH_WORD_SIZE*)(info->start[0]); + int flag, prot, sect, l_sect, barf; + ulong start, now, last; + int rcode = 0; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + ((info->flash_id > FLASH_AMD_COMP) && + ( (info->flash_id & FLASH_VENDMASK) != FLASH_MAN_INTEL ) ) ){ + printf ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + if(info->flash_id < FLASH_AMD_COMP) { +#ifndef CONFIG_FLASH_16BIT + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; +#else + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; + addr[0x0555] = 0x0080; + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; +#endif + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (volatile FLASH_WORD_SIZE *)(info->start[sect]); + addr[0] = (0x00300030 & FLASH_ID_MASK); + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (volatile FLASH_WORD_SIZE*)(info->start[l_sect]); + while ((addr[0] & (0x00800080&FLASH_ID_MASK)) != + (0x00800080&FLASH_ID_MASK) ) + { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + serial_putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile FLASH_WORD_SIZE *)info->start[0]; + addr[0] = (0x00F000F0 & FLASH_ID_MASK); /* reset bank */ + } else { + + + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + barf = 0; +#ifndef CONFIG_FLASH_16BIT + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x00200020; + addr[0] = 0x00D000D0; + while(!(addr[0] & 0x00800080)); /* wait for error or finish */ + if( addr[0] & 0x003A003A) { /* check for error */ + barf = addr[0] & 0x003A0000; + if( barf ) { + barf >>=16; + } else { + barf = addr[0] & 0x0000003A; + } + } +#else + addr = (vu_short*)(info->start[sect]); + addr[0] = 0x0020; + addr[0] = 0x00D0; + while(!(addr[0] & 0x0080)); /* wait for error or finish */ + if( addr[0] & 0x003A) /* check for error */ + barf = addr[0] & 0x003A; +#endif + if(barf) { + printf("\nFlash error in sector at %lx\n",(unsigned long)addr); + if(barf & 0x0002) printf("Block locked, not erased.\n"); + if((barf & 0x0030) == 0x0030) + printf("Command Sequence error.\n"); + if((barf & 0x0030) == 0x0020) + printf("Block Erase error.\n"); + if(barf & 0x0008) printf("Vpp Low error.\n"); + rcode = 1; + } else printf("."); + l_sect = sect; + } + addr = (volatile FLASH_WORD_SIZE *)info->start[0]; + addr[0] = (0x00FF00FF & FLASH_ID_MASK); /* reset bank */ + + } + + } + printf (" done\n"); + return rcode; +} + +/*----------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ +#ifndef CONFIG_FLASH_16BIT + ulong cp, wp, data; + int l; +#else + ulong cp, wp; + ushort data; +#endif + int i, rc; + +#ifndef CONFIG_FLASH_16BIT + + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); + +#else + wp = (addr & ~1); /* get lower word aligned address */ + + /* + * handle unaligned start byte + */ + if (addr - wp) { + data = 0; + data = (data << 8) | *src++; + --cnt; + if ((rc = write_short(info, wp, data)) != 0) { + return (rc); + } + wp += 2; + } + + /* + * handle word aligned part + */ +/* l = 0; used for debuging */ + while (cnt >= 2) { + data = 0; + for (i=0; i<2; ++i) { + data = (data << 8) | *src++; + } + +/* if(!l){ + printf("%x",data); + l = 1; + } used for debuging */ + + if ((rc = write_short(info, wp, data)) != 0) { + return (rc); + } + wp += 2; + cnt -= 2; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<2 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<2; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_short(info, wp, data)); + + +#endif +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +#ifndef CONFIG_FLASH_16BIT +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start,barf; + int flag; + + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + if(info->flash_id > FLASH_AMD_COMP) { + /* AMD stuff */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; + } else { + /* intel stuff */ + *addr = 0x00400040; + } + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + + if(info->flash_id > FLASH_AMD_COMP) { + + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + + } else { + + while(!(addr[0] & 0x00800080)){ /* wait for error or finish */ + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + + if( addr[0] & 0x003A003A) { /* check for error */ + barf = addr[0] & 0x003A0000; + if( barf ) { + barf >>=16; + } else { + barf = addr[0] & 0x0000003A; + } + printf("\nFlash write error at address %lx\n",(unsigned long)dest); + if(barf & 0x0002) printf("Block locked, not erased.\n"); + if(barf & 0x0010) printf("Programming error.\n"); + if(barf & 0x0008) printf("Vpp Low error.\n"); + return(2); + } + + + } + + return (0); + +} + +#else + +static int write_short (flash_info_t *info, ulong dest, ushort data) +{ + vu_short *addr = (vu_short*)(info->start[0]); + ulong start,barf; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_short *)dest) & data) != data) { + return (2); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + if(info->flash_id < FLASH_AMD_COMP) { + /* AMD stuff */ + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; + addr[0x0555] = 0x00A0; + } else { + /* intel stuff */ + *addr = 0x00D0; + *addr = 0x0040; + } + *((vu_short *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + + if(info->flash_id < FLASH_AMD_COMP) { + /* AMD stuff */ + while ((*((vu_short *)dest) & 0x0080) != (data & 0x0080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + + } else { + /* intel stuff */ + while(!(addr[0] & 0x0080)){ /* wait for error or finish */ + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) return (1); + } + + if( addr[0] & 0x003A) { /* check for error */ + barf = addr[0] & 0x003A; + printf("\nFlash write error at address %lx\n",(unsigned long)dest); + if(barf & 0x0002) printf("Block locked, not erased.\n"); + if(barf & 0x0010) printf("Programming error.\n"); + if(barf & 0x0008) printf("Vpp Low error.\n"); + return(2); + } + *addr = 0x00B0; + *addr = 0x0070; + while(!(addr[0] & 0x0080)){ /* wait for error or finish */ + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) return (1); + } + + *addr = 0x00FF; + + } + + return (0); + +} + + +#endif + +/*----------------------------------------------------------------------- + */ + diff --git a/board/etx094/flash.c b/board/etx094/flash.c new file mode 100644 index 00000000000..b3c620e868c --- /dev/null +++ b/board/etx094/flash.c @@ -0,0 +1,744 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); +#ifdef CONFIG_FLASH_16BIT + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V | BR_PS_16; /* 16 Bit data port */ +#else + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V; +#endif + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + if (size_b1) { + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size_b1 & 0xFFFF8000); +#ifdef CONFIG_FLASH_16BIT + memctl->memc_br1 = ((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_V | BR_PS_16; +#else + memctl->memc_br1 = ((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_V; +#endif + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + } else { + memctl->memc_br1 = 0; /* invalidate bank */ + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + return; + } + + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) { + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00002000); + } + return; + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ +#ifdef CONFIG_FLASH_16BIT + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; +#else + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; +#endif + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_SST: printf ("SST "); break; + case FLASH_MAN_STM: printf ("STM "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + case FLASH_SST200A: printf ("39xF200A (2M = 128K x 16)\n"); + break; + case FLASH_SST400A: printf ("39xF400A (4M = 256K x 16)\n"); + break; + case FLASH_SST800A: printf ("39xF800A (8M = 512K x 16)\n"); + break; + case FLASH_STM800AB: printf ("M29W800AB (8M = 512K x 16)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + /* Write auto select command: read Manufacturer ID */ +#ifdef CONFIG_FLASH_16BIT + vu_short *s_addr = (vu_short*)addr; + s_addr[0x5555] = 0x00AA; + s_addr[0x2AAA] = 0x0055; + s_addr[0x5555] = 0x0090; + value = s_addr[0]; + value = value|(value<<16); +#else + addr[0x5555] = 0x00AA00AA; + addr[0x2AAA] = 0x00550055; + addr[0x5555] = 0x00900090; + value = addr[0]; +#endif + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + case SST_MANUFACT: + info->flash_id = FLASH_MAN_SST; + break; + case STM_MANUFACT: + info->flash_id = FLASH_MAN_STM; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } +#ifdef CONFIG_FLASH_16BIT + value = s_addr[1]; + value = value|(value<<16); +#else + value = addr[1]; /* device ID */ +#endif + + switch (value) { + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; +#ifdef CONFIG_FLASH_16BIT + info->sector_count = 19; + info->size = 0x00100000; /* => 1 MB */ +#else + info->sector_count = 19; + info->size = 0x00200000; /* => 2 MB */ +#endif + break; + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; +#ifdef CONFIG_FLASH_16BIT + info->sector_count = 35; + info->size = 0x00200000; /* => 2 MB */ +#else + info->sector_count = 35; + info->size = 0x00400000; /* => 4 MB */ +#endif + + break; +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + case SST_ID_xF200A: + info->flash_id += FLASH_SST200A; + info->sector_count = 64; /* 39xF200A ID ( 2M = 128K x 16 ) */ + info->size = 0x00080000; + break; + case SST_ID_xF400A: + info->flash_id += FLASH_SST400A; + info->sector_count = 128; /* 39xF400A ID ( 4M = 256K x 16 ) */ + info->size = 0x00100000; + break; + case SST_ID_xF800A: + info->flash_id += FLASH_SST800A; + info->sector_count = 256; /* 39xF800A ID ( 8M = 512K x 16 ) */ + info->size = 0x00200000; + break; /* => 2 MB */ + case STM_ID_x800AB: + info->flash_id += FLASH_STM800AB; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) { + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00002000); + } + } else { /* AMD and Fujitsu types */ + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ +#ifdef CONFIG_FLASH_16BIT + + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; +#else + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; +#endif + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address: + * (A7 .. A0) = 0x02 + * D0 = 1 if protected + */ +#ifdef CONFIG_FLASH_16BIT + s_addr = (volatile unsigned short *)(info->start[i]); + info->protect[i] = s_addr[2] & 1; +#else + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; +#endif + } + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { +#ifdef CONFIG_FLASH_16BIT + s_addr = (volatile unsigned short *)(info->start[0]); + *s_addr = 0x00F0; /* reset bank */ +#else + addr = (volatile unsigned long *)info->start[0]; + *addr = 0x00F000F0; /* reset bank */ +#endif + + } + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect; + ulong start, now, last; +#ifdef CONFIG_FLASH_16BIT + vu_short *s_addr = (vu_short*)addr; +#endif + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } +/*#ifndef CONFIG_FLASH_16BIT + ulong type; + type = (info->flash_id & FLASH_VENDMASK); + if ((type != FLASH_MAN_SST) && (type != FLASH_MAN_STM)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return; + } +#endif*/ + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ +#ifdef CONFIG_FLASH_16BIT + vu_short *s_sect_addr = (vu_short*)(info->start[sect]); +#else + vu_long *sect_addr = (vu_long*)(info->start[sect]); +#endif + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#ifdef CONFIG_FLASH_16BIT + + /*printf("\ns_sect_addr=%x",s_sect_addr);*/ + s_addr[0x5555] = 0x00AA; + s_addr[0x2AAA] = 0x0055; + s_addr[0x5555] = 0x0080; + s_addr[0x5555] = 0x00AA; + s_addr[0x2AAA] = 0x0055; + s_sect_addr[0] = 0x0030; +#else + addr[0x5555] = 0x00AA00AA; + addr[0x2AAA] = 0x00550055; + addr[0x5555] = 0x00800080; + addr[0x5555] = 0x00AA00AA; + addr[0x2AAA] = 0x00550055; + sect_addr[0] = 0x00300030; +#endif + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + +#ifdef CONFIG_FLASH_16BIT + while ((s_sect_addr[0] & 0x0080) != 0x0080) { +#else + while ((sect_addr[0] & 0x00800080) != 0x00800080) { +#endif + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + } + } + + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; +#ifdef CONFIG_FLASH_16BIT + s_addr[0] = 0x00F0; /* reset bank */ +#else + addr[0] = 0x00F000F0; /* reset bank */ +#endif + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 4 - Flash not identified + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + if (info->flash_id == FLASH_UNKNOWN) { + return 4; + } + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + +#ifdef CONFIG_FLASH_16BIT + vu_short high_data; + vu_short low_data; + vu_short *s_addr = (vu_short*)addr; +#endif + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + +#ifdef CONFIG_FLASH_16BIT + /* Write the 16 higher-bits */ + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + high_data = ((data>>16) & 0x0000ffff); + + s_addr[0x5555] = 0x00AA; + s_addr[0x2AAA] = 0x0055; + s_addr[0x5555] = 0x00A0; + + *((vu_short *)dest) = high_data; + + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_short *)dest) & 0x0080) != (high_data & 0x0080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + + + /* Write the 16 lower-bits */ +#endif + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); +#ifdef CONFIG_FLASH_16BIT + dest += 0x2; + low_data = (data & 0x0000ffff); + + s_addr[0x5555] = 0x00AA; + s_addr[0x2AAA] = 0x0055; + s_addr[0x5555] = 0x00A0; + *((vu_short *)dest) = low_data; + +#else + addr[0x5555] = 0x00AA00AA; + addr[0x2AAA] = 0x00550055; + addr[0x5555] = 0x00A000A0; + *((vu_long *)dest) = data; +#endif + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + +#ifdef CONFIG_FLASH_16BIT + while ((*((vu_short *)dest) & 0x0080) != (low_data & 0x0080)) { +#else + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { +#endif + + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/evb64260/i2c.c b/board/evb64260/i2c.c new file mode 100644 index 00000000000..22cb8096c83 --- /dev/null +++ b/board/evb64260/i2c.c @@ -0,0 +1,315 @@ +#include <common.h> +#include <mpc8xx.h> +#include <malloc.h> +#include <galileo/gt64260R.h> +#include <galileo/core.h> + +#define MAX_I2C_RETRYS 10 +#define I2C_DELAY 1000 /* Should be at least the # of MHz of Tclk */ +#undef DEBUG_I2C + +#ifdef DEBUG_I2C +#define DP(x) x +#else +#define DP(x) +#endif + +/* Assuming that there is only one master on the bus (us) */ + +static void +i2c_init(int speed, int slaveaddr) +{ + unsigned int n, m, freq, margin, power; + unsigned int actualFreq, actualN=0, actualM=0; + unsigned int control, status; + unsigned int minMargin = 0xffffffff; + unsigned int tclk = 125000000; + + DP(puts("i2c_init\n")); + + for(n = 0 ; n < 8 ; n++) + { + for(m = 0 ; m < 16 ; m++) + { + power = 2<<n; /* power = 2^(n+1) */ + freq = tclk/(10*(m+1)*power); + if (speed > freq) + margin = speed - freq; + else + margin = freq - speed; + if(margin < minMargin) + { + minMargin = margin; + actualFreq = freq; + actualN = n; + actualM = m; + } + } + } + + DP(puts("setup i2c bus\n")); + + /* Setup bus */ + + GT_REG_WRITE(I2C_SOFT_RESET, 0); + + DP(puts("udelay...\n")); + + udelay(I2C_DELAY); + + DP(puts("set baudrate\n")); + + GT_REG_WRITE(I2C_STATUS_BAUDE_RATE, (actualM << 3) | actualN); + GT_REG_WRITE(I2C_CONTROL, (0x1 << 2) | (0x1 << 6)); + + udelay(I2C_DELAY * 10); + + DP(puts("read control, baudrate\n")); + + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + GT_REG_READ(I2C_CONTROL, &control); +} + +static uchar +i2c_start(void) +{ + unsigned int control, status; + int count = 0; + + DP(puts("i2c_start\n")); + + /* Set the start bit */ + + GT_REG_READ(I2C_CONTROL, &control); + control |= (0x1 << 5); + GT_REG_WRITE(I2C_CONTROL, control); + + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + + count = 0; + while ((status & 0xff) != 0x08) { + udelay(I2C_DELAY); + if (count > 20) { + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); /*stop*/ + return (status); + } + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + } + + return (0); +} + +static uchar +i2c_select_device(uchar dev_addr, uchar read, int ten_bit) +{ + unsigned int status, data, bits = 7; + int count = 0; + + DP(puts("i2c_select_device\n")); + + /* Output slave address */ + + if (ten_bit) { + bits = 10; + } + + data = (dev_addr << 1); + /* set the read bit */ + data |= read; + GT_REG_WRITE(I2C_DATA, data); + /* assert the address */ + RESET_REG_BITS(I2C_CONTROL, BIT3); + + udelay(I2C_DELAY); + + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count = 0; + while (((status & 0xff) != 0x40) && ((status & 0xff) != 0x18)) { + udelay(I2C_DELAY); + if (count > 20) { + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); /*stop*/ + return(status); + } + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + } + + if (bits == 10) { + printf("10 bit I2C addressing not yet implemented\n"); + return (0xff); + } + + return (0); +} + +static uchar +i2c_get_data(uchar* return_data, int len) { + + unsigned int data, status; + int count = 0; + + DP(puts("i2c_get_data\n")); + + while (len) { + + /* Get and return the data */ + + RESET_REG_BITS(I2C_CONTROL, (0x1 << 3)); + + udelay(I2C_DELAY * 5); + + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + while ((status & 0xff) != 0x50) { + udelay(I2C_DELAY); + if(count > 2) { + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); /*stop*/ + return 0; + } + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + } + GT_REG_READ(I2C_DATA, &data); + len--; + *return_data = (uchar)data; + return_data++; + } + RESET_REG_BITS(I2C_CONTROL, BIT2|BIT3); + while ((status & 0xff) != 0x58) { + udelay(I2C_DELAY); + if(count > 200) { + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); /*stop*/ + return (status); + } + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + } + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); /* stop */ + + return (0); +} + +static uchar +i2c_write_data(unsigned int data, int len) +{ + unsigned int status; + int count = 0; + + DP(puts("i2c_write_data\n")); + + if (len > 4) + return -1; + + while (len) { + /* Set and assert the data */ + + GT_REG_WRITE(I2C_DATA, (unsigned int)data); + RESET_REG_BITS(I2C_CONTROL, (0x1 << 3)); + + udelay(I2C_DELAY); + + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + while ((status & 0xff) != 0x28) { + udelay(I2C_DELAY); + if(count > 20) { + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); /*stop*/ + return (status); + } + GT_REG_READ(I2C_STATUS_BAUDE_RATE, &status); + count++; + } + len--; + } + GT_REG_WRITE(I2C_CONTROL, (0x1 << 3) | (0x1 << 4)); + GT_REG_WRITE(I2C_CONTROL, (0x1 << 4)); + + udelay(I2C_DELAY * 10); + + return (0); +} + +static uchar +i2c_set_dev_offset(uchar dev_addr, unsigned int offset, int ten_bit) +{ + uchar status; + + DP(puts("i2c_set_dev_offset\n")); + + status = i2c_select_device(dev_addr, 0, ten_bit); + if (status) { +#ifdef DEBUG_I2C + printf("Failed to select device setting offset: 0x%02x\n", + status); +#endif + return status; + } + + status = i2c_write_data(offset, 1); + if (status) { +#ifdef DEBUG_I2C + printf("Failed to write data: 0x%02x\n", status); +#endif + return status; + } + + return (0); +} + +uchar +i2c_read(uchar dev_addr, unsigned int offset, int len, uchar* data, + int ten_bit) +{ + uchar status = 0; + unsigned int i2cFreq = 400000; + + DP(puts("i2c_read\n")); + + i2c_init(i2cFreq,0); + + status = i2c_start(); + + if (status) { +#ifdef DEBUG_I2C + printf("Transaction start failed: 0x%02x\n", status); +#endif + return status; + } + + status = i2c_set_dev_offset(dev_addr, 0, 0); + if (status) { +#ifdef DEBUG_I2C + printf("Failed to set offset: 0x%02x\n", status); +#endif + return status; + } + + i2c_init(i2cFreq,0); + + status = i2c_start(); + if (status) { +#ifdef DEBUG_I2C + printf("Transaction restart failed: 0x%02x\n", status); +#endif + return status; + } + + status = i2c_select_device(dev_addr, 1, ten_bit); + if (status) { +#ifdef DEBUG_I2C + printf("Address not acknowledged: 0x%02x\n", status); +#endif + return status; + } + + status = i2c_get_data(data, len); + if (status) { +#ifdef DEBUG_I2C + printf("Data not recieved: 0x%02x\n", status); +#endif + return status; + } + + return 0; +} diff --git a/board/evb64260/intel_flash.c b/board/evb64260/intel_flash.c new file mode 100644 index 00000000000..ed6a2a02922 --- /dev/null +++ b/board/evb64260/intel_flash.c @@ -0,0 +1,277 @@ +/* + * (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 + * + * Hacked for the Hymod board by Murray.Jensen@cmst.csiro.au, 20-Oct-00 + */ + +#include <common.h> +#include <mpc8xx.h> +#include <galileo/gt64260R.h> +#include <galileo/memory.h> +#include "intel_flash.h" + + +/*----------------------------------------------------------------------- + * Protection Flags: + */ +#define FLAG_PROTECT_SET 0x01 +#define FLAG_PROTECT_CLEAR 0x02 + +static void +bank_reset(flash_info_t *info, int sect) +{ + bank_addr_t addrw, eaddrw; + + addrw = (bank_addr_t)info->start[sect]; + eaddrw = BANK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) { +#ifdef FLASH_DEBUG + printf(" writing reset cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = BANK_CMD_RST; + addrw++; + } +} + +static void +bank_erase_init(flash_info_t *info, int sect) +{ + bank_addr_t addrw, saddrw, eaddrw; + int flag; + +#ifdef FLASH_DEBUG + printf("0x%08x BANK_CMD_PROG\n", BANK_CMD_PROG); + printf("0x%08x BANK_CMD_ERASE1\n", BANK_CMD_ERASE1); + printf("0x%08x BANK_CMD_ERASE2\n", BANK_CMD_ERASE2); + printf("0x%08x BANK_CMD_CLR_STAT\n", BANK_CMD_CLR_STAT); + printf("0x%08x BANK_CMD_RST\n", BANK_CMD_RST); + printf("0x%08x BANK_STAT_RDY\n", BANK_STAT_RDY); + printf("0x%08x BANK_STAT_ERR\n", BANK_STAT_ERR); +#endif + + saddrw = (bank_addr_t)info->start[sect]; + eaddrw = BANK_ADDR_NEXT_WORD(saddrw); + +#ifdef FLASH_DEBUG + printf("erasing sector %d, start addr = 0x%08lx " + "(bank next word addr = 0x%08lx)\n", sect, + (unsigned long)saddrw, (unsigned long)eaddrw); +#endif + + /* Disable intrs which might cause a timeout here */ + flag = disable_interrupts(); + + for (addrw = saddrw; addrw < eaddrw; addrw++) { +#ifdef FLASH_DEBUG + printf(" writing erase cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = BANK_CMD_ERASE1; + *addrw = BANK_CMD_ERASE2; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); +} + +static int +bank_erase_poll(flash_info_t *info, int sect) +{ + bank_addr_t addrw, saddrw, eaddrw; + int sectdone, haderr; + + saddrw = (bank_addr_t)info->start[sect]; + eaddrw = BANK_ADDR_NEXT_WORD(saddrw); + + sectdone = 1; + haderr = 0; + + for (addrw = saddrw; addrw < eaddrw; addrw++) { + bank_word_t stat = *addrw; + +#ifdef FLASH_DEBUG + printf(" checking status at addr " + "0x%08x [0x%08x]\n", + (unsigned long)addrw, stat); +#endif + if ((stat & BANK_STAT_RDY) != BANK_STAT_RDY) + sectdone = 0; + else if ((stat & BANK_STAT_ERR) != 0) { + printf(" failed on sector %d " + "(stat = 0x%08x) at " + "address 0x%p\n", + sect, stat, addrw); + *addrw = BANK_CMD_CLR_STAT; + haderr = 1; + } + } + + if (haderr) + return (-1); + else + return (sectdone); +} + +int +write_word_intel(bank_addr_t addr, bank_word_t value) +{ + bank_word_t stat; + ulong start; + int flag, retval; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = BANK_CMD_PROG; + + *addr = value; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + retval = 0; + + /* data polling for D7 */ + start = get_timer (0); + do { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + retval = 1; + goto done; + } + stat = *addr; + } while ((stat & BANK_STAT_RDY) != BANK_STAT_RDY); + + if ((stat & BANK_STAT_ERR) != 0) { + printf("flash program failed (stat = 0x%08lx) " + "at address 0x%08lx\n", (ulong)stat, (ulong)addr); + *addr = BANK_CMD_CLR_STAT; + retval = 3; + } + +done: + /* reset to read mode */ + *addr = BANK_CMD_RST; + + return (retval); +} + +/*----------------------------------------------------------------------- + */ + +int +flash_erase_intel(flash_info_t *info, int s_first, int s_last) +{ + int prot, sect, haderr; + ulong start, now, last; + +#ifdef FLASH_DEBUG + printf("\nflash_erase: erase %d sectors (%d to %d incl.) from\n" + " Bank # %d: ", s_last - s_first + 1, s_first, s_last, + (info - flash_info) + 1); + flash_print_info(info); +#endif + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf("- Warning: %d protected sector%s will not be erased!\n", + prot, (prot > 1 ? "s" : "")); + } + + start = get_timer (0); + last = 0; + haderr = 0; + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + ulong estart; + int sectdone; + + bank_erase_init(info, sect); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + estart = get_timer(start); + + do { + now = get_timer(start); + + if (now - estart > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout (sect %d)\n", sect); + haderr = 1; + break; + } + +#ifndef FLASH_DEBUG + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } +#endif + + sectdone = bank_erase_poll(info, sect); + + if (sectdone < 0) { + haderr = 1; + break; + } + + } while (!sectdone); + + if (haderr) + break; + } + } + + if (haderr > 0) + printf (" failed\n"); + else + printf (" done\n"); + + /* reset to read mode */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + bank_reset(info, sect); + } + } + return haderr; +} diff --git a/board/fads/flash.c b/board/fads/flash.c new file mode 100644 index 00000000000..50b496ed971 --- /dev/null +++ b/board/fads/flash.c @@ -0,0 +1,624 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long total_size; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) + { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + total_size = 0; + size_b0 = 0xffffffff; + + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) + { + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + total_size), &flash_info[i]); + + if (flash_info[i].flash_id == FLASH_UNKNOWN) + { + printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n", i, size_b1, size_b1>>20); + } + + /* Is this really needed ? - LP */ + if (size_b1 > size_b0) { + printf ("## ERROR: Bank %d (0x%08lx = %ld MB) > Bank %d (0x%08lx = %ld MB)\n", + i, size_b1, size_b1>>20, i-1, size_b0, size_b0>>20); + goto out_error; + } + size_b0 = size_b1; + total_size += size_b1; + } + + /* Compute the Address Mask */ + for (i=0; (total_size >> i) != 0; ++i) {}; + i--; + + if (total_size != (1 << i)) { + printf ("## WARNING: Total FLASH size (0x%08lx = %ld MB) is not a power of 2\n", + total_size, total_size>>20); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = ((((unsigned long)~1) << i) & OR_AM_MSK) | CFG_OR_TIMING_FLASH; + memctl->memc_br0 = CFG_BR0_PRELIM; + + total_size = 0; + + for (i=0; i < CFG_MAX_FLASH_BANKS && flash_info[i].size != 0; ++i) + { + /* Re-do sizing to get full correct info */ + /* Why ? - LP */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + total_size), &flash_info[i]); + + /* This is done by flash_get_size - LP */ + /* flash_get_offsets (CFG_FLASH_BASE + total_size, &flash_info[i]); */ + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[i]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SIZE-1, + &flash_info[i]); +#endif + + total_size += size_b1; + } + + return (total_size); + +out_error: + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) + { + flash_info[i].flash_id = FLASH_UNKNOWN; + flash_info[i].sector_count = -1; + flash_info[i].size = 0; + } + + return (0); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM040) { + /* set sector offsets for uniform sector type */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00040000); + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) + { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) + { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) + { + case FLASH_AM040: printf ("29F040 or 29LV040 (4 Mbit, uniform sectors)\n"); + break; + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n",info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + + for (i=0; i<info->sector_count; ++i) + { + if ((i % 5) == 0) + { + printf ("\n "); + } + + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " "); + } + + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; +#if 0 + ulong base = (ulong)addr; +#endif + uchar value; + + /* Write auto select command: read Manufacturer ID */ +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x90909090; +#endif + + value = addr[0]; + + switch (value + (value << 16)) + { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + break; + } + + value = addr[1]; /* device ID */ + + switch (value) + { + case AMD_ID_F040B: + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + +#if 0 + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +#else + flash_get_offsets ((ulong)addr, info); +#endif + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) + { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) + { + addr = (volatile unsigned long *)info->start[0]; +#if 0 + *addr = 0x00F000F0; /* reset bank */ +#else + *addr = 0xF0F0F0F0; /* reset bank */ +#endif + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x80808080; + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; +#endif + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); +#if 0 + addr[0] = 0x00300030; +#else + addr[0] = 0x30303030; +#endif + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); +#if 0 + while ((addr[0] & 0x00800080) != 0x00800080) +#else + while ((addr[0] & 0xFFFFFFFF) != 0xFFFFFFFF) +#endif + { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; +#if 0 + addr[0] = 0x00F000F0; /* reset bank */ +#else + addr[0] = 0xF0F0F0F0; /* reset bank */ +#endif + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0xA0A0A0A0; +#endif + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); +#if 0 + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) +#else + while ((*((vu_long *)dest) & 0x80808080) != (data & 0x80808080)) +#endif + { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/fads/lamp.c b/board/fads/lamp.c new file mode 100644 index 00000000000..b12147740e4 --- /dev/null +++ b/board/fads/lamp.c @@ -0,0 +1,42 @@ +#include <config.h> +#include <common.h> + +void +signal_delay(unsigned int n) +{ + while (n--); +} + +void +signal_on(void) +{ + *((volatile uint *)BCSR4) &= ~(1<<(31-3)); /* led on */ +} + +void +signal_off(void) +{ + *((volatile uint *)BCSR4) |= (1<<(31-3)); /* led off */ +} + +void +slow_blink(unsigned int n) +{ + while (n--) { + signal_on(); + signal_delay(0x00400000); + signal_off(); + signal_delay(0x00400000); + } +} + +void +fast_blink(unsigned int n) +{ + while (n--) { + signal_on(); + signal_delay(0x00100000); + signal_off(); + signal_delay(0x00100000); + } +} diff --git a/board/genietv/flash.c b/board/genietv/flash.c new file mode 100644 index 00000000000..2314a53e736 --- /dev/null +++ b/board/genietv/flash.c @@ -0,0 +1,470 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) + flash_info[i].flash_id = FLASH_UNKNOWN; + + /* Detect size */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + /* Setup offsets */ + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* Monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + size_b1 = 0 ; + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + * Fix this to support variable sector sizes +*/ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM040) { + /* set sector offsets for bottom boot block type */ + for (i = 0; i < info->sector_count; i++) + info->start[i] = base + (i * 0x00010000); + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) + { + puts ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) + { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) + { + case FLASH_AM040: printf ("29F040 or 29LV040 (4 Mbit, uniform sectors)\n"); + break; + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + if (info->size >> 20) { + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, + info->sector_count); + } else { + printf (" Size: %ld KB in %d Sectors\n", + info->size >> 10, + info->sector_count); + } + + puts (" Sector Start Addresses:"); + + for (i=0; i<info->sector_count; ++i) + { + if ((i % 5) == 0) + { + puts ("\n "); + } + + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " "); + } + + putc ('\n'); + return; +} +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + volatile unsigned char *caddr; + char value; + + caddr = (volatile unsigned char *)addr ; + + /* Write auto select command: read Manufacturer ID */ + +#if 0 + printf("Base address is: %08x\n", caddr); +#endif + + caddr[0x0555] = 0xAA; + caddr[0x02AA] = 0x55; + caddr[0x0555] = 0x90; + + value = caddr[0]; + +#if 0 + printf("Manufact ID: %02x\n", value); +#endif + switch (value) + { + case 0x01: + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + break; + } + + value = caddr[1]; /* device ID */ +#if 0 + printf("Device ID: %02x\n", value); +#endif + switch (value) + { + case AMD_ID_LV040B: + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00080000; + break; /* => 512Kb */ + + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + flash_get_offsets ((ulong)addr, &flash_info[0]); + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) + { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + caddr = (volatile unsigned char *)(info->start[i]); + info->protect[i] = caddr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) + { + caddr = (volatile unsigned char *)info->start[0]; + *caddr = 0xF0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + volatile unsigned char *addr = (volatile unsigned char *)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0xAA; + addr[0x02AA] = 0x55; + addr[0x0555] = 0x80; + addr[0x0555] = 0xAA; + addr[0x02AA] = 0x55; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (volatile unsigned char *)(info->start[sect]); + addr[0] = 0x30; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (volatile unsigned char *)(info->start[l_sect]); + + while ((addr[0] & 0xFF) != 0xFF) + { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned char *)info->start[0]; + + addr[0] = 0xF0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + volatile unsigned char *addr = (volatile unsigned char*)(info->start[0]), + *cdest,*cdata; + ulong start; + int flag, count = 4 ; + + cdest = (volatile unsigned char *)dest ; + cdata = (volatile unsigned char *)&data ; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + + while(count--) + { + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0xAA; + addr[0x02AA] = 0x55; + addr[0x0555] = 0xA0; + + *cdest = *cdata; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*cdest ^ *cdata) & 0x80) + { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + + cdata++ ; + cdest++ ; + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/gth/ee_access.c b/board/gth/ee_access.c new file mode 100644 index 00000000000..716c90ed620 --- /dev/null +++ b/board/gth/ee_access.c @@ -0,0 +1,335 @@ +/* Module for handling DALLAS DS2438, smart battery monitor + Chip can store up to 40 bytes of user data in EEPROM, + perform temp, voltage and current measurements. + Chip also contains a unique serial number. + + Always read/write LSb first + + For documentaion, see data sheet for DS2438, 2438.pdf + + By Thomas.Lange@corelatus.com 001025 */ + +#include <common.h> +#include <config.h> +#include <mpc8xx.h> + +#include <../board/gth/ee_dev.h> + +/* We dont have kernel functions */ +#define printk printf +#define KERN_DEBUG +#define KERN_ERR +#define EIO 1 + +static int Debug = 0; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * lookup table ripped from DS app note 17, understanding and using + * cyclic redundancy checks... + */ + +static u8 crc_lookup[256] = { + 0, 94, 188, 226, 97, 63, 221, 131, + 194, 156, 126, 32, 163, 253, 31, 65, + 157, 195, 33, 127, 252, 162, 64, 30, + 95, 1, 227, 189, 62, 96, 130, 220, + 35, 125, 159, 193, 66, 28, 254, 160, + 225, 191, 93, 3, 128, 222, 60, 98, + 190, 224, 2, 92, 223, 129, 99, 61, + 124, 34, 192, 158, 29, 67, 161, 255, + 70, 24, 250, 164, 39, 121, 155, 197, + 132, 218, 56, 102, 229, 187, 89, 7, + 219, 133, 103, 57, 186, 228, 6, 88, + 25, 71, 165, 251, 120, 38, 196, 154, + 101, 59, 217, 135, 4, 90, 184, 230, + 167, 249, 27, 69, 198, 152, 122, 36, + 248, 166, 68, 26, 153, 199, 37, 123, + 58, 100, 134, 216, 91, 5, 231, 185, + 140, 210, 48, 110, 237, 179, 81, 15, + 78, 16, 242, 172, 47, 113, 147, 205, + 17, 79, 173, 243, 112, 46, 204, 146, + 211, 141, 111, 49, 178, 236, 14, 80, + 175, 241, 19, 77, 206, 144, 114, 44, + 109, 51, 209, 143, 12, 82, 176, 238, + 50, 108, 142, 208, 83, 13, 239, 177, + 240, 174, 76, 18, 145, 207, 45, 115, + 202, 148, 118, 40, 171, 245, 23, 73, + 8, 86, 180, 234, 105, 55, 213, 139, + 87, 9, 235, 181, 54, 104, 138, 212, + 149, 203, 41, 119, 244, 170, 72, 22, + 233, 183, 85, 11, 136, 214, 52, 106, + 43, 117, 151, 201, 74, 20, 246, 168, + 116, 42, 200, 150, 21, 75, 169, 247, + 182, 232, 10, 84, 215, 137, 107, 53 +}; + +static u8 make_new_crc( u8 Old_crc, u8 New_value ){ + /* Compute a new checksum with new byte, using previous checksum as input + See DS app note 17, understanding and using cyclic redundancy checks... + Also see DS2438, page 11 */ + return( crc_lookup[Old_crc ^ New_value ]); +} + +int ee_crc_ok( u8 *Buffer, int Len, u8 Crc ){ + /* Check if the checksum for this buffer is correct */ + u8 Curr_crc=0; + int i; + u8 *Curr_byte = Buffer; + + for(i=0;i<Len;i++){ + Curr_crc = make_new_crc( Curr_crc, *Curr_byte); + Curr_byte++; + } + E_DEBUG("Calculated CRC = 0x%x, read = 0x%x\n", Curr_crc, Crc); + + if(Curr_crc == Crc){ + /* Good */ + return(TRUE); + } + printk(KERN_ERR"EE checksum error, Calculated CRC = 0x%x, read = 0x%x\n", + Curr_crc, Crc); + return(FALSE); +} + +static void +set_idle(void){ + /* Send idle and keep start time + Continous 1 is idle */ + WRITE_PORT(1); +} + +static int +do_reset(void){ + /* Release reset and verify that chip responds with presence pulse */ + int Retries = 0; + while(Retries<5){ + udelay(RESET_LOW_TIME); + + /* Send reset */ + WRITE_PORT(0); + udelay(RESET_LOW_TIME); + + /* Release reset */ + WRITE_PORT(1); + + /* Wait for EEPROM to drive output */ + udelay(PRESENCE_TIMEOUT); + if(!READ_PORT){ + /* Ok, EEPROM is driving a 0 */ + E_DEBUG("Presence detected\n"); + if(Retries){ + E_DEBUG("Retries %d\n",Retries); + } + /* Make sure chip releases pin */ + udelay(PRESENCE_LOW_TIME); + return 0; + } + Retries++; + } + + printk(KERN_ERR"EEPROM did not respond when releasing reset\n"); + + /* Make sure chip releases pin */ + udelay(PRESENCE_LOW_TIME); + + /* Set to idle again */ + set_idle(); + + return(-EIO); +} + +static u8 +read_byte(void){ + /* Read a single byte from EEPROM + Read LSb first */ + int i; + int Value; + u8 Result=0; +#ifndef CFG_IMMR + u32 Flags; +#endif + + E_DEBUG("Reading byte\n"); + + for(i=0;i<8;i++){ + /* Small delay between pulses */ + udelay(1); + +#ifndef CFG_IMMR + /* Disable irq */ + save_flags(Flags); + cli(); +#endif + + /* Pull down pin short time to start read + See page 26 in data sheet */ + + WRITE_PORT(0); + udelay(READ_LOW); + WRITE_PORT(1); + + /* Wait for chip to drive pin */ + udelay(READ_TIMEOUT); + + Value = READ_PORT; + if(Value) + Value=1; + +#ifndef CFG_IMMR + /* Enable irq */ + restore_flags(Flags); +#endif + + /* Wait for chip to release pin */ + udelay(TOTAL_READ_LOW-READ_TIMEOUT); + + /* LSb first */ + Result|=Value<<i; + } + + E_DEBUG("Read byte 0x%x\n",Result); + + return(Result); +} + +static void +write_byte(u8 Byte){ + /* Write a single byte to EEPROM + Write LSb first */ + int i; + int Value; +#ifndef CFG_IMMR + u32 Flags; +#endif + + E_DEBUG("Writing byte 0x%x\n",Byte); + + for(i=0;i<8;i++){ + /* Small delay between pulses */ + udelay(1); + Value = Byte&1; + +#ifndef CFG_IMMR + /* Disable irq */ + save_flags(Flags); + cli(); +#endif + + /* Pull down pin short time for a 1, long time for a 0 + See page 26 in data sheet */ + + WRITE_PORT(0); + if(Value){ + /* Write a 1 */ + udelay(WRITE_1_LOW); + } + else{ + /* Write a 0 */ + udelay(WRITE_0_LOW); + } + + WRITE_PORT(1); + +#ifndef CFG_IMMR + /* Enable irq */ + restore_flags(Flags); +#endif + + if(Value) + /* Wait for chip to read the 1 */ + udelay(TOTAL_WRITE_LOW-WRITE_1_LOW); + Byte>>=1; + } +} + +int ee_do_command( u8 *Tx, int Tx_len, u8 *Rx, int Rx_len, int Send_skip ){ + /* Execute this command string, including + giving reset and setting to idle after command + if Rx_len is set, we read out data from EEPROM */ + int i; + + E_DEBUG("Command, Tx_len %d, Rx_len %d\n", Tx_len, Rx_len ); + + if(do_reset()){ + /* Failed! */ + return(-EIO); + } + + if(Send_skip) + /* Always send SKIP_ROM first to tell chip we are sending a command, + except when we read out rom data for chip */ + write_byte(SKIP_ROM); + + /* Always have Tx data */ + for(i=0;i<Tx_len;i++){ + write_byte(Tx[i]); + } + + if(Rx_len){ + for(i=0;i<Rx_len;i++){ + Rx[i]=read_byte(); + } + } + + set_idle(); + + E_DEBUG("Command done\n"); + + return(0); +} + +int ee_init_data(void){ + int i; + u8 Tx[10]; + int tmp; + volatile immap_t *immap = (immap_t *)CFG_IMMR; + + while(0){ + tmp = 1-tmp; + if(tmp) + immap->im_ioport.iop_padat &= ~PA_FRONT_LED; + else + immap->im_ioport.iop_padat |= PA_FRONT_LED; + udelay(1); + } + + /* Set port to open drain to be able to read data from + port without setting it to input */ + PORT_B_PAR &= ~PB_EEPROM; + PORT_B_ODR |= PB_EEPROM; + SET_PORT_B_OUTPUT(PB_EEPROM); + + /* Set idle mode */ + set_idle(); + + /* Copy all User EEPROM data to scratchpad */ + for(i=0;i<USER_PAGES;i++){ + Tx[0]=RECALL_MEMORY; + Tx[1]=EE_USER_PAGE_0+i; + if(ee_do_command(Tx,2,NULL,0,TRUE)) return(-EIO); + } + + /* Make sure chip doesnt store measurements in NVRAM */ + Tx[0]=WRITE_SCRATCHPAD; + Tx[1]=0; /* Page */ + Tx[2]=9; + if(ee_do_command(Tx,3,NULL,0,TRUE)) return(-EIO); + + Tx[0]=COPY_SCRATCHPAD; + if(ee_do_command(Tx,2,NULL,0,TRUE)) return(-EIO); + + /* FIXME check status bit instead + Could take 10 ms to store in EEPROM */ + for(i=0;i<10;i++){ + udelay(1000); + } + + return(0); +} diff --git a/board/gth/flash.c b/board/gth/flash.c new file mode 100644 index 00000000000..562a3499246 --- /dev/null +++ b/board/gth/flash.c @@ -0,0 +1,649 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + * Protection Flags: + */ +#define FLAG_PROTECT_SET 0x01 +#define FLAG_PROTECT_CLEAR 0x02 + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /*printf("faking");*/ + + return(0x1fffff); + + /* Init: no FLASHes known */ + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) + { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) + { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + +#if 0 + if (FLASH_BASE1_PRELIM != 0x0) { + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20,size_b0, size_b0<<20); + + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + } else { +#endif + size_b1 = 0; + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR0_PRELIM; + memctl->memc_br0 = CFG_BR0_PRELIM; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + if (size_b1) + { + /* memctl->memc_or1 = CFG_OR1_PRELIM; + memctl->memc_br1 = CFG_BR1_PRELIM; */ + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + } + else + { +/* memctl->memc_or1 = CFG_OR1_PRELIM; + FIXME memctl->memc_br1 = CFG_BR1_PRELIM; */ + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} + + +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start adress table */ + if (info->flash_id & FLASH_BTYPE) + { + /* set sector offsets for bottom boot block type */ + for (i = 0; i < info->sector_count; i++) + { + info->start[i] = base + (i * 0x00040000); + } + } + else + { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + for (; i >= 0; i--) + { + info->start[i] = base + i * 0x00040000; + } + } + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + +#if 0 + case FLASH_AM040B: + printf ("AM29F040B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM040T: + printf ("AM29F040T (4 Mbit, top boot sect)\n"); + break; +#endif + case FLASH_AM400B: + printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: + printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: + printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: + printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: + printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: + printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: + printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: + printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, + info->sector_count); + + printf (" Sector Start Addresses:"); + + for (i=0; i<info->sector_count; ++i) + { + if ((i % 5) == 0) + { + printf ("\n "); + } + + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " "); + } + + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; +#if 0 + ulong base = (ulong)addr; +#endif + uchar value; + + /* Write auto select command: read Manufacturer ID */ +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x90909090; +#endif + + value = addr[0]; + + switch (value) + { + case AMD_MANUFACT:case 0x01: + info->flash_id = FLASH_MAN_AMD; + break; + + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + break; + } + + value = addr[1]; /* device ID */ + + switch (value) + { +#if 0 + case AMD_ID_F040B: + info->flash_id += FLASH_AM040B; + info->sector_count = 8; + info->size = 0x00200000; + break; /* => 2 MB */ +#endif + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + +#if 0 + /* set up sector start adress table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +#else + flash_get_offsets ((ulong)addr, &flash_info[0]); +#endif + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) + { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) + { + addr = (volatile unsigned long *)info->start[0]; +#if 0 + *addr = 0x00F000F0; /* reset bank */ +#else + *addr = 0xF0F0F0F0; /* reset bank */ +#endif + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x80808080; + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; +#endif + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); +#if 0 + addr[0] = 0x00300030; +#else + addr[0] = 0x30303030; +#endif + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); +#if 0 + while ((addr[0] & 0x00800080) != 0x00800080) +#else + while ((addr[0] & 0xFFFFFFFF) != 0xFFFFFFFF) +#endif + { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; +#if 0 + addr[0] = 0x00F000F0; /* reset bank */ +#else + addr[0] = 0xF0F0F0F0; /* reset bank */ +#endif + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0xA0A0A0A0; +#endif + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); +#if 0 + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) +#else + while ((*((vu_long *)dest) & 0x80808080) != (data & 0x80808080)) +#endif + { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/hermes/flash.c b/board/hermes/flash.c new file mode 100644 index 00000000000..bb7635ecfe9 --- /dev/null +++ b/board/hermes/flash.c @@ -0,0 +1,460 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_byte (flash_info_t *info, ulong dest, uchar data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size, size<<20); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | + (memctl->memc_br0 & ~(BR_BA_MSK)); + + /* Re-do sizing to get full correct info */ + size = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + flash_info[0].size = size; + + return (size); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + uchar value; + vu_char *caddr = (vu_char *)addr; + ulong base = (ulong)addr; + + + /* Write auto select command: read Manufacturer ID */ + caddr[0x0AAA] = 0xAA; + caddr[0x0555] = 0x55; + caddr[0x0AAA] = 0x90; + + value = caddr[0]; + switch (value) { + case (AMD_MANUFACT & 0xFF): + info->flash_id = FLASH_MAN_AMD; + break; + case (FUJ_MANUFACT & 0xFF): + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = caddr[2]; /* device ID */ + + switch (value) { + case (AMD_ID_LV400T & 0xFF): + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00080000; + break; /* => 512 kB */ + + case (AMD_ID_LV400B & 0xFF): + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00080000; + break; /* => 512 kB */ + + case (AMD_ID_LV800T & 0xFF): + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (AMD_ID_LV800B & 0xFF): + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (AMD_ID_LV160T & 0xFF): + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (AMD_ID_LV160B & 0xFF): + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00200000; + break; /* => 2 MB */ +#if 0 /* enable when device IDs are available */ + case (AMD_ID_LV320T & 0xFF): + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (AMD_ID_LV320B & 0xFF): + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00400000; + break; /* => 4 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection: D0 = 1 if protected */ + caddr = (volatile unsigned char *)(info->start[i]); + info->protect[i] = caddr[4] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + caddr = (vu_char *)info->start[0]; + + *caddr = 0xF0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_char *addr = (vu_char*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0AAA] = 0xAA; + addr[0x0555] = 0x55; + addr[0x0AAA] = 0x80; + addr[0x0AAA] = 0xAA; + addr[0x0555] = 0x55; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_char*)(info->start[sect]); + addr[0] = 0x30; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_char*)(info->start[l_sect]); + while ((addr[0] & 0x80) != 0x80) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (vu_char *)info->start[0]; + addr[0] = 0xF0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + int rc; + + while (cnt > 0) { + if ((rc = write_byte(info, addr++, *src++)) != 0) { + return (rc); + } + --cnt; + } + + return (0); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_byte (flash_info_t *info, ulong dest, uchar data) +{ + vu_char *addr = (vu_char*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_char *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0AAA] = 0xAA; + addr[0x0555] = 0x55; + addr[0x0AAA] = 0xA0; + + *((vu_char *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_char *)dest) & 0x80) != (data & 0x80)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/hymod/eeprom.c b/board/hymod/eeprom.c new file mode 100644 index 00000000000..9d506467c2e --- /dev/null +++ b/board/hymod/eeprom.c @@ -0,0 +1,597 @@ +/* + * (C) Copyright 2001 + * Murray Jensen, CSIRO Manufacturing Science and Technology, + * <Murray.Jensen@cmst.csiro.au> + * + * 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 + */ + +#include <common.h> +#include <mpc8260.h> + +/* imports from fetch.c */ +extern int fetch_and_parse(bd_t *, char *, ulong, int (*)(uchar *, uchar *)); + +int +eeprom_load(unsigned offset, hymod_eeprom_t *ep) +{ + uchar data[HYMOD_EEPROM_SIZE], *dp, *edp; + hymod_eehdr_t *hp; + ulong len, crc; + + memset(ep, 0, sizeof *ep); + memset(data, 0, HYMOD_EEPROM_SIZE); + crc = 0; + + hp = (hymod_eehdr_t *)data; + eeprom_read(CFG_DEF_EEPROM_ADDR, offset, (uchar *)hp, sizeof (*hp)); + offset += sizeof (*hp); + + if (hp->id != HYMOD_EEPROM_ID || hp->ver > HYMOD_EEPROM_VER || + (len = hp->len) > HYMOD_EEPROM_MAXLEN) + return (0); + + dp = (uchar *)(hp + 1); edp = dp + len; + eeprom_read(CFG_DEF_EEPROM_ADDR, offset, dp, len); + offset += len; + + eeprom_read(CFG_DEF_EEPROM_ADDR, offset, (uchar *)&crc, sizeof (ulong)); + + if (crc32(0, data, edp - data) != crc) + return (0); + + ep->ver = hp->ver; + + for (;;) { + hymod_eerec_t *rp = (hymod_eerec_t *)dp; + ulong rtyp; + uchar rlen, *rdat; + uint rsiz; + + if (rp->small.topbit == 0) { + rtyp = rp->small.type; + rlen = rp->small.len; + rdat = rp->small.data; + rsiz = offsetof(hymod_eerec_t, small.data) + rlen; + } + else if (rp->medium.nxtbit == 0) { + rtyp = rp->medium.type; + rlen = rp->medium.len; + rdat = rp->medium.data; + rsiz = offsetof(hymod_eerec_t, medium.data) + rlen; + } + else { + rtyp = rp->large.type; + rlen = rp->large.len; + rdat = rp->large.data; + rsiz = offsetof(hymod_eerec_t, large.data) + rlen; + } + + if (rtyp == 0) + break; + + dp += rsiz; + if (dp > edp) /* error? */ + break; + + switch (rtyp) { + + case HYMOD_EEREC_SERNO: /* serial number */ + if (rlen == sizeof (ulong)) + memcpy(&ep->serno, rdat, sizeof (ulong)); + break; + + case HYMOD_EEREC_DATE: /* date */ + if (rlen == sizeof (hymod_date_t)) + memcpy(&ep->date, rdat, sizeof (hymod_date_t)); + break; + + case HYMOD_EEREC_BATCH: /* batch */ + if (rlen <= HYMOD_MAX_BATCH) + memcpy(ep->batch, rdat, ep->batchlen = rlen); + break; + + case HYMOD_EEREC_TYPE: /* board type */ + if (rlen == 1) + ep->bdtype = *rdat; + break; + + case HYMOD_EEREC_REV: /* board revision */ + if (rlen == 1) + ep->bdrev = *rdat; + break; + + case HYMOD_EEREC_SDRAM: /* sdram size(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_SDRAM) { + int i; + + for (i = 0; i < rlen; i++) + ep->sdramsz[i] = rdat[i]; + ep->nsdram = rlen; + } + break; + + case HYMOD_EEREC_FLASH: /* flash size(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_FLASH) { + int i; + + for (i = 0; i < rlen; i++) + ep->flashsz[i] = rdat[i]; + ep->nflash = rlen; + } + break; + + case HYMOD_EEREC_ZBT: /* zbt ram size(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_ZBT) { + int i; + + for (i = 0; i < rlen; i++) + ep->zbtsz[i] = rdat[i]; + ep->nzbt = rlen; + } + break; + + case HYMOD_EEREC_XLXTYP: /* xilinx fpga type(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_XLX) { + int i; + + for (i = 0; i < rlen; i++) + ep->xlx[i].type = rdat[i]; + ep->nxlx = rlen; + } + break; + + case HYMOD_EEREC_XLXSPD: /* xilinx fpga speed(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_XLX) { + int i; + + for (i = 0; i < rlen; i++) + ep->xlx[i].speed = rdat[i]; + } + break; + + case HYMOD_EEREC_XLXTMP: /* xilinx fpga temperature(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_XLX) { + int i; + + for (i = 0; i < rlen; i++) + ep->xlx[i].temp = rdat[i]; + } + break; + + case HYMOD_EEREC_XLXGRD: /* xilinx fpga grade(s) */ + if (rlen > 0 && rlen <= HYMOD_MAX_XLX) { + int i; + + for (i = 0; i < rlen; i++) + ep->xlx[i].grade = rdat[i]; + } + break; + + case HYMOD_EEREC_CPUTYP: /* CPU type */ + if (rlen == 1) + ep->mpc.type = *rdat; + break; + + case HYMOD_EEREC_CPUSPD: /* CPU speed */ + if (rlen == 1) + ep->mpc.cpuspd = *rdat; + break; + + case HYMOD_EEREC_CPMSPD: /* CPM speed */ + if (rlen == 1) + ep->mpc.cpmspd = *rdat; + break; + + case HYMOD_EEREC_BUSSPD: /* bus speed */ + if (rlen == 1) + ep->mpc.busspd = *rdat; + break; + + case HYMOD_EEREC_HSTYPE: /* high-speed serial chip type */ + if (rlen == 1) + ep->hss.type = *rdat; + break; + + case HYMOD_EEREC_HSCHIN: /* high-speed serial input channels */ + if (rlen == 1) + ep->hss.nchin = *rdat; + break; + + case HYMOD_EEREC_HSCHOUT: /* high-speed serial output channels */ + if (rlen == 1) + ep->hss.nchout = *rdat; + break; + + default: /* ignore */ + break; + } + } + + return (1); +} + +/* maps an ascii "name=value" into a binary eeprom data record */ +typedef + struct _eerec_map { + char *name; + uint type; + uchar *(*handler)(struct _eerec_map *, uchar *, uchar *, uchar *); + uint length; + uint maxlen; + } +eerec_map_t; + +static uchar * +uint_handler(eerec_map_t *rp, uchar *value, uchar *dp, uchar *edp) +{ + uchar *eval; + union { + uchar cval[4]; + ushort sval[2]; + ulong lval; + } rdata; + + rdata.lval = simple_strtol(value, (char **)&eval, 10); + + if (eval == value || *eval != '\0') { + printf("%s record (%s) is not a valid uint\n", rp->name, value); + return (NULL); + } + + if (dp + 2 + rp->length > edp) { + printf("can't fit %s record into eeprom\n", rp->name); + return (NULL); + } + + *dp++ = rp->type; + *dp++ = rp->length; + + switch (rp->length) { + + case 1: + if (rdata.lval >= 256) { + printf("%s record value (%lu) out of range (0-255)\n", + rp->name, rdata.lval); + return (NULL); + } + *dp++ = rdata.cval[3]; + break; + + case 2: + if (rdata.lval >= 65536) { + printf("%s record value (%lu) out of range (0-65535)\n", + rp->name, rdata.lval); + return (NULL); + } + memcpy(dp, &rdata.sval[1], 2); + dp += 2; + break; + + case 4: + memcpy(dp, &rdata.lval, 4); + dp += 4; + break; + + default: + printf("huh? rp->length not 1, 2 or 4! (%d)\n", rp->length); + return (NULL); + } + + return (dp); +} + +static uchar * +date_handler(eerec_map_t *rp, uchar *value, uchar *dp, uchar *edp) +{ + hymod_date_t date; + uchar *p = value, *ep; + + date.year = simple_strtol(p, (char **)&ep, 10); + if (ep == p || *ep++ != '-') { +bad_date: + printf("%s record (%s) is not a valid date\n", rp->name, value); + return (NULL); + } + + date.month = simple_strtol(p = ep, (char **)&ep, 10); + if (ep == p || *ep++ != '-' || date.month == 0 || date.month > 12) + goto bad_date; + + date.day = simple_strtol(p = ep, (char **)&ep, 10); + if (ep == p || *ep != '\0' || date.day == 0 || date.day > 31) + goto bad_date; + + if (dp + 2 + sizeof (hymod_date_t) > edp) { + printf("can't fit %s record into eeprom\n", rp->name); + return (NULL); + } + + *dp++ = rp->type; + *dp++ = sizeof (hymod_date_t); + memcpy(dp, &date, sizeof (hymod_date_t)); + dp += sizeof (hymod_date_t); + + return (dp); +} + +static uchar * +string_handler(eerec_map_t *rp, uchar *value, uchar *dp, uchar *edp) +{ + uint len; + + if ((len = strlen(value)) > rp->maxlen) { + printf("%s record (%s) string is too long (%d>%d)\n", + rp->name, value, len, rp->maxlen); + return (NULL); + } + + if (dp + 2 + len > edp) { + printf("can't fit %s record into eeprom\n", rp->name); + return (NULL); + } + + *dp++ = rp->type; + *dp++ = len; + memcpy(dp, value, len); + dp += len; + + return (dp); +} + +static uchar * +bytes_handler(eerec_map_t *rp, uchar *value, uchar *dp, uchar *edp) +{ + uchar bytes[HYMOD_MAX_BYTES], nbytes = 0; + uchar *p = value, *ep; + + for (;;) { + + if (nbytes >= HYMOD_MAX_BYTES) { + printf("%s record (%s) byte array too long\n", rp->name, value); + return (NULL); + } + + bytes[nbytes++] = simple_strtol(p, (char **)&ep, 10); + + if (ep == p || (*ep != '\0' && *ep != ',')) { + printf("%s record (%s) byte array has invalid uint\n", + rp->name, value); + return (NULL); + } + + if (*ep++ == '\0') + break; + + p = ep; + } + + if (dp + 2 + nbytes > edp) { + printf("can't fit %s record into eeprom\n", rp->name); + return (NULL); + } + + *dp++ = rp->type; + *dp++ = nbytes; + memcpy(dp, bytes, nbytes); + dp += nbytes; + + return (dp); +} + +static eerec_map_t eerec_map[] = { + /* name type handler len max */ + { "serno", HYMOD_EEREC_SERNO, uint_handler, 4, 0 }, + { "date", HYMOD_EEREC_DATE, date_handler, 4, 0 }, + { "batch", HYMOD_EEREC_BATCH, string_handler, 0, HYMOD_MAX_BATCH }, + { "type", HYMOD_EEREC_TYPE, uint_handler, 1, 0 }, + { "rev", HYMOD_EEREC_REV, uint_handler, 1, 0 }, + { "sdram", HYMOD_EEREC_SDRAM, bytes_handler, 0, HYMOD_MAX_SDRAM }, + { "flash", HYMOD_EEREC_FLASH, bytes_handler, 0, HYMOD_MAX_FLASH }, + { "zbt", HYMOD_EEREC_ZBT, bytes_handler, 0, HYMOD_MAX_ZBT }, + { "xlxtyp", HYMOD_EEREC_XLXTYP, bytes_handler, 0, HYMOD_MAX_XLX }, + { "xlxspd", HYMOD_EEREC_XLXSPD, bytes_handler, 0, HYMOD_MAX_XLX }, + { "xlxtmp", HYMOD_EEREC_XLXTMP, bytes_handler, 0, HYMOD_MAX_XLX }, + { "xlxgrd", HYMOD_EEREC_XLXGRD, bytes_handler, 0, HYMOD_MAX_XLX }, + { "cputyp", HYMOD_EEREC_CPUTYP, uint_handler, 1, 0 }, + { "cpuspd", HYMOD_EEREC_CPUSPD, uint_handler, 1, 0 }, + { "cpmspd", HYMOD_EEREC_CPMSPD, uint_handler, 1, 0 }, + { "busspd", HYMOD_EEREC_BUSSPD, uint_handler, 1, 0 }, + { "hstype", HYMOD_EEREC_HSTYPE, uint_handler, 1, 0 }, + { "hschin", HYMOD_EEREC_HSCHIN, uint_handler, 1, 0 }, + { "hschout", HYMOD_EEREC_HSCHOUT, uint_handler, 1, 0 }, +}; + +static int neerecs = sizeof eerec_map / sizeof eerec_map[0]; + +static uchar data[HYMOD_EEPROM_SIZE], *sdp, *dp, *edp; + +static int +eeprom_fetch_callback(uchar *name, uchar *value) +{ + eerec_map_t *rp; + + for (rp = eerec_map; rp < &eerec_map[neerecs]; rp++) + if (strcmp(name, rp->name) == 0) + break; + + if (rp >= &eerec_map[neerecs]) + return (0); + + if ((dp = (*rp->handler)(rp, value, dp, edp)) == NULL) + return (0); + + return (1); +} + +int +eeprom_fetch(unsigned offset, bd_t *bd, char *filename, ulong addr) +{ + hymod_eehdr_t *hp = (hymod_eehdr_t *)&data[0]; + ulong crc; + + hp->id = HYMOD_EEPROM_ID; + hp->ver = HYMOD_EEPROM_VER; + + dp = sdp = (uchar *)(hp + 1); + edp = dp + HYMOD_EEPROM_MAXLEN; + + if (fetch_and_parse(bd, filename, addr, eeprom_fetch_callback) == 0) + return (0); + + hp->len = dp - sdp; + + crc = crc32(0, data, dp - data); + memcpy(dp, &crc, sizeof (ulong)); + dp += sizeof (ulong); + + eeprom_write(CFG_DEF_EEPROM_ADDR, offset, data, dp - data); + + return (1); +} + +static char *type_vals[] = { + "NONE", "IO", "CLP", "DSP", "INPUT", "ALT-INPUT", "DISPLAY" +}; + +static char *xlxtyp_vals[] = { + "NONE", "XCV300E", "XCV400E", "XCV600E" +}; + +static char *xlxspd_vals[] = { + "NONE", "6", "7", "8" +}; + +static char *xlxtmp_vals[] = { + "NONE", "COM", "IND" +}; + +static char *xlxgrd_vals[] = { + "NONE", "NORMAL", "ENGSAMP" +}; + +static char *cputyp_vals[] = { + "NONE", "MPC8260" +}; + +static char *clk_vals[] = { + "NONE", "33", "66", "100", "133", "166", "200" +}; + +static char *hstype_vals[] = { + "NONE", "AMCC-S2064A" +}; + +static void +print_mem(char *l, char *s, uchar n, uchar a[]) +{ + if (n > 0) { + if (n == 1) + printf("%s%dMB %s", s, 1 << (a[0] - 20), l); + else { + ulong t = 0; + int i; + + for (i = 0; i < n; i++) + t += 1 << (a[i] - 20); + + printf("%s%luMB %s (%d banks:", s, t, l, n); + + for (i = 0; i < n; i++) + printf("%dMB%s", 1 << (a[i] - 20), (i == n - 1) ? ")" : ","); + } + } + else + printf("%sNO %s", s, l); +} + +void +eeprom_print(hymod_eeprom_t *ep) +{ + int i; + + printf(" Hymod %s board, rev %03d\n", + type_vals[ep->bdtype], ep->bdrev); + + printf(" serial #: %010lu, date %04d-%02d-%02d", + ep->serno, ep->date.year, ep->date.month, ep->date.day); + if (ep->batchlen > 0) + printf(", batch \"%.*s\"", ep->batchlen, ep->batch); + puts("\n"); + + switch (ep->bdtype) { + + case HYMOD_BDTYPE_IO: + case HYMOD_BDTYPE_CLP: + case HYMOD_BDTYPE_DSP: + printf(" Motorola %s CPU, speeds: %s/%s/%s", + cputyp_vals[ep->mpc.type], clk_vals[ep->mpc.cpuspd], + clk_vals[ep->mpc.cpmspd], clk_vals[ep->mpc.busspd]); + + print_mem("SDRAM", ", ", ep->nsdram, ep->sdramsz); + + print_mem("FLASH", ", ", ep->nflash, ep->flashsz); + + puts("\n"); + + print_mem("ZBT", " ", ep->nzbt, ep->zbtsz); + + if (ep->nxlx > 0) { + hymod_xlx_t *xp; + + if (ep->nxlx == 1) { + xp = &ep->xlx[0]; + printf(", Xilinx %s FPGA (%s/%s/%s)", + xlxtyp_vals[xp->type], xlxspd_vals[xp->speed], + xlxtmp_vals[xp->temp], xlxgrd_vals[xp->grade]); + } + else { + printf(", %d Xilinx FPGAs (", ep->nxlx); + for (i = 0; i < ep->nxlx; i++) { + xp = &ep->xlx[i]; + printf("%s[%s/%s/%s]%s", + xlxtyp_vals[xp->type], xlxspd_vals[xp->speed], + xlxtmp_vals[xp->temp], xlxgrd_vals[xp->grade], + (i == ep->nxlx - 1) ? ")" : ", "); + } + } + } + else + puts(", NO FPGAs"); + + puts("\n"); + + if (ep->hss.type > 0) + printf(" High Speed Serial: %s, %d input%s, %d output%s\n", + hstype_vals[ep->hss.type], + ep->hss.nchin, (ep->hss.nchin == 1 ? "" : "s"), + ep->hss.nchout, (ep->hss.nchout == 1 ? "" : "s")); + break; + + case HYMOD_BDTYPE_INPUT: + case HYMOD_BDTYPE_ALTINPUT: + case HYMOD_BDTYPE_DISPLAY: + break; + + default: + /* crap! */ + printf(" UNKNOWN BOARD TYPE: %d\n", ep->bdtype); + break; + } +} diff --git a/board/hymod/flash.c b/board/hymod/flash.c new file mode 100644 index 00000000000..ee052e39593 --- /dev/null +++ b/board/hymod/flash.c @@ -0,0 +1,745 @@ +/* + * (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 + * + * Hacked for the Hymod board by Murray.Jensen@cmst.csiro.au, 20-Oct-00 + */ + +#include <common.h> +#include <mpc8260.h> +#include <board/hymod/flash.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Protection Flags: + */ +#define FLAG_PROTECT_SET 0x01 +#define FLAG_PROTECT_CLEAR 0x02 + +/*----------------------------------------------------------------------- + * Functions + */ +#if 0 +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static void flash_get_offsets (ulong base, flash_info_t *info); +#endif +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*----------------------------------------------------------------------- + */ + +/* + * probe for the existence of flash at bank word address "addr" + * 0 = yes, 1 = bad Manufacturer's Id, 2 = bad Device Id + */ +static int +bank_probe_word(bank_addr_t addr) +{ + int retval; + + /* reset the flash */ + *addr = BANK_CMD_RST; + + /* check the manufacturer id */ + *addr = BANK_CMD_RD_ID; + if (*BANK_ADDR_REG_MAN(addr) != BANK_RD_ID_MAN) { + retval = -1; + goto out; + } + + /* check the device id */ + *addr = BANK_CMD_RD_ID; + if (*BANK_ADDR_REG_DEV(addr) != BANK_RD_ID_DEV) { + retval = -2; + goto out; + } + + retval = CFG_FLASH_TYPE; + +out: + /* reset the flash again */ + *addr = BANK_CMD_RST; + + return retval; +} + +/* + * probe for flash banks at address "base" and store info for any found + * into flash_info entry "fip". Must find at least one bank. + */ +static void +bank_probe(flash_info_t *fip, bank_addr_t base) +{ + bank_addr_t addr, eaddr; + int nbanks; + + fip->flash_id = FLASH_UNKNOWN; + fip->size = 0L; + fip->sector_count = 0; + + addr = base; + eaddr = BANK_ADDR_BASE(addr, MAX_BANKS); + nbanks = 0; + + while (addr < eaddr) { + bank_addr_t addrw, eaddrw, addrb; + int i, osc, nsc, curtype = -1; + + addrw = addr; + eaddrw = BANK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) { + int thistype; + +#ifdef FLASH_DEBUG + printf(" probing for flash at addr 0x%08lx\n", + (unsigned long)addrw); +#endif + if ((thistype = bank_probe_word(addrw++)) < 0) + goto out; + + if (curtype < 0) + curtype = thistype; + else { + if (thistype != curtype) { + printf("Differing flash type found!\n"); + goto out; + } + } + } + + if (curtype < 0) + goto out; + + /* bank exists - append info for this bank to *fip */ + fip->flash_id = FLASH_MAN_INTEL|curtype; + fip->size += BANK_SIZE; + osc = fip->sector_count; + fip->sector_count += BANK_NBLOCKS; + if ((nsc = fip->sector_count) >= CFG_MAX_FLASH_SECT) + panic("Too many sectors in flash at address 0x%08lx\n", + (unsigned long)base); + + addrb = addr; + for (i = osc; i < nsc; i++) { + fip->start[i] = (ulong)addrb; + fip->protect[i] = 0; + addrb = BANK_ADDR_NEXT_BLK(addrb); + } + + addr = BANK_ADDR_NEXT_BANK(addr); + nbanks++; + } + +out: + if (nbanks == 0) + panic("ERROR: no flash found at address 0x%08lx\n", + (unsigned long)base); +} + +static void +bank_reset(flash_info_t *info, int sect) +{ + bank_addr_t addrw, eaddrw; + + addrw = (bank_addr_t)info->start[sect]; + eaddrw = BANK_ADDR_NEXT_WORD(addrw); + + while (addrw < eaddrw) { +#ifdef FLASH_DEBUG + printf(" writing reset cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = BANK_CMD_RST; + addrw++; + } +} + +static void +bank_erase_init(flash_info_t *info, int sect) +{ + bank_addr_t addrw, saddrw, eaddrw; + int flag; + +#ifdef FLASH_DEBUG + printf("0x%08lx BANK_CMD_PROG\n", BANK_CMD_PROG); + printf("0x%08lx BANK_CMD_ERASE1\n", BANK_CMD_ERASE1); + printf("0x%08lx BANK_CMD_ERASE2\n", BANK_CMD_ERASE2); + printf("0x%08lx BANK_CMD_CLR_STAT\n", BANK_CMD_CLR_STAT); + printf("0x%08lx BANK_CMD_RST\n", BANK_CMD_RST); + printf("0x%08lx BANK_STAT_RDY\n", BANK_STAT_RDY); + printf("0x%08lx BANK_STAT_ERR\n", BANK_STAT_ERR); +#endif + + saddrw = (bank_addr_t)info->start[sect]; + eaddrw = BANK_ADDR_NEXT_WORD(saddrw); + +#ifdef FLASH_DEBUG + printf("erasing sector %d, start addr = 0x%08lx " + "(bank next word addr = 0x%08lx)\n", sect, + (unsigned long)saddrw, (unsigned long)eaddrw); +#endif + + /* Disable intrs which might cause a timeout here */ + flag = disable_interrupts(); + + for (addrw = saddrw; addrw < eaddrw; addrw++) { +#ifdef FLASH_DEBUG + printf(" writing erase cmd to addr 0x%08lx\n", + (unsigned long)addrw); +#endif + *addrw = BANK_CMD_ERASE1; + *addrw = BANK_CMD_ERASE2; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); +} + +static int +bank_erase_poll(flash_info_t *info, int sect) +{ + bank_addr_t addrw, saddrw, eaddrw; + int sectdone, haderr; + + saddrw = (bank_addr_t)info->start[sect]; + eaddrw = BANK_ADDR_NEXT_WORD(saddrw); + + sectdone = 1; + haderr = 0; + + for (addrw = saddrw; addrw < eaddrw; addrw++) { + bank_word_t stat = *addrw; + +#ifdef FLASH_DEBUG + printf(" checking status at addr " + "0x%08lx [0x%08lx]\n", + (unsigned long)addrw, stat); +#endif + if ((stat & BANK_STAT_RDY) != BANK_STAT_RDY) + sectdone = 0; + else if ((stat & BANK_STAT_ERR) != 0) { + printf(" failed on sector %d " + "(stat = 0x%08lx) at " + "address 0x%08lx\n", + sect, stat, + (unsigned long)addrw); + *addrw = BANK_CMD_CLR_STAT; + haderr = 1; + } + } + + if (haderr) + return (-1); + else + return (sectdone); +} + +static int +bank_write_word(bank_addr_t addr, bank_word_t value) +{ + bank_word_t stat; + ulong start; + int flag, retval; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = BANK_CMD_PROG; + + *addr = value; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + retval = 0; + + /* data polling for D7 */ + start = get_timer (0); + do { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + retval = 1; + goto done; + } + stat = *addr; + } while ((stat & BANK_STAT_RDY) != BANK_STAT_RDY); + + if ((stat & BANK_STAT_ERR) != 0) { + printf("flash program failed (stat = 0x%08lx) " + "at address 0x%08lx\n", (ulong)stat, (ulong)addr); + *addr = BANK_CMD_CLR_STAT; + retval = 3; + } + +done: + /* reset to read mode */ + *addr = BANK_CMD_RST; + + return (retval); +} + +/*----------------------------------------------------------------------- + */ + +unsigned long +flash_init(void) +{ + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + bank_probe(&flash_info[0], (bank_addr_t)CFG_FLASH_BASE); + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE == CFG_FLASH_BASE + (void)flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#if defined(CFG_FLASH_ENV_ADDR) + (void)flash_protect(FLAG_PROTECT_SET, + CFG_FLASH_ENV_ADDR, +#if defined(CFG_FLASH_ENV_BUF) + CFG_FLASH_ENV_ADDR + CFG_FLASH_ENV_BUF - 1, +#else + CFG_FLASH_ENV_ADDR + CFG_FLASH_ENV_SIZE - 1, +#endif + &flash_info[0]); +#endif + + return flash_info[0].size; +} + +/*----------------------------------------------------------------------- + */ +#if 0 +static void +flash_get_offsets(ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start adress table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + +} +#endif /* 0 */ + +/*----------------------------------------------------------------------- + */ +void +flash_print_info(flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F320J5: printf ("28F320J5 (32 Mbit, 2 x 16bit)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ +#if 0 +static ulong +flash_get_size(vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; + + value = addr[0]; + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start adress table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; + + *addr = 0x00F000F0; /* reset bank */ + } + + return (info->size); +} +#endif /* 0 */ + + +/*----------------------------------------------------------------------- + */ + +int +flash_erase(flash_info_t *info, int s_first, int s_last) +{ + int prot, sect, haderr; + ulong start, now, last; + int rcode = 0; + +#ifdef FLASH_DEBUG + printf("\nflash_erase: erase %d sectors (%d to %d incl.) from\n" + " Bank # %d: ", s_last - s_first + 1, s_first, s_last, + (info - flash_info) + 1); + flash_print_info(info); +#endif + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf("- Warning: %d protected sector%s will not be erased!\n", + prot, (prot > 1 ? "s" : "")); + } + + start = get_timer (0); + last = 0; + haderr = 0; + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + ulong estart; + int sectdone; + + bank_erase_init(info, sect); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + estart = get_timer(start); + + do { + now = get_timer(start); + + if (now - estart > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout (sect %d)\n", sect); + haderr = 1; + rcode = 1; + break; + } + +#ifndef FLASH_DEBUG + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } +#endif + + sectdone = bank_erase_poll(info, sect); + + if (sectdone < 0) { + haderr = 1; + rcode = 1; + break; + } + + } while (!sectdone); + + if (haderr) + break; + } + } + + if (haderr > 0) + printf (" failed\n"); + else + printf (" done\n"); + + /* reset to read mode */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + bank_reset(info, sect); + } + } + return rcode; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int +write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int +write_word(flash_info_t *info, ulong dest, ulong data) +{ + int retval; + + /* Check if Flash is (sufficiently) erased */ + if ((*(ulong *)dest & data) != data) { + return (2); + } + + retval = bank_write_word((bank_addr_t)dest, (bank_word_t)data); + + return (retval); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/icu862/flash.c b/board/icu862/flash.c new file mode 100644 index 00000000000..79e7cc28aec --- /dev/null +++ b/board/icu862/flash.c @@ -0,0 +1,617 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, + size_b0 >> 20); + } + + if (FLASH_BASE1_PRELIM != 0x0) { + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: Bank 1 (0x%08lx = %ld MB)" + " > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1 >> 20, + size_b0, size_b0 >> 20); + + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + } else { + size_b1 = 0; + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & OR_AM_MSK); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SIZE-1, + &flash_info[0]); +#endif + + /* ICU862 Board has only one Flash Bank */ + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); + +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (((info->flash_id & FLASH_TYPEMASK) == FLASH_AM040) || + ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM033C)) { + /* set sector offsets for uniform sector type */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00040000); + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + puts ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: puts ("AMD "); break; + case FLASH_MAN_FUJ: puts ("FUJITSU "); break; + case FLASH_MAN_BM: puts ("BRIGHT MICRO "); break; + default: puts ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM040: puts ("29F040/29LV040 (4 Mbit, uniform sectors)\n"); + break; + case FLASH_AM400B: puts ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: puts ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: puts ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: puts ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: puts ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: puts ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: puts ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: puts ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + case FLASH_AM033C: puts ("AM29LV033C (32 Mbit)\n"); + break; + default: puts ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + puts (" Sector Start Addresses:"); + + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) { + puts ("\n "); + } + + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + + puts ("\n"); +} + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; +#if 0 + ulong base = (ulong)addr; +#endif + uchar value; + + /* Write auto select command: read Manufacturer ID */ +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x90909090; +#endif + + value = addr[0]; + + switch (value + (value << 16)) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + break; + } + + value = addr[1]; /* device ID */ + + switch (value) { + case AMD_ID_F040B: + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + case AMD_ID_LV033C: + info->flash_id += FLASH_AM033C; + info->sector_count = 64; + info->size = 0x01000000; + break; /* => 16Mb */ + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + +#if 0 + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +#else + flash_get_offsets ((ulong)addr, &flash_info[0]); +#endif + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); +#if 1 + /* We don't know why it happens, but on ICU Board * + * for AMD29033C flash we need to resend the command of * + * reading flash protection for upper 8 Mb of flash */ + if ( i == 32 ) { + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x90909090; + } +#endif + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; +#if 0 + *addr = 0x00F000F0; /* reset bank */ +#else + *addr = 0xF0F0F0F0; /* reset bank */ +#endif + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + puts ("- missing\n"); + } else { + puts ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + puts ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + puts ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x80808080; + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; +#endif + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); +#if 0 + addr[0] = 0x00300030; +#else + addr[0] = 0x30303030; +#endif + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); +#if 0 + while ((addr[0] & 0x00800080) != 0x00800080) +#else + while ((addr[0] & 0xFFFFFFFF) != 0xFFFFFFFF) +#endif + { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + puts ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; +#if 0 + addr[0] = 0x00F000F0; /* reset bank */ +#else + addr[0] = 0xF0F0F0F0; /* reset bank */ +#endif + + puts (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + +#if 0 + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; +#else + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0xA0A0A0A0; +#endif + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); +#if 0 + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) +#else + while ((*((vu_long *)dest) & 0x80808080) != (data & 0x80808080)) +#endif + { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/ip860/flash.c b/board/ip860/flash.c new file mode 100644 index 00000000000..4b0ea9bb019 --- /dev/null +++ b/board/ip860/flash.c @@ -0,0 +1,456 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + volatile ip860_bcsr_t *bcsr = (ip860_bcsr_t *)BCSR_BASE; + unsigned long size; + int i; + + /* Init: enable write, + * or we cannot even write flash commands + */ + bcsr->bd_ctrl |= BD_CTRL_FLWE; + + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size = flash_get_size((vu_long *)FLASH_BASE, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size, size<<20); + } + + /* Remap FLASH according to real size */ + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size & 0xFFFF8000); + memctl->memc_br1 = (CFG_FLASH_BASE & BR_BA_MSK) | + (memctl->memc_br1 & ~(BR_BA_MSK)); + + /* Re-do sizing to get full correct info */ + size = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size; + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + return (size); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* all possible flash types + * (28F016SV, 28F160S3, 28F320S3) + * have the same erase block size: 64 kB per chip, + * of 128 kB per bank + */ + + /* set up sector start address table */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base; + base += 0x00020000; + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: printf ("Intel "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F016SV: printf ("28F016SV (16 Mbit, 32 x 64k)\n"); + break; + case FLASH_28F160S3: printf ("28F160S3 (16 Mbit, 32 x 512K)\n"); + break; + case FLASH_28F320S3: printf ("28F320S3 (32 Mbit, 64 x 512K)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + /* Write "Intelligent Identifier" command: read Manufacturer ID */ + *addr = 0x90909090; + + value = addr[0]; + switch (value) { + case (MT_MANUFACT & 0x00FF00FF): /* MT or => Intel */ + case (INTEL_ALT_MANU & 0x00FF00FF): + info->flash_id = FLASH_MAN_INTEL; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case (INTEL_ID_28F016S): + info->flash_id += FLASH_28F016SV; + info->sector_count = 32; + info->size = 0x00400000; + break; /* => 2x2 MB */ + + case (INTEL_ID_28F160S3): + info->flash_id += FLASH_28F160S3; + info->sector_count = 32; + info->size = 0x00400000; + break; /* => 2x2 MB */ + + case (INTEL_ID_28F320S3): + info->flash_id += FLASH_28F320S3; + info->sector_count = 64; + info->size = 0x00800000; + break; /* => 2x4 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000); + /* don't know how to check sector protection */ + info->protect[i] = 0; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (vu_long *)info->start[0]; + + *addr = 0xFFFFFF; /* reset bank to read array mode */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_INTEL) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + vu_long *addr = (vu_long *)(info->start[sect]); + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Single Block Erase Command */ + *addr = 0x20202020; + /* Confirm */ + *addr = 0xD0D0D0D0; + /* Resume Command, as per errata update */ + *addr = 0xD0D0D0D0; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + while ((*addr & 0x00800080) != 0x00800080) { + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + *addr = 0xFFFFFFFF; /* reset bank */ + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + /* reset to read mode */ + *addr = 0xFFFFFFFF; + } + } + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long *)dest; + ulong start, csr; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*addr & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Write Command */ + *addr = 0x10101010; + + /* Write Data */ + *addr = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + flag = 0; + while (((csr = *addr) & 0x00800080) != 0x00800080) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + flag = 1; + break; + } + } + if (csr & 0x00400040) { +printf ("CSR indicates write error (%08lx) at %08lx\n", csr, (ulong)addr); + flag = 1; + } + + /* Clear Status Registers Command */ + *addr = 0x50505050; + /* Reset to read array mode */ + *addr = 0xFFFFFFFF; + + return (flag); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/ivm/flash.c b/board/ivm/flash.c new file mode 100644 index 00000000000..b5453dff1e4 --- /dev/null +++ b/board/ivm/flash.c @@ -0,0 +1,598 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_data (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0: " + "ID 0x%lx, Size = 0x%08lx = %ld MB\n", + flash_info[0].flash_id, + size_b0, size_b0<<20); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | \ + BR_MS_GPCM | BR_PS_16 | BR_V; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size_b0; + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + + return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_MT: + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + ((i-3) * 0x00020000); + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + return; + + case FLASH_MAN_SST: + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00002000); + } + return; + + case FLASH_MAN_AMD: + case FLASH_MAN_FUJ: + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + return; + default: + printf ("Don't know sector ofsets for flash type 0x%lx\n", + info->flash_id); + return; + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("Fujitsu "); break; + case FLASH_MAN_SST: printf ("SST "); break; + case FLASH_MAN_STM: printf ("STM "); break; + case FLASH_MAN_MT: printf ("MT "); break; + case FLASH_MAN_INTEL: printf ("Intel "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + case FLASH_SST200A: printf ("39xF200A (2M = 128K x 16)\n"); + break; + case FLASH_SST400A: printf ("39xF400A (4M = 256K x 16)\n"); + break; + case FLASH_SST800A: printf ("39xF800A (8M = 512K x 16)\n"); + break; + case FLASH_STM800AB: printf ("M29W800AB (8M = 512K x 16)\n"); + break; + case FLASH_28F008S5: printf ("28F008S5 (1M = 64K x 16)\n"); + break; + case FLASH_28F400_T: printf ("28F400B3 (4Mbit, top boot sector)\n"); + break; + case FLASH_28F400_B: printf ("28F400B3 (4Mbit, bottom boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + if (info->size >= (1 << 20)) { + i = 20; + } else { + i = 10; + } + printf (" Size: %ld %cB in %d Sectors\n", + info->size >> i, + (i == 20) ? 'M' : 'k', + info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + ushort value; + vu_short *saddr = (vu_short *)addr; + + /* Read Manufacturer ID */ + saddr[0] = 0x0090; + value = saddr[0]; + + switch (value) { + case (AMD_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_AMD; + break; + case (FUJ_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_FUJ; + break; + case (SST_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_SST; + break; + case (STM_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_STM; + break; + case (MT_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_MT; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + saddr[0] = 0x00FF; /* restore read mode */ + return (0); /* no or unknown flash */ + } + + value = saddr[1]; /* device ID */ + + switch (value) { + case (AMD_ID_LV400T & 0xFFFF): + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (AMD_ID_LV400B & 0xFFFF): + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (AMD_ID_LV800T & 0xFFFF): + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (AMD_ID_LV800B & 0xFFFF): + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (AMD_ID_LV160T & 0xFFFF): + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (AMD_ID_LV160B & 0xFFFF): + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case (AMD_ID_LV320T & 0xFFFF): + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case (AMD_ID_LV320B & 0xFFFF): + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + case (SST_ID_xF200A & 0xFFFF): + info->flash_id += FLASH_SST200A; + info->sector_count = 64; /* 39xF200A ID ( 2M = 128K x 16 ) */ + info->size = 0x00080000; + break; + case (SST_ID_xF400A & 0xFFFF): + info->flash_id += FLASH_SST400A; + info->sector_count = 128; /* 39xF400A ID ( 4M = 256K x 16 ) */ + info->size = 0x00100000; + break; + case (SST_ID_xF800A & 0xFFFF): + info->flash_id += FLASH_SST800A; + info->sector_count = 256; /* 39xF800A ID ( 8M = 512K x 16 ) */ + info->size = 0x00200000; + break; /* => 2 MB */ + case (STM_ID_x800AB & 0xFFFF): + info->flash_id += FLASH_STM800AB; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + case (MT_ID_28F400_T & 0xFFFF): + info->flash_id += FLASH_28F400_T; + info->sector_count = 7; + info->size = 0x00080000; + break; /* => 512 kB */ + case (MT_ID_28F400_B & 0xFFFF): + info->flash_id += FLASH_28F400_B; + info->sector_count = 7; + info->size = 0x00080000; + break; /* => 512 kB */ + default: + info->flash_id = FLASH_UNKNOWN; + saddr[0] = 0x00FF; /* restore read mode */ + return (0); /* => no or unknown flash */ + + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + saddr[0] = 0x00FF; /* restore read mode */ + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_MT) { + printf ("Can erase only MT flash types - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + vu_short *addr = (vu_short *)(info->start[sect]); + unsigned short status; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = 0x0050; /* clear status register */ + *addr = 0x0020; /* erase setup */ + *addr = 0x00D0; /* erase confirm */ + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + while (((status = *addr) & 0x0080) != 0x0080) { + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + *addr = 0x00FF; /* reset to read mode */ + return 1; + } + + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + *addr = 0x00FF; /* reset to read mode */ + } + } + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 4 - Flash not identified + */ + +#define FLASH_WIDTH 2 /* flash bus width in bytes */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + if (info->flash_id == FLASH_UNKNOWN) { + return 4; + } + + wp = (addr & ~(FLASH_WIDTH-1)); /* get lower FLASH_WIDTH aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<FLASH_WIDTH && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<FLASH_WIDTH; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += FLASH_WIDTH; + } + + /* + * handle FLASH_WIDTH aligned part + */ + while (cnt >= FLASH_WIDTH) { + data = 0; + for (i=0; i<FLASH_WIDTH; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += FLASH_WIDTH; + cnt -= FLASH_WIDTH; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<FLASH_WIDTH && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<FLASH_WIDTH; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_data(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, ulong dest, ulong data) +{ + vu_short *addr = (vu_short *)dest; + ushort sdata = (ushort)data; + ushort status; + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*addr & sdata) != sdata) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = 0x0040; /* write setup */ + *addr = sdata; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + + while (((status = *addr) & 0x0080) != 0x0080) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + *addr = 0x00FF; /* restore read mode */ + return (1); + } + } + + *addr = 0x00FF; /* restore read mode */ + + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/lantec/flash.c b/board/lantec/flash.c new file mode 100644 index 00000000000..df585103334 --- /dev/null +++ b/board/lantec/flash.c @@ -0,0 +1,625 @@ +/* + * (C) Copyright 2000, 2001 + * 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 + */ + +/* + * Derived from ../tqm8xx/flash.c + * [Torsten Stevens, FHG IMS; Bruno Achauer, Exet AG] + */ + +#include <common.h> +#include <mpc8xx.h> + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*---------------------------------------------------------------------*/ +#undef DEBUG_FLASH + +#ifdef DEBUG_FLASH +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + DEBUGF("\n## Get flash bank 1 size @ 0x%08x\n",FLASH_BASE0_PRELIM); + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0: " + "ID 0x%lx, Size = 0x%08lx = %ld MB\n", + flash_info[0].flash_id, + size_b0, size_b0<<20); + } + + DEBUGF("## Get flash bank 2 size @ 0x%08x\n",FLASH_BASE5_PRELIM); + + size_b1 = flash_get_size((vu_long *)FLASH_BASE5_PRELIM, &flash_info[1]); + + DEBUGF("## Prelim. Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + DEBUGF ("## Before remap: " + "BR0: 0x%08x OR0: 0x%08x " + "BR1: 0x%08x OR1: 0x%08x\n", + memctl->memc_br0, memctl->memc_or0, + memctl->memc_br1, memctl->memc_or1); + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | \ + BR_MS_GPCM | BR_PS_32 | BR_V; + + DEBUGF("## BR0: 0x%08x OR0: 0x%08x\n", + memctl->memc_br0, memctl->memc_or0); + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size_b0; + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + + if (size_b1) { + memctl->memc_or5 = CFG_OR_TIMING_FLASH | (-size_b1 & 0xFFFF8000); + memctl->memc_br5 = ((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_PS_32 | BR_V; + + DEBUGF("## BR5: 0x%08x OR5: 0x%08x\n", + memctl->memc_br5, memctl->memc_or5); + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_info[1].size = size_b1; + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[1]); +#endif + } else { + memctl->memc_br5 = 0; /* invalidate bank */ + memctl->memc_or5 = 0; /* invalidate bank */ + + DEBUGF("## DISABLE BR5: 0x%08x OR5: 0x%08x\n", + memctl->memc_br5, memctl->memc_or5); + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + flash_info[1].size = 0; + } + + DEBUGF("## Final Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; + + value = addr[0]; + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; + + *addr = 0x00F000F0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ((addr[0] & 0x00800080) != 0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/mbx8xx/flash.c b/board/mbx8xx/flash.c new file mode 100644 index 00000000000..e1aa47bbaa9 --- /dev/null +++ b/board/mbx8xx/flash.c @@ -0,0 +1,408 @@ +/* + * (C) Copyright 2000 + * Marius Groeger <mgroeger@sysgo.de> + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Flash Routines for AM290[48]0B devices + * + *-------------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> +#include "vpd.h" + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size, totsize; + int i; + ulong addr; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + totsize = 0; + addr = 0xfc000000; + for(i = 0; i < CFG_MAX_FLASH_BANKS; i++) { + size = flash_get_size((vu_long *)addr, &flash_info[i]); + if (flash_info[i].flash_id == FLASH_UNKNOWN) + break; + totsize += size; + addr += size; + } + + addr = 0xfe000000; + for(i = 0; i < CFG_MAX_FLASH_BANKS; i++) { + + size = flash_get_size((vu_long *)addr, &flash_info[i]); + if (flash_info[i].flash_id == FLASH_UNKNOWN) + break; + totsize += size; + addr += size; + } + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + return (totsize); +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id >> 16) { + case 0x1: + printf ("AMD "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case AMD_ID_F040B: + printf ("AM29F040B (4 Mbit)\n"); + break; + case AMD_ID_F080B: + printf ("AM29F080B (8 Mbit)\n"); + break; + case AMD_ID_F016D: + printf ("AM29F016D (16 Mbit)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong vendor, devid; + ulong base = (ulong)addr; + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x90909090; + + vendor = addr[0]; + devid = addr[1] & 0xff; + + /* only support AMD */ + if (vendor != 0x01010101) { + return 0; + } + + vendor &= 0xf; + devid &= 0xff; + + if (devid == AMD_ID_F040B) { + info->flash_id = vendor << 16 | devid; + info->sector_count = 8; + info->size = info->sector_count * 0x10000; + } + else if (devid == AMD_ID_F080B) { + info->flash_id = vendor << 16 | devid; + info->sector_count = 16; + info->size = 4 * info->sector_count * 0x10000; + } + else if (devid == AMD_ID_F016D) { + info->flash_id = vendor << 16 | devid; + info->sector_count = 32; + info->size = 4 * info->sector_count * 0x10000; + } + else { + printf ("## Unknown Flash Type: %08lx\n", devid); + return 0; + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* sector base address */ + info->start[i] = base + i * (info->size / info->sector_count); + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (vu_long *)info->start[0]; + addr[0] = 0xF0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0XAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0x80808080; + addr[0x0555] = 0XAAAAAAAA; + addr[0x02AA] = 0x55555555; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x30303030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ((addr[0] & 0x80808080) != 0x80808080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + serial_putc ('.'); + last = now; + } + } + + DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0xF0F0F0F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0xAAAAAAAA; + addr[0x02AA] = 0x55555555; + addr[0x0555] = 0xA0A0A0A0; + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x80808080) != (data & 0x80808080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/mbx8xx/vpd.c b/board/mbx8xx/vpd.c new file mode 100644 index 00000000000..6f883520cde --- /dev/null +++ b/board/mbx8xx/vpd.c @@ -0,0 +1,196 @@ +/* + * (C) Copyright 2000 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Code in faintly related to linux/arch/ppc/8xx_io: + * MPC8xx CPM I2C interface. Copyright (c) 1999 Dan Malek (dmalek@jlc.net). + * + * This file implements functions to read the MBX's Vital Product Data + * (VPD). I can't use the more general i2c code in mpc8xx/... since I need + * the VPD at a time where there is no RAM available yet. Hence the VPD is + * read into a special area in the DPRAM (see config_MBX.h::CFG_DPRAMVPD). + * + * ----------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#ifdef CONFIG_8xx +#include <commproc.h> +#endif +#include "vpd.h" + +/* Location of receive/transmit buffer descriptors + * Allocate one transmit bd and one receive bd. + * IIC_BD_FREE points to free bd space which we'll use as tx buffer. + */ +#define IIC_BD_TX1 (BD_IIC_START + 0*sizeof(cbd_t)) +#define IIC_BD_TX2 (BD_IIC_START + 1*sizeof(cbd_t)) +#define IIC_BD_RX (BD_IIC_START + 2*sizeof(cbd_t)) +#define IIC_BD_FREE (BD_IIC_START + 3*sizeof(cbd_t)) + +/* FIXME -- replace 0x2000 with offsetof */ +#define VPD_P ((vpd_t *)(CFG_IMMR + 0x2000 + CFG_DPRAMVPD)) + +/* transmit/receive buffers */ +#define IIC_RX_LENGTH 128 + +#define WITH_MICROCODE_PATCH + +vpd_packet_t * vpd_find_packet(u_char ident) +{ + vpd_packet_t *packet; + vpd_t *vpd = VPD_P; + + packet = (vpd_packet_t *)&vpd->packets; + while ((packet->identifier != ident) && packet->identifier != 0xFF) + { + packet = (vpd_packet_t *)((char *)packet + packet->size + 2); + } + return packet; +} + +void vpd_init(void) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cp = &(im->im_cpm); + volatile i2c8xx_t *i2c = (i2c8xx_t *)&(im->im_i2c); + volatile iic_t *iip; +#ifdef WITH_MICROCODE_PATCH + ulong reloc = 0; +#endif + + iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + + /* + * kludge: when running from flash, no microcode patch can be + * installed. However, the DPMEM usually contains non-zero + * garbage at the relocatable patch base location, so lets clear + * it now. This way the rest of the code can support the microcode + * patch dynamically. + */ + if ((ulong)vpd_init & 0xff000000) + iip->iic_rpbase = 0; + +#ifdef WITH_MICROCODE_PATCH + /* Check for and use a microcode relocation patch. */ + if ((reloc = iip->iic_rpbase)) + iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#endif + /* Initialize Port B IIC pins */ + cp->cp_pbpar |= 0x00000030; + cp->cp_pbdir |= 0x00000030; + cp->cp_pbodr |= 0x00000030; + + i2c->i2c_i2mod = 0x04; /* filter clock */ + i2c->i2c_i2add = 0x34; /* select an arbitrary (unique) address */ + i2c->i2c_i2brg = 0x07; /* make clock run maximum slow */ + i2c->i2c_i2cmr = 0x00; /* disable interrupts */ + i2c->i2c_i2cer = 0x1f; /* clear events */ + i2c->i2c_i2com = 0x01; /* configure i2c to work as master */ + + if (vpd_read(0xa4, (uchar*)VPD_P, VPD_EEPROM_SIZE, 0) != VPD_EEPROM_SIZE) + { + hang(); + } +} + + +/* Read from I2C. + * This is a two step process. First, we send the "dummy" write + * to set the device offset for the read. Second, we perform + * the read operation. + */ +int vpd_read(uint iic_device, uchar *buf, int count, int offset) +{ + volatile immap_t *im = (immap_t *)CFG_IMMR; + volatile cpm8xx_t *cp = &(im->im_cpm); + volatile i2c8xx_t *i2c = (i2c8xx_t *)&(im->im_i2c); + volatile iic_t *iip; + volatile cbd_t *tbdf1, *tbdf2, *rbdf; + uchar *tb; + uchar event; +#ifdef WITH_MICROCODE_PATCH + ulong reloc = 0; +#endif + + iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; +#ifdef WITH_MICROCODE_PATCH + /* Check for and use a microcode relocation patch. */ + if ((reloc = iip->iic_rpbase)) + iip = (iic_t *)&cp->cp_dpmem[iip->iic_rpbase]; +#endif + tbdf1 = (cbd_t *)&cp->cp_dpmem[IIC_BD_TX1]; + tbdf2 = (cbd_t *)&cp->cp_dpmem[IIC_BD_TX2]; + rbdf = (cbd_t *)&cp->cp_dpmem[IIC_BD_RX]; + + /* Send a "dummy write" operation. This is a write request with + * only the offset sent, followed by another start condition. + * This will ensure we start reading from the first location + * of the EEPROM. + */ + tb = (uchar*)&cp->cp_dpmem[IIC_BD_FREE]; + tb[0] = iic_device & 0xfe; /* device address */ + tb[1] = offset; /* offset */ + tbdf1->cbd_bufaddr = (uint)tb; + tbdf1->cbd_datlen = 2; + tbdf1->cbd_sc = 0x8400; + + tb += 2; + tb[0] = iic_device | 1; /* device address */ + tbdf2->cbd_bufaddr = (uint)tb; + tbdf2->cbd_datlen = count+1; + tbdf2->cbd_sc = 0xbc00; + + rbdf->cbd_bufaddr = (uint)buf; + rbdf->cbd_datlen = 0; + rbdf->cbd_sc = 0xb000; + + iip->iic_tbase = IIC_BD_TX1; + iip->iic_tbptr = IIC_BD_TX1; + iip->iic_rbase = IIC_BD_RX; + iip->iic_rbptr = IIC_BD_RX; + iip->iic_rfcr = 0x15; + iip->iic_tfcr = 0x15; + iip->iic_mrblr = count; + iip->iic_rstate = 0; + iip->iic_tstate = 0; + + i2c->i2c_i2cer = 0x1f; /* clear event mask */ + i2c->i2c_i2mod |= 1; /* enable iic operation */ + i2c->i2c_i2com |= 0x80; /* start master */ + + /* wait for IIC transfer */ + do { + __asm__ volatile ("eieio"); + event = i2c->i2c_i2cer; + } while (event == 0); + + if ((event & 0x10) || (event & 0x04)) { + count = -1; + goto bailout; + } + +bailout: + i2c->i2c_i2mod &= ~1; /* turn off iic operation */ + i2c->i2c_i2cer = 0x1f; /* clear event mask */ + + return count; +} diff --git a/board/mousse/flash.h b/board/mousse/flash.h new file mode 100644 index 00000000000..b7e4619c01c --- /dev/null +++ b/board/mousse/flash.h @@ -0,0 +1,78 @@ +#ifndef FLASH_LIB_H +#define FLASH_LIB_H + +#include <common.h> + +/* PIO operations max */ +#define FLASH_PROGRAM_POLLS 100000 + +/* 10 Seconds default */ +#define FLASH_ERASE_SECTOR_TIMEOUT (10*1000 /*SEC*/ ) + +/* Flash device info structure */ +typedef struct flash_dev_s { + char name[24]; /* Bank Name */ + int bank; /* Bank 0 or 1 */ + unsigned int base; /* Base address */ + int sectors; /* Sector count */ + int lgSectorSize; /* Log2(usable bytes/sector) */ + int vendorID; /* Expected vendor ID */ + int deviceID; /* Expected device ID */ + int found; /* Set if found by flashLibInit */ + int swap; /* Set for bank 1 if byte swap req'd */ +} flash_dev_t; + +#define FLASH_MAX_POS(dev) \ + ((dev)->sectors << (dev)->lgSectorSize) + +#define FLASH_SECTOR_POS(dev, sector) \ + ((sector) << (dev)->lgSectorSize) + +/* AMD 29F040 */ +#define FLASH0_BANK 0 +#define FLASH0_VENDOR_ID 0x01 +#define FLASH0_DEVICE_ID 0x49 + +/* AMD29LV160DB */ +#define FLASH1_BANK 1 +#define FLASH1_VENDOR_ID 0x0001 +#define FLASH1_DEVICE_ID 0x2249 + +extern flash_dev_t flashDev[]; +extern int flashDevCount; + +/* + * Device pointers + * + * These must be kept in sync with the table in flashLib.c. + */ +#define FLASH_DEV_BANK0_SA0 (&flashDev[0]) +#define FLASH_DEV_BANK0_SA1 (&flashDev[1]) +#define FLASH_DEV_BANK0_SA2 (&flashDev[2]) +#define FLASH_DEV_BANK0_LOW (&flashDev[3]) /* 960K */ +#define FLASH_DEV_BANK0_BOOT (&flashDev[4]) /* PLCC */ +#define FLASH_DEV_BANK0_HIGH (&flashDev[5]) /* 512K PLCC shadow */ + +unsigned long flash_init(void); +int flashEraseSector(flash_dev_t *dev, int sector); +int flashErase(flash_dev_t *dev); +int flashRead(flash_dev_t *dev, int pos, char *buf, int len); +int flashWrite(flash_dev_t *dev, int pos, char *buf, int len); +int flashWritable(flash_dev_t *dev, int pos, int len); +int flashDiag(flash_dev_t *dev); +int flashDiagAll(void); + +ulong flash_get_size (vu_long *addr, flash_info_t *info); +void flash_print_info (flash_info_t *info); +int flash_erase (flash_info_t *info, int s_first, int s_last); +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt); + +/* + * Flash info indices. + */ +#define FLASH_BANK_KERNEL 0 +#define FLASH_BANK_BOOT 1 +#define FLASH_BANK_AUX 2 +#define FIRST_SECTOR 0 + +#endif /* !FLASH_LIB_H */ diff --git a/board/mpc8260ads/flash.c b/board/mpc8260ads/flash.c new file mode 100644 index 00000000000..ec6a3b3fedf --- /dev/null +++ b/board/mpc8260ads/flash.c @@ -0,0 +1,508 @@ +/* + * (C) Copyright 2000, 2001 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2001, Stuart Hughes, Lineo Inc, stuarth@lineo.com + * Add support the Sharp chips on the mpc8260ads. + * I started with board/ip860/flash.c and made changes I found in + * the MTD project by David Schleef. + * + * 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 + */ + +#include <common.h> + + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static int clear_block_lock_bit(vu_long * addr); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ +#ifndef CONFIG_MPC8260ADS + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + volatile ip860_bcsr_t *bcsr = (ip860_bcsr_t *)BCSR_BASE; +#endif + unsigned long size; + int i; + + /* Init: enable write, + * or we cannot even write flash commands + */ +#ifndef CONFIG_MPC8260ADS + bcsr->bd_ctrl |= BD_CTRL_FLWE; +#endif + + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + + /* set the default sector offset */ + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size = flash_get_size((vu_long *)FLASH_BASE, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size, size<<20); + } +#ifndef CONFIG_MPC8260ADS + /* Remap FLASH according to real size */ + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size & 0xFFFF8000); + memctl->memc_br1 = (CFG_FLASH_BASE & BR_BA_MSK) | + (memctl->memc_br1 & ~(BR_BA_MSK)); +#endif + + /* Re-do sizing to get full correct info */ + size = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size; + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + return (size); +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: printf ("Intel "); break; + case FLASH_MAN_SHARP: printf ("Sharp "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F016SV: printf ("28F016SV (16 Mbit, 32 x 64k)\n"); + break; + case FLASH_28F160S3: printf ("28F160S3 (16 Mbit, 32 x 512K)\n"); + break; + case FLASH_28F320S3: printf ("28F320S3 (32 Mbit, 64 x 512K)\n"); + break; + case FLASH_LH28F016SCT: printf ("28F016SC (16 Mbit, 32 x 64K)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + ulong sector_offset; + + /* Write "Intelligent Identifier" command: read Manufacturer ID */ + *addr = 0x90909090; + + value = addr[0] & 0x00FF00FF; + switch (value) { + case MT_MANUFACT: /* SHARP, MT or => Intel */ + case INTEL_ALT_MANU: + info->flash_id = FLASH_MAN_INTEL; + break; + default: + printf("unknown manufacturer: %x\n", (unsigned int)value); + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case (INTEL_ID_28F016S): + info->flash_id += FLASH_28F016SV; + info->sector_count = 32; + info->size = 0x00400000; + sector_offset = 0x20000; + break; /* => 2x2 MB */ + + case (INTEL_ID_28F160S3): + info->flash_id += FLASH_28F160S3; + info->sector_count = 32; + info->size = 0x00400000; + sector_offset = 0x20000; + break; /* => 2x2 MB */ + + case (INTEL_ID_28F320S3): + info->flash_id += FLASH_28F320S3; + info->sector_count = 64; + info->size = 0x00800000; + sector_offset = 0x20000; + break; /* => 2x4 MB */ + + case SHARP_ID_28F016SCL: + case SHARP_ID_28F016SCZ: + info->flash_id = FLASH_MAN_SHARP | FLASH_LH28F016SCT; + info->sector_count = 32; + info->size = 0x00800000; + sector_offset = 0x40000; + break; /* => 4x2 MB */ + + + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base; + base += sector_offset; + /* don't know how to check sector protection */ + info->protect[i] = 0; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (vu_long *)info->start[0]; + + *addr = 0xFFFFFF; /* reset bank to read array mode */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ( ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_INTEL) + && ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_SHARP) ) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + /* Make Sure Block Lock Bit is not set. */ + if(clear_block_lock_bit((vu_long *)(info->start[s_first]))){ + return 1; + } + + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + vu_long *addr = (vu_long *)(info->start[sect]); + + last = start = get_timer (0); + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Reset Array */ + *addr = 0xffffffff; + /* Clear Status Register */ + *addr = 0x50505050; + /* Single Block Erase Command */ + *addr = 0x20202020; + /* Confirm */ + *addr = 0xD0D0D0D0; + + if((info->flash_id & FLASH_TYPEMASK) != FLASH_LH28F016SCT) { + /* Resume Command, as per errata update */ + *addr = 0xD0D0D0D0; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + while ((*addr & 0x80808080) != 0x80808080) { + if(*addr & 0x20202020){ + printf("Error in Block Erase - Lock Bit may be set!\n"); + printf("Status Register = 0x%X\n", (uint)*addr); + *addr = 0xFFFFFFFF; /* reset bank */ + return 1; + } + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + *addr = 0xFFFFFFFF; /* reset bank */ + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + /* reset to read mode */ + *addr = 0xFFFFFFFF; + } + } + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long *)dest; + ulong start, csr; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*addr & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Write Command */ + *addr = 0x10101010; + + /* Write Data */ + *addr = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + flag = 0; + while (((csr = *addr) & 0x80808080) != 0x80808080) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + flag = 1; + break; + } + } + if (csr & 0x40404040) { + printf ("CSR indicates write error (%08lx) at %08lx\n", csr, (ulong)addr); + flag = 1; + } + + /* Clear Status Registers Command */ + *addr = 0x50505050; + /* Reset to read array mode */ + *addr = 0xFFFFFFFF; + + return (flag); +} + +/*----------------------------------------------------------------------- + * Clear Block Lock Bit, returns: + * 0 - OK + * 1 - Timeout + */ + +static int clear_block_lock_bit(vu_long * addr) +{ + ulong start, now; + + /* Reset Array */ + *addr = 0xffffffff; + /* Clear Status Register */ + *addr = 0x50505050; + + *addr = 0x60606060; + *addr = 0xd0d0d0d0; + + start = get_timer (0); + while(*addr != 0x80808080){ + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout on clearing Block Lock Bit\n"); + *addr = 0xFFFFFFFF; /* reset bank */ + return 1; + } + } + return 0; +} diff --git a/board/mpl/common/flash.c b/board/mpl/common/flash.c new file mode 100644 index 00000000000..f0c541b44dd --- /dev/null +++ b/board/mpl/common/flash.c @@ -0,0 +1,860 @@ +/* + * (C) Copyright 2000, 2001 + * 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 + */ + +/* + * Modified 4/5/2001 + * Wait for completion of each sector erase command issued + * 4/5/2001 + * Chris Hallinan - DS4.COM, Inc. - clh@net1plus.com + */ + +/* + * Modified 3/7/2001 + * - adopted for pip405, Denis Peter, MPL AG Switzerland + * TODO: + * clean-up + */ + +#include <common.h> +#include <ppc4xx.h> +#include <asm/processor.h> +#ifdef CONFIG_PIP405 +#include "../pip405/pip405.h" +#endif +#ifdef CONFIG_MIP405 +#include "../mip405/mip405.h" +#endif +#include "common_util.h" + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +void unlock_intel_sectors(flash_info_t *info,ulong addr,ulong cnt); + + +#ifdef CONFIG_ADCIOP +#define ADDR0 0x0aa9 +#define ADDR1 0x0556 +#define FLASH_WORD_SIZE unsigned char +#endif + +#ifdef CONFIG_CPCI405 +#define ADDR0 0x5555 +#define ADDR1 0x2aaa +#define FLASH_WORD_SIZE unsigned short +#endif + +#ifdef CONFIG_PIP405 +#define ADDR0 0x5555 +#define ADDR1 0x2aaa +#define FLASH_WORD_SIZE unsigned short +#endif + +#ifdef CONFIG_MIP405 +#define ADDR0 0x5555 +#define ADDR1 0x2aaa +#define FLASH_WORD_SIZE unsigned short +#endif + +#define FALSE 0 +#define TRUE 1 + +/*----------------------------------------------------------------------- + */ + + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + unsigned long pbcr; + unsigned long base_b0, base_b1; + unsigned char rc; + + rc=switch_cs(FALSE); /* map Flash High */ + + if(rc) + printf("(MPS Boot) "); + else + printf("(Flash Boot) "); + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + /* Only one bank */ + if (CFG_MAX_FLASH_BANKS == 1) + { + /* Setup offsets */ + /* flash_get_offsets (FLASH_BASE0_PRELIM, &flash_info[0]); */ + /* Monitor protection ON by default */ +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + size_b1 = 0 ; + flash_info[0].size = size_b0; + } + + /* 2 banks */ + else + { + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + /* Re-do sizing to get full correct info */ + + if (size_b1) + { + mtdcr(ebccfga, pb0cr); + pbcr = mfdcr(ebccfgd); + mtdcr(ebccfga, pb0cr); + base_b1 = -size_b1; + pbcr = (pbcr & 0x0001ffff) | base_b1 | (((size_b1/1024/1024)-1)<<17); + mtdcr(ebccfgd, pbcr); + /* printf("pb1cr = %x\n", pbcr); */ + } + + if (size_b0) + { + mtdcr(ebccfga, pb1cr); + pbcr = mfdcr(ebccfgd); + mtdcr(ebccfga, pb1cr); + base_b0 = base_b1 - size_b0; + pbcr = (pbcr & 0x0001ffff) | base_b0 | (((size_b0/1024/1024)-1)<<17); + mtdcr(ebccfgd, pbcr); + /* printf("pb0cr = %x\n", pbcr); */ + } + + size_b0 = flash_get_size((vu_long *)base_b0, &flash_info[0]); + + flash_get_offsets (base_b0, &flash_info[0]); + + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + base_b0+size_b0-CFG_MONITOR_LEN, + base_b0+size_b0-1, + &flash_info[0]); + + if (size_b1) { + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)base_b1, &flash_info[1]); + + flash_get_offsets (base_b1, &flash_info[1]); + + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + base_b1+size_b1-CFG_MONITOR_LEN, + base_b1+size_b1-1, + &flash_info[1]); + /* monitor protection OFF by default (one is enough) */ + (void)flash_protect(FLAG_PROTECT_CLEAR, + base_b0+size_b0-CFG_MONITOR_LEN, + base_b0+size_b0-1, + &flash_info[0]); + } else { + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + }/* else 2 banks */ + switch_cs(rc); /* switch mode back */ + return (size_b0 + size_b1); +} + + +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + return; +} +#if 0 +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) || + (info->flash_id == FLASH_AM040)){ + for (i = 0; i < info->sector_count; i++) + info->start[i] = base + (i * 0x00010000); + } + else { + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00010000; + } + } + } +} + +#endif +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + int k; + int size; + int erased; + volatile unsigned long *flash; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_SST: printf ("SST "); break; + case FLASH_MAN_INTEL: printf ("Intel "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM040: printf ("AM29F040 (512 Kbit, uniform sector size)\n"); + break; + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + case FLASH_SST800A: printf ("SST39LF/VF800 (8 Mbit, uniform sector size)\n"); + break; + case FLASH_SST160A: printf ("SST39LF/VF160 (16 Mbit, uniform sector size)\n"); + break; + case FLASH_INTEL320T: printf ("TE28F320C3 (32 Mbit, top sector size)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld KB in %d Sectors\n", + info->size >> 10, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + /* + * Check if whole sector is erased + */ + if (i != (info->sector_count-1)) + size = info->start[i+1] - info->start[i]; + else + size = info->start[0] + info->size - info->start[i]; + erased = 1; + flash = (volatile unsigned long *)info->start[i]; + size = size >> 2; /* divide by 4 for longword access */ + for (k=0; k<size; k++) + { + if (*flash++ != 0xffffffff) + { + erased = 0; + break; + } + } + if ((i % 5) == 0) + printf ("\n "); +#if 0 /* test-only */ + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " +#else + printf (" %08lX%s%s", + info->start[i], + erased ? " E" : " ", + info->protect[i] ? "RO " : " " +#endif + ); + } + printf ("\n"); + +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + FLASH_WORD_SIZE value; + ulong base = (ulong)addr; + volatile FLASH_WORD_SIZE *addr2 = (FLASH_WORD_SIZE *)addr; + + /* Write auto select command: read Manufacturer ID */ + addr2[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; + addr2[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + addr2[ADDR0] = (FLASH_WORD_SIZE)0x00900090; + + value = addr2[0]; + /* printf("flash_get_size value: %x\n",value); */ + switch (value) { + case (FLASH_WORD_SIZE)AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case (FLASH_WORD_SIZE)FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + case (FLASH_WORD_SIZE)INTEL_MANUFACT: + info->flash_id = FLASH_MAN_INTEL; + break; + case (FLASH_WORD_SIZE)SST_MANUFACT: + info->flash_id = FLASH_MAN_SST; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + value = addr2[1]; /* device ID */ + /* printf("Device value %x\n",value); */ + switch (value) { + case (FLASH_WORD_SIZE)AMD_ID_F040B: + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x0080000; /* => 512 ko */ + break; + case (FLASH_WORD_SIZE)AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00080000; + break; /* => 0.5 MB */ + + case (FLASH_WORD_SIZE)AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00080000; + break; /* => 0.5 MB */ + + case (FLASH_WORD_SIZE)AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (FLASH_WORD_SIZE)AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00100000; + break; /* => 1 MB */ + + case (FLASH_WORD_SIZE)AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00200000; + break; /* => 2 MB */ + + case (FLASH_WORD_SIZE)AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00200000; + break; /* => 2 MB */ +#if 0 /* enable when device IDs are available */ + case (FLASH_WORD_SIZE)AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (FLASH_WORD_SIZE)AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00400000; + break; /* => 4 MB */ +#endif + case (FLASH_WORD_SIZE)SST_ID_xF800A: + info->flash_id += FLASH_SST800A; + info->sector_count = 16; + info->size = 0x00100000; + break; /* => 1 MB */ + case (FLASH_WORD_SIZE)INTEL_ID_28F320C3T: + info->flash_id += FLASH_INTEL320T; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 4 MB */ + + + case (FLASH_WORD_SIZE)SST_ID_xF160A: + info->flash_id += FLASH_SST160A; + info->sector_count = 32; + info->size = 0x00200000; + break; /* => 2 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + if (((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) || + (info->flash_id == FLASH_AM040)){ + for (i = 0; i < info->sector_count; i++) + info->start[i] = base + (i * 0x00010000); + } else { + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00004000; + info->start[2] = base + 0x00006000; + info->start[3] = base + 0x00008000; + for (i = 4; i < info->sector_count; i++) + info->start[i] = base + (i * 0x00010000) - 0x00030000; + } + else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + if(info->sector_count==71) { + + info->start[i--] = base + info->size - 0x00002000; + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000A000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x0000E000; + for (; i >= 0; i--) + info->start[i] = base + i * 0x000010000; + } + else { + info->start[i--] = base + info->size - 0x00004000; + info->start[i--] = base + info->size - 0x00006000; + info->start[i--] = base + info->size - 0x00008000; + for (; i >= 0; i--) + info->start[i] = base + i * 0x00010000; + } + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr2 = (volatile FLASH_WORD_SIZE *)(info->start[i]); + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) + info->protect[i] = 0; + else + info->protect[i] = addr2[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { +#if 0 /* test-only */ +#ifdef CONFIG_ADCIOP + addr2 = (volatile unsigned char *)info->start[0]; + addr2[ADDR0] = 0xAA; + addr2[ADDR1] = 0x55; + addr2[ADDR0] = 0xF0; /* reset bank */ +#else + addr2 = (FLASH_WORD_SIZE *)info->start[0]; + *addr2 = (FLASH_WORD_SIZE)0x00F000F0; /* reset bank */ +#endif +#else /* test-only */ + addr2 = (FLASH_WORD_SIZE *)info->start[0]; + *addr2 = (FLASH_WORD_SIZE)0x00F000F0; /* reset bank */ +#endif /* test-only */ + } + return (info->size); +} + +int wait_for_DQ7(flash_info_t *info, int sect) +{ + ulong start, now, last; + volatile FLASH_WORD_SIZE *addr = (FLASH_WORD_SIZE *)(info->start[sect]); + + start = get_timer (0); + last = start; + while ((addr[0] & (FLASH_WORD_SIZE)0x00800080) != (FLASH_WORD_SIZE)0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return -1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + return 0; +} + +int intel_wait_for_DQ7(flash_info_t *info, int sect) +{ + ulong start, now, last; + volatile FLASH_WORD_SIZE *addr = (FLASH_WORD_SIZE *)(info->start[sect]); + + start = get_timer (0); + last = start; + while ((addr[0] & (FLASH_WORD_SIZE)0x00800080) != (FLASH_WORD_SIZE)0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return -1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + addr[0]=(FLASH_WORD_SIZE)0x00500050; + return 0; +} + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + volatile FLASH_WORD_SIZE *addr = (FLASH_WORD_SIZE *)(info->start[0]); + volatile FLASH_WORD_SIZE *addr2; + int flag, prot, sect, l_sect; + int i; + + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("Can't erase unknown flash type - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr2 = (FLASH_WORD_SIZE *)(info->start[sect]); + /* printf("Erasing sector %p\n", addr2); */ /* CLH */ + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) { + addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; + addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + addr[ADDR0] = (FLASH_WORD_SIZE)0x00800080; + addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; + addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + addr2[0] = (FLASH_WORD_SIZE)0x00500050; /* block erase */ + for (i=0; i<50; i++) + udelay(1000); /* wait 1 ms */ + wait_for_DQ7(info, sect); + } + else { + if((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL){ + addr2[0] = (FLASH_WORD_SIZE)0x00600060; /* unlock sector */ + addr2[0] = (FLASH_WORD_SIZE)0x00D000D0; /* sector erase */ + intel_wait_for_DQ7(info, sect); + addr2[0] = (FLASH_WORD_SIZE)0x00200020; /* sector erase */ + addr2[0] = (FLASH_WORD_SIZE)0x00D000D0; /* sector erase */ + intel_wait_for_DQ7(info, sect); + } + else { + addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; + addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + addr[ADDR0] = (FLASH_WORD_SIZE)0x00800080; + addr[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; + addr[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + addr2[0] = (FLASH_WORD_SIZE)0x00300030; /* sector erase */ + wait_for_DQ7(info, sect); + } + } + l_sect = sect; + /* + * Wait for each sector to complete, it's more + * reliable. According to AMD Spec, you must + * issue all erase commands within a specified + * timeout. This has been seen to fail, especially + * if printf()s are included (for debug)!! + */ + /* wait_for_DQ7(info, sect); */ + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + +#if 0 + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + wait_for_DQ7(info, l_sect); + +DONE: +#endif + /* reset to read mode */ + addr = (FLASH_WORD_SIZE *)info->start[0]; + addr[0] = (FLASH_WORD_SIZE)0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +void unlock_intel_sectors(flash_info_t *info,ulong addr,ulong cnt) +{ + int i; + volatile FLASH_WORD_SIZE *addr2; + long c; + c= (long)cnt; + for(i=info->sector_count-1;i>0;i--) + { + if(addr>=info->start[i]) + break; + } + do { + addr2 = (FLASH_WORD_SIZE *)(info->start[i]); + addr2[0] = (FLASH_WORD_SIZE)0x00600060; /* unlock sector setup */ + addr2[0] = (FLASH_WORD_SIZE)0x00D000D0; /* unlock sector */ + intel_wait_for_DQ7(info, i); + i++; + c-=(info->start[i]-info->start[i-1]); + }while(c>0); + + +} + + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + if((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL){ + unlock_intel_sectors(info,addr,cnt); + } + wp = (addr & ~3); /* get lower word aligned address */ + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + if((wp % 0x10000)==0) + printf("."); /* show Progress */ + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + rc=write_word(info, wp, data); + return rc; +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static FLASH_WORD_SIZE *read_val = (FLASH_WORD_SIZE *)0x200000; + +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + volatile FLASH_WORD_SIZE *addr2 = (FLASH_WORD_SIZE *)(info->start[0]); + volatile FLASH_WORD_SIZE *dest2 = (FLASH_WORD_SIZE *)dest; + volatile FLASH_WORD_SIZE *data2 = (FLASH_WORD_SIZE *)&data; + ulong start; + int flag; + int i; + + /* Check if Flash is (sufficiently) erased */ + if ((*((volatile FLASH_WORD_SIZE *)dest) & + (FLASH_WORD_SIZE)data) != (FLASH_WORD_SIZE)data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + for (i=0; i<4/sizeof(FLASH_WORD_SIZE); i++) + { + if((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL){ + /* intel style writting */ + dest2[i] = (FLASH_WORD_SIZE)0x00500050; + dest2[i] = (FLASH_WORD_SIZE)0x00400040; + *read_val++ = data2[i]; + dest2[i] = data2[i]; + if (flag) + enable_interrupts(); + /* data polling for D7 */ + start = get_timer (0); + udelay(10); + while ((dest2[i] & (FLASH_WORD_SIZE)0x00800080) != (FLASH_WORD_SIZE)0x00800080) + { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) + return (1); + } + dest2[i] = (FLASH_WORD_SIZE)0x00FF00FF; /* return to read mode */ + udelay(10); + dest2[i] = (FLASH_WORD_SIZE)0x00FF00FF; /* return to read mode */ + if(dest2[i]!=data2[i]) + printf("Error at %p 0x%04X != 0x%04X\n",&dest2[i],dest2[i],data2[i]); + } + else { + addr2[ADDR0] = (FLASH_WORD_SIZE)0x00AA00AA; + addr2[ADDR1] = (FLASH_WORD_SIZE)0x00550055; + addr2[ADDR0] = (FLASH_WORD_SIZE)0x00A000A0; + dest2[i] = data2[i]; + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + /* data polling for D7 */ + start = get_timer (0); + while ((dest2[i] & (FLASH_WORD_SIZE)0x00800080) != + (data2[i] & (FLASH_WORD_SIZE)0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/mpl/common/isa.c b/board/mpl/common/isa.c new file mode 100644 index 00000000000..40731fc79f4 --- /dev/null +++ b/board/mpl/common/isa.c @@ -0,0 +1,469 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * 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 + * + * + * TODO: clean-up + */ + +#include <common.h> +#include <asm/processor.h> +#include <devices.h> +#include "isa.h" +#include "piix4_pci.h" +#include "kbd.h" +#include "video.h" + +extern int drv_isa_kbd_init (void); + +#undef ISA_DEBUG + +#ifdef ISA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + + + +/* fdc (logical device 0) */ +const SIO_LOGDEV_TABLE sio_fdc[] = { + {0x60, 3}, /* set IO to FDPort (3F0) */ + {0x61, 0xF0}, /* set IO to FDPort (3F0) */ + {0x70, 06}, /* set IRQ 6 for FDPort */ + {0x74, 02}, /* set DMA 2 for FDPort */ + {0xF0, 0x05}, /* set to PS2 type */ + {0xF1, 0x00}, /* default value */ + {0x30, 1}, /* and activate the device */ + {0xFF, 0} /* end of device table */ +}; +/* paralell port (logical device 3) */ +const SIO_LOGDEV_TABLE sio_pport[] = { + {0x60, 3}, /* set IO to PPort (378) */ + {0x61, 0x78}, /* set IO to PPort (378) */ + {0x70, 07}, /* set IRQ 7 for PPort */ + {0xF1, 00}, /* set PPort to normal */ + {0x30, 1}, /* and activate the device */ + {0xFF, 0} /* end of device table */ +}; +/* paralell port (logical device 3) Floppy assigned to lpt */ +const SIO_LOGDEV_TABLE sio_pport_fdc[] = { + {0x60, 3}, /* set IO to PPort (378) */ + {0x61, 0x78}, /* set IO to PPort (378) */ + {0x70, 07}, /* set IRQ 7 for PPort */ + {0xF1, 02}, /* set PPort to Floppy */ + {0x30, 1}, /* and activate the device */ + {0xFF, 0} /* end of device table */ +}; +/* uart 1 (logical device 4) */ +const SIO_LOGDEV_TABLE sio_com1[] = { + {0x60, 3}, /* set IO to COM1 (3F8) */ + {0x61, 0xF8}, /* set IO to COM1 (3F8) */ + {0x70, 04}, /* set IRQ 4 for COM1 */ + {0x30, 1}, /* and activate the device */ + {0xFF, 0} /* end of device table */ +}; +/* uart 2 (logical device 5) */ +const SIO_LOGDEV_TABLE sio_com2[] = { + {0x60, 2}, /* set IO to COM2 (2F8) */ + {0x61, 0xF8}, /* set IO to COM2 (2F8) */ + {0x70, 03}, /* set IRQ 3 for COM2 */ + {0x30, 1}, /* and activate the device */ + {0xFF, 0} /* end of device table */ +}; + +/* keyboard controller (logical device 7) */ +const SIO_LOGDEV_TABLE sio_keyboard[] = { + {0x70, 1}, /* set IRQ 1 for keyboard */ + {0x72, 12}, /* set IRQ 12 for mouse */ + {0xF0, 0}, /* disable Port92 (this is a PowerPC!!) */ + {0x30, 1}, /* and activate the device */ + {0xFF, 0} /* end of device table */ +}; + + +/******************************************************************************* +* Config SuperIO FDC37C672 +********************************************************************************/ +unsigned char open_cfg_super_IO(int address) +{ + out8(CFG_ISA_IO_BASE_ADDRESS | address,0x55); /* open config */ + out8(CFG_ISA_IO_BASE_ADDRESS | address,0x20); /* set address to DEV ID */ + if(in8(CFG_ISA_IO_BASE_ADDRESS | address | 0x1)==0x40) /* ok Device ID is correct */ + return TRUE; + else + return FALSE; +} + +void close_cfg_super_IO(int address) +{ + out8(CFG_ISA_IO_BASE_ADDRESS | address,0xAA); /* close config */ +} + + +unsigned char read_cfg_super_IO(int address, unsigned char function, unsigned char regaddr) +{ + /* assuming config reg is open */ + out8(CFG_ISA_IO_BASE_ADDRESS | address,0x7); /* points to the function reg */ + out8(CFG_ISA_IO_BASE_ADDRESS | address | 1,function); /* set the function no */ + out8(CFG_ISA_IO_BASE_ADDRESS | address,regaddr); /* sets the address in the function */ + return in8(CFG_ISA_IO_BASE_ADDRESS | address | 1); +} + +void write_cfg_super_IO(int address, unsigned char function, unsigned char regaddr, unsigned char data) +{ + /* assuming config reg is open */ + out8(CFG_ISA_IO_BASE_ADDRESS | address,0x7); /* points to the function reg */ + out8(CFG_ISA_IO_BASE_ADDRESS | address | 1,function); /* set the function no */ + out8(CFG_ISA_IO_BASE_ADDRESS | address,regaddr); /* sets the address in the function */ + out8(CFG_ISA_IO_BASE_ADDRESS | address | 1,data); /* writes the data */ +} + +void isa_write_table(SIO_LOGDEV_TABLE *ldt,unsigned char ldev) +{ + while (ldt->index != 0xFF) { + write_cfg_super_IO(SIO_CFG_PORT, ldev, ldt->index, ldt->val); + ldt++; + } /* endwhile */ +} + +void isa_sio_loadtable(void) +{ + unsigned char *s = getenv("floppy"); + /* setup Floppy device 0*/ + isa_write_table((SIO_LOGDEV_TABLE *)&sio_fdc,0); + /* setup parallel port device 3 */ + if(s && !strncmp(s, "lpt", 3)) { + printf("SIO: Floppy assigned to LPT\n"); + /* floppy is assigned to the LPT */ + isa_write_table((SIO_LOGDEV_TABLE *)&sio_pport_fdc,3); + } + else { + /*printf("Floppy assigned to internal port\n");*/ + isa_write_table((SIO_LOGDEV_TABLE *)&sio_pport,3); + } + /* setup Com1 port device 4 */ + isa_write_table((SIO_LOGDEV_TABLE *)&sio_com1,4); + /* setup Com2 port device 5 */ + isa_write_table((SIO_LOGDEV_TABLE *)&sio_com2,5); + /* setup keyboards device 7 */ + isa_write_table((SIO_LOGDEV_TABLE *)&sio_keyboard,7); +} + + +void isa_sio_setup(void) +{ + if(open_cfg_super_IO(SIO_CFG_PORT)==TRUE) + { + isa_sio_loadtable(); + close_cfg_super_IO(0x3F0); + } +} + + + +/****************************************************************************** + * IRQ Controller + * we use the Vector mode + */ + +struct isa_irq_action { + interrupt_handler_t *handler; + void *arg; + int count; +}; + +static struct isa_irq_action isa_irqs[16]; + + +/* + * This contains the irq mask for both 8259A irq controllers, + */ +static unsigned int cached_irq_mask = 0xffff; + +#define cached_imr1 (unsigned char)cached_irq_mask +#define cached_imr2 (unsigned char)(cached_irq_mask>>8) +#define IMR_1 CFG_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT1_OCW1 +#define IMR_2 CFG_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT2_OCW1 +#define ICW1_1 CFG_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT1_ICW1 +#define ICW1_2 CFG_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT2_ICW1 +#define ICW2_1 CFG_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT1_ICW2 +#define ICW2_2 CFG_ISA_IO_BASE_ADDRESS + PIIX4_ISA_INT2_ICW2 +#define ICW3_1 ICW2_1 +#define ICW3_2 ICW2_2 +#define ICW4_1 ICW2_1 +#define ICW4_2 ICW2_2 +#define ISR_1 ICW1_1 +#define ISR_2 ICW1_2 + + +void disable_8259A_irq(unsigned int irq) +{ + unsigned int mask = 1 << irq; + + cached_irq_mask |= mask; + if (irq & 8) + out8(IMR_2,cached_imr2); + else + out8(IMR_1,cached_imr1); +} + +void enable_8259A_irq(unsigned int irq) +{ + unsigned int mask = ~(1 << irq); + + cached_irq_mask &= mask; + if (irq & 8) + out8(IMR_2,cached_imr2); + else + out8(IMR_1,cached_imr1); +} +/* +int i8259A_irq_pending(unsigned int irq) +{ + unsigned int mask = 1<<irq; + int ret; + + if (irq < 8) + ret = inb(0x20) & mask; + else + ret = inb(0xA0) & (mask >> 8); + spin_unlock_irqrestore(&i8259A_lock, flags); + + return ret; +} +*/ + +/* + * This function assumes to be called rarely. Switching between + * 8259A registers is slow. + */ +int i8259A_irq_real(unsigned int irq) +{ + int value; + int irqmask = 1<<irq; + + if (irq < 8) { + out8(ISR_1,0x0B); /* ISR register */ + value = in8(ISR_1) & irqmask; + out8(ISR_1,0x0A); /* back to the IRR register */ + return value; + } + out8(ISR_2,0x0B); /* ISR register */ + value = in8(ISR_2) & (irqmask >> 8); + out8(ISR_2,0x0A); /* back to the IRR register */ + return value; +} + +/* + * Careful! The 8259A is a fragile beast, it pretty + * much _has_ to be done exactly like this (mask it + * first, _then_ send the EOI, and the order of EOI + * to the two 8259s is important! + */ +void mask_and_ack_8259A(unsigned int irq) +{ + unsigned int irqmask = 1 << irq; + unsigned int temp_irqmask = cached_irq_mask; + /* + * Lightweight spurious IRQ detection. We do not want + * to overdo spurious IRQ handling - it's usually a sign + * of hardware problems, so we only do the checks we can + * do without slowing down good hardware unnecesserily. + * + * Note that IRQ7 and IRQ15 (the two spurious IRQs + * usually resulting from the 8259A-1|2 PICs) occur + * even if the IRQ is masked in the 8259A. Thus we + * can check spurious 8259A IRQs without doing the + * quite slow i8259A_irq_real() call for every IRQ. + * This does not cover 100% of spurious interrupts, + * but should be enough to warn the user that there + * is something bad going on ... + */ + if (temp_irqmask & irqmask) + goto spurious_8259A_irq; + temp_irqmask |= irqmask; + +handle_real_irq: + if (irq & 8) { + in8(IMR_2); /* DUMMY - (do we need this?) */ + out8(IMR_2,(unsigned char)(temp_irqmask>>8)); + out8(ISR_2,0x60+(irq&7));/* 'Specific EOI' to slave */ + out8(ISR_1,0x62); /* 'Specific EOI' to master-IRQ2 */ + out8(IMR_2,cached_imr2); /* turn it on again */ + } else { + in8(IMR_1); /* DUMMY - (do we need this?) */ + out8(IMR_1,(unsigned char)temp_irqmask); + out8(ISR_1,0x60+irq); /* 'Specific EOI' to master */ + out8(IMR_1,cached_imr1); /* turn it on again */ + } + + return; + +spurious_8259A_irq: + /* + * this is the slow path - should happen rarely. + */ + if (i8259A_irq_real(irq)) + /* + * oops, the IRQ _is_ in service according to the + * 8259A - not spurious, go handle it. + */ + goto handle_real_irq; + + { + static int spurious_irq_mask; + /* + * At this point we can be sure the IRQ is spurious, + * lets ACK and report it. [once per IRQ] + */ + if (!(spurious_irq_mask & irqmask)) { + PRINTF("spurious 8259A interrupt: IRQ%d.\n", irq); + spurious_irq_mask |= irqmask; + } + /* irq_err_count++; */ + /* + * Theoretically we do not have to handle this IRQ, + * but in Linux this does not cause problems and is + * simpler for us. + */ + goto handle_real_irq; + } +} + +void init_8259A(void) +{ + out8(IMR_1,0xff); /* mask all of 8259A-1 */ + out8(IMR_2,0xff); /* mask all of 8259A-2 */ + + out8(ICW1_1,0x11); /* ICW1: select 8259A-1 init */ + out8(ICW2_1,0x20 + 0); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ + out8(ICW3_1,0x04); /* 8259A-1 (the master) has a slave on IR2 */ + out8(ICW4_1,0x01); /* master expects normal EOI */ + out8(ICW1_2,0x11); /* ICW2: select 8259A-2 init */ + out8(ICW2_2,0x20 + 8); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ + out8(ICW3_2,0x02); /* 8259A-2 is a slave on master's IR2 */ + out8(ICW4_2,0x01); /* (slave's support for AEOI in flat mode + is to be investigated) */ + udelay(10000); /* wait for 8259A to initialize */ + out8(IMR_1,cached_imr1); /* restore master IRQ mask */ + udelay(10000); /* wait for 8259A to initialize */ + out8(IMR_2,cached_imr2); /* restore slave IRQ mask */ +} + + +#define PCI_INT_ACK_ADDR 0xEED00000 + +int handle_isa_int(void) +{ + unsigned long irqack; + unsigned char isr1,isr2,irq; + /* first we acknokledge the int via the PCI bus */ + irqack=in32(PCI_INT_ACK_ADDR); + /* now we get the ISRs */ + isr2=in8(ISR_2); + isr1=in8(ISR_1); + irq=(unsigned char)irqack; + if((irq==7)&&((isr1&0x80)==0)) { + PRINTF("IRQ7 detected but not in ISR\n"); + } + else { + /* we should handle cascaded interrupts here also */ + /* printf("ISA Irq %d\n",irq); */ + isa_irqs[irq].count++; + if (isa_irqs[irq].handler != NULL) + (*isa_irqs[irq].handler)(isa_irqs[irq].arg); /* call isr */ + else + { + PRINTF ("bogus interrupt vector 0x%x\n", irq); + } + } + /* issue EOI instruction to clear the IRQ */ + mask_and_ack_8259A(irq); + return 0; +} + + + +/****************************************************************** + * Install and free an ISA interrupt handler. + */ + +void isa_irq_install_handler(int vec, interrupt_handler_t *handler, void *arg) +{ + if (isa_irqs[vec].handler != NULL) { + printf ("ISA Interrupt vector %d: handler 0x%x replacing 0x%x\n", + vec, (uint)handler, (uint)isa_irqs[vec].handler); + } + isa_irqs[vec].handler = handler; + isa_irqs[vec].arg = arg; + enable_8259A_irq(vec); + PRINTF ("Install ISA IRQ %d ==> %p, @ %p mask=%04x\n", vec, handler, &isa_irqs[vec].handler,cached_irq_mask); + +} + +void isa_irq_free_handler(int vec) +{ + disable_8259A_irq(vec); + isa_irqs[vec].handler = NULL; + isa_irqs[vec].arg = NULL; + printf ("Free ISA IRQ %d mask=%04x\n", vec, cached_irq_mask); + +} + +/****************************************************************************/ +void isa_init_irq_contr(void) +{ + int i; + /* disable all Interrupts */ + /* first write icws controller 1 */ + for(i=0;i<16;i++) + { + isa_irqs[i].handler=NULL; + isa_irqs[i].arg=NULL; + isa_irqs[i].count=0; + } + init_8259A(); + out8(IMR_2,0xFF); +} + + +/****************************************************************** + * Init the ISA bus and devices. + */ + + +int isa_init(void) +{ + isa_sio_setup(); + drv_isa_kbd_init(); + return 0; +} + + + diff --git a/board/mpl/common/kbd.c b/board/mpl/common/kbd.c new file mode 100644 index 00000000000..5b87cdb5395 --- /dev/null +++ b/board/mpl/common/kbd.c @@ -0,0 +1,655 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland, d.peter@mpl.ch + * + * 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 + * + * + * Source partly derived from: + * linux/drivers/char/pc_keyb.c + * + * + */ +#include <common.h> +#include <asm/processor.h> +#include <devices.h> +#include "isa.h" +#include "kbd.h" + + +unsigned char kbd_read_status(void); +unsigned char kbd_read_input(void); +void kbd_send_data(unsigned char data); +void disable_8259A_irq(unsigned int irq); +void enable_8259A_irq(unsigned int irq); + +/* used only by send_data - set by keyboard_interrupt */ + + +#undef KBG_DEBUG + +#ifdef KBG_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#define KBD_STAT_KOBF 0x01 +#define KBD_STAT_IBF 0x02 +#define KBD_STAT_SYS 0x04 +#define KBD_STAT_CD 0x08 +#define KBD_STAT_LOCK 0x10 +#define KBD_STAT_MOBF 0x20 +#define KBD_STAT_TI_OUT 0x40 +#define KBD_STAT_PARERR 0x80 + +#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */ +#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ +#define KBD_TIMEOUT 2000 /* Timeout in ms for keyboard command acknowledge */ +/* + * Keyboard Controller Commands + */ + +#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ +#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ +#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ +#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ +#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ +#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ +#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ +#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if + initiated by the auxiliary device */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ + +/* + * Keyboard Commands + */ + +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ + +/* + * Keyboard Replies + */ + +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ + +/* + * Status Register Bits + */ + +#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ +#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ +#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ +#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ +#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ +#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ +#define KBD_STAT_PERR 0x80 /* Parity error */ + +#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF) + +/* + * Controller Mode Register Bits + */ + +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ +#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ +#define KBD_MODE_SYS 0x04 /* The system flag (?) */ +#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ +#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ +#define KBD_MODE_RFU 0x80 + + +#define KDB_DATA_PORT 0x60 +#define KDB_COMMAND_PORT 0x64 + +#define LED_SCR 0x01 /* scroll lock led */ +#define LED_CAP 0x04 /* caps lock led */ +#define LED_NUM 0x02 /* num lock led */ + +#define KBD_BUFFER_LEN 0x20 /* size of the keyboardbuffer */ + + + + +static volatile char kbd_buffer[KBD_BUFFER_LEN]; +static volatile int in_pointer = 0; +static volatile int out_pointer = 0; + + +static unsigned char num_lock = 0; +static unsigned char caps_lock = 0; +static unsigned char scroll_lock = 0; +static unsigned char shift = 0; +static unsigned char ctrl = 0; +static unsigned char alt = 0; +static unsigned char e0 = 0; +static unsigned char leds = 0; + +#define DEVNAME "kbd" + +/* Simple translation table for the keys */ + +static unsigned char kbd_plain_xlate[] = { + 0xff,0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', /* 0x00 - 0x0f */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\r',0xff, 'a', 's', /* 0x10 - 0x1f */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',0xff,'\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */ + 'b', 'n', 'm', ',', '.', '/',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */ + '\r',0xff,0xff + }; + +static unsigned char kbd_shift_xlate[] = { + 0xff,0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', /* 0x00 - 0x0f */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\r',0xff, 'A', 'S', /* 0x10 - 0x1f */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */ + 'B', 'N', 'M', '<', '>', '?',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */ + '\r',0xff,0xff + }; + +static unsigned char kbd_ctrl_xlate[] = { + 0xff,0x1b, '1',0x00, '3', '4', '5',0x1E, '7', '8', '9', '0',0x1F, '=','\b','\t', /* 0x00 - 0x0f */ + 0x11,0x17,0x05,0x12,0x14,0x18,0x15,0x09,0x0f,0x10,0x1b,0x1d,'\n',0xff,0x01,0x13, /* 0x10 - 0x1f */ + 0x04,0x06,0x08,0x09,0x0a,0x0b,0x0c, ';','\'', '~',0x00,0x1c,0x1a,0x18,0x03,0x16, /* 0x20 - 0x2f */ + 0x02,0x0e,0x0d, '<', '>', '?',0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */ + '\r',0xff,0xff + }; + +/****************************************************************** + * Init + ******************************************************************/ +int isa_kbd_init(void) +{ + char* result; + result=kbd_initialize(); + if(result==NULL) { + PRINTF("AT Keyboard initialized\n"); + irq_install_handler(25, (interrupt_handler_t *)handle_isa_int, NULL); + isa_irq_install_handler(KBD_INTERRUPT, (interrupt_handler_t *)kbd_interrupt, NULL); + return (1); + } + else { + printf("%s\n",result); + return (-1); + } +} + +#ifdef CFG_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console (void); +#else +int overwrite_console (void) +{ + return (0); +} +#endif + +int drv_isa_kbd_init (void) +{ + int error; + device_t kbddev ; + char *stdinname = getenv ("stdin"); + + if(isa_kbd_init()==-1) + return -1; + memset (&kbddev, 0, sizeof(kbddev)); + strcpy(kbddev.name, DEVNAME); + kbddev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + kbddev.putc = NULL ; + kbddev.puts = NULL ; + kbddev.getc = kbd_getc ; + kbddev.tstc = kbd_testc ; + + error = device_register (&kbddev); + if(error==0) { + /* check if this is the standard input device */ + if(strcmp(stdinname,DEVNAME)==0) { + /* reassign the console */ + if(overwrite_console()) { + return 1; + } + error=console_assign(stdin,DEVNAME); + if(error==0) + return 1; + else + return error; + } + return 1; + } + return error; +} + +/****************************************************************** + * Queue handling + ******************************************************************/ +/* puts character in the queue and sets up the in and out pointer */ +void kbd_put_queue(char data) +{ + if((in_pointer+1)==KBD_BUFFER_LEN) { + if(out_pointer==0) { + return; /* buffer full */ + } else{ + in_pointer=0; + } + } else { + if((in_pointer+1)==out_pointer) + return; /* buffer full */ + in_pointer++; + } + kbd_buffer[in_pointer]=data; + return; +} + +/* test if a character is in the queue */ +int kbd_testc(void) +{ + if(in_pointer==out_pointer) + return(0); /* no data */ + else + return(1); +} +/* gets the character from the queue */ +int kbd_getc(void) +{ + char c; + while(in_pointer==out_pointer); + if((out_pointer+1)==KBD_BUFFER_LEN) + out_pointer=0; + else + out_pointer++; + c=kbd_buffer[out_pointer]; + return (int)c; + +} + + +/* set LEDs */ + +void kbd_set_leds(void) +{ + if(caps_lock==0) + leds&=~LED_CAP; /* switch caps_lock off */ + else + leds|=LED_CAP; /* switch on LED */ + if(num_lock==0) + leds&=~LED_NUM; /* switch LED off */ + else + leds|=LED_NUM; /* switch on LED */ + if(scroll_lock==0) + leds&=~LED_SCR; /* switch LED off */ + else + leds|=LED_SCR; /* switch on LED */ + kbd_send_data(KBD_CMD_SET_LEDS); + kbd_send_data(leds); +} + + +void handle_keyboard_event(unsigned char scancode) +{ + unsigned char keycode; + + /* Convert scancode to keycode */ + PRINTF("scancode %x\n",scancode); + if(scancode==0xe0) { + e0=1; /* special charakters */ + return; + } + if(e0==1) { + e0=0; /* delete flag */ + if(!( ((scancode&0x7F)==0x38)|| /* the right ctrl key */ + ((scancode&0x7F)==0x1D)|| /* the right alt key */ + ((scancode&0x7F)==0x35)|| /* the right '/' key */ + ((scancode&0x7F)==0x1C) )) /* the right enter key */ + /* we swallow unknown e0 codes */ + return; + } + /* special cntrl keys */ + switch(scancode) + { + case 0x2A: + case 0x36: /* shift pressed */ + shift=1; + return; /* do nothing else */ + case 0xAA: + case 0xB6: /* shift released */ + shift=0; + return; /* do nothing else */ + case 0x38: /* alt pressed */ + alt=1; + return; /* do nothing else */ + case 0xB8: /* alt released */ + alt=0; + return; /* do nothing else */ + case 0x1d: /* ctrl pressed */ + ctrl=1; + return; /* do nothing else */ + case 0x9d: /* ctrl released */ + ctrl=0; + return; /* do nothing else */ + case 0x46: /* scrollock pressed */ + scroll_lock=~scroll_lock; + kbd_set_leds(); + return; /* do nothing else */ + case 0x3A: /* capslock pressed */ + caps_lock=~caps_lock; + kbd_set_leds(); + return; + case 0x45: /* numlock pressed */ + num_lock=~num_lock; + kbd_set_leds(); + return; + case 0xC6: /* scroll lock released */ + case 0xC5: /* num lock released */ + case 0xBA: /* caps lock released */ + return; /* just swallow */ + } + if((scancode&0x80)==0x80) /* key released */ + return; + /* now, decide which table we need */ + if(scancode > (sizeof(kbd_plain_xlate)/sizeof(kbd_plain_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown scancode %X\n",scancode); + return; /* swallow it */ + } + /* setup plain code first */ + keycode=kbd_plain_xlate[scancode]; + if(caps_lock==1) { /* caps_lock is pressed, overwrite plain code */ + if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown caps-locked scancode %X\n",scancode); + return; /* swallow it */ + } + keycode=kbd_shift_xlate[scancode]; + if(keycode<'A') { /* we only want the alphas capital */ + keycode=kbd_plain_xlate[scancode]; + } + } + if(shift==1) { /* shift overwrites caps_lock */ + if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown shifted scancode %X\n",scancode); + return; /* swallow it */ + } + keycode=kbd_shift_xlate[scancode]; + } + if(ctrl==1) { /* ctrl overwrites caps_lock and shift */ + if(scancode > (sizeof(kbd_ctrl_xlate)/sizeof(kbd_ctrl_xlate[0]))) { /* scancode not in list */ + PRINTF("unkown ctrl scancode %X\n",scancode); + return; /* swallow it */ + } + keycode=kbd_ctrl_xlate[scancode]; + } + /* check if valid keycode */ + if(keycode==0xff) { + PRINTF("unkown scancode %X\n",scancode); + return; /* swallow unknown codes */ + } + + kbd_put_queue(keycode); + PRINTF("%x\n",keycode); +} + +/* + * This reads the keyboard status port, and does the + * appropriate action. + * + */ +unsigned char handle_kbd_event(void) +{ + unsigned char status = kbd_read_status(); + unsigned int work = 10000; + + while ((--work > 0) && (status & KBD_STAT_OBF)) { + unsigned char scancode; + + scancode = kbd_read_input(); + + /* Error bytes must be ignored to make the + Synaptics touchpads compaq use work */ + /* Ignore error bytes */ + if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) + { + if (status & KBD_STAT_MOUSE_OBF) + ; /* not supported: handle_mouse_event(scancode); */ + else + handle_keyboard_event(scancode); + } + status = kbd_read_status(); + } + if (!work) + PRINTF("pc_keyb: controller jammed (0x%02X).\n", status); + return status; +} + + + +/****************************************************************************** + * Lowlevel Part of keyboard section + */ +unsigned char kbd_read_status(void) +{ + return(in8(CFG_ISA_IO_BASE_ADDRESS + KDB_COMMAND_PORT)); +} + +unsigned char kbd_read_input(void) +{ + return(in8(CFG_ISA_IO_BASE_ADDRESS + KDB_DATA_PORT)); +} + +void kbd_write_command(unsigned char cmd) +{ + out8(CFG_ISA_IO_BASE_ADDRESS + KDB_COMMAND_PORT,cmd); +} + +void kbd_write_output(unsigned char data) +{ + out8(CFG_ISA_IO_BASE_ADDRESS + KDB_DATA_PORT, data); +} + +int kbd_read_data(void) +{ + int val; + unsigned char status; + + val=-1; + status = kbd_read_status(); + if (status & KBD_STAT_OBF) { + val = kbd_read_input(); + if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) + val = -2; + } + return val; +} + +int kbd_wait_for_input(void) +{ + unsigned long timeout; + int val; + + timeout = KBD_TIMEOUT; + val=kbd_read_data(); + while(val < 0) + { + if(timeout--==0) + return -1; + udelay(1000); + val=kbd_read_data(); + } + return val; +} + + +int kb_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT * 10; + + do { + unsigned char status = handle_kbd_event(); + if (!(status & KBD_STAT_IBF)) + return 0; /* ok */ + udelay(1000); + timeout--; + } while (timeout); + return 1; +} + +void kbd_write_command_w(int data) +{ + if(kb_wait()) + PRINTF("timeout in kbd_write_command_w\n"); + kbd_write_command(data); +} + +void kbd_write_output_w(int data) +{ + if(kb_wait()) + PRINTF("timeout in kbd_write_output_w\n"); + kbd_write_output(data); +} + +void kbd_send_data(unsigned char data) +{ + unsigned char status; + disable_8259A_irq(1); /* disable interrupt */ + kbd_write_output_w(data); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + enable_8259A_irq(1); /* enable interrupt */ +} + + +char * kbd_initialize(void) +{ + int status; + + in_pointer = 0; /* delete in Buffer */ + out_pointer = 0; + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) + return "Kbd: failed self test"; + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_KBD_TEST); + if (kbd_wait_for_input() != 0x00) + return "Kbd: interface failed self test"; + /* + * Enable the keyboard by allowing the keyboard clock to run. + */ + kbd_write_command_w(KBD_CCMD_KBD_ENABLE); + status = kbd_wait_for_input(); + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_RESET); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + { + PRINTF("status: %X\n",status); + return "Kbd: reset failed, no ACK"; + } + } while (1); + if (kbd_wait_for_input() != KBD_REPLY_POR) + return "Kbd: reset failed, no POR"; + + /* + * Set keyboard controller mode. During this, the keyboard should be + * in the disabled state. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_DISABLE); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Kbd: disable keyboard: no ACK"; + } while (1); + + kbd_write_command_w(KBD_CCMD_WRITE_MODE); + kbd_write_output_w(KBD_MODE_KBD_INT + | KBD_MODE_SYS + | KBD_MODE_DISABLE_MOUSE + | KBD_MODE_KCC); + + /* ibm powerpc portables need this to use scan-code set 1 -- Cort */ + kbd_write_command_w(KBD_CCMD_READ_MODE); + if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { + /* + * If the controller does not support conversion, + * Set the keyboard to scan-code set 1. + */ + kbd_write_output_w(0xF0); + kbd_wait_for_input(); + kbd_write_output_w(0x01); + kbd_wait_for_input(); + } + kbd_write_output_w(KBD_CMD_ENABLE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: enable keyboard: no ACK"; + + /* + * Finally, set the typematic rate to maximum. + */ + kbd_write_output_w(KBD_CMD_SET_RATE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: Set rate: no ACK"; + kbd_write_output_w(0x00); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: Set rate: no ACK"; + return NULL; +} + +void kbd_interrupt(void) +{ + handle_kbd_event(); +} + + + +/* eof */ + diff --git a/board/mpl/common/usb_uhci.c b/board/mpl/common/usb_uhci.c new file mode 100644 index 00000000000..83624a9dae7 --- /dev/null +++ b/board/mpl/common/usb_uhci.c @@ -0,0 +1,1152 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * 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 + * + * Note: Part of this code has been derived from linux + * + */ + +/********************************************************************** + * How it works: + * ------------- + * The framelist / Transfer descriptor / Queue Heads are similar like + * in the linux usb_uhci.c. + * + * During initialization, the following skeleton is allocated in init_skel: + * + * framespecific | common chain + * + * framelist[] + * [ 0 ]-----> TD ---------\ + * [ 1 ]-----> TD ----------> TD ------> QH -------> QH -------> QH ---> NULL + * ... TD ---------/ + * [1023]-----> TD --------/ + * + * ^^ ^^ ^^ ^^ ^^ + * 7 TDs for 1 TD for Start of Start of End Chain + * INT (2-128ms) 1ms-INT CTRL Chain BULK Chain + * + * + * Since this is a bootloader, the isochronous transfer descriptor have been removed. + * + * Interrupt Transfers. + * -------------------- + * For Interupt transfers USB_MAX_TEMP_INT_TD Transfer descriptor are available. They + * will be inserted after the appropriate (depending the interval setting) skeleton TD. + * If an interrupt has been detected the dev->irqhandler is called. The status and number + * of transfered bytes is stored in dev->irq_status resp. dev->irq_act_len. If the + * dev->irqhandler returns 0, the interrupt TD is removed and disabled. If an 1 is returned, + * the interrupt TD will be reactivated. + * + * Control Transfers + * ----------------- + * Control Transfers are issued by filling the tmp_td with the appropriate data and connect + * them to the qh_cntrl queue header. Before other control/bulk transfers can be issued, + * the programm has to wait for completion. This does not allows asynchronous data transfer. + * + * Bulk Transfers + * -------------- + * Bulk Transfers are issued by filling the tmp_td with the appropriate data and connect + * them to the qh_bulk queue header. Before other control/bulk transfers can be issued, + * the programm has to wait for completion. This does not allows asynchronous data transfer. + * + * + */ + +#include <common.h> +#include <pci.h> + +#ifdef CONFIG_USB_UHCI + +#include <usb.h> +#include "usb_uhci.h" + +#define USB_MAX_TEMP_TD 128 /* number of temporary TDs for bulk and control transfers */ +#define USB_MAX_TEMP_INT_TD 32 /* number of temporary TDs for Interrupt transfers */ + + +#undef USB_UHCI_DEBUG + +#ifdef USB_UHCI_DEBUG +#define USB_UHCI_PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define USB_UHCI_PRINTF(fmt,args...) +#endif + + +static int irqvec = -1; /* irq vector, if -1 uhci is stopped / reseted */ +unsigned int usb_base_addr; /* base address */ + +static uhci_td_t td_int[8]; /* Interrupt Transfer descriptors */ +static uhci_qh_t qh_cntrl; /* control Queue Head */ +static uhci_qh_t qh_bulk; /* bulk Queue Head */ +static uhci_qh_t qh_end; /* end Queue Head */ +static uhci_td_t td_last; /* last TD (linked with end chain) */ + +/* temporary tds */ +static uhci_td_t tmp_td[USB_MAX_TEMP_TD]; /* temporary bulk/control td's */ +static uhci_td_t tmp_int_td[USB_MAX_TEMP_INT_TD]; /* temporary interrupt td's */ + +static unsigned long framelist[1024] __attribute__ ((aligned (0x1000))); /* frame list */ + +static struct virt_root_hub rh; /* struct for root hub */ + +/********************************************************************** + * some forward decleration + */ +int uhci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len,struct devrequest *setup); + +/* fill a td with the approproiate data. Link, status, info and buffer + * are used by the USB controller itselfes, dev is used to identify the + * "connected" device + */ +void usb_fill_td(uhci_td_t* td,unsigned long link,unsigned long status, + unsigned long info, unsigned long buffer, unsigned long dev) +{ + td->link=swap_32(link); + td->status=swap_32(status); + td->info=swap_32(info); + td->buffer=swap_32(buffer); + td->dev_ptr=dev; +} + +/* fill a qh with the approproiate data. Head and element are used by the USB controller + * itselfes. As soon as a valid dev_ptr is filled, a td chain is connected to the qh. + * Please note, that after completion of the td chain, the entry element is removed / + * marked invalid by the USB controller. + */ +void usb_fill_qh(uhci_qh_t* qh,unsigned long head,unsigned long element) +{ + qh->head=swap_32(head); + qh->element=swap_32(element); + qh->dev_ptr=0L; +} + +/* get the status of a td->status + */ +unsigned long usb_uhci_td_stat(unsigned long status) +{ + unsigned long result=0; + result |= (status & TD_CTRL_NAK) ? USB_ST_NAK_REC : 0; + result |= (status & TD_CTRL_STALLED) ? USB_ST_STALLED : 0; + result |= (status & TD_CTRL_DBUFERR) ? USB_ST_BUF_ERR : 0; + result |= (status & TD_CTRL_BABBLE) ? USB_ST_BABBLE_DET : 0; + result |= (status & TD_CTRL_CRCTIMEO) ? USB_ST_CRC_ERR : 0; + result |= (status & TD_CTRL_BITSTUFF) ? USB_ST_BIT_ERR : 0; + result |= (status & TD_CTRL_ACTIVE) ? USB_ST_NOT_PROC : 0; + return result; +} + +/* get the status and the transfered len of a td chain. + * called from the completion handler + */ +int usb_get_td_status(uhci_td_t *td,struct usb_device *dev) +{ + unsigned long temp,info; + unsigned long stat; + uhci_td_t *mytd=td; + + if(dev->devnum==rh.devnum) + return 0; + dev->act_len=0; + stat=0; + do { + temp=swap_32((unsigned long)mytd->status); + stat=usb_uhci_td_stat(temp); + info=swap_32((unsigned long)mytd->info); + if(((info & 0xff)!= USB_PID_SETUP) && + (((info >> 21) & 0x7ff)!= 0x7ff) && + (temp & 0x7FF)!=0x7ff) + { /* if not setup and not null data pack */ + dev->act_len+=(temp & 0x7FF) + 1; /* the transfered len is act_len + 1 */ + } + if(stat) { /* status no ok */ + dev->status=stat; + return -1; + } + temp=swap_32((unsigned long)mytd->link); + mytd=(uhci_td_t *)(temp & 0xfffffff0); + }while((temp & 0x1)==0); /* process all TDs */ + dev->status=stat; + return 0; /* Ok */ +} + + +/*------------------------------------------------------------------- + * LOW LEVEL STUFF + * assembles QHs und TDs for control, bulk and iso + *-------------------------------------------------------------------*/ + +/* Submits a control message. That is a Setup, Data and Status transfer. + * Routine does not wait for completion. + */ +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len,struct devrequest *setup) +{ + unsigned long destination, status; + int maxsze = usb_maxpacket(dev, pipe); + unsigned long dataptr; + int len; + int pktsze; + int i=0; + + if (!maxsze) { + USB_UHCI_PRINTF("uhci_submit_control_urb: pipesize for pipe %lx is zero\n", pipe); + return -1; + } + if(((pipe>>8)&0x7f)==rh.devnum) { + /* this is the root hub -> redirect it */ + return uhci_submit_rh_msg(dev,pipe,buffer,transfer_len,setup); + } + USB_UHCI_PRINTF("uhci_submit_control start len %x, maxsize %x\n",transfer_len,maxsze); + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* Setup stage */ + /* 3 errors */ + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + /* (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD); */ + /* Build the TD for the control request, try forever, 8 bytes of data */ + usb_fill_td(&tmp_td[i],UHCI_PTR_TERM ,status, destination | (7 << 21),(unsigned long)setup,(unsigned long)dev); +#if 0 + { + char *sp=(char *)setup; + printf("SETUP to pipe %lx: %x %x %x %x %x %x %x %x\n", pipe, + sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); + } +#endif + dataptr = (unsigned long)buffer; + len=transfer_len; + + /* If direction is "send", change the frame from SETUP (0x2D) + to OUT (0xE1). Else change it from SETUP to IN (0x69). */ + destination = (pipe & PIPE_DEVEP_MASK) | ((pipe & USB_DIR_IN)==0 ? USB_PID_OUT : USB_PID_IN); + while (len > 0) { + /* data stage */ + pktsze = len; + i++; + if (pktsze > maxsze) + pktsze = maxsze; + destination ^= 1 << TD_TOKEN_TOGGLE; /* toggle DATA0/1 */ + usb_fill_td(&tmp_td[i],UHCI_PTR_TERM, status, destination | ((pktsze - 1) << 21),dataptr,(unsigned long)dev); /* Status, pktsze bytes of data */ + tmp_td[i-1].link=swap_32((unsigned long)&tmp_td[i]); + + dataptr += pktsze; + len -= pktsze; + } + + /* Build the final TD for control status */ + /* It's only IN if the pipe is out AND we aren't expecting data */ + + destination &= ~UHCI_PID; + if (((pipe & USB_DIR_IN)==0) || (transfer_len == 0)) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + i++; + status &=~TD_CTRL_SPD; + /* no limit on errors on final packet , 0 bytes of data */ + usb_fill_td(&tmp_td[i],UHCI_PTR_TERM, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),0,(unsigned long)dev); + tmp_td[i-1].link=swap_32((unsigned long)&tmp_td[i]); /* queue status td */ + /* usb_show_td(i+1);*/ + USB_UHCI_PRINTF("uhci_submit_control end (%d tmp_tds used)\n",i); + /* first mark the control QH element terminated */ + qh_cntrl.element=0xffffffffL; + /* set qh active */ + qh_cntrl.dev_ptr=(unsigned long)dev; + /* fill in tmp_td_chain */ + qh_cntrl.element=swap_32((unsigned long)&tmp_td[0]); + return 0; +} + +/*------------------------------------------------------------------- + * Prepare TDs for bulk transfers. + */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len) +{ + unsigned long destination, status,info; + unsigned long dataptr; + int maxsze = usb_maxpacket(dev, pipe); + int len; + int i=0; + + if(transfer_len < 0) { + printf("Negative transfer length in submit_bulk\n"); + return -1; + } + if (!maxsze) + return -1; + /* The "pipe" thing contains the destination in bits 8--18. */ + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); + /* 3 errors */ + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + /* ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); */ + /* Build the TDs for the bulk request */ + len = transfer_len; + dataptr = (unsigned long)buffer; + do { + int pktsze = len; + if (pktsze > maxsze) + pktsze = maxsze; + /* pktsze bytes of data */ + info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) | + (usb_gettoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); + + if((len-pktsze)==0) + status |= TD_CTRL_IOC; /* last one generates INT */ + + usb_fill_td(&tmp_td[i],UHCI_PTR_TERM, status, info,dataptr,(unsigned long)dev); /* Status, pktsze bytes of data */ + if(i>0) + tmp_td[i-1].link=swap_32((unsigned long)&tmp_td[i]); + i++; + dataptr += pktsze; + len -= pktsze; + usb_dotoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + } while (len > 0); + /* first mark the bulk QH element terminated */ + qh_bulk.element=0xffffffffL; + /* set qh active */ + qh_bulk.dev_ptr=(unsigned long)dev; + /* fill in tmp_td_chain */ + qh_bulk.element=swap_32((unsigned long)&tmp_td[0]); + return 0; +} + + +/* search a free interrupt td + */ +uhci_td_t *uhci_alloc_int_td(void) +{ + int i; + for(i=0;i<USB_MAX_TEMP_INT_TD;i++) { + if(tmp_int_td[i].dev_ptr==0) /* no device assigned -> free TD */ + return &tmp_int_td[i]; + } + return NULL; +} + +#if 0 +void uhci_show_temp_int_td(void) +{ + int i; + for(i=0;i<USB_MAX_TEMP_INT_TD;i++) { + if((tmp_int_td[i].dev_ptr&0x01)!=0x1L) /* no device assigned -> free TD */ + printf("temp_td %d is assigned to dev %lx\n",i,tmp_int_td[i].dev_ptr); + } + printf("all others temp_tds are free\n"); +} +#endif +/*------------------------------------------------------------------- + * submits USB interrupt (ie. polling ;-) + */ +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len, int interval) +{ + int nint, n; + unsigned long status, destination; + unsigned long info,tmp; + uhci_td_t *mytd; + if (interval < 0 || interval >= 256) + return -1; + + if (interval == 0) + nint = 0; + else { + for (nint = 0, n = 1; nint <= 8; nint++, n += n) /* round interval down to 2^n */ + { + if(interval < n) { + interval = n / 2; + break; + } + } + nint--; + } + + USB_UHCI_PRINTF("Rounded interval to %i, chain %i\n", interval, nint); + mytd=uhci_alloc_int_td(); + if(mytd==NULL) { + printf("No free INT TDs found\n"); + return -1; + } + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | (3 << 27); +/* (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); +*/ + + destination =(pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe) | (((transfer_len - 1) & 0x7ff) << 21); + + info = destination | (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); + tmp = swap_32(td_int[nint].link); + usb_fill_td(mytd,tmp,status, info,(unsigned long)buffer,(unsigned long)dev); + /* Link it */ + tmp = swap_32((unsigned long)mytd); + td_int[nint].link=tmp; + + usb_dotoggle (dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + + return 0; +} + +/********************************************************************** + * Low Level functions + */ + + +void reset_hc(void) +{ + + /* Global reset for 100ms */ + out16r( usb_base_addr + USBPORTSC1,0x0204); + out16r( usb_base_addr + USBPORTSC2,0x0204); + out16r( usb_base_addr + USBCMD,USBCMD_GRESET | USBCMD_RS); + /* Turn off all interrupts */ + out16r(usb_base_addr + USBINTR,0); + wait_ms(50); + out16r( usb_base_addr + USBCMD,0); + wait_ms(10); +} + +void start_hc(void) +{ + int timeout = 1000; + + while(in16r(usb_base_addr + USBCMD) & USBCMD_HCRESET) { + if (!--timeout) { + printf("USBCMD_HCRESET timed out!\n"); + break; + } + } + /* Turn on all interrupts */ + out16r(usb_base_addr + USBINTR,USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP); + /* Start at frame 0 */ + out16r(usb_base_addr + USBFRNUM,0); + /* set Framebuffer base address */ + out32r(usb_base_addr+USBFLBASEADD,(unsigned long)&framelist); + /* Run and mark it configured with a 64-byte max packet */ + out16r(usb_base_addr + USBCMD,USBCMD_RS | USBCMD_CF | USBCMD_MAXP); +} + +/* Initialize the skeleton + */ +void usb_init_skel(void) +{ + unsigned long temp; + int n; + + for(n=0;n<USB_MAX_TEMP_INT_TD;n++) + tmp_int_td[n].dev_ptr=0L; /* no devices connected */ + /* last td */ + usb_fill_td(&td_last,UHCI_PTR_TERM,TD_CTRL_IOC ,0,0,0L); + /* usb_fill_td(&td_last,UHCI_PTR_TERM,0,0,0); */ + /* End Queue Header */ + usb_fill_qh(&qh_end,UHCI_PTR_TERM,(unsigned long)&td_last); + /* Bulk Queue Header */ + temp=(unsigned long)&qh_end; + usb_fill_qh(&qh_bulk,temp | UHCI_PTR_QH,UHCI_PTR_TERM); + /* Control Queue Header */ + temp=(unsigned long)&qh_bulk; + usb_fill_qh(&qh_cntrl, temp | UHCI_PTR_QH,UHCI_PTR_TERM); + /* 1ms Interrupt td */ + temp=(unsigned long)&qh_cntrl; + usb_fill_td(&td_int[0],temp | UHCI_PTR_QH,0,0,0,0L); + temp=(unsigned long)&td_int[0]; + for(n=1; n<8; n++) + usb_fill_td(&td_int[n],temp,0,0,0,0L); + for (n = 0; n < 1024; n++) { + /* link all framelist pointers to one of the interrupts */ + int m, o; + if ((n&127)==127) + framelist[n]= swap_32((unsigned long)&td_int[0]); + else + for (o = 1, m = 2; m <= 128; o++, m += m) + if ((n & (m - 1)) == ((m - 1) / 2)) + framelist[n]= swap_32((unsigned long)&td_int[o]); + } +} + +/* check the common skeleton for completed transfers, and update the status + * of the "connected" device. Called from the IRQ routine. + */ +void usb_check_skel(void) +{ + struct usb_device *dev; + /* start with the control qh */ + if(qh_cntrl.dev_ptr!=0) /* it's a device assigned check if this caused IRQ */ + { + dev=(struct usb_device *)qh_cntrl.dev_ptr; + usb_get_td_status(&tmp_td[0],dev); /* update status */ + if(!(dev->status & USB_ST_NOT_PROC)) { /* is not active anymore, disconnect devices */ + qh_cntrl.dev_ptr=0; + } + } + /* now process the bulk */ + if(qh_bulk.dev_ptr!=0) /* it's a device assigned check if this caused IRQ */ + { + dev=(struct usb_device *)qh_bulk.dev_ptr; + usb_get_td_status(&tmp_td[0],dev); /* update status */ + if(!(dev->status & USB_ST_NOT_PROC)) { /* is not active anymore, disconnect devices */ + qh_bulk.dev_ptr=0; + } + } +} + +/* check the interrupt chain, ubdate the status of the appropriate device, + * call the appropriate irqhandler and reactivate the TD if the irqhandler + * returns with 1 + */ +void usb_check_int_chain(void) +{ + int i,res; + unsigned long link,status; + struct usb_device *dev; + uhci_td_t *td,*prevtd; + + for(i=0;i<8;i++) { + prevtd=&td_int[i]; /* the first previous td is the skeleton td */ + link=swap_32(td_int[i].link) & 0xfffffff0; /* next in chain */ + td=(uhci_td_t *)link; /* assign it */ + /* all interrupt TDs are finally linked to the td_int[0]. + * so we process all until we find the td_int[0]. + * if int0 chain points to a QH, we're also done + */ + while(((i>0) && (link != (unsigned long)&td_int[0])) || + ((i==0) && !(swap_32(td->link) & UHCI_PTR_QH))) + { + /* check if a device is assigned with this td */ + status=swap_32(td->status); + if((td->dev_ptr!=0L) && !(status & TD_CTRL_ACTIVE)) { + /* td is not active and a device is assigned -> call irqhandler */ + dev=(struct usb_device *)td->dev_ptr; + dev->irq_act_len=((status & 0x7FF)==0x7FF) ? 0 : (status & 0x7FF) + 1; /* transfered length */ + dev->irq_status=usb_uhci_td_stat(status); /* get status */ + res=dev->irq_handle(dev); /* call irqhandler */ + if(res==1) { + /* reactivate */ + status|=TD_CTRL_ACTIVE; + td->status=swap_32(status); + prevtd=td; /* previous td = this td */ + } + else { + prevtd->link=td->link; /* link previous td directly to the nex td -> unlinked */ + /* remove device pointer */ + td->dev_ptr=0L; + } + } /* if we call the irq handler */ + link=swap_32(td->link) & 0xfffffff0; /* next in chain */ + td=(uhci_td_t *)link; /* assign it */ + } /* process all td in this int chain */ + } /* next interrupt chain */ +} + + +/* usb interrupt service routine. + */ +void handle_usb_interrupt(void) +{ + unsigned short status; + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + + status = in16r(usb_base_addr + USBSTS); + + if (!status) /* shared interrupt, not mine */ + return; + if (status != 1) { + /* remove host controller halted state */ + if ((status&0x20) && ((in16r(usb_base_addr+USBCMD) && USBCMD_RS)==0)) { + out16r(usb_base_addr + USBCMD, USBCMD_RS | in16r(usb_base_addr + USBCMD)); + } + } + usb_check_int_chain(); /* call interrupt handlers for int tds */ + usb_check_skel(); /* call completion handler for common transfer routines */ + out16r(usb_base_addr+USBSTS,status); +} + + +/* init uhci + */ +int usb_lowlevel_init(void) +{ + unsigned char temp; + int busdevfunc; + + busdevfunc=pci_find_device(USB_UHCI_VEND_ID,USB_UHCI_DEV_ID,0); /* get PCI Device ID */ + if(busdevfunc==-1) { + printf("Error USB UHCI (%04X,%04X) not found\n",USB_UHCI_VEND_ID,USB_UHCI_DEV_ID); + return -1; + } + pci_read_config_byte(busdevfunc,PCI_INTERRUPT_LINE,&temp); + irqvec = temp; + irq_free_handler(irqvec); + USB_UHCI_PRINTF("Interrupt Line = %d, is %d\n",irqvec); + pci_read_config_byte(busdevfunc,PCI_INTERRUPT_PIN,&temp); + USB_UHCI_PRINTF("Interrupt Pin = %ld\n",temp); + pci_read_config_dword(busdevfunc,PCI_BASE_ADDRESS_4,&usb_base_addr); + USB_UHCI_PRINTF("IO Base Address = 0x%lx\n",usb_base_addr); + usb_base_addr&=0xFFFFFFF0; + usb_base_addr+=CFG_ISA_IO_BASE_ADDRESS; + rh.devnum = 0; + usb_init_skel(); + reset_hc(); + start_hc(); + irq_install_handler(irqvec, (interrupt_handler_t *)handle_usb_interrupt, NULL); + return 0; +} + +/* stop uhci + */ +int usb_lowlevel_stop(void) +{ + if(irqvec==-1) + return 1; + irq_free_handler(irqvec); + reset_hc(); + irqvec=-1; + return 0; +} + +/******************************************************************************************* + * Virtual Root Hub + * Since the uhci does not have a real HUB, we simulate one ;-) + */ +#undef USB_RH_DEBUG + +#ifdef USB_RH_DEBUG +#define USB_RH_PRINTF(fmt,args...) printf (fmt ,##args) +static void usb_display_wValue(unsigned short wValue,unsigned short wIndex); +static void usb_display_Req(unsigned short req); +#else +#define USB_RH_PRINTF(fmt,args...) +static void usb_display_wValue(unsigned short wValue,unsigned short wIndex) {} +static void usb_display_Req(unsigned short req) {} +#endif + +static unsigned char root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x01, /* __u8 iManufacturer; */ + 0x00, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static unsigned char root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + + +static unsigned char root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'U', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + + +/* + * Root Hub Control Pipe (interrupt Pipes are not supported) + */ + + +int uhci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, void *buffer,int transfer_len,struct devrequest *cmd) +{ + void *data = buffer; + int leni = transfer_len; + int len = 0; + int status = 0; + int stat = 0; + int i; + + unsigned short cstatus; + + unsigned short bmRType_bReq; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; + + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + printf("Root-Hub submit IRQ: NOT implemented\n"); +#if 0 + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer (urb); +#endif + return 0; + } + bmRType_bReq = cmd->requesttype | cmd->request << 8; + wValue = swap_16(cmd->value); + wIndex = swap_16(cmd->index); + wLength = swap_16(cmd->length); + usb_display_Req(bmRType_bReq); + for (i = 0; i < 8; i++) + rh.c_p_r[i] = 0; + USB_RH_PRINTF("Root-Hub: adr: %2x cmd(%1x): %02x%02x %04x %04x %04x\n", + dev->devnum, 8, cmd->requesttype,cmd->request, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(unsigned short *) data = swap_16(1); + len=2; + break; + case RH_GET_STATUS | RH_INTERFACE: + *(unsigned short *) data = swap_16(0); + len=2; + break; + case RH_GET_STATUS | RH_ENDPOINT: + *(unsigned short *) data = swap_16(0); + len=2; + break; + case RH_GET_STATUS | RH_CLASS: + *(unsigned long *) data = swap_32(0); + len=4; + break; /* hub power ** */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + + status = in16r(usb_base_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on ** */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(unsigned short *) data = swap_16(status); + *(unsigned short *) (data + 2) = swap_16(cstatus); + len=4; + break; + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + len=0; + break; + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case (RH_C_HUB_OVER_CURRENT): + len=0; /* hub power over current ** */ + break; + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + usb_display_wValue(wValue,wIndex); + switch (wValue) { + case (RH_PORT_ENABLE): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) & ~USBPORTSC_PE; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + case (RH_PORT_SUSPEND): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) & ~USBPORTSC_SUSP; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + case (RH_PORT_POWER): + len=0; /* port power ** */ + break; + case (RH_C_PORT_CONNECTION): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) | USBPORTSC_CSC; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + case (RH_C_PORT_ENABLE): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) | USBPORTSC_PEC; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + case (RH_C_PORT_SUSPEND): +/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + len=0; + break; + case (RH_C_PORT_OVER_CURRENT): + len=0; + break; + case (RH_C_PORT_RESET): + rh.c_p_r[wIndex - 1] = 0; + len=0; + break; + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + usb_display_wValue(wValue,wIndex); + switch (wValue) { + case (RH_PORT_SUSPEND): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) | USBPORTSC_SUSP; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + case (RH_PORT_RESET): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) | USBPORTSC_PR; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + wait_ms(10); + status = (status & 0xfff5) & ~USBPORTSC_PR; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + udelay(10); + status = (status & 0xfff5) | USBPORTSC_PE; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + wait_ms(10); + status = (status & 0xfff5) | 0xa; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + case (RH_PORT_POWER): + len=0; /* port power ** */ + break; + case (RH_PORT_ENABLE): + status = in16r(usb_base_addr+USBPORTSC1+2*(wIndex-1)); + status = (status & 0xfff5) | USBPORTSC_PE; + out16r(usb_base_addr+USBPORTSC1+2*(wIndex-1),status); + len=0; + break; + } + break; + + case RH_SET_ADDRESS: + rh.devnum = wValue; + len=0; + break; + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + i=sizeof(root_hub_config_des); + status=i > wLength ? wLength : i; + len = leni > status ? status : leni; + memcpy (data, root_hub_dev_des, len); + break; + case (0x02): /* configuration descriptor */ + i=sizeof(root_hub_config_des); + status=i > wLength ? wLength : i; + len = leni > status ? status : leni; + memcpy (data, root_hub_config_des, len); + break; + case (0x03): /*string descriptors */ + if(wValue==0x0300) { + i=sizeof(root_hub_str_index0); + status = i > wLength ? wLength : i; + len = leni > status ? status : leni; + memcpy (data, root_hub_str_index0, len); + break; + } + if(wValue==0x0301) { + i=sizeof(root_hub_str_index1); + status = i > wLength ? wLength : i; + len = leni > status ? status : leni; + memcpy (data, root_hub_str_index1, len); + break; + } + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = 2; + i=sizeof(root_hub_hub_des); + status= i > wLength ? wLength : i; + len = leni > status ? status : leni; + memcpy (data, root_hub_hub_des, len); + break; + case RH_GET_CONFIGURATION: + *(unsigned char *) data = 0x01; + len = 1; + break; + case RH_SET_CONFIGURATION: + len=0; + break; + default: + stat = USB_ST_STALLED; + } + USB_RH_PRINTF("Root-Hub stat %lx port1: %x port2: %x\n\n",stat, + in16r(usb_base_addr + USBPORTSC1), in16r(usb_base_addr + USBPORTSC2)); + dev->act_len=len; + dev->status=stat; + return stat; + +} + +/******************************************************************************** + * Some Debug Routines + */ + +#ifdef USB_RH_DEBUG + +static void usb_display_Req(unsigned short req) +{ + USB_RH_PRINTF("- Root-Hub Request: "); + switch (req) { + case RH_GET_STATUS: + USB_RH_PRINTF("Get Status "); + break; + case RH_GET_STATUS | RH_INTERFACE: + USB_RH_PRINTF("Get Status Interface "); + break; + case RH_GET_STATUS | RH_ENDPOINT: + USB_RH_PRINTF("Get Status Endpoint "); + break; + case RH_GET_STATUS | RH_CLASS: + USB_RH_PRINTF("Get Status Class"); + break; /* hub power ** */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + USB_RH_PRINTF("Get Status Class Others"); + break; + case RH_CLEAR_FEATURE | RH_ENDPOINT: + USB_RH_PRINTF("Clear Feature Endpoint "); + break; + case RH_CLEAR_FEATURE | RH_CLASS: + USB_RH_PRINTF("Clear Feature Class "); + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + USB_RH_PRINTF("Clear Feature Other Class "); + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + USB_RH_PRINTF("Set Feature Other Class "); + break; + case RH_SET_ADDRESS: + USB_RH_PRINTF("Set Address "); + break; + case RH_GET_DESCRIPTOR: + USB_RH_PRINTF("Get Descriptor "); + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + USB_RH_PRINTF("Get Descriptor Class "); + break; + case RH_GET_CONFIGURATION: + USB_RH_PRINTF("Get Configuration "); + break; + case RH_SET_CONFIGURATION: + USB_RH_PRINTF("Get Configuration "); + break; + default: + USB_RH_PRINTF("****UNKNOWN**** 0x%04X ",req); + } + USB_RH_PRINTF("\n"); + +} + +static void usb_display_wValue(unsigned short wValue,unsigned short wIndex) +{ + switch (wValue) { + case (RH_PORT_ENABLE): + USB_RH_PRINTF("Root-Hub: Enable Port %d\n",wIndex); + break; + case (RH_PORT_SUSPEND): + USB_RH_PRINTF("Root-Hub: Suspend Port %d\n",wIndex); + break; + case (RH_PORT_POWER): + USB_RH_PRINTF("Root-Hub: Port Power %d\n",wIndex); + break; + case (RH_C_PORT_CONNECTION): + USB_RH_PRINTF("Root-Hub: C Port Connection Port %d\n",wIndex); + break; + case (RH_C_PORT_ENABLE): + USB_RH_PRINTF("Root-Hub: C Port Enable Port %d\n",wIndex); + break; + case (RH_C_PORT_SUSPEND): + USB_RH_PRINTF("Root-Hub: C Port Suspend Port %d\n",wIndex); + break; + case (RH_C_PORT_OVER_CURRENT): + USB_RH_PRINTF("Root-Hub: C Port Over Current Port %d\n",wIndex); + break; + case (RH_C_PORT_RESET): + USB_RH_PRINTF("Root-Hub: C Port reset Port %d\n",wIndex); + break; + default: + USB_RH_PRINTF("Root-Hub: unknown %x %x\n",wValue,wIndex); + break; + } +} + +#endif + + + +#ifdef USB_UHCI_DEBUG + +static int usb_display_td(uhci_td_t *td) +{ + unsigned long tmp; + int valid; + + printf("TD at %p:\n",td); + + tmp=swap_32(td->link); + printf("Link points to 0x%08lX, %s first, %s, %s\n",tmp&0xfffffff0, + ((tmp & 0x4)==0x4) ? "Depth" : "Breath", + ((tmp & 0x2)==0x2) ? "QH" : "TD", + ((tmp & 0x1)==0x1) ? "invalid" : "valid"); + valid=((tmp & 0x1)==0x0); + tmp=swap_32(td->status); + printf(" %s %ld Errors %s %s %s \n %s %s %s %s %s %s\n Len 0x%lX\n", + (((tmp>>29)&0x1)==0x1) ? "SPD Enable" : "SPD Disable", + ((tmp>>28)&0x3), + (((tmp>>26)&0x1)==0x1) ? "Low Speed" : "Full Speed", + (((tmp>>25)&0x1)==0x1) ? "ISO " : "", + (((tmp>>24)&0x1)==0x1) ? "IOC " : "", + (((tmp>>23)&0x1)==0x1) ? "Active " : "Inactive ", + (((tmp>>22)&0x1)==0x1) ? "Stalled" : "", + (((tmp>>21)&0x1)==0x1) ? "Data Buffer Error" : "", + (((tmp>>20)&0x1)==0x1) ? "Babble" : "", + (((tmp>>19)&0x1)==0x1) ? "NAK" : "", + (((tmp>>18)&0x1)==0x1) ? "Bitstuff Error" : "", + (tmp&0x7ff)); + tmp=swap_32(td->info); + printf(" MaxLen 0x%lX\n",((tmp>>21)&0x7FF)); + printf(" %s Endpoint 0x%lX Dev Addr 0x%lX PID 0x%lX\n",((tmp>>19)&0x1)==0x1 ? "TOGGLE" : "", + ((tmp>>15)&0xF),((tmp>>8)&0x7F),tmp&0xFF); + tmp=swap_32(td->buffer); + printf(" Buffer 0x%08lX\n",tmp); + printf(" DEV %08lX\n",td->dev_ptr); + return valid; +} + + +void usb_show_td(int max) +{ + int i; + if(max>0) { + for(i=0;i<max;i++) { + usb_display_td(&tmp_td[i]); + } + } + else { + i=0; + do { + printf("tmp_td[%d]\n",i); + }while(usb_display_td(&tmp_td[i++])); + } +} + + +#endif +#endif /* CONFIG_USB_UHCI */ + +/* EOF */ diff --git a/board/musenki/flash.c b/board/musenki/flash.c new file mode 100644 index 00000000000..a0a038f753a --- /dev/null +++ b/board/musenki/flash.c @@ -0,0 +1,513 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc824x.h> + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*---------------------------------------------------------------------*/ +#undef DEBUG_FLASH + +#ifdef DEBUG_FLASH +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_char *addr, flash_info_t *info); +static int write_data (flash_info_t *info, uchar *dest, uchar data); +static void flash_get_offsets (ulong base, flash_info_t *info); + + + +/* + * don't ask. its stupid, but more than one soul has had to live with this mistake + * "swaptab[i]" is the value of "i" with the bits reversed. + */ + +#define MUSENKI_BROKEN_FLASH 1 + +#ifdef MUSENKI_BROKEN_FLASH +unsigned char swaptab[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +#define BS(b) (swaptab[b]) + +#else + +#define BS(b) (b) + +#endif + +#define BYTEME(x) ((x) & 0xFF) + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + DEBUGF("\n## Get flash bank 1 size @ 0x%08x\n",CFG_FLASH_BASE0_PRELIM); + + size_b0 = flash_get_size((vu_char *)CFG_FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0: " + "ID 0x%lx, Size = 0x%08lx = %ld MB\n", + flash_info[0].flash_id, + size_b0, size_b0<<20); + } + + DEBUGF("## Get flash bank 2 size @ 0x%08x\n",CFG_FLASH_BASE1_PRELIM); + size_b1 = flash_get_size((vu_char *)CFG_FLASH_BASE1_PRELIM, &flash_info[1]); + + DEBUGF("## Prelim. Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size_b0; + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + DEBUGF("protect monitor %x @ %x\n", CFG_MONITOR_BASE, CFG_MONITOR_LEN); + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + DEBUGF("protect environtment %x @ %x\n", CFG_ENV_ADDR, CFG_ENV_SECT_SIZE); + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + + if (size_b1) { + flash_info[1].size = size_b1; + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[1]); +#endif + } else { + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + flash_info[1].size = 0; + } + + DEBUGF("## Final Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base; + base += 0x00020000; /* 128k per bank */ + } + return; + + default: + printf ("Don't know sector ofsets for flash type 0x%lx\n", info->flash_id); + return; + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("Fujitsu "); break; + case FLASH_MAN_SST: printf ("SST "); break; + case FLASH_MAN_STM: printf ("STM "); break; + case FLASH_MAN_INTEL: printf ("Intel "); break; + case FLASH_MAN_MT: printf ("MT "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F320J3A: printf ("28F320J3A (32Mbit = 128K x 32)\n"); + break; + case FLASH_28F640J3A: printf ("28F640J3A (64Mbit = 128K x 64)\n"); + break; + case FLASH_28F128J3A: printf ("28F128J3A (128Mbit = 128K x 128)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + if (info->size >= (1 << 20)) { + i = 20; + } else { + i = 10; + } + printf (" Size: %ld %cB in %d Sectors\n", + info->size >> i, + (i == 20) ? 'M' : 'k', + info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ +static ulong flash_get_size (vu_char *addr, flash_info_t *info) +{ + vu_char manuf, device; + + addr[0] = BS(0x90); + manuf = BS(addr[0]); + DEBUGF("Manuf. ID @ 0x%08lx: 0x%08x\n", (vu_char *)addr, manuf); + + switch (manuf) { + case BYTEME(AMD_MANUFACT): + info->flash_id = FLASH_MAN_AMD; + break; + case BYTEME(FUJ_MANUFACT): + info->flash_id = FLASH_MAN_FUJ; + break; + case BYTEME(SST_MANUFACT): + info->flash_id = FLASH_MAN_SST; + break; + case BYTEME(STM_MANUFACT): + info->flash_id = FLASH_MAN_STM; + break; + case BYTEME(INTEL_MANUFACT): + info->flash_id = FLASH_MAN_INTEL; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + addr[0] = BS(0xFF); /* restore read mode, (yes, BS is a NOP) */ + return 0; /* no or unknown flash */ + } + + device = BS(addr[2]); /* device ID */ + + DEBUGF("Device ID @ 0x%08x: 0x%08x\n", (&addr[1]), device); + + switch (device) { + case BYTEME(INTEL_ID_28F320J3A): + info->flash_id += FLASH_28F320J3A; + info->sector_count = 32; + info->size = 0x00400000; + break; /* => 4 MB */ + + case BYTEME(INTEL_ID_28F640J3A): + info->flash_id += FLASH_28F640J3A; + info->sector_count = 64; + info->size = 0x00800000; + break; /* => 8 MB */ + + case BYTEME(INTEL_ID_28F128J3A): + info->flash_id += FLASH_28F128J3A; + info->sector_count = 128; + info->size = 0x01000000; + break; /* => 16 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + addr[0] = BS(0xFF); /* restore read mode (yes, a NOP) */ + return 0; /* => no or unknown flash */ + + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + addr[0] = BS(0xFF); /* restore read mode */ + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_INTEL) { + printf ("Can erase only Intel flash types - aborted\n"); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + vu_char *addr = (vu_char *)(info->start[sect]); + unsigned long status; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = BS(0x50); /* clear status register */ + *addr = BS(0x20); /* erase setup */ + *addr = BS(0xD0); /* erase confirm */ + + /* re-enable interrupts if necessary */ + if (flag) { + enable_interrupts(); + } + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + while (((status = BS(*addr)) & BYTEME(0x00800080)) != BYTEME(0x00800080)) { + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + *addr = BS(0xB0); /* suspend erase */ + *addr = BS(0xFF); /* reset to read mode */ + return 1; + } + + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + *addr = BS(0xFF); /* reset to read mode */ + } + } + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 4 - Flash not identified + */ + +#define FLASH_WIDTH 1 /* flash bus width in bytes */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + uchar *wp = (uchar *)addr; + int rc; + + if (info->flash_id == FLASH_UNKNOWN) { + return 4; + } + + while (cnt > 0) { + if ((rc = write_data(info, wp, *src)) != 0) { + return rc; + } + wp++; + src++; + cnt--; + } + + return cnt; +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, uchar *dest, uchar data) +{ + vu_char *addr = (vu_char *)dest; + ulong status; + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((BS(*addr) & data) != data) { + return 2; + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = BS(0x40); /* write setup */ + *addr = data; + + /* re-enable interrupts if necessary */ + if (flag) { + enable_interrupts(); + } + + start = get_timer (0); + + while (((status = BS(*addr)) & BYTEME(0x00800080)) != BYTEME(0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + *addr = BS(0xFF); /* restore read mode */ + return 1; + } + } + + *addr = BS(0xFF); /* restore read mode */ + + return 0; +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/nx823/flash.c b/board/nx823/flash.c new file mode 100644 index 00000000000..616a13f2c6f --- /dev/null +++ b/board/nx823/flash.c @@ -0,0 +1,465 @@ +/* + * (C) Copyright 2001 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ +extern u_long *my_sernum; /* from nx823.c */ + +/*----------------------------------------------------------------------- + * Protection Flags: + */ +#define FLAG_PROTECT_SET 0x01 +#define FLAG_PROTECT_CLEAR 0x02 + +/* Board support for 1 or 2 flash devices */ +#undef FLASH_PORT_WIDTH32 +#define FLASH_PORT_WIDTH16 + +#ifdef FLASH_PORT_WIDTH16 +#define FLASH_PORT_WIDTH ushort +#define FLASH_PORT_WIDTHV vu_short +#else +#define FLASH_PORT_WIDTH ulong +#define FLASH_PORT_WIDTHV vu_long +#endif + +#define FPW FLASH_PORT_WIDTH +#define FPWV FLASH_PORT_WIDTHV + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (FPW *addr, flash_info_t *info); +static int write_data (flash_info_t *info, ulong dest, FPW data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + size_b0 = flash_get_size((FPW *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_PS_16 | BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((FPW *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + /* monitor protection ON by default */ + (void)flash_protect(FLAG_PROTECT_SET, + CFG_FLASH_BASE, + CFG_FLASH_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); + + flash_info[0].size = size_b0; + + return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + return; + } + + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) { + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000); + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F320J3A: + printf ("28F320J3A\n"); break; + case FLASH_28F640J3A: + printf ("28F640J3A\n"); break; + case FLASH_28F128J3A: + printf ("28F128J3A\n"); break; + default: printf ("Unknown Chip Type\n"); break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (FPW *addr, flash_info_t *info) +{ + FPW value; + + /* Write auto select command: read Manufacturer ID */ + addr[0x5555] = (FPW)0x00AA00AA; + addr[0x2AAA] = (FPW)0x00550055; + addr[0x5555] = (FPW)0x00900090; + + value = addr[0]; + + switch (value) { + case (FPW)INTEL_MANUFACT: + info->flash_id = FLASH_MAN_INTEL; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + addr[0] = (FPW)0x00FF00FF; /* restore read mode */ + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case (FPW)INTEL_ID_28F320J3A: + info->flash_id += FLASH_28F320J3A; + info->sector_count = 32; + info->size = 0x00400000; + break; /* => 4 MB */ + + case (FPW)INTEL_ID_28F640J3A: + info->flash_id += FLASH_28F640J3A; + info->sector_count = 64; + info->size = 0x00800000; + break; /* => 8 MB */ + + case (FPW)INTEL_ID_28F128J3A: + info->flash_id += FLASH_28F128J3A; + info->sector_count = 128; + info->size = 0x01000000; + break; /* => 16 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + break; + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + addr[0] = (FPW)0x00FF00FF; /* restore read mode */ + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect; + ulong type, start, now, last; + int rcode = 0; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + type = (info->flash_id & FLASH_VENDMASK); + if ((type != FLASH_MAN_INTEL)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer (0); + last = start; + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + FPWV *addr = (FPWV *)(info->start[sect]); + FPW status; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = (FPW)0x00500050; /* clear status register */ + *addr = (FPW)0x00200020; /* erase setup */ + *addr = (FPW)0x00D000D0; /* erase confirm */ + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + while (((status = *addr) & (FPW)0x00800080) != (FPW)0x00800080) { + if ((now=get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + *addr = (FPW)0x00B000B0; /* suspend erase */ + *addr = (FPW)0x00FF00FF; /* reset to read mode */ + rcode = 1; + break; + } + + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + *addr = (FPW)0x00FF00FF; /* reset to read mode */ + printf (" done\n"); + } + } + return rcode; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + * 4 - Flash not identified + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp; + FPW data; + int count, i, l, rc, port_width; + + if (info->flash_id == FLASH_UNKNOWN) { + return 4; + } +/* get lower word aligned address */ +#ifdef FLASH_PORT_WIDTH16 + wp = (addr & ~1); + port_width = 2; +#else + wp = (addr & ~3); + port_width = 4; +#endif + + /* save sernum if needed */ + if (addr >= CFG_FLASH_SN_SECTOR && addr < CFG_FLASH_SN_BASE) + { + u_long dest = CFG_FLASH_SN_BASE; + u_short *sn = (u_short *)my_sernum; + + printf("(saving sernum)"); + for (i=0; i<4; i++) + { + if ((rc = write_data(info, dest, sn[i])) != 0) { + return (rc); + } + dest += port_width; + } + } + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<port_width && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<port_width; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += port_width; + } + + /* + * handle word aligned part + */ + count = 0; + while (cnt >= port_width) { + data = 0; + for (i=0; i<port_width; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += port_width; + cnt -= port_width; + if (count++ > 0x800) + { + putc('.'); + count = 0; + } + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<port_width && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<port_width; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_data(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word or halfword to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, ulong dest, FPW data) +{ + FPWV *addr = (FPWV *)dest; + ulong status; + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*addr & data) != data) { + printf("not erased at %08lx (%x)\n",(ulong)addr,*addr); + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *addr = (FPW)0x00400040; /* write setup */ + *addr = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + + while (((status = *addr) & (FPW)0x00800080) != (FPW)0x00800080) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + *addr = (FPW)0x00FF00FF; /* restore read mode */ + return (1); + } + } + + *addr = (FPW)0x00FF00FF; /* restore read mode */ + + return (0); +} + diff --git a/board/pcippc2/flash.c b/board/pcippc2/flash.c new file mode 100644 index 00000000000..20f4d0343b8 --- /dev/null +++ b/board/pcippc2/flash.c @@ -0,0 +1,573 @@ +/* + * (C) Copyright 2001 + * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc. + * + * (C) Copyright 2002 + * 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 + */ + +#include <config.h> +#include <common.h> +#include <flash.h> +#include <asm/io.h> + +/*---------------------------------------------------------------------*/ +#undef DEBUG_FLASH + +#ifdef DEBUG_FLASH +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + +static ulong flash_get_size (ulong addr, flash_info_t *info); +static int flash_get_offsets (ulong base, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_reset (ulong addr); + +unsigned long flash_init (void) +{ + unsigned int i; + unsigned long flash_size = 0; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + flash_info[i].sector_count = 0; + flash_info[i].size = 0; + } + + DEBUGF("\n## Get flash size @ 0x%08x\n", CFG_FLASH_BASE); + + flash_size = flash_get_size (CFG_FLASH_BASE, flash_info); + + DEBUGF("## Flash bank size: %08lx\n", flash_size); + + if (flash_size) { +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE && \ + CFG_MONITOR_BASE < CFG_FLASH_BASE + CFG_FLASH_MAX_SIZE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE + CFG_MONITOR_LEN - 1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1, + &flash_info[0]); +#endif + + } else { + puts ("Warning: the BOOT Flash is not initialised !"); + } + + return flash_size; +} + +/* + * The following code cannot be run from FLASH! + */ +static ulong flash_get_size (ulong addr, flash_info_t *info) +{ + short i; + uchar value; + + /* Write auto select command: read Manufacturer ID */ + out8(addr + 0x0555, 0xAA); + iobarrier_rw(); + out8(addr + 0x02AA, 0x55); + iobarrier_rw(); + out8(addr + 0x0555, 0x90); + iobarrier_rw(); + + value = in8(addr); + iobarrier_rw(); + + DEBUGF("Manuf. ID @ 0x%08lx: 0x%08x\n", (ulong)addr, value); + + switch (value | (value << 16)) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + + case STM_MANUFACT: + info->flash_id = FLASH_MAN_STM; + break; + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + flash_reset (addr); + return 0; + } + + value = in8(addr + 1); /* device ID */ + iobarrier_rw(); + + DEBUGF("Device ID @ 0x%08lx: 0x%08x\n", addr+1, value); + + switch (value) { + case AMD_ID_F040B: + DEBUGF("Am29F040B\n"); + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00080000; + break; /* => 512 kB */ + + case AMD_ID_LV040B: + DEBUGF("Am29LV040B\n"); + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00080000; + break; /* => 512 kB */ + + case AMD_ID_LV400T: + DEBUGF("Am29LV400T\n"); + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + DEBUGF("Am29LV400B\n"); + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + DEBUGF("Am29LV800T\n"); + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + DEBUGF("Am29LV400B\n"); + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + DEBUGF("Am29LV160T\n"); + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + DEBUGF("Am29LV160B\n"); + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV320T: + DEBUGF("Am29LV320T\n"); + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + +#if 0 + /* Has the same ID as AMD_ID_LV320T, to be fixed */ + case AMD_ID_LV320B: + DEBUGF("Am29LV320B\n"); + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + + case AMD_ID_LV033C: + DEBUGF("Am29LV033C\n"); + info->flash_id += FLASH_AM033C; + info->sector_count = 64; + info->size = 0x01000000; + break; /* => 16Mb */ + + case STM_ID_F040B: + DEBUGF("M29F040B\n"); + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00080000; + break; /* => 512 kB */ + + default: + info->flash_id = FLASH_UNKNOWN; + flash_reset (addr); + return (0); /* => no or unknown flash */ + + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + if (! flash_get_offsets (addr, info)) { + flash_reset (addr); + return 0; + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + value = in8(info->start[i] + 2); + iobarrier_rw(); + info->protect[i] = (value & 1) != 0; + } + + /* + * Reset bank to read mode + */ + flash_reset (addr); + + return (info->size); +} + +static int flash_get_offsets (ulong base, flash_info_t *info) +{ + unsigned int i; + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM040: + /* set sector offsets for uniform sector type */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + i * info->size / + info->sector_count; + } + break; + default: + return 0; + } + + return 1; +} + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + volatile ulong addr = info->start[0]; + int flag, prot, sect, l_sect; + ulong start, now, last; + + if (s_first < 0 || s_first > s_last) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + out8(addr + 0x555, 0xAA); + iobarrier_rw(); + out8(addr + 0x2AA, 0x55); + iobarrier_rw(); + out8(addr + 0x555, 0x80); + iobarrier_rw(); + out8(addr + 0x555, 0xAA); + iobarrier_rw(); + out8(addr + 0x2AA, 0x55); + iobarrier_rw(); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = info->start[sect]; + out8(addr, 0x30); + iobarrier_rw(); + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = info->start[l_sect]; + + DEBUGF ("Start erase timeout: %d\n", CFG_FLASH_ERASE_TOUT); + + while ((in8(addr) & 0x80) != 0x80) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + flash_reset (info->start[0]); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + iobarrier_rw(); + } + +DONE: + /* reset to read mode */ + flash_reset (info->start[0]); + + printf (" done\n"); + return 0; +} + +/* + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/* + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + volatile ulong addr = info->start[0]; + ulong start; + int i; + + /* Check if Flash is (sufficiently) erased */ + if ((in32(dest) & data) != data) { + return (2); + } + + /* write each byte out */ + for (i = 0; i < 4; i++) { + char *data_ch = (char *)&data; + int flag = disable_interrupts(); + + out8(addr + 0x555, 0xAA); + iobarrier_rw(); + out8(addr + 0x2AA, 0x55); + iobarrier_rw(); + out8(addr + 0x555, 0xA0); + iobarrier_rw(); + out8(dest+i, data_ch[i]); + iobarrier_rw(); + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((in8(dest+i) & 0x80) != (data_ch[i] & 0x80)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + flash_reset (addr); + return (1); + } + iobarrier_rw(); + } + } + + flash_reset (addr); + return (0); +} + +/* + * Reset bank to read mode + */ +static void flash_reset (ulong addr) +{ + out8(addr, 0xF0); /* reset bank */ + iobarrier_rw(); +} + +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break; + case FLASH_MAN_STM: printf ("SGS THOMSON "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM040: printf ("29F040 or 29LV040 (4 Mbit, uniform sectors)\n"); + break; + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + if (info->size % 0x100000 == 0) { + printf (" Size: %ld MB in %d Sectors\n", + info->size / 0x100000, info->sector_count); + } else if (info->size % 0x400 == 0) { + printf (" Size: %ld KB in %d Sectors\n", + info->size / 0x400, info->sector_count); + } else { + printf (" Size: %ld B in %d Sectors\n", + info->size, info->sector_count); + } + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); +} diff --git a/board/pcippc2/pcippc2.h b/board/pcippc2/pcippc2.h new file mode 100644 index 00000000000..6e9e2ff05e7 --- /dev/null +++ b/board/pcippc2/pcippc2.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2002 + * 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 + */ + +#ifndef _PCIPPC2_H_ +#define _PCIPPC2_H_ + +#include <config.h> +#include <common.h> + +#include "hardware.h" + +#define FPGA(r, p) (pcippc2_fpga0_phys + HW_FPGA0_##r##_##p) +#define UART(r) (pcippc2_fpga0_phys + HW_FPGA0_UART1 + NS16550_##r * 4) +#define RTC(r) (pcippc2_fpga1_phys + HW_FPGA1_RTC + r) + +extern u32 pcippc2_fpga0_phys; +extern u32 pcippc2_fpga1_phys; + +extern u32 pcippc2_sdram_size (void); + +extern void pcippc2_fpga_init (void); + +extern void cpc710_pci_init (void); +extern void cpc710_pci_enable_timeout (void); + +extern unsigned long + cpc710_ram_init (void); + +#endif diff --git a/board/pcippc2/pcippc2_fpga.c b/board/pcippc2/pcippc2_fpga.c new file mode 100644 index 00000000000..7f6739ddabc --- /dev/null +++ b/board/pcippc2/pcippc2_fpga.c @@ -0,0 +1,87 @@ +/* + * (C) Copyright 2002 + * 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 + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> + +#include "pci.h" + +#include "hardware.h" +#include "pcippc2.h" + +u32 pcippc2_fpga0_phys; +u32 pcippc2_fpga1_phys; + +void pcippc2_fpga_init (void) +{ + pci_dev_t bdf = pci_find_device(FPGA_VENDOR_ID, FPGA_DEVICE_ID, 0); + unsigned int addr; + u16 cmd; + + if (bdf == -1) + { + puts("Unable to find FPGA !\n"); + hang(); + } + + pci_read_config_word(bdf, PCI_COMMAND, &cmd); + if ((cmd & (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)) != (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)) + { + puts("FPGA is not configured !\n"); + hang(); + } + + pci_read_config_dword(bdf, PCI_BASE_ADDRESS_0, &addr); + if (addr & 0x1) + { + /* IO space + */ + pcippc2_fpga0_phys = pci_io_to_phys(bdf, addr & 0xfffffffc); + } + else + { + /* Memory space + */ + pcippc2_fpga0_phys = pci_mem_to_phys(bdf, addr & 0xfffffff0); + } + + pci_read_config_dword(bdf, PCI_BASE_ADDRESS_1, &addr); + if (addr & 0x1) + { + /* IO space + */ + pcippc2_fpga1_phys = pci_io_to_phys(bdf, addr & 0xfffffffc); + } + else + { + /* Memory space + */ + pcippc2_fpga1_phys = pci_mem_to_phys(bdf, addr & 0xfffffff0); + } + + /* Interrupts are not used + */ + out32(FPGA(INT, INTR_MASK), 0xffffffff); + iobarrier_rw(); +} diff --git a/board/rpxsuper/flash.c b/board/rpxsuper/flash.c new file mode 100644 index 00000000000..0c298ba858f --- /dev/null +++ b/board/rpxsuper/flash.c @@ -0,0 +1,434 @@ +/* + * (C) Copyright 2000 + * Marius Groeger <mgroeger@sysgo.de> + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Flash Routines for AMD 29F080B devices + * Added support for 64bit and AMD 29DL323B + * + *-------------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> +#include <asm/io.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + +#define RD_SWP32(x) in_le32((volatile u32*)x) + +/*----------------------------------------------------------------------- + * Functions + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + unsigned long size; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* for now, only support the 4 MB Flash SIMM */ + size = flash_get_size((vu_long *)CFG_FLASH0_BASE, &flash_info[0]); + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE >= CFG_FLASH0_BASE + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#if (CFG_ENV_IS_IN_FLASH == 1) && defined(CFG_ENV_ADDR) +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, + &flash_info[0]); +#endif + + return /*size*/ (CFG_FLASH0_SIZE * 1024 * 1024); +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case (AMD_MANUFACT & FLASH_VENDMASK): + printf ("AMD "); + break; + case (FUJ_MANUFACT & FLASH_VENDMASK): + printf ("FUJITSU "); + break; + case (SST_MANUFACT & FLASH_VENDMASK): + printf ("SST "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case (AMD_ID_DL323B & FLASH_TYPEMASK): + printf("AM29DL323B (32 MBit)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if ((i % 5) == 0) printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + vu_long vendor[2], devid[2]; + ulong base = (ulong)addr; + + /* Reset and Write auto select command: read Manufacturer ID */ + addr[0] = 0xf0f0f0f0; + addr[2 * 0x0555] = 0xAAAAAAAA; + addr[2 * 0x02AA] = 0x55555555; + addr[2 * 0x0555] = 0x90909090; + addr[1] = 0xf0f0f0f0; + addr[2 * 0x0555 + 1] = 0xAAAAAAAA; + addr[2 * 0x02AA + 1] = 0x55555555; + addr[2 * 0x0555 + 1] = 0x90909090; + udelay (1000); + + vendor[0] = RD_SWP32(&addr[0]); + vendor[1] = RD_SWP32(&addr[1]); + if (vendor[0] != vendor[1] || vendor[0] != AMD_MANUFACT) { + info->size = 0; + goto out; + } + + devid[0] = RD_SWP32(&addr[2]); + devid[1] = RD_SWP32(&addr[3]); + + if (devid[0] == AMD_ID_DL323B) { + /* + * we have 2 Banks + * Bank 1 (23 Sectors): 0-7=8kbyte, 8-22=64kbyte + * Bank 2 (48 Sectors): 23-70=64kbyte + */ + info->flash_id = (AMD_MANUFACT & FLASH_VENDMASK) | + (AMD_ID_DL323B & FLASH_TYPEMASK); + info->sector_count = 71; + info->size = 4 * (8 * 8 + 63 * 64) * 1024; + } + else { + info->size = 0; + goto out; + } + + /* set up sector start address table */ + for (i = 0; i < 8; i++) { + info->start[i] = base + (i * 0x8000); + } + for (i = 8; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x40000) + 8 * 0x8000 - 8 * 0x40000; + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address */ + addr = (volatile unsigned long *)(info->start[i]); + addr[2 * 0x0555] = 0xAAAAAAAA; + addr[2 * 0x02AA] = 0x55555555; + addr[2 * 0x0555] = 0x90909090; + addr[2 * 0x0555 + 1] = 0xAAAAAAAA; + addr[2 * 0x02AA + 1] = 0x55555555; + addr[2 * 0x0555 + 1] = 0x90909090; + udelay (1000); + base = RD_SWP32(&addr[4]); + base |= RD_SWP32(&addr[5]); + info->protect[i] = base & 0x00010001 ? 1 : 0; + } + addr = (vu_long*)info->start[0]; + +out: + /* reset command */ + addr[0] = 0xf0f0f0f0; + addr[1] = 0xf0f0f0f0; + + return info->size; +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[2 * 0x0555] = 0xAAAAAAAA; + addr[2 * 0x02AA] = 0x55555555; + addr[2 * 0x0555] = 0x80808080; + addr[2 * 0x0555] = 0xAAAAAAAA; + addr[2 * 0x02AA] = 0x55555555; + addr[2 * 0x0555 + 1] = 0xAAAAAAAA; + addr[2 * 0x02AA + 1] = 0x55555555; + addr[2 * 0x0555 + 1] = 0x80808080; + addr[2 * 0x0555 + 1] = 0xAAAAAAAA; + addr[2 * 0x02AA + 1] = 0x55555555; + udelay (100); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x30303030; + addr[1] = 0x30303030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ( (addr[0] & 0x80808080) != 0x80808080 || + (addr[1] & 0x80808080) != 0x80808080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + serial_putc ('.'); + last = now; + } + } + + DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0xF0F0F0F0; /* reset bank */ + addr[1] = 0xF0F0F0F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + if ((dest & 0x00000004) == 0) { + addr[2 * 0x0555] = 0xAAAAAAAA; + addr[2 * 0x02AA] = 0x55555555; + addr[2 * 0x0555] = 0xA0A0A0A0; + } + else { + addr[2 * 0x0555 + 1] = 0xAAAAAAAA; + addr[2 * 0x02AA + 1] = 0x55555555; + addr[2 * 0x0555 + 1] = 0xA0A0A0A0; + } + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x80808080) != (data & 0x80808080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/rpxsuper/mii_phy.c b/board/rpxsuper/mii_phy.c new file mode 100644 index 00000000000..319f9595a3f --- /dev/null +++ b/board/rpxsuper/mii_phy.c @@ -0,0 +1,108 @@ +#include <common.h> +#include <mii_phy.h> +#include "rpxsuper.h" + +#define MII_MDIO 0x01 +#define MII_MDCK 0x02 +#define MII_MDIR 0x04 + +void +mii_discover_phy(void) +{ + int known; + unsigned short phy_reg; + unsigned long phy_id; + + known = 0; + printf("Discovering phy @ 0: "); + phy_id = mii_phy_read(2) << 16; + phy_id |= mii_phy_read(3); + if ((phy_id & 0xFFFFFC00) == 0x00137800) { + printf("Level One "); + if ((phy_id & 0x000003F0) == 0xE0) { + printf("LXT971A Revision %d\n", (int)(phy_id & 0xF)); + known = 1; + } + else printf("unknown type\n"); + } + else printf("unknown OUI = 0x%08lX\n", phy_id); + + phy_reg = mii_phy_read(1); + if (!(phy_reg & 0x0004)) printf("Link is down\n"); + if (!(phy_reg & 0x0020)) printf("Auto-negotiation not complete\n"); + if (phy_reg & 0x0002) printf("Jabber condition detected\n"); + if (phy_reg & 0x0010) printf("Remote fault condition detected \n"); + + if (known) { + phy_reg = mii_phy_read(17); + if (phy_reg & 0x0400) + printf("Phy operating at %d MBit/s in %s-duplex mode\n", + phy_reg & 0x4000 ? 100 : 10, + phy_reg & 0x0200 ? "full" : "half"); + else + printf("bad link!!\n"); +/* +left off: no link, green 100MBit, yellow 10MBit +right off: no activity, green full-duplex, yellow half-duplex +*/ + mii_phy_write(20, 0x0452); + } +} + +unsigned short +mii_phy_read(unsigned short reg) +{ + int i; + unsigned short tmp, val = 0, adr = 0; + t_rpx_regs *regs = (t_rpx_regs*)CFG_REGS_BASE; + + tmp = 0x6002 | (adr << 7) | (reg << 2); + regs->bcsr4 = 0xC3; + for (i = 0; i < 64; i++) { + regs->bcsr4 ^= MII_MDCK; + } + for (i = 0; i < 16; i++) { + regs->bcsr4 &= ~MII_MDCK; + if (tmp & 0x8000) regs->bcsr4 |= MII_MDIO; + else regs->bcsr4 &= ~MII_MDIO; + regs->bcsr4 |= MII_MDCK; + tmp <<= 1; + } + regs->bcsr4 |= MII_MDIR; + for (i = 0; i < 16; i++) { + val <<= 1; + regs->bcsr4 = MII_MDIO | (regs->bcsr4 | MII_MDCK); + if (regs->bcsr4 & MII_MDIO) val |= 1; + regs->bcsr4 = MII_MDIO | (regs->bcsr4 &= ~MII_MDCK); + } + return val; +} + +void +mii_phy_write(unsigned short reg, unsigned short val) +{ + int i; + unsigned short tmp, adr = 0; + t_rpx_regs *regs = (t_rpx_regs*)CFG_REGS_BASE; + + tmp = 0x5002 | (adr << 7) | (reg << 2); + regs->bcsr4 = 0xC3; + for (i = 0; i < 64; i++) { + regs->bcsr4 ^= MII_MDCK; + } + for (i = 0; i < 16; i++) { + regs->bcsr4 &= ~MII_MDCK; + if (tmp & 0x8000) regs->bcsr4 |= MII_MDIO; + else regs->bcsr4 &= ~MII_MDIO; + regs->bcsr4 |= MII_MDCK; + tmp <<= 1; + } + for (i = 0; i < 16; i++) { + regs->bcsr4 &= ~MII_MDCK; + if (val & 0x8000) regs->bcsr4 |= MII_MDIO; + else regs->bcsr4 &= ~MII_MDIO; + regs->bcsr4 |= MII_MDCK; + val <<= 1; + } +} + diff --git a/board/rsdproto/flash.c b/board/rsdproto/flash.c new file mode 100644 index 00000000000..654012f607c --- /dev/null +++ b/board/rsdproto/flash.c @@ -0,0 +1,402 @@ +/* + * (C) Copyright 2000 + * Marius Groeger <mgroeger@sysgo.de> + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Flash Routines for AM290[48]0B devices + * + *-------------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +/* flash hardware ids */ +#define VENDOR_AMD 0x0001 +#define AMD_29DL323C_B 0x2253 + +/* Define this to include autoselect sequence in flash_init(). Does NOT + * work when executing from flash itself, so this should be turned + * on only when debugging the RAM version. + */ +#undef WITH_AUTOSELECT + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if 1 +#define D(x) +#else +#define D(x) printf x +#endif + +/*----------------------------------------------------------------------- + * Functions + */ + +static unsigned char write_ull(flash_info_t *info, + unsigned long address, + volatile unsigned long long data); + +/* from flash_asm.S */ +extern void ull_write(unsigned long long volatile *address, + unsigned long long volatile *data); +extern void ull_read(unsigned long long volatile *address, + unsigned long long volatile *data); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + int i; + ulong addr; + +#ifdef WITH_AUTOSELECT + { + unsigned long long *f_addr = (unsigned long long *)PHYS_FLASH; + unsigned long long f_command, vendor, device; + /* Perform Autoselect */ + f_command = 0x00AA00AA00AA00AAULL; + ull_write(&f_addr[0x555], &f_command); + f_command = 0x0055005500550055ULL; + ull_write(&f_addr[0x2AA], &f_command); + f_command = 0x0090009000900090ULL; + ull_write(&f_addr[0x555], &f_command); + ull_read(&f_addr[0], &vendor); + vendor &= 0xffff; + ull_read(&f_addr[1], &device); + device &= 0xffff; + f_command = 0x00F000F000F000F0ULL; + ull_write(&f_addr[0x555], &f_command); + if (vendor != VENDOR_AMD || device != AMD_29DL323C_B) + return 0; + } +#endif + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* 1st bank: 8 x 32 KB sectors */ + flash_info[0].flash_id = VENDOR_AMD << 16 | AMD_29DL323C_B; + flash_info[0].sector_count = 8; + flash_info[0].size = flash_info[0].sector_count * 32 * 1024; + addr = PHYS_FLASH; + for(i = 0; i < flash_info[0].sector_count; i++) { + flash_info[0].start[i] = addr; + addr += flash_info[0].size / flash_info[0].sector_count; + } + /* 1st bank: 63 x 256 KB sectors */ + flash_info[1].flash_id = VENDOR_AMD << 16 | AMD_29DL323C_B; + flash_info[1].sector_count = 63; + flash_info[1].size = flash_info[1].sector_count * 256 * 1024; + for(i = 0; i < flash_info[1].sector_count; i++) { + flash_info[1].start[i] = addr; + addr += flash_info[1].size / flash_info[1].sector_count; + } + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE >= PHYS_FLASH + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + +#if (CFG_ENV_IS_IN_FLASH == 1) && defined(CFG_ENV_ADDR) +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, + &flash_info[0]); + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, + &flash_info[1]); +#endif + + return flash_info[0].size + flash_info[1].size; +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id >> 16) { + case VENDOR_AMD: + printf ("AMD "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case AMD_29DL323C_B: + printf ("AM29DL323CB (32 Mbit)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + int flag, prot, sect, l_sect; + ulong start; + unsigned long long volatile *f_addr; + unsigned long long volatile f_command; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) { + prot++; + } + } + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + f_addr = (unsigned long long *)info->start[0]; + f_command = 0x00AA00AA00AA00AAULL; + ull_write(&f_addr[0x555], &f_command); + f_command = 0x0055005500550055ULL; + ull_write(&f_addr[0x2AA], &f_command); + f_command = 0x0080008000800080ULL; + ull_write(&f_addr[0x555], &f_command); + f_command = 0x00AA00AA00AA00AAULL; + ull_write(&f_addr[0x555], &f_command); + f_command = 0x0055005500550055ULL; + ull_write(&f_addr[0x2AA], &f_command); + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + /* Start erase on unprotected sectors */ + for (l_sect = -1, sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + + f_addr = + (unsigned long long *)(info->start[sect]); + f_command = 0x0030003000300030ULL; + ull_write(f_addr, &f_command); + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + do + { + if (get_timer(start) > CFG_FLASH_ERASE_TOUT) + { /* write reset command, command address is unimportant */ + /* this command turns the flash back to read mode */ + f_addr = + (unsigned long long *)(info->start[l_sect]); + f_command = 0x00F000F000F000F0ULL; + ull_write(f_addr, &f_command); + printf (" timeout\n"); + return 1; + } + } while(*f_addr != 0xFFFFFFFFFFFFFFFFULL); + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + unsigned long cp, wp; + unsigned long long data; + int i, l, rc; + + wp = (addr & ~7); /* get lower long long aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<8 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<8; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_ull(info, wp, data)) != 0) { + return rc; + } + wp += 4; + } + + /* + * handle long long aligned part + */ + while (cnt >= 8) { + data = 0; + for (i=0; i<8; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_ull(info, wp, data)) != 0) { + return rc; + } + wp += 8; + cnt -= 8; + } + + if (cnt == 0) { + return ERR_OK; + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<8 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<8; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return write_ull(info, wp, data); +} + +/*--------------------------------------------------------------------------- +* +* FUNCTION NAME: write_ull +* +* DESCRIPTION: writes 8 bytes to flash +* +* EXTERNAL EFFECT: nothing +* +* PARAMETERS: 32 bit long pointer to address, 64 bit long pointer to data +* +* RETURNS: 0 if OK, 1 if timeout, 4 if parameter error +*--------------------------------------------------------------------------*/ + +static unsigned char write_ull(flash_info_t *info, + unsigned long address, + volatile unsigned long long data) +{ + static unsigned long long f_command; + static unsigned long long *f_addr; + ulong start; + + /* address muss be 8-aligned! */ + if (address & 0x7) + return ERR_ALIGN; + + f_addr = (unsigned long long *)info->start[0]; + f_command = 0x00AA00AA00AA00AAULL; + ull_write(&f_addr[0x555], &f_command); + f_command = 0x0055005500550055ULL; + ull_write(&f_addr[0x2AA], &f_command); + f_command = 0x00A000A000A000A0ULL; + ull_write(&f_addr[0x555], &f_command); + + f_addr = (unsigned long long *)address; + f_command = data; + ull_write(f_addr, &f_command); + + start = get_timer (0); + do + { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) + { + /* write reset command, command address is unimportant */ + /* this command turns the flash back to read mode */ + f_addr = (unsigned long long *)info->start[0]; + f_command = 0x00F000F000F000F0ULL; + ull_write(f_addr, &f_command); + return ERR_TIMOUT; + } + } while(*((unsigned long long *)address) != data); + + return 0; +} diff --git a/board/siemens/CCM/flash.c b/board/siemens/CCM/flash.c new file mode 100644 index 00000000000..e56114f80cc --- /dev/null +++ b/board/siemens/CCM/flash.c @@ -0,0 +1,553 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + if (size_b1) { + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size_b1 & 0xFFFF8000); + memctl->memc_br1 = ((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +#endif + } else { + memctl->memc_br1 = 0; /* invalidate bank */ + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + flash_info[0].size = size_b0; + flash_info[1].size = size_b1; + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; + + value = addr[0]; + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; + + *addr = 0x00F000F0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ((addr[0] & 0x00800080) != 0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/siemens/IAD210/atm.c b/board/siemens/IAD210/atm.c new file mode 100644 index 00000000000..c77e35912ed --- /dev/null +++ b/board/siemens/IAD210/atm.c @@ -0,0 +1,653 @@ + +#include <common.h> +#include <mpc8xx.h> +#include <commproc.h> + +#include "atm.h" +#include <linux/stddef.h> + +#define SYNC __asm__("sync") +#define ALIGN(p, a) ((char *)(((uint32)(p)+(a)-1) & ~((uint32)(a)-1))) + +#define FALSE 1 +#define TRUE 0 +#define OK 0 +#define ERROR -1 + +struct atm_connection_t g_conn[NUM_CONNECTIONS] = +{ + { NULL, 10, NULL, 10, NULL, NULL, NULL, NULL }, /* OAM */ +}; + +struct atm_driver_t g_atm = +{ + FALSE, /* loaded */ + FALSE, /* started */ + NULL, /* csram */ + 0, /* csram_size */ + NULL, /* am_top */ + NULL, /* ap_top */ + NULL, /* int_reload_ptr */ + NULL, /* int_serv_ptr */ + NULL, /* rbd_base_ptr */ + NULL, /* tbd_base_ptr */ + 0 /* linerate */ +}; + +char csram[1024]; /* more than enough for doing nothing*/ + +int atmLoad(void); +void atmUnload(void); +int atmMemInit(void); +void atmIntInit(void); +void atmApcInit(void); +void atmAmtInit(void); +void atmCpmInit(void); +void atmUtpInit(void); + +/***************************************************************************** + * + * FUNCTION NAME: atmLoad + * + * DESCRIPTION: Basic ATM initialization. + * + * PARAMETERS: none + * + * RETURNS: OK or ERROR + * + ****************************************************************************/ +int atmLoad() +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile cpmtimer8xx_t *timers = &immap->im_cpmtimer; + volatile iop8xx_t *iop = &immap->im_ioport; + + timers->cpmt_tgcr &= 0x0FFF; SYNC; /* Disable Timer 4 */ + immap->im_cpm.cp_scc[4].scc_gsmrl = 0x0; SYNC; /* Disable SCC4 */ + iop->iop_pdpar &= 0x3FFF; SYNC; /* Disable SAR and UTOPIA */ + + if ( atmMemInit() != OK ) return ERROR; + + atmIntInit(); + atmApcInit(); + atmAmtInit(); + atmCpmInit(); + atmUtpInit(); + + g_atm.loaded = TRUE; + + return OK; +} + +/***************************************************************************** + * + * FUNCTION NAME: atmUnload + * + * DESCRIPTION: Disables ATM and UTOPIA. + * + * PARAMETERS: none + * + * RETURNS: void + * + ****************************************************************************/ +void atmUnload() +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile cpmtimer8xx_t *timers = &immap->im_cpmtimer; + volatile iop8xx_t *iop = &immap->im_ioport; + + timers->cpmt_tgcr &= 0x0FFF; SYNC; /* Disable Timer 4 */ + immap->im_cpm.cp_scc[4].scc_gsmrl = 0x0; SYNC; /* Disable SCC4 */ + iop->iop_pdpar &= 0x3FFF; SYNC; /* Disable SAR and UTOPIA */ + g_atm.loaded = FALSE; +} + +/***************************************************************************** + * + * FUNCTION NAME: atmMemInit + * + * DESCRIPTION: + * + * The ATM driver uses the following resources: + * + * A. Memory in DPRAM to hold + * + * 1/ CT = Connection Table ( RCT & TCT ) + * 2/ TCTE = Transmit Connection Table Extension + * 3/ MPHYPT = Multi-PHY Pointing Table + * 4/ APCP = APC Parameter Table + * 5/ APCT_PRIO_1 = APC Table ( priority 1 for AAL1/2 ) + * 6/ APCT_PRIO_2 = APC Table ( priority 2 for VBR ) + * 7/ APCT_PRIO_3 = APC Table ( priority 3 for UBR ) + * 8/ TQ = Transmit Queue + * 9/ AM = Address Matching Table + * 10/ AP = Address Pointing Table + * + * B. Memory in cache safe RAM to hold + * + * 1/ INT = Interrupt Queue + * 2/ RBD = Receive Buffer Descriptors + * 3/ TBD = Transmit Buffer Descriptors + * + * This function + * 1. clears the ATM DPRAM area, + * 2. Allocates and clears cache safe memory, + * 3. Initializes 'g_conn'. + * + * PARAMETERS: none + * + * RETURNS: OK or ERROR + * + ****************************************************************************/ +int atmMemInit() +{ + int i; + unsigned immr = CFG_IMMR; + int total_num_rbd = 0; + int total_num_tbd = 0; + + memset((char *)CFG_IMMR + 0x2000 + ATM_DPRAM_BEGIN, 0x00, ATM_DPRAM_SIZE); + + g_atm.csram_size = NUM_INT_ENTRIES * SIZE_OF_INT_ENTRY; + + for ( i = 0; i < NUM_CONNECTIONS; ++i ) { + total_num_rbd += g_conn[i].num_rbd; + total_num_tbd += g_conn[i].num_tbd; + } + + g_atm.csram_size += total_num_rbd * SIZE_OF_RBD + total_num_tbd * SIZE_OF_TBD + 4; + + g_atm.csram = &csram[0]; + memset(&(g_atm.csram), 0x00, g_atm.csram_size); + + g_atm.int_reload_ptr = (uint32 *)ALIGN(g_atm.csram, 4); + g_atm.rbd_base_ptr = (struct atm_bd_t *)(g_atm.int_reload_ptr + NUM_INT_ENTRIES); + g_atm.tbd_base_ptr = (struct atm_bd_t *)(g_atm.rbd_base_ptr + total_num_rbd); + + g_conn[0].rbd_ptr = g_atm.rbd_base_ptr; + g_conn[0].tbd_ptr = g_atm.tbd_base_ptr; + g_conn[0].ct_ptr = CT_PTR(immr); + g_conn[0].tcte_ptr = TCTE_PTR(immr); + + return OK; +} + +/***************************************************************************** + * + * FUNCTION NAME: atmIntInit + * + * DESCRIPTION: + * + * Initialization of the MPC860 ESAR Interrupt Queue. + * This function + * - clears all entries in the INT, + * - sets the WRAP bit of the last INT entry, + * - initializes the 'int_serv_ptr' attribuut of the AtmDriver structure + * to the first INT entry. + * + * PARAMETERS: none + * + * RETURNS: void + * + * REMARKS: + * + * - The INT resides in external cache safe memory. + * - The base address of the INT is stored in g_atm.int_reload_ptr. + * - The number of entries in the INT is given by NUM_INT_ENTRIES. + * - The INTBASE field in SAR Parameter RAM is set by atmCpmInit(). + * + ****************************************************************************/ +void atmIntInit() +{ + int i; + for ( i = 0; i < NUM_INT_ENTRIES - 1; ++i) g_atm.int_reload_ptr[i] = 0; + g_atm.int_reload_ptr[i] = INT_WRAP; + g_atm.int_serv_ptr = g_atm.int_reload_ptr; +} + +/***************************************************************************** + * + * FUNCTION NAME: atmApcInit + * + * DESCRIPTION: + * + * This function initializes the following ATM Pace Controller related + * data structures: + * + * - 1 MPHY Pointing Table (contains only one entry) + * - 3 APC Parameter Tables (one PHY with 3 priorities) + * - 3 APC Tables (one table for each priority) + * - 1 Transmit Queue (one transmit queue per PHY) + * + * PARAMETERS: none + * + * RETURNS: void + * + ****************************************************************************/ +void atmApcInit() +{ + int i; + /* unsigned immr = CFG_IMMR; */ + uint16 * mphypt_ptr = MPHYPT_PTR(CFG_IMMR); + struct apc_params_t * apcp_ptr = APCP_PTR(CFG_IMMR); + uint16 * apct_prio1_ptr = APCT1_PTR(CFG_IMMR); + uint16 * tq_ptr = TQ_PTR(CFG_IMMR); + /***************************************************/ + /* Initialize MPHY Pointing Table (only one entry) */ + /***************************************************/ + *mphypt_ptr = APCP_BASE; + + /********************************************/ + /* Initialize APC parameters for priority 1 */ + /********************************************/ + apcp_ptr->apct_base1 = APCT_PRIO_1_BASE; + apcp_ptr->apct_end1 = APCT_PRIO_1_BASE + NUM_APCT_PRIO_1_ENTRIES * 2; + apcp_ptr->apct_ptr1 = APCT_PRIO_1_BASE; + apcp_ptr->apct_sptr1 = APCT_PRIO_1_BASE; + apcp_ptr->etqbase = TQ_BASE; + apcp_ptr->etqend = TQ_BASE + ( NUM_TQ_ENTRIES - 1 ) * 2; + apcp_ptr->etqaptr = TQ_BASE; + apcp_ptr->etqtptr = TQ_BASE; + apcp_ptr->apc_mi = 8; + apcp_ptr->ncits = 0x0100; /* NCITS = 1 */ + apcp_ptr->apcnt = 0; + apcp_ptr->reserved1 = 0; + apcp_ptr->eapcst = 0x2009; /* LAST, ESAR, MPHY */ + apcp_ptr->ptp_counter = 0; + apcp_ptr->ptp_txch = 0; + apcp_ptr->reserved2 = 0; + + + /***************************************************/ + /* Initialize APC Tables with empty slots (0xFFFF) */ + /***************************************************/ + for ( i = 0; i < NUM_APCT_PRIO_1_ENTRIES; ++i ) *(apct_prio1_ptr++) = 0xFFFF; + + /************************/ + /* Clear Transmit Queue */ + /************************/ + for ( i = 0; i < NUM_TQ_ENTRIES; ++i ) *(tq_ptr++) = 0; +} + +/***************************************************************************** + * + * FUNCTION NAME: atmAmtInit + * + * DESCRIPTION: + * + * This function clears the first entry in the Address Matching Table and + * lets the first entry in the Address Pointing table point to the first + * entry in the TCT table (i.e. the raw cell channel). + * + * PARAMETERS: none + * + * RETURNS: void + * + * REMARKS: + * + * The values for the AMBASE, AMEND and APBASE registers in SAR parameter + * RAM are initialized by atmCpmInit(). + * + ****************************************************************************/ +void atmAmtInit() +{ + unsigned immr = CFG_IMMR; + + g_atm.am_top = AM_PTR(immr); + g_atm.ap_top = AP_PTR(immr); + + *(g_atm.ap_top--) = CT_BASE; + *(g_atm.am_top--) = 0; +} + +/***************************************************************************** + * + * FUNCTION NAME: atmCpmInit + * + * DESCRIPTION: + * + * This function initializes the Utopia Interface Parameter RAM Map + * (SCC4, ATM Protocol) of the Communication Processor Modudule. + * + * PARAMETERS: none + * + * RETURNS: void + * + ****************************************************************************/ +void atmCpmInit() +{ + unsigned immr = CFG_IMMR; + + memset((char *)immr + 0x3F00, 0x00, 0xC0); + + /*-----------------------------------------------------------------*/ + /* RBDBASE - Receive buffer descriptors base address */ + /* The RBDs reside in cache safe external memory. */ + /*-----------------------------------------------------------------*/ + *RBDBASE(immr) = (uint32)g_atm.rbd_base_ptr; + + /*-----------------------------------------------------------------*/ + /* SRFCR - SAR receive function code */ + /* 0-2 rsvd = 000 */ + /* 3-4 BO = 11 Byte ordering (big endian). */ + /* 5-7 FC = 000 Value driven on the address type signals AT[1-3] */ + /* when the SDMA channel accesses memory. */ + /*-----------------------------------------------------------------*/ + *SRFCR(immr) = 0x18; + + /*-----------------------------------------------------------------*/ + /* SRSTATE - SAR receive status */ + /* 0 EXT = 0 Extended mode off. */ + /* 1 ACP = 0 Valid only if EXT = 1. */ + /* 2 EC = 0 Standard 53-byte ATM cell. */ + /* 3 SNC = 0 In sync. Must be set to 0 during initialization. */ + /* 4 ESAR = 1 Enhanced SAR functionality enabled. */ + /* 5 MCF = 1 Management Cell Filter active. */ + /* 6 SER = 0 UTOPIA mode. */ + /* 7 MPY = 1 Multiple PHY mode. */ + /*-----------------------------------------------------------------*/ + *SRSTATE(immr) = 0x0D; + + /*-----------------------------------------------------------------*/ + /* MRBLR - Maximum receive buffer length register. */ + /* Must be cleared for ATM operation (see also SMRBLR). */ + /*-----------------------------------------------------------------*/ + *MRBLR(immr) = 0; + + /*-----------------------------------------------------------------*/ + /* RSTATE - SCC internal receive state parameters */ + /* The first byte must be initialized with the value of SRFCR. */ + /*-----------------------------------------------------------------*/ + *RSTATE(immr) = (uint32)(*SRFCR(immr)) << 24; + + /*-----------------------------------------------------------------*/ + /* STFCR - SAR transmit function code */ + /* 0-2 rsvd = 000 */ + /* 3-4 BO = 11 Byte ordering (big endian). */ + /* 5-7 FC = 000 Value driven on the address type signals AT[1-3] */ + /* when the SDMA channel accesses memory. */ + /*-----------------------------------------------------------------*/ + *STFCR(immr) = 0x18; + + /*-----------------------------------------------------------------*/ + /* SRSTATE - SAR transmit status */ + /* 0 EXT = 0 : Extended mode off */ + /* 1 rsvd = 0 : */ + /* 2 EC = 0 : Standard 53-byte ATM cell */ + /* 3 rsvd = 0 : */ + /* 4 ESAR = 1 : Enhanced SAR functionality enabled */ + /* 5 rsvd = 0 : */ + /* 6 SER = 0 : UTOPIA mode */ + /* 7 MPY = 1 : Multiple PHY mode */ + /*-----------------------------------------------------------------*/ + *STSTATE(immr) = 0x09; + + /*-----------------------------------------------------------------*/ + /* TBDBASE - Transmit buffer descriptors base address */ + /* The TBDs reside in cache safe external memory. */ + /*-----------------------------------------------------------------*/ + *TBDBASE(immr) = (uint32)g_atm.tbd_base_ptr; + + /*-----------------------------------------------------------------*/ + /* TSTATE - SCC internal transmit state parameters */ + /* The first byte must be initialized with the value of STFCR. */ + /*-----------------------------------------------------------------*/ + *TSTATE(immr) = (uint32)(*STFCR(immr)) << 24; + + /*-----------------------------------------------------------------*/ + /* CTBASE - Connection table base address */ + /* Offset from the beginning of DPRAM (64-byte aligned). */ + /*-----------------------------------------------------------------*/ + *CTBASE(immr) = CT_BASE; + + /*-----------------------------------------------------------------*/ + /* INTBASE - Interrupt queue base pointer. */ + /* The interrupt queue resides in cache safe external memory. */ + /*-----------------------------------------------------------------*/ + *INTBASE(immr) = (uint32)g_atm.int_reload_ptr; + + /*-----------------------------------------------------------------*/ + /* INTPTR - Pointer into interrupt queue. */ + /* Initialize to INTBASE. */ + /*-----------------------------------------------------------------*/ + *INTPTR(immr) = *INTBASE(immr); + + /*-----------------------------------------------------------------*/ + /* C_MASK - Constant mask for CRC32 */ + /* Must be initialized to 0xDEBB20E3. */ + /*-----------------------------------------------------------------*/ + *C_MASK(immr) = 0xDEBB20E3; + + /*-----------------------------------------------------------------*/ + /* INT_ICNT - Interrupt threshold value */ + /*-----------------------------------------------------------------*/ + *INT_ICNT(immr) = 1; + + /*-----------------------------------------------------------------*/ + /* INT_CNT - Interrupt counter */ + /* Initalize to INT_ICNT. Decremented for each interrupt entry */ + /* reported in the interrupt queue. On zero an interrupt is */ + /* signaled to the host by setting the GINT bit in the event */ + /* register. The counter is reinitialized with INT_ICNT. */ + /*-----------------------------------------------------------------*/ + *INT_CNT(immr) = *INT_ICNT(immr); + + /*-----------------------------------------------------------------*/ + /* SMRBLR - SAR maximum receive buffer length register. */ + /* Must be a multiple of 48 bytes. Common for all ATM connections. */ + /*-----------------------------------------------------------------*/ + *SMRBLR(immr) = SAR_RXB_SIZE; + + /*-----------------------------------------------------------------*/ + /* APCST - APC status register. */ + /* 0 rsvd 0 */ + /* 1-2 CSER 11 Initialize with the same value as NSER. */ + /* 3-4 NSER 11 Next serial or UTOPIA channel. */ + /* 5-7 rsvd 000 */ + /* 8-10 rsvd 000 */ + /* 11 rsvd 0 */ + /* 12 ESAR 1 UTOPIA Level 2 MPHY enabled. */ + /* 13 DIS 0 APC disable. Must be initiazed to 0. */ + /* 14 PL2 0 Not used. */ + /* 15 MPY 1 Multiple PHY mode on. */ + /*-----------------------------------------------------------------*/ + *APCST(immr) = 0x7809; + + /*-----------------------------------------------------------------*/ + /* APCPTR - Pointer to the APC parameter table */ + /* In MPHY master mode this parameter points to the MPHY pointing */ + /* table. 2-byte aligned. */ + /*-----------------------------------------------------------------*/ + *APCPTR(immr) = MPHYPT_BASE; + + /*-----------------------------------------------------------------*/ + /* HMASK - Header mask */ + /* Each incoming cell is masked with HMASK before being compared */ + /* to the entries in the address matching table. */ + /*-----------------------------------------------------------------*/ + *HMASK(immr) = AM_HMASK; + + /*-----------------------------------------------------------------*/ + /* AMBASE - Address matching table base address */ + /*-----------------------------------------------------------------*/ + *AMBASE(immr) = AM_BASE; + + /*-----------------------------------------------------------------*/ + /* AMEND - Address matching table end address */ + /*-----------------------------------------------------------------*/ + *AMEND(immr) = AM_BASE; + + /*-----------------------------------------------------------------*/ + /* APBASE - Address pointing table base address */ + /*-----------------------------------------------------------------*/ + *APBASE(immr) = AP_BASE; + + /*-----------------------------------------------------------------*/ + /* MPHYST - MPHY status register */ + /* 0-1 rsvd 00 */ + /* 2-6 NMPHY 00000 1 PHY */ + /* 7-9 rsvd 000 */ + /* 10-14 CMPHY 00000 Initialize with same value as NMPHY */ + /*-----------------------------------------------------------------*/ + *MPHYST(immr) = 0x0000; + + /*-----------------------------------------------------------------*/ + /* TCTEBASE - Transmit connection table extension base address */ + /* Offset from the beginning of DPRAM (32-byte aligned). */ + /*-----------------------------------------------------------------*/ + *TCTEBASE(immr) = TCTE_BASE; + + /*-----------------------------------------------------------------*/ + /* Clear not used registers. */ + /*-----------------------------------------------------------------*/ +} + +/***************************************************************************** + * + * FUNCTION NAME: atmUtpInit + * + * DESCRIPTION: + * + * This function initializes the ATM interface for + * + * - UTOPIA mode + * - muxed bus + * - master operation + * - multi PHY (because of a bug in the MPC860P rev. E.0) + * - internal clock = SYSCLK / 2 + * + * EXTERNAL EFFECTS: + * + * After calling this function, the MPC860ESAR UTOPIA bus is + * active and uses the following ports/pins: + * + * Port Pin Signal Description + * ------ --- ------- ------------------------------------------- + * PB[15] R17 TxClav Transmit cell available input/output signal + * PC[15] D16 RxClav Receive cell available input/output signal + * PD[15] U17 UTPB[0] UTOPIA bus bit 0 input/output signal + * PD[14] V19 UTPB[1] UTOPIA bus bit 1 input/output signal + * PD[13] V18 UTPB[2] UTOPIA bus bit 2 input/output signal + * PD[12] R16 UTPB[3] UTOPIA bus bit 3 input/output signal + * PD[11] T16 RXENB Receive enable input/output signal + * PD[10] W18 TXENB Transmit enable input/output signal + * PD[9] V17 UTPCLK UTOPIA clock input/output signal + * PD[7] T15 UTPB[4] UTOPIA bus bit 4 input/output signal + * PD[6] V16 UTPB[5] UTOPIA bus bit 5 input/output signal + * PD[5] U15 UTPB[6] UTOPIA bus bit 6 input/output signal + * PD[4] U16 UTPB[7] UTOPIA bus bit 7 input/output signal + * PD[3] W16 SOC Start of cell input/output signal + * + * PARAMETERS: none + * + * RETURNS: void + * + * REMARK: + * + * The ATM parameters and data structures must be configured before + * initializing the UTOPIA port. The UTOPIA port activates immediately + * upon initialization, and if its associated data structures are not + * initialized, the CPM will lock up. + * + ****************************************************************************/ +void atmUtpInit() +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile iop8xx_t *iop = &immap->im_ioport; + volatile car8xx_t *car = &immap->im_clkrst; + volatile cpm8xx_t *cpm = &immap->im_cpm; + int flag; + + flag = disable_interrupts(); + + /*-----------------------------------------------------------------*/ + /* SCCR - System Clock Control Register */ + /* */ + /* The UTOPIA clock can be selected to be internal clock or */ + /* external clock (selected by the UTOPIA mode register). */ + /* In case of internal clock, the UTOPIA clock is derived from */ + /* the system frequency divided by two dividers. */ + /* Bits 27-31 of the SCCR register are defined to control the */ + /* UTOPIA clock. */ + /* */ + /* SCCR[27:29] DFUTP Division factor. Divide the system clock */ + /* by 2^DFUTP. */ + /* SCCR[30:31] DFAUTP Additional division factor. Divide the */ + /* system clock by the following value: */ + /* 00 = divide by 1 */ + /* 00 = divide by 3 */ + /* 10 = divide by 5 */ + /* 11 = divide by 7 */ + /* */ + /* Note that the UTOPIA clock must be programmed as to operate */ + /* within the range SYSCLK/10 .. 50Mhz. */ + /*-----------------------------------------------------------------*/ + car->car_sccr &= 0xFFFFFFE0; + car->car_sccr |= 0x00000008; /* UTPCLK = SYSCLK / 4 */ + + /*-----------------------------------------------------------------*/ + /* RCCR - RISC Controller Configuration Register */ + /* */ + /* RCCR[8] DR1M IDMA Request 0 Mode */ + /* 0 = edge sensitive */ + /* 1 = level sensitive */ + /* RCCR[9] DR0M IDMA Request 0 Mode */ + /* 0 = edge sensitive */ + /* 1 = level sensitive */ + /* RCCR[10:11] DRQP IDMA Request Priority */ + /* 00 = IDMA req. have more prio. than SCCs */ + /* 01 = IDMA req. have less prio. then SCCs */ + /* 10 = IDMA requests have the lowest prio. */ + /* 11 = reserved */ + /* */ + /* The RCCR[DR0M] and RCCR[DR1M] bits must be set to enable UTOPIA */ + /* operation. Also, program RCCR[DPQP] to 01 to give SCC transfers */ + /* higher priority. */ + /*-----------------------------------------------------------------*/ + cpm->cp_rccr &= 0xFF0F; + cpm->cp_rccr |= 0x00D0; + + /*-----------------------------------------------------------------*/ + /* Port B - TxClav Signal */ + /*-----------------------------------------------------------------*/ + cpm->cp_pbpar |= 0x00010000; /* PBPAR[15] = 1 */ + cpm->cp_pbdir &= 0xFFFEFFFF; /* PBDIR[15] = 0 */ + + /*-----------------------------------------------------------------*/ + /* UTOPIA Mode Register */ + /* */ + /* - muxed bus (master operation only) */ + /* - multi PHY (because of a bug in the MPC860P rev.E.0) */ + /* - internal clock */ + /* - no loopback */ + /* - do no activate statistical counters */ + /*-----------------------------------------------------------------*/ + iop->utmode = 0x00000004; SYNC; + + /*-----------------------------------------------------------------*/ + /* Port D - UTOPIA Data and Control Signals */ + /* */ + /* 15-12 UTPB[0:3] UTOPIA bus bit 0 - 3 input/output signals */ + /* 11 RXENB UTOPIA receive enable input/output signal */ + /* 10 TXENB UTOPIA transmit enable input/output signal */ + /* 9 TUPCLK UTOPIA clock input/output signal */ + /* 8 MII-MDC Used by MII in simult. MII and UTOPIA operation */ + /* 7-4 UTPB[4:7] UTOPIA bus bit 4 - 7 input/output signals */ + /* 3 SOC UTOPIA Start of cell input/output signal */ + /* 2 Reserved */ + /* 1 Enable UTOPIA mode */ + /* 0 Enable SAR */ + /*-----------------------------------------------------------------*/ + iop->iop_pdpar |= 0xDF7F; SYNC; + iop->iop_pddir &= 0x2080; SYNC; + + /*-----------------------------------------------------------------*/ + /* Port C - RxClav Signal */ + /*-----------------------------------------------------------------*/ + iop->iop_pcpar |= 0x0001; /* PCPAR[15] = 1 */ + iop->iop_pcdir &= 0xFFFE; /* PCDIR[15] = 0 */ + iop->iop_pcso &= 0xFFFE; /* PCSO[15] = 0 */ + + if (flag) + enable_interrupts(); +} diff --git a/board/siemens/IAD210/flash.c b/board/siemens/IAD210/flash.c new file mode 100644 index 00000000000..1ed526249fe --- /dev/null +++ b/board/siemens/IAD210/flash.c @@ -0,0 +1,502 @@ +/* + * (C) Copyright 2000, 2001, 2002 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + size = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size, size<<20); + } + + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size & 0xFFFF8000); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V; + + /* Re-do sizing to get full correct info */ + size = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size; + + return (size); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; + + value = addr[0]; + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + switch (value) { + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; + + *addr = 0x00F000F0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ((addr[0] & 0x00800080) != 0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/siemens/SCM/flash.c b/board/siemens/SCM/flash.c new file mode 100644 index 00000000000..dd7a4cc838e --- /dev/null +++ b/board/siemens/SCM/flash.c @@ -0,0 +1,488 @@ +/* + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Flash Routines for AMD devices on the TQM8260 board + * + *-------------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +#define V_ULONG(a) (*(volatile unsigned long *)( a )) +#define V_BYTE(a) (*(volatile unsigned char *)( a )) + + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + + +/*----------------------------------------------------------------------- + */ +void flash_reset (void) +{ + if (flash_info[0].flash_id != FLASH_UNKNOWN) { + V_ULONG (flash_info[0].start[0]) = 0x00F000F0; + V_ULONG (flash_info[0].start[0] + 4) = 0x00F000F0; + } +} + +/*----------------------------------------------------------------------- + */ +ulong flash_get_size (ulong baseaddr, flash_info_t * info) +{ + short i; + unsigned long flashtest_h, flashtest_l; + + /* Write auto select command sequence and test FLASH answer */ + V_ULONG (baseaddr + ((ulong) 0x0555 << 3)) = 0x00AA00AA; + V_ULONG (baseaddr + ((ulong) 0x02AA << 3)) = 0x00550055; + V_ULONG (baseaddr + ((ulong) 0x0555 << 3)) = 0x00900090; + V_ULONG (baseaddr + 4 + ((ulong) 0x0555 << 3)) = 0x00AA00AA; + V_ULONG (baseaddr + 4 + ((ulong) 0x02AA << 3)) = 0x00550055; + V_ULONG (baseaddr + 4 + ((ulong) 0x0555 << 3)) = 0x00900090; + + flashtest_h = V_ULONG (baseaddr); /* manufacturer ID */ + flashtest_l = V_ULONG (baseaddr + 4); + + switch ((int) flashtest_h) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + flashtest_h = V_ULONG (baseaddr + 8); /* device ID */ + flashtest_l = V_ULONG (baseaddr + 12); + if (flashtest_h != flashtest_l) { + info->flash_id = FLASH_UNKNOWN; + } else { + switch (flashtest_h) { + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00400000; + break; /* 4 * 1 MB = 4 MB */ + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00400000; + break; /* 4 * 1 MB = 4 MB */ + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00800000; + break; /* 4 * 2 MB = 8 MB */ + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00800000; + break; /* 4 * 2 MB = 8 MB */ + case AMD_ID_DL322T: + info->flash_id += FLASH_AMDL322T; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_DL322B: + info->flash_id += FLASH_AMDL322B; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_DL323T: + info->flash_id += FLASH_AMDL323T; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_DL323B: + info->flash_id += FLASH_AMDL323B; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_LV640U: + info->flash_id += FLASH_AM640U; + info->sector_count = 128; + info->size = 0x02000000; + break; /* 4 * 8 MB = 32 MB */ + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* no or unknown flash */ + } + } + + if (flashtest_h == AMD_ID_LV640U) { + + /* set up sector start adress table (uniform sector type) */ + for (i = 0; i < info->sector_count; i++) + info->start[i] = baseaddr + (i * 0x00040000); + + } else if (info->flash_id & FLASH_BTYPE) { + + /* set up sector start adress table (bottom sector type) */ + info->start[0] = baseaddr + 0x00000000; + info->start[1] = baseaddr + 0x00010000; + info->start[2] = baseaddr + 0x00018000; + info->start[3] = baseaddr + 0x00020000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = baseaddr + (i * 0x00040000) - 0x000C0000; + } + + } else { + + /* set up sector start adress table (top sector type) */ + i = info->sector_count - 1; + info->start[i--] = baseaddr + info->size - 0x00010000; + info->start[i--] = baseaddr + info->size - 0x00018000; + info->start[i--] = baseaddr + info->size - 0x00020000; + for (; i >= 0; i--) { + info->start[i] = baseaddr + i * 0x00040000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + if ((V_ULONG (info->start[i] + 16) & 0x00010001) || + (V_ULONG (info->start[i] + 20) & 0x00010001)) { + info->protect[i] = 1; /* D0 = 1 if protected */ + } else { + info->protect[i] = 0; + } + } + + flash_reset (); + return (info->size); +} + +/*----------------------------------------------------------------------- + */ +unsigned long flash_init (void) +{ + unsigned long size_b0 = 0; + int i; + + /* Init: no FLASHes known */ + for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here (only one bank) */ + + size_b0 = flash_get_size (CFG_FLASH0_BASE, &flash_info[0]); + if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b0 == 0) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0 >> 20); + } + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE >= CFG_FLASH0_BASE + flash_protect (FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE + CFG_MONITOR_LEN - 1, &flash_info[0]); +#endif + +#if (CFG_ENV_IS_IN_FLASH == 1) && defined(CFG_ENV_ADDR) +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif + flash_protect (FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]); +#endif + + return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t * info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: + printf ("AMD "); + break; + case FLASH_MAN_FUJ: + printf ("FUJITSU "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM800T: + printf ("29LV800T (8 M, top sector)\n"); + break; + case FLASH_AM800B: + printf ("29LV800T (8 M, bottom sector)\n"); + break; + case FLASH_AM160T: + printf ("29LV160T (16 M, top sector)\n"); + break; + case FLASH_AM160B: + printf ("29LV160B (16 M, bottom sector)\n"); + break; + case FLASH_AMDL322T: + printf ("29DL322T (32 M, top sector)\n"); + break; + case FLASH_AMDL322B: + printf ("29DL322B (32 M, bottom sector)\n"); + break; + case FLASH_AMDL323T: + printf ("29DL323T (32 M, top sector)\n"); + break; + case FLASH_AMDL323B: + printf ("29DL323B (32 M, bottom sector)\n"); + break; + case FLASH_AM640U: + printf ("29LV640D (64 M, uniform sector)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ +int flash_erase (flash_info_t * info, int s_first, int s_last) +{ + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) + prot++; + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts (); + + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00800080; + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00800080; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055; + udelay (1000); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + V_ULONG (info->start[sect]) = 0x00300030; + V_ULONG (info->start[sect] + 4) = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts (); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + while ((V_ULONG (info->start[l_sect]) & 0x00800080) != 0x00800080 || + (V_ULONG (info->start[l_sect] + 4) & 0x00800080) != 0x00800080) + { + if ((now = get_timer (start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + serial_putc ('.'); + last = now; + } + } + + DONE: + /* reset to read mode */ + flash_reset (); + + printf (" done\n"); + return 0; +} + +static int write_dword (flash_info_t *, ulong, unsigned char *); + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ + ulong dp; + static unsigned char bb[8]; + int i, l, rc, cc = cnt; + + dp = (addr & ~7); /* get lower dword aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - dp) != 0) { + for (i = 0; i < 8; i++) + bb[i] = (i < l || (i - l) >= cc) ? V_BYTE (dp + i) : *src++; + if ((rc = write_dword (info, dp, bb)) != 0) { + return (rc); + } + dp += 8; + cc -= 8 - l; + } + + /* + * handle word aligned part + */ + while (cc >= 8) { + if ((rc = write_dword (info, dp, src)) != 0) { + return (rc); + } + dp += 8; + src += 8; + cc -= 8; + } + + if (cc <= 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + for (i = 0; i < 8; i++) { + bb[i] = (i < cc) ? *src++ : V_BYTE (dp + i); + } + return (write_dword (info, dp, bb)); +} + +/*----------------------------------------------------------------------- + * Write a dword to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_dword (flash_info_t * info, ulong dest, unsigned char *pdata) +{ + ulong start, cl, ch; + int flag, i; + + for (ch = 0, i = 0; i < 4; i++) + ch = (ch << 8) + *pdata++; /* high word */ + for (cl = 0, i = 0; i < 4; i++) + cl = (cl << 8) + *pdata++; /* low word */ + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *) dest) & ch) != ch + || (*((vu_long *) (dest + 4)) & cl) != cl) { + return (2); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts (); + + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00A000A0; + V_ULONG (dest) = ch; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00A000A0; + V_ULONG (dest + 4) = cl; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts (); + + /* data polling for D7 */ + start = get_timer (0); + while (((V_ULONG (dest) & 0x00800080) != (ch & 0x00800080)) || + ((V_ULONG (dest + 4) & 0x00800080) != (cl & 0x00800080))) { + if (get_timer (start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} diff --git a/board/siemens/pcu_e/flash.c b/board/siemens/pcu_e/flash.c new file mode 100644 index 00000000000..b8c0df77432 --- /dev/null +++ b/board/siemens/pcu_e/flash.c @@ -0,0 +1,700 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*---------------------------------------------------------------------*/ +#undef DEBUG_FLASH + +#ifdef DEBUG_FLASH +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_data (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + * + * The PCU E uses an address map where flash banks are aligned top + * down, so that the "first" flash bank ends at top of memory, and + * the monitor entry point is at address (0xFFF00100). The second + * flash bank is mapped immediately below bank 0. + * + * This is NOT in conformance to the "official" memory map! + * + */ + +#define PCU_MONITOR_BASE ( (flash_info[0].start[0] + flash_info[0].size - 1) \ + - (0xFFFFFFFF - CFG_MONITOR_BASE) ) + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long base, size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + /* + * Warning: + * + * Since the PCU E memory map assigns flash banks top down, + * we swap the numbering later if both banks are equipped, + * so they look like a contiguous area of memory. + */ + DEBUGF("\n## Get flash bank 1 size @ 0x%08x\n",FLASH_BASE0_PRELIM); + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + + DEBUGF("## Get flash bank 2 size @ 0x%08x\n",FLASH_BASE6_PRELIM); + size_b1 = flash_get_size((vu_long *)FLASH_BASE6_PRELIM, &flash_info[1]); + + DEBUGF("## Prelim. Flash bank sizes: %08lx + 0x%08lx\n", size_b0, size_b1); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } + + DEBUGF ("## Before remap: " + "BR0: 0x%08x OR0: 0x%08x " + "BR6: 0x%08x OR6: 0x%08x\n", + memctl->memc_br0, memctl->memc_or0, + memctl->memc_br6, memctl->memc_or6); + + /* Remap FLASH according to real size */ + base = 0 - size_b0; + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & 0xFFFF8000); + memctl->memc_br0 = (base & BR_BA_MSK) | BR_PS_16 | BR_MS_GPCM | BR_V; + + DEBUGF("## BR0: 0x%08x OR0: 0x%08x\n", + memctl->memc_br0, memctl->memc_or0); + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)base, &flash_info[0]); + base = 0 - size_b0; + + flash_info[0].size = size_b0; + + flash_get_offsets (base, &flash_info[0]); + + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + PCU_MONITOR_BASE, + PCU_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + + if (size_b1) { + flash_info_t tmp_info; + + memctl->memc_or6 = CFG_OR_TIMING_FLASH | (-size_b1 & 0xFFFF8000); + memctl->memc_br6 = ((base - size_b1) & BR_BA_MSK) | + BR_PS_16 | BR_MS_GPCM | BR_V; + + DEBUGF("## New BR6: 0x%08x OR6: 0x%08x\n", + memctl->memc_br6, memctl->memc_or6); + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(base - size_b1), + &flash_info[1]); + base -= size_b1; + + flash_get_offsets (base, &flash_info[1]); + + flash_info[1].size = size_b1; + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[1]); +#endif + /* + * Swap bank numbers so that addresses are in ascending order + */ + tmp_info = flash_info[0]; + flash_info[0] = flash_info[1]; + flash_info[1] = tmp_info; + } else { + memctl->memc_br1 = 0; /* invalidate bank */ + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + } + + + DEBUGF("## Final Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + short n; + + if (info->flash_id == FLASH_UNKNOWN) { + return; + } + + if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_AMD) { + return; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AMDL322T: + case FLASH_AMDL323T: + case FLASH_AMDL324T: + /* set sector offsets for top boot block type */ + + base += info->size; + i = info->sector_count; + for (n=0; n<8; ++n) { /* 8 x 8k boot sectors */ + base -= 8 << 10; + --i; + info->start[i] = base; + } + while (i > 0) { /* 64k regular sectors */ + base -= 64 << 10; + --i; + info->start[i] = base; + } + return; + case FLASH_AMDL322B: + case FLASH_AMDL323B: + case FLASH_AMDL324B: + /* set sector offsets for bottom boot block type */ + for (i=0; i<8; ++i) { /* 8 x 8k boot sectors */ + info->start[i] = base; + base += 8 << 10; + } + while (base < info->size) { /* 64k regular sectors */ + info->start[i] = base; + base += 64 << 10; + ++i; + } + return; + case FLASH_AMDL640: + /* set sector offsets for dual boot block type */ + for (i=0; i<8; ++i) { /* 8 x 8k boot sectors */ + info->start[i] = base; + base += 8 << 10; + } + n = info->sector_count - 8; + while (i < n) { /* 64k regular sectors */ + info->start[i] = base; + base += 64 << 10; + ++i; + } + while (i < info->sector_count) { /* 8 x 8k boot sectors */ + info->start[i] = base; + base += 8 << 10; + ++i; + } + return; + default: + return; + } + /* NOTREACHED */ +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AMDL322B: printf ("AM29DL322B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AMDL322T: printf ("AM29DL322T (32 Mbit, top boot sector)\n"); + break; + case FLASH_AMDL323B: printf ("AM29DL323B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AMDL323T: printf ("AM29DL323T (32 Mbit, top boot sector)\n"); + break; + case FLASH_AMDL324B: printf ("AM29DL324B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AMDL324T: printf ("AM29DL324T (32 Mbit, top boot sector)\n"); + break; + case FLASH_AMDL640: printf ("AM29DL640D (64 Mbit, dual boot sector)\n"); + break; + default: printf ("Unknown Chip Type 0x%lX\n", + info->flash_id); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ushort value; + vu_short *saddr = (vu_short *)addr; + + /* Write auto select command: read Manufacturer ID */ + saddr[0x0555] = 0x00AA; + saddr[0x02AA] = 0x0055; + saddr[0x0555] = 0x0090; + + value = saddr[0]; + + DEBUGF("Manuf. ID @ 0x%08lx: 0x%04x\n", (ulong)addr, value); + + switch (value) { + case (AMD_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_AMD; + break; + case (FUJ_MANUFACT & 0xFFFF): + info->flash_id = FLASH_MAN_FUJ; + break; + default: + DEBUGF("Unknown Manufacturer ID\n"); + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = saddr[1]; /* device ID */ + + DEBUGF("Device ID @ 0x%08lx: 0x%04x\n", (ulong)(&addr[1]), value); + + switch (value) { + + case (AMD_ID_DL322T & 0xFFFF): + info->flash_id += FLASH_AMDL322T; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 8 MB */ + + case (AMD_ID_DL322B & 0xFFFF): + info->flash_id += FLASH_AMDL322B; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 8 MB */ + + case (AMD_ID_DL323T & 0xFFFF): + info->flash_id += FLASH_AMDL323T; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 8 MB */ + + case (AMD_ID_DL323B & 0xFFFF): + info->flash_id += FLASH_AMDL323B; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 8 MB */ + + case (AMD_ID_DL324T & 0xFFFF): + info->flash_id += FLASH_AMDL324T; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 8 MB */ + + case (AMD_ID_DL324B & 0xFFFF): + info->flash_id += FLASH_AMDL324B; + info->sector_count = 71; + info->size = 0x00400000; + break; /* => 8 MB */ + case (AMD_ID_DL640 & 0xFFFF): + info->flash_id += FLASH_AMDL640; + info->sector_count = 142; + info->size = 0x00800000; + break; + default: + DEBUGF("Unknown Device ID\n"); + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + + } + + flash_get_offsets ((ulong)addr, info); + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { +#if 0 + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + saddr = (vu_short *)(info->start[i]); + info->protect[i] = saddr[2] & 1; +#else + info->protect[i] =0; +#endif + } + + if (info->sector_count > CFG_MAX_FLASH_SECT) { + printf ("** ERROR: sector count %d > max (%d) **\n", + info->sector_count, CFG_MAX_FLASH_SECT); + info->sector_count = CFG_MAX_FLASH_SECT; + } + + saddr = (vu_short *)info->start[0]; + *saddr = 0x00F0; /* restore read mode */ + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_short *addr = (vu_short*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; + addr[0x0555] = 0x0080; + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_short*)(info->start[sect]); + addr[0] = 0x0030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_short*)(info->start[l_sect]); + while ((addr[0] & 0x0080) != 0x0080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (vu_short *)info->start[0]; + addr[0] = 0x00F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +#define FLASH_WIDTH 2 /* flash bus width in bytes */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~(FLASH_WIDTH-1)); /* get lower FLASH_WIDTH aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<FLASH_WIDTH && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<FLASH_WIDTH; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += FLASH_WIDTH; + } + + /* + * handle FLASH_WIDTH aligned part + */ + while (cnt >= FLASH_WIDTH) { + data = 0; + for (i=0; i<FLASH_WIDTH; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_data(info, wp, data)) != 0) { + return (rc); + } + wp += FLASH_WIDTH; + cnt -= FLASH_WIDTH; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<FLASH_WIDTH && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<FLASH_WIDTH; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_data(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, ulong dest, ulong data) +{ + vu_short *addr = (vu_short*)(info->start[0]); + vu_short *sdest = (vu_short *)dest; + ushort sdata = (ushort)data; + ushort sval; + ulong start, passed; + int flag, rc; + + /* Check if Flash is (sufficiently) erased */ + if ((*sdest & sdata) != sdata) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA; + addr[0x02AA] = 0x0055; + addr[0x0555] = 0x00A0; + +#ifdef WORKAROUND_FOR_BROKEN_HARDWARE + /* work around the timeout bugs */ + udelay(20); +#endif + + *sdest = sdata; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + rc = 0; + /* data polling for D7 */ + start = get_timer (0); + + for (passed=0; passed < CFG_FLASH_WRITE_TOUT; passed=get_timer(start)) { + + sval = *sdest; + + if ((sval & 0x0080) == (sdata & 0x0080)) + break; + + if ((sval & 0x0020) == 0) /* DQ5: Timeout? */ + continue; + + sval = *sdest; + + if ((sval & 0x0080) != (sdata & 0x0080)) + rc = 1; + + break; + } + + if (rc) { + DEBUGF ("Program cycle failed @ addr 0x%08lX: val %04X data %04X\n", + dest, sval, sdata); + } + + if (passed >= CFG_FLASH_WRITE_TOUT) { + DEBUGF ("Timeout @ addr 0x%08lX: val %04X data %04X\n", + dest, sval, sdata); + rc = 1; + } + + /* reset to read mode */ + addr = (vu_short *)info->start[0]; + addr[0] = 0x00F0; /* reset bank */ + + return (rc); +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/sixnet/flash.c b/board/sixnet/flash.c new file mode 100644 index 00000000000..c201875ba0e --- /dev/null +++ b/board/sixnet/flash.c @@ -0,0 +1,746 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/* NOTE - CONFIG_FLASH_16BIT means the CPU interface is 16-bit, it + * has nothing to do with the flash chip being 8-bit or 16-bit. + */ +#ifdef CONFIG_FLASH_16BIT +typedef unsigned short FLASH_PORT_WIDTH; +typedef volatile unsigned short FLASH_PORT_WIDTHV; +#define FLASH_ID_MASK 0xFFFF +#else +typedef unsigned long FLASH_PORT_WIDTH; +typedef volatile unsigned long FLASH_PORT_WIDTHV; +#define FLASH_ID_MASK 0xFFFFFFFF +#endif + +#define FPW FLASH_PORT_WIDTH +#define FPWV FLASH_PORT_WIDTHV + +#define ORMASK(size) ((-size) & OR_AM_MSK) + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size(FPWV *addr, flash_info_t *info); +static void flash_reset(flash_info_t *info); +static int write_word_intel(flash_info_t *info, FPWV *dest, FPW data); +static int write_word_amd(flash_info_t *info, FPWV *dest, FPW data); +static void flash_get_offsets(ulong base, flash_info_t *info); +#ifdef CFG_FLASH_PROTECTION +static void flash_sync_real_protect(flash_info_t *info); +#endif + +/*----------------------------------------------------------------------- + * flash_init() + * + * sets up flash_info and returns size of FLASH (bytes) + */ +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b; + int i; + + /* Init: no FLASHes known */ + for (i=0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + size_b = flash_get_size((FPW *)CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size_b; + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx\n",size_b); + } + + /* Remap FLASH according to real size, so only at proper address */ + memctl->memc_or0 = (memctl->memc_or0 & ~OR_AM_MSK) | ORMASK(size_b); + + /* Do this again (was done already in flast_get_size), just + * in case we move it when remap the FLASH. + */ + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + +#ifdef CFG_FLASH_PROTECTION + /* read the hardware protection status (if any) into the + * protection array in flash_info. + */ + flash_sync_real_protect(&flash_info[0]); +#endif + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + + return (size_b); +} + +/*----------------------------------------------------------------------- + */ +static void flash_reset(flash_info_t *info) +{ + FPWV *base = (FPWV *)(info->start[0]); + + /* Put FLASH back in read mode */ + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) + *base = (FPW)0x00FF00FF; /* Intel Read Mode */ + else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD) + *base = (FPW)0x00F000F0; /* AMD Read Mode */ +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL + && (info->flash_id & FLASH_BTYPE)) { + int bootsect_size; /* number of bytes/boot sector */ + int sect_size; /* number of bytes/regular sector */ + + bootsect_size = 0x00002000 * (sizeof(FPW)/2); + sect_size = 0x00010000 * (sizeof(FPW)/2); + + /* set sector offsets for bottom boot block type */ + for (i = 0; i < 8; ++i) { + info->start[i] = base + (i * bootsect_size); + } + for (i = 8; i < info->sector_count; i++) { + info->start[i] = base + ((i - 7) * sect_size); + } + } + else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD + && (info->flash_id & FLASH_TYPEMASK) == FLASH_AM640U) { + + int sect_size; /* number of bytes/sector */ + + sect_size = 0x00010000 * (sizeof(FPW)/2); + + /* set up sector start address table (uniform sector type) */ + for( i = 0; i < info->sector_count; i++ ) + info->start[i] = base + (i * sect_size); + } +} + +/*----------------------------------------------------------------------- + */ + +void flash_print_info (flash_info_t *info) +{ + int i; + uchar *boottype; + uchar *bootletter; + uchar *fmt; + uchar botbootletter[] = "B"; + uchar topbootletter[] = "T"; + uchar botboottype[] = "bottom boot sector"; + uchar topboottype[] = "top boot sector"; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_SST: printf ("SST "); break; + case FLASH_MAN_STM: printf ("STM "); break; + case FLASH_MAN_INTEL: printf ("INTEL "); break; + default: printf ("Unknown Vendor "); break; + } + + /* check for top or bottom boot, if it applies */ + if (info->flash_id & FLASH_BTYPE) { + boottype = botboottype; + bootletter = botbootletter; + } + else { + boottype = topboottype; + bootletter = topbootletter; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM640U: + fmt = "29LV641D (64 Mbit, uniform sectors)\n"; + break; + case FLASH_28F800C3B: + case FLASH_28F800C3T: + fmt = "28F800C3%s (8 Mbit, %s)\n"; + break; + case FLASH_INTEL800B: + case FLASH_INTEL800T: + fmt = "28F800B3%s (8 Mbit, %s)\n"; + break; + case FLASH_28F160C3B: + case FLASH_28F160C3T: + fmt = "28F160C3%s (16 Mbit, %s)\n"; + break; + case FLASH_INTEL160B: + case FLASH_INTEL160T: + fmt = "28F160B3%s (16 Mbit, %s)\n"; + break; + case FLASH_28F320C3B: + case FLASH_28F320C3T: + fmt = "28F320C3%s (32 Mbit, %s)\n"; + break; + case FLASH_INTEL320B: + case FLASH_INTEL320T: + fmt = "28F320B3%s (32 Mbit, %s)\n"; + break; + case FLASH_28F640C3B: + case FLASH_28F640C3T: + fmt = "28F640C3%s (64 Mbit, %s)\n"; + break; + case FLASH_INTEL640B: + case FLASH_INTEL640T: + fmt = "28F640B3%s (64 Mbit, %s)\n"; + break; + default: + fmt = "Unknown Chip Type\n"; + break; + } + + printf (fmt, bootletter, boottype); + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, + info->sector_count); + + printf (" Sector Start Addresses:"); + + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) { + printf ("\n "); + } + + printf (" %08lX%s", info->start[i], + info->protect[i] ? " (RO)" : " "); + } + + printf ("\n"); +} + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +ulong flash_get_size (FPWV *addr, flash_info_t *info) +{ + /* Write auto select command: read Manufacturer ID */ + + /* Write auto select command sequence and test FLASH answer */ + addr[0x0555] = (FPW)0x00AA00AA; /* for AMD, Intel ignores this */ + addr[0x02AA] = (FPW)0x00550055; /* for AMD, Intel ignores this */ + addr[0x0555] = (FPW)0x00900090; /* selects Intel or AMD */ + + /* The manufacturer codes are only 1 byte, so just use 1 byte. + * This works for any bus width and any FLASH device width. + */ + switch (addr[0] & 0xff) { + + case (uchar)AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + + case (uchar)INTEL_MANUFACT: + info->flash_id = FLASH_MAN_INTEL; + break; + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + break; + } + + /* Check 16 bits or 32 bits of ID so work on 32 or 16 bit bus. */ + if (info->flash_id != FLASH_UNKNOWN) switch (addr[1]) { + + case (FPW)AMD_ID_LV640U: /* 29LV640 and 29LV641 have same ID */ + info->flash_id += FLASH_AM640U; + info->sector_count = 128; + info->size = 0x00800000 * (sizeof(FPW)/2); + break; /* => 8 or 16 MB */ + + case (FPW)INTEL_ID_28F800C3B: + info->flash_id += FLASH_28F800C3B; + info->sector_count = 23; + info->size = 0x00100000 * (sizeof(FPW)/2); + break; /* => 1 or 2 MB */ + + case (FPW)INTEL_ID_28F800B3B: + info->flash_id += FLASH_INTEL800B; + info->sector_count = 23; + info->size = 0x00100000 * (sizeof(FPW)/2); + break; /* => 1 or 2 MB */ + + case (FPW)INTEL_ID_28F160C3B: + info->flash_id += FLASH_28F160C3B; + info->sector_count = 39; + info->size = 0x00200000 * (sizeof(FPW)/2); + break; /* => 2 or 4 MB */ + + case (FPW)INTEL_ID_28F160B3B: + info->flash_id += FLASH_INTEL160B; + info->sector_count = 39; + info->size = 0x00200000 * (sizeof(FPW)/2); + break; /* => 2 or 4 MB */ + + case (FPW)INTEL_ID_28F320C3B: + info->flash_id += FLASH_28F320C3B; + info->sector_count = 71; + info->size = 0x00400000 * (sizeof(FPW)/2); + break; /* => 4 or 8 MB */ + + case (FPW)INTEL_ID_28F320B3B: + info->flash_id += FLASH_INTEL320B; + info->sector_count = 71; + info->size = 0x00400000 * (sizeof(FPW)/2); + break; /* => 4 or 8 MB */ + + case (FPW)INTEL_ID_28F640C3B: + info->flash_id += FLASH_28F640C3B; + info->sector_count = 135; + info->size = 0x00800000 * (sizeof(FPW)/2); + break; /* => 8 or 16 MB */ + + case (FPW)INTEL_ID_28F640B3B: + info->flash_id += FLASH_INTEL640B; + info->sector_count = 135; + info->size = 0x00800000 * (sizeof(FPW)/2); + break; /* => 8 or 16 MB */ + + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* => no or unknown flash */ + } + + flash_get_offsets((ulong)addr, info); + + /* Put FLASH back in read mode */ + flash_reset(info); + + return (info->size); +} + +#ifdef CFG_FLASH_PROTECTION +/*----------------------------------------------------------------------- + */ + +static void flash_sync_real_protect(flash_info_t *info) +{ + FPWV *addr = (FPWV *)(info->start[0]); + FPWV *sect; + int i; + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F800C3B: + case FLASH_28F800C3T: + case FLASH_28F160C3B: + case FLASH_28F160C3T: + case FLASH_28F320C3B: + case FLASH_28F320C3T: + case FLASH_28F640C3B: + case FLASH_28F640C3T: + /* check for protected sectors */ + *addr = (FPW)0x00900090; + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02. + * D0 = 1 for each device if protected. + * If at least one device is protected the sector is marked + * protected, but mixed protected and unprotected devices + * within a sector should never happen. + */ + sect = (FPWV *)(info->start[i]); + info->protect[i] = (sect[2] & (FPW)(0x00010001)) ? 1 : 0; + } + + /* Put FLASH back in read mode */ + flash_reset(info); + break; + + case FLASH_AM640U: + default: + /* no hardware protect that we support */ + break; + } +} +#endif + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + FPWV *addr; + int flag, prot, sect; + int intel = (info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL; + ulong start, now, last; + int rcode = 0; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_INTEL800B: + case FLASH_INTEL160B: + case FLASH_INTEL320B: + case FLASH_INTEL640B: + case FLASH_28F800C3B: + case FLASH_28F160C3B: + case FLASH_28F320C3B: + case FLASH_28F640C3B: + case FLASH_AM640U: + break; + case FLASH_UNKNOWN: + default: + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + start = get_timer(0); + last = start; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last && rcode == 0; sect++) { + + if (info->protect[sect] != 0) /* protected, skip it */ + continue; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr = (FPWV *)(info->start[sect]); + if (intel) { + *addr = (FPW)0x00500050; /* clear status register */ + *addr = (FPW)0x00200020; /* erase setup */ + *addr = (FPW)0x00D000D0; /* erase confirm */ + } + else { + /* must be AMD style if not Intel */ + FPWV *base; /* first address in bank */ + + base = (FPWV *)(info->start[0]); + base[0x0555] = (FPW)0x00AA00AA; /* unlock */ + base[0x02AA] = (FPW)0x00550055; /* unlock */ + base[0x0555] = (FPW)0x00800080; /* erase mode */ + base[0x0555] = (FPW)0x00AA00AA; /* unlock */ + base[0x02AA] = (FPW)0x00550055; /* unlock */ + *addr = (FPW)0x00300030; /* erase sector */ + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 50us for AMD, 80us for Intel. + * Let's wait 1 ms. + */ + udelay (1000); + + while ((*addr & (FPW)0x00800080) != (FPW)0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + + if (intel) { + /* suspend erase */ + *addr = (FPW)0x00B000B0; + } + + flash_reset(info); /* reset to read mode */ + rcode = 1; /* failed */ + break; + } + + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + + flash_reset(info); /* reset to read mode */ + } + + printf (" done\n"); + return rcode; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + FPW data = 0; /* 16 or 32 bit word, matches flash bus width on MPC8XX */ + int bytes; /* number of bytes to program in current word */ + int left; /* number of bytes left to program */ + int i, res; + + for (left = cnt, res = 0; + left > 0 && res == 0; + addr += sizeof(data), left -= sizeof(data) - bytes) { + + bytes = addr & (sizeof(data) - 1); + addr &= ~(sizeof(data) - 1); + + /* combine source and destination data so can program + * an entire word of 16 or 32 bits + */ + for (i = 0; i < sizeof(data); i++) { + data <<= 8; + if (i < bytes || i - bytes >= left ) + data += *((uchar *)addr + i); + else + data += *src++; + } + + /* write one word to the flash */ + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: + res = write_word_amd(info, (FPWV *)addr, data); + break; + case FLASH_MAN_INTEL: + res = write_word_intel(info, (FPWV *)addr, data); + break; + default: + /* unknown flash type, error! */ + printf ("missing or unknown FLASH type\n"); + res = 1; /* not really a timeout, but gives error */ + break; + } + } + + return (res); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash for AMD FLASH + * A word is 16 or 32 bits, whichever the bus width of the flash bank + * (not an individual chip) is. + * + * returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word_amd (flash_info_t *info, FPWV *dest, FPW data) +{ + ulong start; + int flag; + int res = 0; /* result, assume success */ + FPWV *base; /* first address in flash bank */ + + /* Check if Flash is (sufficiently) erased */ + if ((*dest & data) != data) { + return (2); + } + + + base = (FPWV *)(info->start[0]); + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + base[0x0555] = (FPW)0x00AA00AA; /* unlock */ + base[0x02AA] = (FPW)0x00550055; /* unlock */ + base[0x0555] = (FPW)0x00A000A0; /* selects program mode */ + + *dest = data; /* start programming the data */ + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + + /* data polling for D7 */ + while (res == 0 && (*dest & (FPW)0x00800080) != (data & (FPW)0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + *dest = (FPW)0x00F000F0; /* reset bank */ + res = 1; + } + } + + return (res); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash for Intel FLASH + * A word is 16 or 32 bits, whichever the bus width of the flash bank + * (not an individual chip) is. + * + * returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word_intel (flash_info_t *info, FPWV *dest, FPW data) +{ + ulong start; + int flag; + int res = 0; /* result, assume success */ + + /* Check if Flash is (sufficiently) erased */ + if ((*dest & data) != data) { + return (2); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + *dest = (FPW)0x00500050; /* clear status register */ + *dest = (FPW)0x00FF00FF; /* make sure in read mode */ + *dest = (FPW)0x00400040; /* program setup */ + + *dest = data; /* start programming the data */ + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + start = get_timer (0); + + while (res == 0 && (*dest & (FPW)0x00800080) != (FPW)0x00800080) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + *dest = (FPW)0x00B000B0; /* Suspend program */ + res = 1; + } + } + + if (res == 0 && (*dest & (FPW)0x00100010)) + res = 1; /* write failed, time out error is close enough */ + + *dest = (FPW)0x00500050; /* clear status register */ + *dest = (FPW)0x00FF00FF; /* make sure in read mode */ + + return (res); +} + +#ifdef CFG_FLASH_PROTECTION +/*----------------------------------------------------------------------- + */ +int flash_real_protect (flash_info_t * info, long sector, int prot) +{ + int rcode = 0; /* assume success */ + FPWV *addr; /* address of sector */ + FPW value; + + addr = (FPWV *) (info->start[sector]); + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_28F800C3B: + case FLASH_28F800C3T: + case FLASH_28F160C3B: + case FLASH_28F160C3T: + case FLASH_28F320C3B: + case FLASH_28F320C3T: + case FLASH_28F640C3B: + case FLASH_28F640C3T: + flash_reset (info); /* make sure in read mode */ + *addr = (FPW) 0x00600060L; /* lock command setup */ + if (prot) + *addr = (FPW) 0x00010001L; /* lock sector */ + else + *addr = (FPW) 0x00D000D0L; /* unlock sector */ + flash_reset (info); /* reset to read mode */ + + /* now see if it really is locked/unlocked as requested */ + *addr = (FPW) 0x00900090; + /* read sector protection at sector address, (A7 .. A0) = 0x02. + * D0 = 1 for each device if protected. + * If at least one device is protected the sector is marked + * protected, but return failure. Mixed protected and + * unprotected devices within a sector should never happen. + */ + value = addr[2] & (FPW) 0x00010001; + if (value == 0) + info->protect[sector] = 0; + else if (value == (FPW) 0x00010001) + info->protect[sector] = 1; + else { + /* error, mixed protected and unprotected */ + rcode = 1; + info->protect[sector] = 1; + } + if (info->protect[sector] != prot) + rcode = 1; /* failed to protect/unprotect as requested */ + + /* reload all protection bits from hardware for now */ + flash_sync_real_protect (info); + break; + + case FLASH_AM640U: + default: + /* no hardware protect that we support */ + info->protect[sector] = prot; + break; + } + + return rcode; +} +#endif diff --git a/board/spd8xx/flash.c b/board/spd8xx/flash.c new file mode 100644 index 00000000000..8c0bb4f5679 --- /dev/null +++ b/board/spd8xx/flash.c @@ -0,0 +1,57 @@ +/* + * (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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + /* All Speech Design board memory (DRAM and EPROM) initialisation is + done in dram_init(). + The caller of ths function here expects the total size and will hang, + if we give here back 0. So we return the EPROM size. */ + + return (1024 * 1024); /* 1 MB */ +} + +/*----------------------------------------------------------------------- + */ + +void flash_print_info (flash_info_t *info) +{ + printf("no FLASH memory in MPC823TS board\n"); + return; +} + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + return 1; +} + +/*----------------------------------------------------------------------- + */ diff --git a/board/tqm8260/flash.c b/board/tqm8260/flash.c new file mode 100644 index 00000000000..dd7a4cc838e --- /dev/null +++ b/board/tqm8260/flash.c @@ -0,0 +1,488 @@ +/* + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Flash Routines for AMD devices on the TQM8260 board + * + *-------------------------------------------------------------------- + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +#define V_ULONG(a) (*(volatile unsigned long *)( a )) +#define V_BYTE(a) (*(volatile unsigned char *)( a )) + + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + + +/*----------------------------------------------------------------------- + */ +void flash_reset (void) +{ + if (flash_info[0].flash_id != FLASH_UNKNOWN) { + V_ULONG (flash_info[0].start[0]) = 0x00F000F0; + V_ULONG (flash_info[0].start[0] + 4) = 0x00F000F0; + } +} + +/*----------------------------------------------------------------------- + */ +ulong flash_get_size (ulong baseaddr, flash_info_t * info) +{ + short i; + unsigned long flashtest_h, flashtest_l; + + /* Write auto select command sequence and test FLASH answer */ + V_ULONG (baseaddr + ((ulong) 0x0555 << 3)) = 0x00AA00AA; + V_ULONG (baseaddr + ((ulong) 0x02AA << 3)) = 0x00550055; + V_ULONG (baseaddr + ((ulong) 0x0555 << 3)) = 0x00900090; + V_ULONG (baseaddr + 4 + ((ulong) 0x0555 << 3)) = 0x00AA00AA; + V_ULONG (baseaddr + 4 + ((ulong) 0x02AA << 3)) = 0x00550055; + V_ULONG (baseaddr + 4 + ((ulong) 0x0555 << 3)) = 0x00900090; + + flashtest_h = V_ULONG (baseaddr); /* manufacturer ID */ + flashtest_l = V_ULONG (baseaddr + 4); + + switch ((int) flashtest_h) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + flashtest_h = V_ULONG (baseaddr + 8); /* device ID */ + flashtest_l = V_ULONG (baseaddr + 12); + if (flashtest_h != flashtest_l) { + info->flash_id = FLASH_UNKNOWN; + } else { + switch (flashtest_h) { + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00400000; + break; /* 4 * 1 MB = 4 MB */ + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00400000; + break; /* 4 * 1 MB = 4 MB */ + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00800000; + break; /* 4 * 2 MB = 8 MB */ + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00800000; + break; /* 4 * 2 MB = 8 MB */ + case AMD_ID_DL322T: + info->flash_id += FLASH_AMDL322T; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_DL322B: + info->flash_id += FLASH_AMDL322B; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_DL323T: + info->flash_id += FLASH_AMDL323T; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_DL323B: + info->flash_id += FLASH_AMDL323B; + info->sector_count = 71; + info->size = 0x01000000; + break; /* 4 * 4 MB = 16 MB */ + case AMD_ID_LV640U: + info->flash_id += FLASH_AM640U; + info->sector_count = 128; + info->size = 0x02000000; + break; /* 4 * 8 MB = 32 MB */ + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* no or unknown flash */ + } + } + + if (flashtest_h == AMD_ID_LV640U) { + + /* set up sector start adress table (uniform sector type) */ + for (i = 0; i < info->sector_count; i++) + info->start[i] = baseaddr + (i * 0x00040000); + + } else if (info->flash_id & FLASH_BTYPE) { + + /* set up sector start adress table (bottom sector type) */ + info->start[0] = baseaddr + 0x00000000; + info->start[1] = baseaddr + 0x00010000; + info->start[2] = baseaddr + 0x00018000; + info->start[3] = baseaddr + 0x00020000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = baseaddr + (i * 0x00040000) - 0x000C0000; + } + + } else { + + /* set up sector start adress table (top sector type) */ + i = info->sector_count - 1; + info->start[i--] = baseaddr + info->size - 0x00010000; + info->start[i--] = baseaddr + info->size - 0x00018000; + info->start[i--] = baseaddr + info->size - 0x00020000; + for (; i >= 0; i--) { + info->start[i] = baseaddr + i * 0x00040000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + if ((V_ULONG (info->start[i] + 16) & 0x00010001) || + (V_ULONG (info->start[i] + 20) & 0x00010001)) { + info->protect[i] = 1; /* D0 = 1 if protected */ + } else { + info->protect[i] = 0; + } + } + + flash_reset (); + return (info->size); +} + +/*----------------------------------------------------------------------- + */ +unsigned long flash_init (void) +{ + unsigned long size_b0 = 0; + int i; + + /* Init: no FLASHes known */ + for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here (only one bank) */ + + size_b0 = flash_get_size (CFG_FLASH0_BASE, &flash_info[0]); + if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b0 == 0) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0 >> 20); + } + + /* + * protect monitor and environment sectors + */ + +#if CFG_MONITOR_BASE >= CFG_FLASH0_BASE + flash_protect (FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE + CFG_MONITOR_LEN - 1, &flash_info[0]); +#endif + +#if (CFG_ENV_IS_IN_FLASH == 1) && defined(CFG_ENV_ADDR) +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif + flash_protect (FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]); +#endif + + return (size_b0); +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t * info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: + printf ("AMD "); + break; + case FLASH_MAN_FUJ: + printf ("FUJITSU "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM800T: + printf ("29LV800T (8 M, top sector)\n"); + break; + case FLASH_AM800B: + printf ("29LV800T (8 M, bottom sector)\n"); + break; + case FLASH_AM160T: + printf ("29LV160T (16 M, top sector)\n"); + break; + case FLASH_AM160B: + printf ("29LV160B (16 M, bottom sector)\n"); + break; + case FLASH_AMDL322T: + printf ("29DL322T (32 M, top sector)\n"); + break; + case FLASH_AMDL322B: + printf ("29DL322B (32 M, bottom sector)\n"); + break; + case FLASH_AMDL323T: + printf ("29DL323T (32 M, top sector)\n"); + break; + case FLASH_AMDL323B: + printf ("29DL323B (32 M, bottom sector)\n"); + break; + case FLASH_AM640U: + printf ("29LV640D (64 M, uniform sector)\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); + return; +} + +/*----------------------------------------------------------------------- + */ +int flash_erase (flash_info_t * info, int s_first, int s_last) +{ + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) + prot++; + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts (); + + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00800080; + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00800080; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055; + udelay (1000); + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + V_ULONG (info->start[sect]) = 0x00300030; + V_ULONG (info->start[sect] + 4) = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts (); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + while ((V_ULONG (info->start[l_sect]) & 0x00800080) != 0x00800080 || + (V_ULONG (info->start[l_sect] + 4) & 0x00800080) != 0x00800080) + { + if ((now = get_timer (start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + serial_putc ('.'); + last = now; + } + } + + DONE: + /* reset to read mode */ + flash_reset (); + + printf (" done\n"); + return 0; +} + +static int write_dword (flash_info_t *, ulong, unsigned char *); + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ + ulong dp; + static unsigned char bb[8]; + int i, l, rc, cc = cnt; + + dp = (addr & ~7); /* get lower dword aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - dp) != 0) { + for (i = 0; i < 8; i++) + bb[i] = (i < l || (i - l) >= cc) ? V_BYTE (dp + i) : *src++; + if ((rc = write_dword (info, dp, bb)) != 0) { + return (rc); + } + dp += 8; + cc -= 8 - l; + } + + /* + * handle word aligned part + */ + while (cc >= 8) { + if ((rc = write_dword (info, dp, src)) != 0) { + return (rc); + } + dp += 8; + src += 8; + cc -= 8; + } + + if (cc <= 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + for (i = 0; i < 8; i++) { + bb[i] = (i < cc) ? *src++ : V_BYTE (dp + i); + } + return (write_dword (info, dp, bb)); +} + +/*----------------------------------------------------------------------- + * Write a dword to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_dword (flash_info_t * info, ulong dest, unsigned char *pdata) +{ + ulong start, cl, ch; + int flag, i; + + for (ch = 0, i = 0; i < 4; i++) + ch = (ch << 8) + *pdata++; /* high word */ + for (cl = 0, i = 0; i < 4; i++) + cl = (cl << 8) + *pdata++; /* low word */ + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *) dest) & ch) != ch + || (*((vu_long *) (dest + 4)) & cl) != cl) { + return (2); + } + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts (); + + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00A000A0; + V_ULONG (dest) = ch; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA; + V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055; + V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00A000A0; + V_ULONG (dest + 4) = cl; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts (); + + /* data polling for D7 */ + start = get_timer (0); + while (((V_ULONG (dest) & 0x00800080) != (ch & 0x00800080)) || + ((V_ULONG (dest + 4) & 0x00800080) != (cl & 0x00800080))) { + if (get_timer (start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} diff --git a/board/w7o/fsboot.c b/board/w7o/fsboot.c new file mode 100644 index 00000000000..800583d4a58 --- /dev/null +++ b/board/w7o/fsboot.c @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2001 + * Wave 7 Optics, Inc. + * + * 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 + */ + +#include <common.h> +#include <config.h> +#include <command.h> +#include <cmd_elf.h> + +/* + * FIXME: Add code to test image and it's header. + */ +static int +image_check(ulong addr) +{ + return valid_elf_image(addr); +} + +void +init_fsboot(void) +{ + char *envp; + ulong loadaddr; + ulong testaddr; + ulong alt_loadaddr; + char buf[9]; + + /* + * Get test image address + */ + if ((envp = getenv("testaddr")) != NULL) + testaddr = simple_strtoul(envp, NULL, 16); + else + testaddr = -1; + + /* + * Are we going to test boot and image? + */ + if ((testaddr != -1) && image_check(testaddr)) { + + /* Set alt_loadaddr */ + alt_loadaddr = testaddr; + sprintf(buf, "%lX", alt_loadaddr); + setenv("alt_loadaddr", buf); + + /* Clear test_addr */ + setenv("testaddr", NULL); + + /* + * Save current environment with alt_loadaddr, + * and cleared testaddr. + */ + saveenv(); + + /* + * Setup temporary loadaddr to alt_loadaddr + * XXX - DO NOT SAVE ENVIRONMENT! + */ + loadaddr = alt_loadaddr; + sprintf(buf, "%lX", loadaddr); + setenv("loadaddr", buf); + + } else { /* Normal boot */ + setenv("alt_loadaddr", NULL); /* Clear alt_loadaddr */ + setenv("testaddr", NULL); /* Clear testaddr */ + saveenv(); + } + + return; +} + diff --git a/board/w7o/watchdog.c b/board/w7o/watchdog.c new file mode 100644 index 00000000000..3fca5d3f353 --- /dev/null +++ b/board/w7o/watchdog.c @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * 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 + */ + +/* + * W7O board level hardware watchdog. + */ +#include <common.h> +#include <config.h> + +#ifdef CONFIG_HW_WATCHDOG +#include <watchdog.h> + +void hw_watchdog_reset(void) +{ + volatile ushort *hwd = (ushort *)(CFG_W7O_EBC_PB7CR & 0xfff00000); + + /* + * Read the LMG's hwd register and toggle the + * watchdog bit to reset it. On the LMC, just + * reading it is enough, but toggling the bit + * doen't hurt either. + */ + *hwd = *hwd ^ 0x8000; + +} /* hw_watchdog_reset() */ + +#endif /* CONFIG_HW_WATCHDOG */ + diff --git a/board/westel/amx860/flash.c b/board/westel/amx860/flash.c new file mode 100644 index 00000000000..28238c1ca00 --- /dev/null +++ b/board/westel/amx860/flash.c @@ -0,0 +1,637 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <mpc8xx.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +#if defined(CFG_ENV_IS_IN_FLASH) +# ifndef CFG_ENV_ADDR +# define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) +# endif +# ifndef CFG_ENV_SIZE +# define CFG_ENV_SIZE CFG_ENV_SECT_SIZE +# endif +# ifndef CFG_ENV_SECT_SIZE +# define CFG_ENV_SECT_SIZE CFG_ENV_SIZE +# endif +#endif + +/*---------------------------------------------------------------------*/ +#undef DEBUG_FLASH + +#ifdef DEBUG_FLASH +#define DEBUGF(fmt,args...) printf(fmt ,##args) +#else +#define DEBUGF(fmt,args...) +#endif +/*---------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size (vu_long *addr, flash_info_t *info); +static int write_word (flash_info_t *info, ulong dest, ulong data); +static void flash_get_offsets (ulong base, flash_info_t *info); + +/*----------------------------------------------------------------------- + */ + +unsigned long flash_init (void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + volatile memctl8xx_t *memctl = &immap->im_memctl; + unsigned long size_b0, size_b1; + int i; + + /* Init: no FLASHes known */ + for (i=0; i<CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + } + + /* Static FLASH Bank configuration here - FIXME XXX */ + + DEBUGF("\n## Get flash bank 1 size @ 0x%08x\n",FLASH_BASE0_PRELIM); + + size_b0 = flash_get_size((vu_long *)FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) { + printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", + size_b0, size_b0<<20); + } + +#if defined(FLASH_BASE1_PRELIM) && (FLASH_BASE1_PRELIM != 0) + DEBUGF("## Get flash bank 2 size @ 0x%08x\n",FLASH_BASE1_PRELIM); + + size_b1 = flash_get_size((vu_long *)FLASH_BASE1_PRELIM, &flash_info[1]); + + if (size_b1 > size_b0) { + printf ("## ERROR: " + "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", + size_b1, size_b1<<20, + size_b0, size_b0<<20 + ); + flash_info[0].flash_id = FLASH_UNKNOWN; + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[0].sector_count = -1; + flash_info[1].sector_count = -1; + flash_info[0].size = 0; + flash_info[1].size = 0; + return (0); + } +#else + size_b1 = 0; +#endif /* FLASH_BASE1_PRELIM */ + + DEBUGF("## Prelim. Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + DEBUGF ("## Before remap: " + "BR0: 0x%08x OR0: 0x%08x " + "BR1: 0x%08x OR1: 0x%08x\n", + memctl->memc_br0, memctl->memc_or0, + memctl->memc_br1, memctl->memc_or1); + + /* Remap FLASH according to real size */ + memctl->memc_or0 = CFG_OR_TIMING_FLASH | (-size_b0 & OR_AM_MSK); + memctl->memc_br0 = (CFG_FLASH_BASE & BR_BA_MSK) | BR_MS_GPCM | BR_V; + + DEBUGF("## BR0: 0x%08x OR0: 0x%08x\n", + memctl->memc_br0, memctl->memc_or0); + + /* Re-do sizing to get full correct info */ + size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]); + + flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); + + flash_info[0].size = size_b0; + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[0]); +#endif + +#ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[0]); +#endif + + if (size_b1) { + memctl->memc_or1 = CFG_OR_TIMING_FLASH | (-size_b1 & OR_AM_MSK); + memctl->memc_br1 = ((CFG_FLASH_BASE + size_b0) & BR_BA_MSK) | + BR_MS_GPCM | BR_V; + + DEBUGF("## BR1: 0x%08x OR1: 0x%08x\n", + memctl->memc_br1, memctl->memc_or1); + + /* Re-do sizing to get full correct info */ + size_b1 = flash_get_size((vu_long *)(CFG_FLASH_BASE + size_b0), + &flash_info[1]); + + flash_info[1].size = size_b1; + + flash_get_offsets (CFG_FLASH_BASE + size_b0, &flash_info[1]); + +# if CFG_MONITOR_BASE >= CFG_FLASH_BASE + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE+CFG_MONITOR_LEN-1, + &flash_info[1]); +# endif + +# ifdef CFG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1, + &flash_info[1]); +#endif + } else { +#ifndef CONFIG_AMX_RAM_EXT + memctl->memc_br1 = 0; /* invalidate bank */ + memctl->memc_or1 = 0; /* invalidate bank */ +#endif + + DEBUGF("## DISABLE BR1: 0x%08x OR1: 0x%08x\n", + memctl->memc_br1, memctl->memc_or1); + + flash_info[1].flash_id = FLASH_UNKNOWN; + flash_info[1].sector_count = -1; + flash_info[1].size = 0; + } + + DEBUGF("## Final Flash bank sizes: %08lx + 0x%08lx\n",size_b0,size_b1); + + return (size_b0 + size_b1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_get_offsets (ulong base, flash_info_t *info) +{ + int i; + + /* set up sector start address table */ + if ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM040) { + /* set sector offsets for uniform sector type */ + for (i = 0; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00040000); + } + } else if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_AMD: printf ("AMD "); break; + case FLASH_MAN_FUJ: printf ("FUJITSU "); break; + case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break; + default: printf ("Unknown Vendor "); break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_AM040: printf ("29F040 or 29LV040 (4 Mbit, uniform sectors)\n"); + break; + case FLASH_AM400B: printf ("AM29LV400B (4 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM400T: printf ("AM29LV400T (4 Mbit, top boot sector)\n"); + break; + case FLASH_AM800B: printf ("AM29LV800B (8 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM800T: printf ("AM29LV800T (8 Mbit, top boot sector)\n"); + break; + case FLASH_AM160B: printf ("AM29LV160B (16 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM160T: printf ("AM29LV160T (16 Mbit, top boot sector)\n"); + break; + case FLASH_AM320B: printf ("AM29LV320B (32 Mbit, bottom boot sect)\n"); + break; + case FLASH_AM320T: printf ("AM29LV320T (32 Mbit, top boot sector)\n"); + break; + default: printf ("Unknown Chip Type\n"); + break; + } + + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf (" Sector Start Addresses:"); + for (i=0; i<info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n "); + printf (" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " " + ); + } + printf ("\n"); +} + +/*----------------------------------------------------------------------- + */ + + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ + +static ulong flash_get_size (vu_long *addr, flash_info_t *info) +{ + short i; + ulong value; + ulong base = (ulong)addr; + + /* Write auto select command: read Manufacturer ID */ + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00900090; + + value = addr[0]; + + DEBUGF("Manuf. ID @ 0x%08lx: 0x%08lx\n", (ulong)addr, value); + + switch (value) { + case AMD_MANUFACT: + info->flash_id = FLASH_MAN_AMD; + break; + case FUJ_MANUFACT: + info->flash_id = FLASH_MAN_FUJ; + break; + default: + info->flash_id = FLASH_UNKNOWN; + info->sector_count = 0; + info->size = 0; + return (0); /* no or unknown flash */ + } + + value = addr[1]; /* device ID */ + + DEBUGF("Device ID @ 0x%08lx: 0x%08lx\n", (ulong)(&addr[1]), value); + + switch (value) { + case AMD_ID_F040B: + info->flash_id += FLASH_AM040; + info->sector_count = 8; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV400T: + info->flash_id += FLASH_AM400T; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV400B: + info->flash_id += FLASH_AM400B; + info->sector_count = 11; + info->size = 0x00100000; + break; /* => 1 MB */ + + case AMD_ID_LV800T: + info->flash_id += FLASH_AM800T; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV800B: + info->flash_id += FLASH_AM800B; + info->sector_count = 19; + info->size = 0x00200000; + break; /* => 2 MB */ + + case AMD_ID_LV160T: + info->flash_id += FLASH_AM160T; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ + + case AMD_ID_LV160B: + info->flash_id += FLASH_AM160B; + info->sector_count = 35; + info->size = 0x00400000; + break; /* => 4 MB */ +#if 0 /* enable when device IDs are available */ + case AMD_ID_LV320T: + info->flash_id += FLASH_AM320T; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ + + case AMD_ID_LV320B: + info->flash_id += FLASH_AM320B; + info->sector_count = 67; + info->size = 0x00800000; + break; /* => 8 MB */ +#endif + default: + info->flash_id = FLASH_UNKNOWN; + return (0); /* => no or unknown flash */ + } + + /* set up sector start address table */ + if (info->flash_id & FLASH_BTYPE) { + /* set sector offsets for bottom boot block type */ + info->start[0] = base + 0x00000000; + info->start[1] = base + 0x00008000; + info->start[2] = base + 0x0000C000; + info->start[3] = base + 0x00010000; + for (i = 4; i < info->sector_count; i++) { + info->start[i] = base + (i * 0x00020000) - 0x00060000; + } + } else { + /* set sector offsets for top boot block type */ + i = info->sector_count - 1; + info->start[i--] = base + info->size - 0x00008000; + info->start[i--] = base + info->size - 0x0000C000; + info->start[i--] = base + info->size - 0x00010000; + for (; i >= 0; i--) { + info->start[i] = base + i * 0x00020000; + } + } + + /* check for protected sectors */ + for (i = 0; i < info->sector_count; i++) { + /* read sector protection at sector address, (A7 .. A0) = 0x02 */ + /* D0 = 1 if protected */ + addr = (volatile unsigned long *)(info->start[i]); + info->protect[i] = addr[2] & 1; + } + + /* + * Prevent writes to uninitialized FLASH. + */ + if (info->flash_id != FLASH_UNKNOWN) { + addr = (volatile unsigned long *)info->start[0]; + + *addr = 0x00F000F0; /* reset bank */ + } + + return (info->size); +} + + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ + vu_long *addr = (vu_long*)(info->start[0]); + int flag, prot, sect, l_sect; + ulong start, now, last; + + if ((s_first < 0) || (s_first > s_last)) { + if (info->flash_id == FLASH_UNKNOWN) { + printf ("- missing\n"); + } else { + printf ("- no sectors to erase\n"); + } + return 1; + } + + if ((info->flash_id == FLASH_UNKNOWN) || + (info->flash_id > FLASH_AMD_COMP)) { + printf ("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return 1; + } + + prot = 0; + for (sect=s_first; sect<=s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + printf ("\n"); + } + + l_sect = -1; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00800080; + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + + /* Start erase on unprotected sectors */ + for (sect = s_first; sect<=s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + addr = (vu_long*)(info->start[sect]); + addr[0] = 0x00300030; + l_sect = sect; + } + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* wait at least 80us - let's wait 1 ms */ + udelay (1000); + + /* + * We wait for the last triggered sector + */ + if (l_sect < 0) + goto DONE; + + start = get_timer (0); + last = start; + addr = (vu_long*)(info->start[l_sect]); + while ((addr[0] & 0x00800080) != 0x00800080) { + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return 1; + } + /* show that we're waiting */ + if ((now - last) > 1000) { /* every second */ + putc ('.'); + last = now; + } + } + +DONE: + /* reset to read mode */ + addr = (volatile unsigned long *)info->start[0]; + addr[0] = 0x00F000F0; /* reset bank */ + + printf (" done\n"); + return 0; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ + +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong cp, wp, data; + int i, l, rc; + + wp = (addr & ~3); /* get lower word aligned address */ + + /* + * handle unaligned start bytes + */ + if ((l = addr - wp) != 0) { + data = 0; + for (i=0, cp=wp; i<l; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + for (; i<4 && cnt>0; ++i) { + data = (data << 8) | *src++; + --cnt; + ++cp; + } + for (; cnt==0 && i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + } + + /* + * handle word aligned part + */ + while (cnt >= 4) { + data = 0; + for (i=0; i<4; ++i) { + data = (data << 8) | *src++; + } + if ((rc = write_word(info, wp, data)) != 0) { + return (rc); + } + wp += 4; + cnt -= 4; + } + + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + data = 0; + for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { + data = (data << 8) | *src++; + --cnt; + } + for (; i<4; ++i, ++cp) { + data = (data << 8) | (*(uchar *)cp); + } + + return (write_word(info, wp, data)); +} + +/*----------------------------------------------------------------------- + * Write a word to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_word (flash_info_t *info, ulong dest, ulong data) +{ + vu_long *addr = (vu_long*)(info->start[0]); + ulong start; + int flag; + + /* Check if Flash is (sufficiently) erased */ + if ((*((vu_long *)dest) & data) != data) { + return (2); + } + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts(); + + addr[0x0555] = 0x00AA00AA; + addr[0x02AA] = 0x00550055; + addr[0x0555] = 0x00A000A0; + + *((vu_long *)dest) = data; + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts(); + + /* data polling for D7 */ + start = get_timer (0); + while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + return (1); + } + } + return (0); +} + +/*----------------------------------------------------------------------- + */ diff --git a/common/docecc.c b/common/docecc.c new file mode 100644 index 00000000000..09e8233d81a --- /dev/null +++ b/common/docecc.c @@ -0,0 +1,519 @@ +/* + * ECC algorithm for M-systems disk on chip. We use the excellent Reed + * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the + * GNU GPL License. The rest is simply to convert the disk on chip + * syndrom into a standard syndom. + * + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * + * $Id: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $ + * + * 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 <config.h> +#include <common.h> +#include <malloc.h> + +#include <linux/mtd/doc2000.h> + +#undef ECC_DEBUG +#undef PSYCHO_DEBUG + +#if (CONFIG_COMMANDS & CFG_CMD_DOC) + +#define min(x,y) ((x)<(y)?(x):(y)) + +/* need to undef it (from asm/termbits.h) */ +#undef B0 + +#define MM 10 /* Symbol size in bits */ +#define KK (1023-4) /* Number of data symbols per block */ +#define B0 510 /* First root of generator polynomial, alpha form */ +#define PRIM 1 /* power of alpha used to generate roots of generator poly */ +#define NN ((1 << MM) - 1) + +typedef unsigned short dtype; + +/* 1+x^3+x^10 */ +static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 }; + +/* This defines the type used to store an element of the Galois Field + * used by the code. Make sure this is something larger than a char if + * if anything larger than GF(256) is used. + * + * Note: unsigned char will work up to GF(256) but int seems to run + * faster on the Pentium. + */ +typedef int gf; + +/* No legal value in index form represents zero, so + * we need a special value for this purpose + */ +#define A0 (NN) + +/* Compute x % NN, where NN is 2**MM - 1, + * without a slow divide + */ +static inline gf +modnn(int x) +{ + while (x >= NN) { + x -= NN; + x = (x >> MM) + (x & NN); + } + return x; +} + +#define CLEAR(a,n) {\ +int ci;\ +for(ci=(n)-1;ci >=0;ci--)\ +(a)[ci] = 0;\ +} + +#define COPY(a,b,n) {\ +int ci;\ +for(ci=(n)-1;ci >=0;ci--)\ +(a)[ci] = (b)[ci];\ +} + +#define COPYDOWN(a,b,n) {\ +int ci;\ +for(ci=(n)-1;ci >=0;ci--)\ +(a)[ci] = (b)[ci];\ +} + +#define Ldec 1 + +/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m] + lookup tables: index->polynomial form alpha_to[] contains j=alpha**i; + polynomial form -> index form index_of[j=alpha**i] = i + alpha=2 is the primitive element of GF(2**m) + HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows: + Let @ represent the primitive element commonly called "alpha" that + is the root of the primitive polynomial p(x). Then in GF(2^m), for any + 0 <= i <= 2^m-2, + @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) + where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation + of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for + example the polynomial representation of @^5 would be given by the binary + representation of the integer "alpha_to[5]". + Similarily, index_of[] can be used as follows: + As above, let @ represent the primitive element of GF(2^m) that is + the root of the primitive polynomial p(x). In order to find the power + of @ (alpha) that has the polynomial representation + a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) + we consider the integer "i" whose binary representation with a(0) being LSB + and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry + "index_of[i]". Now, @^index_of[i] is that element whose polynomial + representation is (a(0),a(1),a(2),...,a(m-1)). + NOTE: + The element alpha_to[2^m-1] = 0 always signifying that the + representation of "@^infinity" = 0 is (0,0,0,...,0). + Similarily, the element index_of[0] = A0 always signifying + that the power of alpha which has the polynomial representation + (0,0,...,0) is "infinity". + +*/ + +static void +generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1]) +{ + register int i, mask; + + mask = 1; + Alpha_to[MM] = 0; + for (i = 0; i < MM; i++) { + Alpha_to[i] = mask; + Index_of[Alpha_to[i]] = i; + /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */ + if (Pp[i] != 0) + Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */ + mask <<= 1; /* single left-shift */ + } + Index_of[Alpha_to[MM]] = MM; + /* + * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by + * poly-repr of @^i shifted left one-bit and accounting for any @^MM + * term that may occur when poly-repr of @^i is shifted. + */ + mask >>= 1; + for (i = MM + 1; i < NN; i++) { + if (Alpha_to[i - 1] >= mask) + Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1); + else + Alpha_to[i] = Alpha_to[i - 1] << 1; + Index_of[Alpha_to[i]] = i; + } + Index_of[0] = A0; + Alpha_to[NN] = 0; +} + +/* + * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content + * of the feedback shift register after having processed the data and + * the ECC. + * + * Return number of symbols corrected, or -1 if codeword is illegal + * or uncorrectable. If eras_pos is non-null, the detected error locations + * are written back. NOTE! This array must be at least NN-KK elements long. + * The corrected data are written in eras_val[]. They must be xor with the data + * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] . + * + * First "no_eras" erasures are declared by the calling program. Then, the + * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2). + * If the number of channel errors is not greater than "t_after_eras" the + * transmitted codeword will be recovered. Details of algorithm can be found + * in R. Blahut's "Theory ... of Error-Correcting Codes". + + * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure + * will result. The decoder *could* check for this condition, but it would involve + * extra time on every decoding operation. + * */ +static int +eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1], + gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK], + int no_eras) +{ + int deg_lambda, el, deg_omega; + int i, j, r,k; + gf u,q,tmp,num1,num2,den,discr_r; + gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly + * and syndrome poly */ + gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1]; + gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK]; + int syn_error, count; + + syn_error = 0; + for(i=0;i<NN-KK;i++) + syn_error |= bb[i]; + + if (!syn_error) { + /* if remainder is zero, data[] is a codeword and there are no + * errors to correct. So return data[] unmodified + */ + count = 0; + goto finish; + } + + for(i=1;i<=NN-KK;i++){ + s[i] = bb[0]; + } + for(j=1;j<NN-KK;j++){ + if(bb[j] == 0) + continue; + tmp = Index_of[bb[j]]; + + for(i=1;i<=NN-KK;i++) + s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)]; + } + + /* undo the feedback register implicit multiplication and convert + syndromes to index form */ + + for(i=1;i<=NN-KK;i++) { + tmp = Index_of[s[i]]; + if (tmp != A0) + tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM); + s[i] = tmp; + } + + CLEAR(&lambda[1],NN-KK); + lambda[0] = 1; + + if (no_eras > 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])]; + for (i = 1; i < no_eras; i++) { + u = modnn(PRIM*eras_pos[i]); + for (j = i+1; j > 0; j--) { + tmp = Index_of[lambda[j - 1]]; + if(tmp != A0) + lambda[j] ^= Alpha_to[modnn(u + tmp)]; + } + } +#ifdef ECC_DEBUG + /* Test code that verifies the erasure locator polynomial just constructed + Needed only for decoder debugging. */ + + /* find roots of the erasure location polynomial */ + for(i=1;i<=no_eras;i++) + reg[i] = Index_of[lambda[i]]; + count = 0; + for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) { + q = 1; + for (j = 1; j <= no_eras; j++) + if (reg[j] != A0) { + reg[j] = modnn(reg[j] + j); + q ^= Alpha_to[reg[j]]; + } + if (q != 0) + continue; + /* store root and error location number indices */ + root[count] = i; + loc[count] = k; + count++; + } + if (count != no_eras) { + printf("\n lambda(x) is WRONG\n"); + count = -1; + goto finish; + } +#ifdef PSYCHO_DEBUG + printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + for (i = 0; i < count; i++) + printf("%d ", loc[i]); + printf("\n"); +#endif +#endif + } + for(i=0;i<NN-KK+1;i++) + b[i] = Index_of[lambda[i]]; + + /* + * Begin Berlekamp-Massey algorithm to determine error+erasure + * locator polynomial + */ + r = no_eras; + el = no_eras; + while (++r <= NN-KK) { /* r is the step number */ + /* Compute discrepancy at the r-th step in poly-form */ + discr_r = 0; + for (i = 0; i < r; i++){ + if ((lambda[i] != 0) && (s[r - i] != A0)) { + discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])]; + } + } + discr_r = Index_of[discr_r]; /* Index form */ + if (discr_r == A0) { + /* 2 lines below: B(x) <-- x*B(x) */ + COPYDOWN(&b[1],b,NN-KK); + b[0] = A0; + } else { + /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ + t[0] = lambda[0]; + for (i = 0 ; i < NN-KK; i++) { + if(b[i] != A0) + t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])]; + else + t[i+1] = lambda[i+1]; + } + if (2 * el <= r + no_eras - 1) { + el = r + no_eras - el; + /* + * 2 lines below: B(x) <-- inv(discr_r) * + * lambda(x) + */ + for (i = 0; i <= NN-KK; i++) + b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN); + } else { + /* 2 lines below: B(x) <-- x*B(x) */ + COPYDOWN(&b[1],b,NN-KK); + b[0] = A0; + } + COPY(lambda,t,NN-KK+1); + } + } + + /* Convert lambda to index form and compute deg(lambda(x)) */ + deg_lambda = 0; + for(i=0;i<NN-KK+1;i++){ + lambda[i] = Index_of[lambda[i]]; + if(lambda[i] != A0) + deg_lambda = i; + } + /* + * Find roots of the error+erasure locator polynomial by Chien + * Search + */ + COPY(®[1],&lambda[1],NN-KK); + count = 0; /* Number of roots of lambda(x) */ + for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) { + q = 1; + for (j = deg_lambda; j > 0; j--){ + if (reg[j] != A0) { + reg[j] = modnn(reg[j] + j); + q ^= Alpha_to[reg[j]]; + } + } + if (q != 0) + continue; + /* store root (index-form) and error location number */ + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if(++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**(NN-KK)). in index form. Also find deg(omega). + */ + deg_omega = 0; + for (i = 0; i < NN-KK;i++){ + tmp = 0; + j = (deg_lambda < i) ? deg_lambda : i; + for(;j >= 0; j--){ + if ((s[i + 1 - j] != A0) && (lambda[j] != A0)) + tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])]; + } + if(tmp != 0) + deg_omega = i; + omega[i] = Index_of[tmp]; + } + omega[NN-KK] = A0; + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count-1; j >=0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != A0) + num1 ^= Alpha_to[modnn(omega[i] + i * root[j])]; + } + num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) { + if(lambda[i+1] != A0) + den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])]; + } + if (den == 0) { +#ifdef ECC_DEBUG + printf("\n ERROR: denominator = 0\n"); +#endif + /* Convert to dual- basis */ + count = -1; + goto finish; + } + /* Apply error to data */ + if (num1 != 0) { + eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])]; + } else { + eras_val[j] = 0; + } + } + finish: + for(i=0;i<count;i++) + eras_pos[i] = loc[i]; + return count; +} + +/***************************************************************************/ +/* The DOC specific code begins here */ + +#define SECTOR_SIZE 512 +/* The sector bytes are packed into NB_DATA MM bits words */ +#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM) + +/* + * Correct the errors in 'sector[]' by using 'ecc1[]' which is the + * content of the feedback shift register applyied to the sector and + * the ECC. Return the number of errors corrected (and correct them in + * sector), or -1 if error + */ +int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6]) +{ + int parity, i, nb_errors; + gf bb[NN - KK + 1]; + gf error_val[NN-KK]; + int error_pos[NN-KK], pos, bitpos, index, val; + dtype *Alpha_to, *Index_of; + + /* init log and exp tables here to save memory. However, it is slower */ + Alpha_to = malloc((NN + 1) * sizeof(dtype)); + if (!Alpha_to) + return -1; + + Index_of = malloc((NN + 1) * sizeof(dtype)); + if (!Index_of) { + free(Alpha_to); + return -1; + } + + generate_gf(Alpha_to, Index_of); + + parity = ecc1[1]; + + bb[0] = (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8); + bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6); + bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4); + bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2); + + nb_errors = eras_dec_rs(Alpha_to, Index_of, bb, + error_val, error_pos, 0); + if (nb_errors <= 0) + goto the_end; + + /* correct the errors */ + for(i=0;i<nb_errors;i++) { + pos = error_pos[i]; + if (pos >= NB_DATA && pos < KK) { + nb_errors = -1; + goto the_end; + } + if (pos < NB_DATA) { + /* extract bit position (MSB first) */ + pos = 10 * (NB_DATA - 1 - pos) - 6; + /* now correct the following 10 bits. At most two bytes + can be modified since pos is even */ + index = (pos >> 3) ^ 1; + bitpos = pos & 7; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = error_val[i] >> (2 + bitpos); + parity ^= val; + if (index < SECTOR_SIZE) + sector[index] ^= val; + } + index = ((pos >> 3) + 1) ^ 1; + bitpos = (bitpos + 10) & 7; + if (bitpos == 0) + bitpos = 8; + if ((index >= 0 && index < SECTOR_SIZE) || + index == (SECTOR_SIZE + 1)) { + val = error_val[i] << (8 - bitpos); + parity ^= val; + if (index < SECTOR_SIZE) + sector[index] ^= val; + } + } + } + + /* use parity to test extra errors */ + if ((parity & 0xff) != 0) + nb_errors = -1; + + the_end: + free(Alpha_to); + free(Index_of); + return nb_errors; +} + +#endif /* (CONFIG_COMMANDS & CFG_CMD_DOC) */ diff --git a/common/flash.c b/common/flash.c new file mode 100644 index 00000000000..fa8942b92ce --- /dev/null +++ b/common/flash.c @@ -0,0 +1,219 @@ +/* + * (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 + */ + +#include <common.h> +#include <flash.h> + +#if !defined(CFG_NO_FLASH) + +extern flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ + +/*----------------------------------------------------------------------- + * Set protection status for monitor sectors + * + * The monitor is always located in the _first_ Flash bank. + * If necessary you have to map the second bank at lower addresses. + */ +void +flash_protect (int flag, ulong from, ulong to, flash_info_t *info) +{ + ulong b_end = info->start[0] + info->size - 1; /* bank end address */ + short s_end = info->sector_count - 1; /* index of last sector */ + int i; + + /* Do nothing if input data is bad. */ + if (info->sector_count == 0 || info->size == 0 || to < from) { + return; + } + + /* There is nothing to do if we have no data about the flash + * or the protect range and flash range don't overlap. + */ + if (info->flash_id == FLASH_UNKNOWN || + to < info->start[0] || from > b_end) { + return; + } + + for (i=0; i<info->sector_count; ++i) { + ulong end; /* last address in current sect */ + + end = (i == s_end) ? b_end : info->start[i + 1] - 1; + + /* Update protection if any part of the sector + * is in the specified range. + */ + if (from <= end && to >= info->start[i]) { + if (flag & FLAG_PROTECT_CLEAR) { +#if defined(CFG_FLASH_PROTECTION) + flash_real_protect(info, i, 0); +#else + info->protect[i] = 0; +#endif /* CFG_FLASH_PROTECTION */ + } + else if (flag & FLAG_PROTECT_SET) { +#if defined(CFG_FLASH_PROTECTION) + flash_real_protect(info, i, 1); +#else + info->protect[i] = 1; +#endif /* CFG_FLASH_PROTECTION */ + } + } + } +} + +/*----------------------------------------------------------------------- + */ + +flash_info_t * +addr2info (ulong addr) +{ +#ifndef CONFIG_SPD823TS + flash_info_t *info; + int i; + + for (i=0, info=&flash_info[0]; i<CFG_MAX_FLASH_BANKS; ++i, ++info) { + if (info->flash_id != FLASH_UNKNOWN && + addr >= info->start[0] && + /* WARNING - The '- 1' is needed if the flash + * is at the end of the address space, since + * info->start[0] + info->size wraps back to 0. + * Please don't change this unless you understand this. + */ + addr <= info->start[0] + info->size - 1) { + return (info); + } + } +#endif /* CONFIG_SPD823TS */ + + return (NULL); +} + +/*----------------------------------------------------------------------- + * Copy memory to flash. + * Make sure all target addresses are within Flash bounds, + * and no protected sectors are hit. + * Returns: + * ERR_OK 0 - OK + * ERR_TIMOUT 1 - write timeout + * ERR_NOT_ERASED 2 - Flash not erased + * ERR_PROTECTED 4 - target range includes protected sectors + * ERR_INVAL 8 - target address not in Flash memory + * ERR_ALIGN 16 - target address not aligned on boundary + * (only some targets require alignment) + */ +int +flash_write (uchar *src, ulong addr, ulong cnt) +{ +#ifdef CONFIG_SPD823TS + return (ERR_TIMOUT); /* any other error codes are possible as well */ +#else + int i; + ulong end = addr + cnt - 1; + flash_info_t *info_first = addr2info (addr); + flash_info_t *info_last = addr2info (end ); + flash_info_t *info; + + if (cnt == 0) { + return (ERR_OK); + } + + if (!info_first || !info_last) { + return (ERR_INVAL); + } + + for (info = info_first; info <= info_last; ++info) { + ulong b_end = info->start[0] + info->size; /* bank end addr */ + short s_end = info->sector_count - 1; + for (i=0; i<info->sector_count; ++i) { + ulong e_addr = (i == s_end) ? b_end : info->start[i + 1]; + + if ((end >= info->start[i]) && (addr < e_addr) && + (info->protect[i] != 0) ) { + return (ERR_PROTECTED); + } + } + } + + /* finally write data to flash */ + for (info = info_first; info <= info_last && cnt>0; ++info) { + ulong len; + + len = info->start[0] + info->size - addr; + if (len > cnt) + len = cnt; + if ((i = write_buff(info, src, addr, len)) != 0) { + return (i); + } + cnt -= len; + addr += len; + src += len; + } + return (ERR_OK); +#endif /* CONFIG_SPD823TS */ +} + +/*----------------------------------------------------------------------- + */ + +void flash_perror (int err) +{ + switch (err) { + case ERR_OK: + break; + case ERR_TIMOUT: + puts ("Timeout writing to Flash\n"); + break; + case ERR_NOT_ERASED: + puts ("Flash not Erased\n"); + break; + case ERR_PROTECTED: + puts ("Can't write to protected Flash sectors\n"); + break; + case ERR_INVAL: + puts ("Outside available Flash\n"); + break; + case ERR_ALIGN: + puts ("Start and/or end address not on sector boundary\n"); + break; + case ERR_UNKNOWN_FLASH_VENDOR: + puts ("Unknown Vendor of Flash\n"); + break; + case ERR_UNKNOWN_FLASH_TYPE: + puts ("Unknown Type of Flash\n"); + break; + case ERR_PROG_ERROR: + puts ("General Flash Programming Error\n"); + break; + default: + printf ("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err); + break; + } +} + +/*----------------------------------------------------------------------- + */ +#endif /* !CFG_NO_FLASH */ diff --git a/common/lists.c b/common/lists.c new file mode 100644 index 00000000000..3f117b568ee --- /dev/null +++ b/common/lists.c @@ -0,0 +1,734 @@ +#include <common.h> +#include <malloc.h> +#include <lists.h> + +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#define CAT4CHARS(a,b,c,d) ((a<<24) | (b<<16) | (c<<8) | d) + +/* increase list size by 10% every time it is full */ +#define kDefaultAllocationPercentIncrease 10 + +/* always increase list size by 4 items when it is full */ +#define kDefaultAllocationminNumItemsIncrease 4 + +/* + * how many items to expand the list by when it becomes full + * = current listSize (in items) + (hiword percent of list size) + loword + */ +#define NUMITEMSPERALLOC(list) MAX(((*list)->listSize * \ + ((*list)->percentIncrease + 100)) / 100, \ + (*list)->minNumItemsIncrease ) + +#define ITEMPTR(list,item) &(((char *)&(*list)->itemList)[(*(list))->itemSize * (item)]) + +#define LIST_SIGNATURE CAT4CHARS('L', 'I', 'S', 'T'); + +#define calloc(size,num) malloc(size*num) + +/********************************************************************/ + +Handle NewHandle (unsigned int numBytes) +{ + void *memPtr; + HandleRecord *hanPtr; + + memPtr = calloc (numBytes, 1); + hanPtr = (HandleRecord *) calloc (sizeof (HandleRecord), 1); + if (hanPtr && (memPtr || numBytes == 0)) { + hanPtr->ptr = memPtr; + hanPtr->size = numBytes; + return (Handle) hanPtr; + } else { + free (memPtr); + free (hanPtr); + return NULL; + } +} +/********************************************************************/ + +void DisposeHandle (Handle handle) +{ + if (handle) { + free (*handle); + free ((void *) handle); + } +} +/********************************************************************/ + +unsigned int GetHandleSize (Handle handle) +{ + return ((HandleRecord *) handle)->size; +} +/********************************************************************/ + +int SetHandleSize (Handle handle, unsigned int newSize) +{ + HandleRecord *hanRecPtr = (HandleRecord *) handle; + void *newPtr, *oldPtr; + unsigned int oldSize; + + + oldPtr = hanRecPtr->ptr; + oldSize = hanRecPtr->size; + + if (oldSize == newSize) + return 1; + + if (oldPtr == NULL) { + newPtr = malloc (newSize); + } else { + newPtr = realloc (oldPtr, newSize); + } + if (newPtr || (newSize == 0)) { + hanRecPtr->ptr = newPtr; + hanRecPtr->size = newSize; + if (newSize > oldSize) + memset ((char *) newPtr + oldSize, 0, newSize - oldSize); + return 1; + } else + return 0; +} + +#ifdef CFG_ALL_LIST_FUNCTIONS + +/* Used to compare list elements by their raw data contents */ +static int ListMemBlockCmp (void *a, void *b, int size) +{ + return memcmp (a, b, size); +} + +/***************************************************************************/ + +/* + * Binary search numElements of size elementSize in array for a match + * to the. item. Return the index of the element that matches + * (0 - numElements - 1). If no match is found return the -i-1 where + * i is the index (0 - numElements) where the item should be placed. + * (*theCmp)(a,b) should return <0 if a<b, 0 if a==b, >0 if a>b. + * + * This function is like the C-Library function bsearch() except that + * this function returns the index where the item should be placed if + * it is not found. + */ +int BinSearch ( void *array, int numElements, int elementSize, + void *itemPtr, CompareFunction compareFunction) +{ + int low, high, mid, cmp; + void *arrayItemPtr; + + for (low = 0, high = numElements - 1, mid = 0, cmp = -1; low <= high;) { + mid = (low + high) >> 1; + + arrayItemPtr = (void *) (((char *) array) + (mid * elementSize)); + cmp = compareFunction + ? compareFunction (itemPtr, arrayItemPtr) + : ListMemBlockCmp (itemPtr, arrayItemPtr, elementSize); + if (cmp == 0) { + return mid; + } else if (cmp < 0) { + high = mid - 1; + } else { + low = mid + 1; + } + } + if (cmp > 0) + mid++; + + return -mid - 1; +} + +#endif /* CFG_ALL_LIST_FUNCTIONS */ + +/*******************************************************************************/ + +/* + * If numNewItems == 0 then expand the list by the number of items + * indicated by its allocation policy. + * If numNewItems > 0 then expand the list by exactly the number of + * items indicated. + * If numNewItems < 0 then expand the list by the absolute value of + * numNewItems plus the number of items indicated by its allocation + * policy. + * Returns 1 for success, 0 if out of memory +*/ +static int ExpandListSpace (list_t list, int numNewItems) +{ + if (numNewItems == 0) { + numNewItems = NUMITEMSPERALLOC (list); + } else if (numNewItems < 0) { + numNewItems = (-numNewItems) + NUMITEMSPERALLOC (list); + } + + if (SetHandleSize ((Handle) list, + sizeof (ListStruct) + + ((*list)->listSize + + numNewItems) * (*list)->itemSize)) { + (*list)->listSize += numNewItems; + return 1; + } else { + return 0; + } +} + +/*******************************/ + +#ifdef CFG_ALL_LIST_FUNCTIONS + +/* + * This function reallocate the list, minus any currently unused + * portion of its allotted memory. + */ +void ListCompact (list_t list) +{ + + if (!SetHandleSize ((Handle) list, + sizeof (ListStruct) + + (*list)->numItems * (*list)->itemSize)) { + return; + } + + (*list)->listSize = (*list)->numItems; +} + +#endif /* CFG_ALL_LIST_FUNCTIONS */ + +/*******************************/ + +list_t ListCreate (int elementSize) +{ + list_t list; + + list = (list_t) (NewHandle (sizeof (ListStruct))); /* create empty list */ + if (list) { + (*list)->signature = LIST_SIGNATURE; + (*list)->numItems = 0; + (*list)->listSize = 0; + (*list)->itemSize = elementSize; + (*list)->percentIncrease = kDefaultAllocationPercentIncrease; + (*list)->minNumItemsIncrease = + kDefaultAllocationminNumItemsIncrease; + } + + return list; +} + +/*******************************/ + +void ListSetAllocationPolicy (list_t list, int minItemsPerAlloc, + int percentIncreasePerAlloc) +{ + (*list)->percentIncrease = percentIncreasePerAlloc; + (*list)->minNumItemsIncrease = minItemsPerAlloc; +} + +/*******************************/ + +void ListDispose (list_t list) +{ + DisposeHandle ((Handle) list); +} +/*******************************/ + +#ifdef CFG_ALL_LIST_FUNCTIONS + +void ListDisposePtrList (list_t list) +{ + int index; + int numItems; + + if (list) { + numItems = ListNumItems (list); + + for (index = 1; index <= numItems; index++) + free (*(void **) ListGetPtrToItem (list, index)); + + ListDispose (list); + } +} + +/*******************************/ + +/* + * keeps memory, resets the number of items to 0 + */ +void ListClear (list_t list) +{ + if (!list) + return; + (*list)->numItems = 0; +} + +/*******************************/ + +/* + * copy is only as large as necessary + */ +list_t ListCopy (list_t originalList) +{ + list_t tempList = NULL; + int numItems; + + if (!originalList) + return NULL; + + tempList = ListCreate ((*originalList)->itemSize); + if (tempList) { + numItems = ListNumItems (originalList); + + if (!SetHandleSize ((Handle) tempList, + sizeof (ListStruct) + + numItems * (*tempList)->itemSize)) { + ListDispose (tempList); + return NULL; + } + + (*tempList)->numItems = (*originalList)->numItems; + (*tempList)->listSize = (*originalList)->numItems; + (*tempList)->itemSize = (*originalList)->itemSize; + (*tempList)->percentIncrease = (*originalList)->percentIncrease; + (*tempList)->minNumItemsIncrease = + (*originalList)->minNumItemsIncrease; + + memcpy (ITEMPTR (tempList, 0), ITEMPTR (originalList, 0), + numItems * (*tempList)->itemSize); + } + + return tempList; +} + +/********************************/ + +/* + * list1 = list1 + list2 + */ +int ListAppend (list_t list1, list_t list2) +{ + int numItemsL1, numItemsL2; + + if (!list2) + return 1; + + if (!list1) + return 0; + if ((*list1)->itemSize != (*list2)->itemSize) + return 0; + + numItemsL1 = ListNumItems (list1); + numItemsL2 = ListNumItems (list2); + + if (numItemsL2 == 0) + return 1; + + if (!SetHandleSize ((Handle) list1, + sizeof (ListStruct) + (numItemsL1 + numItemsL2) * + (*list1)->itemSize)) { + return 0; + } + + (*list1)->numItems = numItemsL1 + numItemsL2; + (*list1)->listSize = numItemsL1 + numItemsL2; + + memmove (ITEMPTR (list1, numItemsL1), + ITEMPTR (list2, 0), + numItemsL2 * (*list2)->itemSize); + + return 1; +} + +#endif /* CFG_ALL_LIST_FUNCTIONS */ + +/*******************************/ + +/* + * returns 1 if the item is inserted, returns 0 if out of memory or + * bad arguments were passed. + */ +int ListInsertItem (list_t list, void *ptrToItem, int itemPosition) +{ + return ListInsertItems (list, ptrToItem, itemPosition, 1); +} + +/*******************************/ + +int ListInsertItems (list_t list, void *ptrToItems, int firstItemPosition, + int numItemsToInsert) +{ + int numItems = (*list)->numItems; + + if (firstItemPosition == numItems + 1) + firstItemPosition = LIST_END; + else if (firstItemPosition > numItems) + return 0; + + if ((*list)->numItems >= (*list)->listSize) { + if (!ExpandListSpace (list, -numItemsToInsert)) + return 0; + } + + if (firstItemPosition == LIST_START) { + if (numItems == 0) { + /* special case for empty list */ + firstItemPosition = LIST_END; + } else { + firstItemPosition = 1; + } + } + + if (firstItemPosition == LIST_END) { /* add at the end of the list */ + if (ptrToItems) + memcpy (ITEMPTR (list, numItems), ptrToItems, + (*list)->itemSize * numItemsToInsert); + else + memset (ITEMPTR (list, numItems), 0, + (*list)->itemSize * numItemsToInsert); + + (*list)->numItems += numItemsToInsert; + } else { /* move part of list up to make room for new item */ + memmove (ITEMPTR (list, firstItemPosition - 1 + numItemsToInsert), + ITEMPTR (list, firstItemPosition - 1), + (numItems + 1 - firstItemPosition) * (*list)->itemSize); + + if (ptrToItems) + memmove (ITEMPTR (list, firstItemPosition - 1), ptrToItems, + (*list)->itemSize * numItemsToInsert); + else + memset (ITEMPTR (list, firstItemPosition - 1), 0, + (*list)->itemSize * numItemsToInsert); + + (*list)->numItems += numItemsToInsert; + } + + return 1; +} + +#ifdef CFG_ALL_LIST_FUNCTIONS + +/*******************************/ + +int ListEqual (list_t list1, list_t list2) +{ + if (list1 == list2) + return 1; + + if (list1 == NULL || list2 == NULL) + return 0; + + if ((*list1)->itemSize == (*list1)->itemSize) { + if ((*list1)->numItems == (*list2)->numItems) { + return (memcmp (ITEMPTR (list1, 0), ITEMPTR (list2, 0), + (*list1)->itemSize * (*list1)->numItems) == 0); + } + } + + return 0; +} + +/*******************************/ + +/* + * The item pointed to by ptrToItem is copied over the current item + * at itemPosition + */ +void ListReplaceItem (list_t list, void *ptrToItem, int itemPosition) +{ + ListReplaceItems (list, ptrToItem, itemPosition, 1); +} + +/*******************************/ + +/* + * The item pointed to by ptrToItems is copied over the current item + * at itemPosition + */ +void ListReplaceItems ( list_t list, void *ptrToItems, + int firstItemPosition, int numItemsToReplace) +{ + + if (firstItemPosition == LIST_END) + firstItemPosition = (*list)->numItems; + else if (firstItemPosition == LIST_START) + firstItemPosition = 1; + + memmove (ITEMPTR (list, firstItemPosition - 1), ptrToItems, + (*list)->itemSize * numItemsToReplace); +} + +/*******************************/ + +void ListGetItem (list_t list, void *itemDestination, int itemPosition) +{ + ListGetItems (list, itemDestination, itemPosition, 1); +} + +#endif /* CFG_ALL_LIST_FUNCTIONS */ + +/*******************************/ + +#if defined(CFG_ALL_LIST_FUNCTIONS) || defined(CFG_DEVICE_DEREGISTER) + +void ListRemoveItem (list_t list, void *itemDestination, int itemPosition) +{ + ListRemoveItems (list, itemDestination, itemPosition, 1); +} + +/*******************************/ + +void ListRemoveItems (list_t list, void *itemsDestination, + int firstItemPosition, int numItemsToRemove) +{ + int firstItemAfterChunk, numToMove; + + if (firstItemPosition == LIST_START) + firstItemPosition = 1; + else if (firstItemPosition == LIST_END) + firstItemPosition = (*list)->numItems; + + if (itemsDestination != NULL) + memcpy (itemsDestination, ITEMPTR (list, firstItemPosition - 1), + (*list)->itemSize * numItemsToRemove); + + firstItemAfterChunk = firstItemPosition + numItemsToRemove; + numToMove = (*list)->numItems - (firstItemAfterChunk - 1); + + if (numToMove > 0) { + /* + * move part of list down to cover hole left by removed item + */ + memmove (ITEMPTR (list, firstItemPosition - 1), + ITEMPTR (list, firstItemAfterChunk - 1), + (*list)->itemSize * numToMove); + } + + (*list)->numItems -= numItemsToRemove; +} +#endif /* CFG_ALL_LIST_FUNCTIONS || CFG_DEVICE_DEREGISTER */ + +/*******************************/ + +void ListGetItems (list_t list, void *itemsDestination, + int firstItemPosition, int numItemsToGet) +{ + + if (firstItemPosition == LIST_START) + firstItemPosition = 1; + else if (firstItemPosition == LIST_END) + firstItemPosition = (*list)->numItems; + + memcpy (itemsDestination, + ITEMPTR (list, firstItemPosition - 1), + (*list)->itemSize * numItemsToGet); +} + +/*******************************/ + +/* + * Returns a pointer to the item at itemPosition. returns null if an + * errors occurred. + */ +void *ListGetPtrToItem (list_t list, int itemPosition) +{ + if (itemPosition == LIST_START) + itemPosition = 1; + else if (itemPosition == LIST_END) + itemPosition = (*list)->numItems; + + return ITEMPTR (list, itemPosition - 1); +} + +/*******************************/ + +/* + * returns a pointer the lists data (abstraction violation for + * optimization) + */ +void *ListGetDataPtr (list_t list) +{ + return &((*list)->itemList[0]); +} + +/********************************/ + +#ifdef CFG_ALL_LIST_FUNCTIONS + +int ListApplyToEach (list_t list, int ascending, + ListApplicationFunc funcToApply, + void *callbackData) +{ + int result = 0, index; + + if (!list || !funcToApply) + goto Error; + + if (ascending) { + for (index = 1; index <= ListNumItems (list); index++) { + result = funcToApply (index, + ListGetPtrToItem (list, index), + callbackData); + if (result < 0) + goto Error; + } + } else { + for (index = ListNumItems (list); + index > 0 && index <= ListNumItems (list); + index--) { + result = funcToApply (index, + ListGetPtrToItem (list, index), + callbackData); + if (result < 0) + goto Error; + } + } + +Error: + return result; +} + +#endif /* CFG_ALL_LIST_FUNCTIONS */ + +/********************************/ + +int ListGetItemSize (list_t list) +{ + return (*list)->itemSize; +} + +/********************************/ + +int ListNumItems (list_t list) +{ + return (*list)->numItems; +} + +/*******************************/ + +#ifdef CFG_ALL_LIST_FUNCTIONS + +void ListRemoveDuplicates (list_t list, CompareFunction compareFunction) +{ + int numItems, index, startIndexForFind, duplicatesIndex; + + numItems = ListNumItems (list); + + for (index = 1; index < numItems; index++) { + startIndexForFind = index + 1; + while (startIndexForFind <= numItems) { + duplicatesIndex = + ListFindItem (list, + ListGetPtrToItem (list, index), + startIndexForFind, + compareFunction); + if (duplicatesIndex > 0) { + ListRemoveItem (list, NULL, duplicatesIndex); + numItems--; + startIndexForFind = duplicatesIndex; + } else { + break; + } + } + } +} + +/*******************************/ + + +/*******************************/ + +int ListFindItem (list_t list, void *ptrToItem, int startingPosition, + CompareFunction compareFunction) +{ + int numItems, size, index, cmp; + void *listItemPtr; + + if ((numItems = (*list)->numItems) == 0) + return 0; + + size = (*list)->itemSize; + + if (startingPosition == LIST_START) + startingPosition = 1; + else if (startingPosition == LIST_END) + startingPosition = numItems; + + for (index = startingPosition; index <= numItems; index++) { + listItemPtr = ITEMPTR (list, index - 1); + cmp = compareFunction + ? compareFunction (ptrToItem, listItemPtr) + : ListMemBlockCmp (ptrToItem, listItemPtr, size); + if (cmp == 0) + return index; + } + + return 0; +} + +/*******************************/ + +int ShortCompare (void *a, void *b) +{ + if (*(short *) a < *(short *) b) + return -1; + if (*(short *) a > *(short *) b) + return 1; + return 0; +} + +/*******************************/ + +int IntCompare (void *a, void *b) +{ + if (*(int *) a < *(int *) b) + return -1; + if (*(int *) a > *(int *) b) + return 1; + return 0; +} + +/*******************************/ + +int CStringCompare (void *a, void *b) +{ + return strcmp (*(char **) a, *(char **) b); +} + +/*******************************/ + + +int ListBinSearch (list_t list, void *ptrToItem, + CompareFunction compareFunction) +{ + int index; + + index = BinSearch (ITEMPTR (list, 0), + (int) (*list)->numItems, + (int) (*list)->itemSize, ptrToItem, + compareFunction); + + if (index >= 0) + index++; /* lists start from 1 */ + else + index = 0; /* item not found */ + + return index; +} + +/**************************************************************************/ + +/* + * Reserves memory for numItems in the list. If it succeeds then + * numItems items can be inserted without possibility of an out of + * memory error (useful to simplify error recovery in complex + * functions). Returns 1 if success, 0 if out of memory. + */ +int ListPreAllocate (list_t list, int numItems) +{ + if ((*list)->listSize - (*list)->numItems < numItems) { + return ExpandListSpace (list, + numItems - ((*list)->listSize - + (*list)->numItems)); + } else { + return 1; /* enough items are already pre-allocated */ + } +} + +#endif /* CFG_ALL_LIST_FUNCTIONS */ diff --git a/common/miiphybb.c b/common/miiphybb.c new file mode 100644 index 00000000000..dfc19922b1f --- /dev/null +++ b/common/miiphybb.c @@ -0,0 +1,231 @@ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * 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 provides a bit-banged interface to the ethernet MII management + * channel. + */ + +#include <common.h> +#include <ioports.h> +#include <ppc_asm.tmpl> + +#ifdef CONFIG_BITBANGMII + + +/***************************************************************************** + * + * Utility to send the preamble, address, and register (common to read + * and write). + */ +static void miiphy_pre(char read, + unsigned char addr, + unsigned char reg) +{ + int j; /* counter */ + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, MDIO_PORT); + + /* + * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure. + * The IEEE spec says this is a PHY optional requirement. The AMD + * 79C874 requires one after power up and one after a MII communications + * error. This means that we are doing more preambles than we need, + * but it is safer and will be much more robust. + */ + + MDIO_ACTIVE; + MDIO(1); + for(j = 0; j < 32; j++) + { + MDC(0); + MIIDELAY; + MDC(1); + MIIDELAY; + } + + /* send the start bit (01) and the read opcode (10) or write (10) */ + MDC(0); MDIO(0); MIIDELAY; MDC(1); MIIDELAY; + MDC(0); MDIO(1); MIIDELAY; MDC(1); MIIDELAY; + MDC(0); MDIO(read); MIIDELAY; MDC(1); MIIDELAY; + MDC(0); MDIO(!read); MIIDELAY; MDC(1); MIIDELAY; + + /* send the PHY address */ + for(j = 0; j < 5; j++) + { + MDC(0); + if((addr & 0x10) == 0) + { + MDIO(0); + } + else + { + MDIO(1); + } + MIIDELAY; + MDC(1); + MIIDELAY; + addr <<= 1; + } + + /* send the register address */ + for(j = 0; j < 5; j++) + { + MDC(0); + if((reg & 0x10) == 0) + { + MDIO(0); + } + else + { + MDIO(1); + } + MIIDELAY; + MDC(1); + MIIDELAY; + reg <<= 1; + } +} + + +/***************************************************************************** + * + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +int miiphy_read(unsigned char addr, + unsigned char reg, + unsigned short *value) +{ + short rdreg; /* register working value */ + int j; /* counter */ + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, MDIO_PORT); + + miiphy_pre(1, addr, reg); + + /* tri-state our MDIO I/O pin so we can read */ + MDC(0); + MDIO_TRISTATE; + MIIDELAY; + MDC(1); + MIIDELAY; + + /* check the turnaround bit: the PHY should be driving it to zero */ + if(MDIO_READ != 0) + { + /* printf("PHY didn't drive TA low\n"); */ + for(j = 0; j < 32; j++) + { + MDC(0); + MIIDELAY; + MDC(1); + MIIDELAY; + } + return(-1); + } + + MDC(0); + MIIDELAY; + + /* read 16 bits of register data, MSB first */ + rdreg = 0; + for(j = 0; j < 16; j++) + { + MDC(1); + MIIDELAY; + rdreg <<= 1; + rdreg |= MDIO_READ; + MDC(0); + MIIDELAY; + } + + MDC(1); + MIIDELAY; + MDC(0); + MIIDELAY; + MDC(1); + MIIDELAY; + + *value = rdreg; + +#ifdef DEBUG + printf ("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, *value); +#endif + + return 0; +} + + +/***************************************************************************** + * + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +int miiphy_write(unsigned char addr, + unsigned char reg, + unsigned short value) +{ + int j; /* counter */ + volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, MDIO_PORT); + + miiphy_pre(0, addr, reg); + + /* send the turnaround (10) */ + MDC(0); MDIO(1); MIIDELAY; MDC(1); MIIDELAY; + MDC(0); MDIO(0); MIIDELAY; MDC(1); MIIDELAY; + + /* write 16 bits of register data, MSB first */ + for(j = 0; j < 16; j++) + { + MDC(0); + if((value & 0x00008000) == 0) + { + MDIO(0); + } + else + { + MDIO(1); + } + MIIDELAY; + MDC(1); + MIIDELAY; + value <<= 1; + } + + /* + * Tri-state the MDIO line. + */ + MDIO_TRISTATE; + MDC(0); + MIIDELAY; + MDC(1); + MIIDELAY; + + return 0; +} + +#endif /* CONFIG_BITBANGMII */ + diff --git a/common/s_record.c b/common/s_record.c new file mode 100644 index 00000000000..c52bf1bb65b --- /dev/null +++ b/common/s_record.c @@ -0,0 +1,195 @@ +/* + * (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 + */ + +#include <common.h> +#include <s_record.h> + +static int hex1_bin (char c); +static int hex2_bin (char *s); + +int srec_decode (char *input, int *count, ulong *addr, char *data) +{ + int i; + int v; /* conversion buffer */ + int srec_type; /* S-Record type */ + unsigned char chksum; /* buffer for checksum */ + + chksum = 0; + + /* skip anything before 'S', and the 'S' itself. + * Return error if not found + */ + + for (; *input; ++input) { + if (*input == 'S') { /* skip 'S' */ + ++input; + break; + } + } + if (*input == '\0') { /* no more data? */ + return (SREC_EMPTY); + } + + v = *input++; /* record type */ + + if ((*count = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + + chksum += *count; + input += 2; + + switch (v) { /* record type */ + + case '0': /* start record */ + srec_type = SREC_START; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '1': + srec_type = SREC_DATA2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + case '2': + srec_type = SREC_DATA3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + case '3': /* data record with a */ + srec_type = SREC_DATA4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; +/*** case '4' ***/ + case '5': /* count record, addr field contains */ + srec_type = SREC_COUNT; /* a 2 byte record counter */ + *count = 0; /* no data */ + break; +/*** case '6' -- not used ***/ + case '7': /* end record with a */ + srec_type = SREC_END4; /* 4 byte addr field */ + *count -= 5; /* - checksum and addr */ + break; + case '8': /* end record with a */ + srec_type = SREC_END3; /* 3 byte addr field */ + *count -= 4; /* - checksum and addr */ + break; + case '9': /* end record with a */ + srec_type = SREC_END2; /* 2 byte addr field */ + *count -= 3; /* - checksum and addr */ + break; + default: + return (SREC_E_BADTYPE); + } + + /* read address field */ + *addr = 0; + + switch (v) { + case '3': /* 4 byte addr field */ + case '7': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + case '2': /* 3 byte addr field */ + case '8': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + /* FALL THRU */ + case '0': /* 2 byte addr field */ + case '1': + case '5': + case '9': + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + *addr <<= 8; + *addr += v; + chksum += v; + input += 2; + + break; + default: + return (SREC_E_BADTYPE); + } + + /* convert data and calculate checksum */ + for (i=0; i < *count; ++i) { + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + data[i] = v; + chksum += v; + input += 2; + } + + /* read anc check checksum */ + if ((v = hex2_bin(input)) < 0) { + return (SREC_E_NOSREC); + } + + if ((unsigned char)v != (unsigned char)~chksum) { + return (SREC_E_BADCHKS); + } + + return (srec_type); +} + +static int hex1_bin (char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c + 10 - 'a'); + if (c >= 'A' && c <= 'F') + return (c + 10 - 'A'); + return (-1); +} + +static int hex2_bin (char *s) +{ + int i, j; + + if ((i = hex1_bin(*s++)) < 0) { + return (-1); + } + if ((j = hex1_bin(*s)) < 0) { + return (-1); + } + + return ((i<<4) + j); +} diff --git a/common/usb.c b/common/usb.c new file mode 100644 index 00000000000..a5b29a56b46 --- /dev/null +++ b/common/usb.c @@ -0,0 +1,1066 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Most of this source has been derived from the Linux USB + * project. + * + * 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 + * + */ + + +/* + * How it works: + * + * Since this is a bootloader, the devices will not be automatic + * (re)configured on hotplug, but after a restart of the USB the + * device should work. + * + * For each transfer (except "Interrupt") we wait for completion. + */ +#include <common.h> +#include <command.h> +#include <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_USB) + +#include <usb.h> +#ifdef CONFIG_4xx +#include <405gp_pci.h> +#endif + + + +#undef USB_DEBUG + +#ifdef USB_DEBUG +#define USB_PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define USB_PRINTF(fmt,args...) +#endif + +static struct usb_device usb_dev[USB_MAX_DEVICE]; +static int dev_index; +static int running; +static int asynch_allowed; +static struct devrequest setup_packet; + +/********************************************************************** + * some forward declerations... + */ +void usb_scan_devices(void); + +int usb_hub_probe(struct usb_device *dev, int ifnum); +void usb_hub_reset(void); + +/*********************************************************************** + * wait_ms + */ + +void __inline__ wait_ms(unsigned long ms) +{ + while(ms-->0) + udelay(1000); +} +/*************************************************************************** + * Init USB Device + */ + +int usb_init(void) +{ + int result; + + running=0; + dev_index=0; + asynch_allowed=1; + usb_hub_reset(); + /* init low_level USB */ + printf("USB: "); + result = usb_lowlevel_init(); + /* if lowlevel init is OK, scan the bus for devices i.e. search HUBs and configure them */ + if(result==0) { + printf("scanning bus for devices... "); + running=1; + usb_scan_devices(); + return 0; + } + else { + printf("Error, couldn't init Lowlevel part\n"); + return -1; + } +} + +/****************************************************************************** + * Stop USB this stops the LowLevel Part and deregisters USB devices. + */ +int usb_stop(void) +{ + asynch_allowed=1; + usb_hub_reset(); + return usb_lowlevel_stop(); +} + +/* + * disables the asynch behaviour of the control message. This is used for data + * transfers that uses the exclusiv access to the control and bulk messages. + */ +void usb_disable_asynch(int disable) +{ + asynch_allowed=!disable; +} + + +/*------------------------------------------------------------------- + * Message wrappers. + * + */ + +/* + * submits an Interrupt Message + */ +int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer,int transfer_len, int interval) +{ + return submit_int_msg(dev,pipe,buffer,transfer_len,interval); +} + +/* + * submits a control message and waits for comletion (at least timeout * 1ms) + * If timeout is 0, we don't wait for completion (used as example to set and + * clear keyboards LEDs). For data transfers, (storage transfers) we don't + * allow control messages with 0 timeout, by previousely resetting the flag + * asynch_allowed (usb_disable_asynch(1)). + * returns the transfered length if OK or -1 if error. The transfered length + * and the current status are stored in the dev->act_len and dev->status. + */ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, + unsigned char request, unsigned char requesttype, + unsigned short value, unsigned short index, + void *data, unsigned short size, int timeout) +{ + if((timeout==0)&&(!asynch_allowed)) /* request for a asynch control pipe is not allowed */ + return -1; + /* set setup command */ + setup_packet.requesttype = requesttype; + setup_packet.request = request; + setup_packet.value = swap_16(value); + setup_packet.index = swap_16(index); + setup_packet.length = swap_16(size); + USB_PRINTF("usb_control_msg: request: 0x%X, requesttype: 0x%X\nvalue 0x%X index 0x%X length 0x%X\n", + request,requesttype,value,index,size); + dev->status=USB_ST_NOT_PROC; /*not yet processed */ + + submit_control_msg(dev,pipe,data,size,&setup_packet); + if(timeout==0) { + return (int)size; + } + while(timeout--) { + if(!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + wait_ms(1); + } + if(dev->status==0) + return dev->act_len; + else { + return -1; + } +} + +/*------------------------------------------------------------------- + * submits bulk message, and waits for completion. returns 0 if Ok or + * -1 if Error. + * synchronous behavior + */ +int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + if (len < 0) + return -1; + dev->status=USB_ST_NOT_PROC; /*not yet processed */ + submit_bulk_msg(dev,pipe,data,len); + while(timeout--) { + if(!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + wait_ms(1); + } + *actual_length=dev->act_len; + if(dev->status==0) + return 0; + else + return -1; +} + + +/*------------------------------------------------------------------- + * Max Packet stuff + */ + +/* + * returns the max packet size, depending on the pipe direction and + * the configurations values + */ +int usb_maxpacket(struct usb_device *dev,unsigned long pipe) +{ + if((pipe & USB_DIR_IN)==0) /* direction is out -> use emaxpacket out */ + return(dev->epmaxpacketout[((pipe>>15) & 0xf)]); + else + return(dev->epmaxpacketin[((pipe>>15) & 0xf)]); +} + +/* + * set the max packed value of all endpoints in the given configuration + */ +int usb_set_maxpacket(struct usb_device *dev) +{ + int i,ii,b; + struct usb_endpoint_descriptor *ep; + + for(i=0; i<dev->config.bNumInterfaces;i++) { + for(ii=0; ii<dev->config.if_desc[i].bNumEndpoints; ii++) { + ep=&dev->config.if_desc[i].ep_desc[ii]; + b=ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + + if((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_CONTROL) { /* Control => bidirectional */ + dev->epmaxpacketout[b] = ep->wMaxPacketSize; + dev->epmaxpacketin [b] = ep->wMaxPacketSize; + USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n",b,dev->epmaxpacketin[b]); + } + else { + if ((ep->bEndpointAddress & 0x80)==0) { /* OUT Endpoint */ + if(ep->wMaxPacketSize > dev->epmaxpacketout[b]) { + dev->epmaxpacketout[b] = ep->wMaxPacketSize; + USB_PRINTF("##EP epmaxpacketout[%d] = %d\n",b,dev->epmaxpacketout[b]); + } + } + else { /* IN Endpoint */ + if(ep->wMaxPacketSize > dev->epmaxpacketin[b]) { + dev->epmaxpacketin[b] = ep->wMaxPacketSize; + USB_PRINTF("##EP epmaxpacketin[%d] = %d\n",b,dev->epmaxpacketin[b]); + } + } /* if out */ + } /* if control */ + } /* for each endpoint */ + } + return 0; +} + +/******************************************************************************* + * Parse the config, located in buffer, and fills the dev->config structure. + * Note that all little/big endian swapping are done automatically. + */ +int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int cfgno) +{ + struct usb_descriptor_header *head; + int index,ifno,epno; + ifno=-1; + epno=-1; + + dev->configno=cfgno; + head =(struct usb_descriptor_header *)&buffer[0]; + if(head->bDescriptorType!=USB_DT_CONFIG) { + printf(" ERROR: NOT USB_CONFIG_DESC %x\n",head->bDescriptorType); + return -1; + } + memcpy(&dev->config,buffer,buffer[0]); + dev->config.wTotalLength=swap_16(dev->config.wTotalLength); + dev->config.no_of_if=0; + + index=dev->config.bLength; + /* Ok the first entry must be a configuration entry, now process the others */ + head=(struct usb_descriptor_header *)&buffer[index]; + while(index+1 < dev->config.wTotalLength) { + switch(head->bDescriptorType) { + case USB_DT_INTERFACE: + ifno=dev->config.no_of_if; + dev->config.no_of_if++; /* found an interface desc, increase numbers */ + memcpy(&dev->config.if_desc[ifno],&buffer[index],buffer[index]); /* copy new desc */ + dev->config.if_desc[ifno].no_of_ep=0; + + break; + case USB_DT_ENDPOINT: + epno=dev->config.if_desc[ifno].no_of_ep; + dev->config.if_desc[ifno].no_of_ep++; /* found an endpoint */ + memcpy(&dev->config.if_desc[ifno].ep_desc[epno],&buffer[index],buffer[index]); + dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize + =swap_16(dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize); + USB_PRINTF("if %d, ep %d\n",ifno,epno); + break; + default: + if(head->bLength==0) + return 1; + USB_PRINTF("unknown Description Type : %x\n",head->bDescriptorType); + { + int i; + unsigned char *ch; + ch=(unsigned char *)head; + for(i=0;i<head->bLength; i++) + USB_PRINTF("%02X ",*ch++); + USB_PRINTF("\n\n\n"); + } + break; + } + index+=head->bLength; + head=(struct usb_descriptor_header *)&buffer[index]; + } + return 1; +} + +/*********************************************************************** + * Clears an endpoint + * endp: endpoint number in bits 0-3; + * direction flag in bit 7 (1 = IN, 0 = OUT) + */ +int usb_clear_halt(struct usb_device *dev, int pipe) +{ + int result; + unsigned short status; + int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7); + + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, USB_CNTL_TIMEOUT * 3); + + /* don't clear if failed */ + if (result < 0) + return result; + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp, + &status, sizeof(status), USB_CNTL_TIMEOUT * 3); + if (result < 0) + return result; + USB_PRINTF("usb_clear_halt: status 0x%x\n",status); + if (status & 1) + return -1; /* still halted */ + usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + /* toggle is reset on clear */ + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + return 0; +} + + +/********************************************************************** + * get_descriptor type + */ +int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) +{ + int res; + res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (type << 8) + index, 0, + buf, size, USB_CNTL_TIMEOUT); + return res; +} + +/********************************************************************** + * gets configuration cfgno and store it in the buffer + */ +int usb_get_configuration_no(struct usb_device *dev,unsigned char *buffer,int cfgno) +{ + int result; + unsigned int tmp; + struct usb_config_descriptor *config; + + + config=(struct usb_config_descriptor *)&buffer[0]; + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); + if (result < 8) { + if (result < 0) + printf("unable to get descriptor, error %lX\n",dev->status); + else + printf("config descriptor too short (expected %i, got %i)\n",8,result); + return -1; + } + tmp=swap_16(config->wTotalLength); + + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp); + USB_PRINTF("get_conf_no %d Result %d, wLength %d\n",cfgno,result,tmp); + return result; +} + +/******************************************************************** + * set address of a device to the value in dev->devnum. + * This can only be done by addressing the device via the default address (0) + */ +int usb_set_address(struct usb_device *dev) +{ + int res; + + USB_PRINTF("set address %d\n",dev->devnum); + res=usb_control_msg(dev, usb_snddefctrl(dev), + USB_REQ_SET_ADDRESS, 0, + (dev->devnum),0, + NULL,0, USB_CNTL_TIMEOUT); + return res; +} + +/******************************************************************** + * set interface number to interface + */ +int usb_set_interface(struct usb_device *dev, int interface, int alternate) +{ + struct usb_interface_descriptor *if_face = NULL; + int ret, i; + + for (i=0; i<dev->config.bNumInterfaces; i++) { + if (dev->config.if_desc[i].bInterfaceNumber == interface) { + if_face = &dev->config.if_desc[i]; + break; + } + } + if (!if_face) { + printf("selecting invalid interface %d", interface); + return -1; + } + + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate, + interface, NULL, 0, USB_CNTL_TIMEOUT * 5)) < 0) + return ret; + + if_face->act_altsetting = (unsigned char)alternate; + usb_set_maxpacket(dev); + return 0; +} + +/******************************************************************** + * set configuration number to configuration + */ +int usb_set_configuration(struct usb_device *dev, int configuration) +{ + int res; + USB_PRINTF("set configuration %d\n",configuration); + /* set setup command */ + res=usb_control_msg(dev, usb_sndctrlpipe(dev,0), + USB_REQ_SET_CONFIGURATION, 0, + configuration,0, + NULL,0, USB_CNTL_TIMEOUT); + if(res==0) { + dev->toggle[0] = 0; + dev->toggle[1] = 0; + return 0; + } + else + return -1; +} + +/******************************************************************** + * set protocol to protocol + */ +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + protocol, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * set idle + */ +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (duration << 8) | report_id, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get report + */ +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get class descriptor + */ +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get string index in buffer + */ +int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * usb_string: + * Get string index and translate it to ascii. + * returns string length (> 0) or error (< 0) + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + + unsigned char mybuf[256]; + unsigned char *tbuf; + int err; + unsigned int u, idx; + + if (size <= 0 || !buf || !index) + return -1; + buf[0] = 0; + tbuf=&mybuf[0]; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_get_string(dev, 0, 0, tbuf, 4); + if (err < 0) { + USB_PRINTF("error getting string descriptor 0 (error=%x)\n",dev->status); + return -1; + } else if (tbuf[0] < 4) { + USB_PRINTF("string descriptor 0 too short\n"); + return -1; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3]<< 8); + /* always use the first langid listed */ + USB_PRINTF("USB device number %d default language ID 0x%x\n", + dev->devnum, dev->string_langid); + } + } + /* Just ask for a maximum length string and then take the length + * that was returned. */ + err = usb_get_string(dev, dev->string_langid, index, tbuf, 4); + if (err < 0) + return err; + u=tbuf[0]; + USB_PRINTF("Strn Len %d, index %d\n",u,index); + err = usb_get_string(dev, dev->string_langid, index, tbuf, u); + if (err < 0) + return err; + size--; /* leave room for trailing NULL char in output buffer */ + for (idx = 0, u = 2; u < err; u += 2) { + if (idx >= size) + break; + if (tbuf[u+1]) /* high byte */ + buf[idx++] = '?'; /* non-ASCII character */ + else + buf[idx++] = tbuf[u]; + } + buf[idx] = 0; + err = idx; + return err; +} + + +/******************************************************************** + * USB device handling: + * the USB device are static allocated [USB_MAX_DEVICE]. + */ + + +/* returns a pointer to the device with the index [index]. + * if the device is not assigned (dev->devnum==-1) returns NULL + */ +struct usb_device * usb_get_dev_index(int index) +{ + if(usb_dev[index].devnum==-1) + return NULL; + else + return &usb_dev[index]; +} + + +/* returns a pointer of a new device structure or NULL, if + * no device struct is available + */ +struct usb_device * usb_alloc_new_device(void) +{ + int i; + USB_PRINTF("New Device %d\n",dev_index); + if(dev_index==USB_MAX_DEVICE) { + printf("ERROR, to many USB Devices max=%d\n",USB_MAX_DEVICE); + return NULL; + } + usb_dev[dev_index].devnum=dev_index+1; /* default Address is 0, real addresses start with 1 */ + usb_dev[dev_index].maxchild=0; + for(i=0;i<USB_MAXCHILDREN;i++) + usb_dev[dev_index].children[i]=NULL; + usb_dev[dev_index].parent=NULL; + dev_index++; + return &usb_dev[dev_index-1]; +} + + +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + int addr, err; + int tmp; + unsigned char tmpbuf[256]; + + dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ + dev->maxpacketsize = 0; /* Default to 8 byte max packet size */ + dev->epmaxpacketin [0] = 8; + dev->epmaxpacketout[0] = 8; + + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); + if (err < 8) { + printf("\n USB device not responding, giving up (status=%lX)\n",dev->status); + return 1; + } + dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + switch (dev->descriptor.bMaxPacketSize0) { + case 8: dev->maxpacketsize = 0; break; + case 16: dev->maxpacketsize = 1; break; + case 32: dev->maxpacketsize = 2; break; + case 64: dev->maxpacketsize = 3; break; + } + dev->devnum = addr; + + err = usb_set_address(dev); /* set address */ + + if (err < 0) { + printf("\n USB device not accepting new address (error=%lX)\n", dev->status); + return 1; + } + + wait_ms(10); /* Let the SET_ADDRESS settle */ + + tmp = sizeof(dev->descriptor); + + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor)); + if (err < tmp) { + if (err < 0) + printf("unable to get device descriptor (error=%d)\n",err); + else + printf("USB device descriptor short read (expected %i, got %i)\n",tmp,err); + return 1; + } + /* correct le values */ + dev->descriptor.bcdUSB=swap_16(dev->descriptor.bcdUSB); + dev->descriptor.idVendor=swap_16(dev->descriptor.idVendor); + dev->descriptor.idProduct=swap_16(dev->descriptor.idProduct); + dev->descriptor.bcdDevice=swap_16(dev->descriptor.bcdDevice); + /* only support for one config for now */ + usb_get_configuration_no(dev,&tmpbuf[0],0); + usb_parse_config(dev,&tmpbuf[0],0); + usb_set_maxpacket(dev); + /* we set the default configuration here */ + if (usb_set_configuration(dev, dev->config.bConfigurationValue)) { + printf("failed to set default configuration len %d, status %lX\n",dev->act_len,dev->status); + return -1; + } + USB_PRINTF("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", + dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber); + memset(dev->mf, 0, sizeof(dev->mf)); + memset(dev->prod, 0, sizeof(dev->prod)); + memset(dev->serial, 0, sizeof(dev->serial)); + if (dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, dev->mf, sizeof(dev->mf)); + if (dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, dev->prod, sizeof(dev->prod)); + if (dev->descriptor.iSerialNumber) + usb_string(dev, dev->descriptor.iSerialNumber, dev->serial, sizeof(dev->serial)); + USB_PRINTF("Manufacturer %s\n", dev->mf); + USB_PRINTF("Product %s\n", dev->prod); + USB_PRINTF("SerialNumber %s\n", dev->serial); + /* now prode if the device is a hub */ + usb_hub_probe(dev,0); + return 0; +} + +/* build device Tree */ +void usb_scan_devices(void) +{ + int i; + struct usb_device *dev; + + /* first make all devices unknown */ + for(i=0;i<USB_MAX_DEVICE;i++) { + memset(&usb_dev[i],0,sizeof(struct usb_device)); + usb_dev[i].devnum=-1; + } + dev_index=0; + /* device 0 is always present (root hub, so let it analyze) */ + dev=usb_alloc_new_device(); + usb_new_device(dev); + printf("%d USB Devices found\n",dev_index); + /* insert "driver" if possible */ +#ifdef CONFIG_USB_KEYBOARD + drv_usb_kbd_init(); + USB_PRINTF("scan end\n"); +#endif +} + + +/**************************************************************************** + * HUB "Driver" + * Probes device for being a hub and configurate it + */ + +#undef USB_HUB_DEBUG + +#ifdef USB_HUB_DEBUG +#define USB_HUB_PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define USB_HUB_PRINTF(fmt,args...) +#endif + + +static struct usb_hub_device hub_dev[USB_MAX_HUB]; +static int usb_hub_index; + + +int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT); +} + +int usb_clear_hub_feature(struct usb_device *dev, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, USB_CNTL_TIMEOUT); +} + +int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, USB_CNTL_TIMEOUT); +} + +int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, USB_CNTL_TIMEOUT); +} + +int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + +int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + + +static void usb_hub_power_on(struct usb_hub_device *hub) +{ + int i; + struct usb_device *dev; + + dev=hub->pusb_dev; + /* Enable power to the ports */ + USB_HUB_PRINTF("enabling power on all ports\n"); + for (i = 0; i < dev->maxchild; i++) { + usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + USB_HUB_PRINTF("port %d returns %lX\n",i+1,dev->status); + wait_ms(hub->desc.bPwrOn2PwrGood * 2); + } +} + +void usb_hub_reset(void) +{ + usb_hub_index=0; +} + +struct usb_hub_device *usb_hub_allocate(void) +{ + if(usb_hub_index<USB_MAX_HUB) { + return &hub_dev[usb_hub_index++]; + } + printf("ERROR: USB_MAX_HUB (%d) reached\n",USB_MAX_HUB); + return NULL; +} + +#define MAX_TRIES 5 + +void usb_hub_port_connect_change(struct usb_device *dev, int port) +{ + struct usb_device *usb; + struct usb_port_status portsts; + unsigned short portstatus, portchange; + int tries; + + /* Check status */ + if (usb_get_port_status(dev, port + 1, &portsts)<0) { + USB_HUB_PRINTF("get_port_status failed\n"); + return; + } + + portstatus = swap_16(portsts.wPortStatus); + portchange = swap_16(portsts.wPortChange); + USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, portchange, + portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed"); + + /* Clear the connection change status */ + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); + + /* Disconnect any existing devices under this port */ + if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && + (!(portstatus & USB_PORT_STAT_ENABLE)))|| (dev->children[port])) { + USB_HUB_PRINTF("usb_disconnect(&hub->children[port]);\n"); + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return; + } + wait_ms(200); + + /* Reset the port */ + + for(tries=0;tries<MAX_TRIES;tries++) { + + usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); + wait_ms(200); + + if (usb_get_port_status(dev, port + 1, &portsts)<0) { + USB_HUB_PRINTF("get_port_status failed status %lX\n",dev->status); + return; + } + portstatus = swap_16(portsts.wPortStatus); + portchange = swap_16(portsts.wPortChange); + USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus ,portchange, + portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed"); + USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d USB_PORT_STAT_ENABLE %d\n", + (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); + if ((portchange & USB_PORT_STAT_C_CONNECTION) || + !(portstatus & USB_PORT_STAT_CONNECTION)) + return; + + if (portstatus & USB_PORT_STAT_ENABLE) + break; + + wait_ms(200); + } + + if (tries==MAX_TRIES) { + USB_HUB_PRINTF("Cannot enable port %i after %i retries, disabling port.\n", port+1, MAX_TRIES); + USB_HUB_PRINTF("Maybe the USB cable is bad?\n"); + return; + } + + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); + wait_ms(200); + + /* Allocate a new device struct for it */ + usb=usb_alloc_new_device(); + usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + + dev->children[port] = usb; + usb->parent=dev; + /* Run it through the hoops (find a driver, etc) */ + if (usb_new_device(usb)) { + /* Woops, disable the port */ + USB_HUB_PRINTF("hub: disabling port %d\n", port + 1); + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); + } +} + + +int usb_hub_configure(struct usb_device *dev) +{ + unsigned char buffer[256], *bitmap; + struct usb_hub_descriptor *descriptor; + struct usb_hub_status *hubsts; + int i; + struct usb_hub_device *hub; + + /* "allocate" Hub device */ + hub=usb_hub_allocate(); + if(hub==NULL) + return -1; + hub->pusb_dev=dev; + /* Get the the hub descriptor */ + if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { + USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor, giving up %lX\n",dev->status); + return -1; + } + descriptor = (struct usb_hub_descriptor *)buffer; + if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) { + USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor 2nd giving up %lX\n",dev->status); + return -1; + } + memcpy((unsigned char *)&hub->desc,buffer,descriptor->bLength); + /* adjust 16bit values */ + hub->desc.wHubCharacteristics=swap_16(descriptor->wHubCharacteristics); + /* set the bitmap */ + bitmap=(unsigned char *)&hub->desc.DeviceRemovable[0]; + memset(bitmap,0xff,(USB_MAXCHILDREN+1+7)/8); /* devices not removable by default */ + bitmap=(unsigned char *)&hub->desc.PortPowerCtrlMask[0]; + memset(bitmap,0xff,(USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ + for(i=0;i<((hub->desc.bNbrPorts + 1 + 7)/8);i++) { + hub->desc.DeviceRemovable[i]=descriptor->DeviceRemovable[i]; + } + for(i=0;i<((hub->desc.bNbrPorts + 1 + 7)/8);i++) { + hub->desc.DeviceRemovable[i]=descriptor->PortPowerCtrlMask[i]; + } + dev->maxchild = descriptor->bNbrPorts; + USB_HUB_PRINTF("%d ports detected\n", dev->maxchild); + + switch (hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) { + case 0x00: + USB_HUB_PRINTF("ganged power switching\n"); + break; + case 0x01: + USB_HUB_PRINTF("individual port power switching\n"); + break; + case 0x02: + case 0x03: + USB_HUB_PRINTF("unknown reserved power switching mode\n"); + break; + } + + if (hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND) + USB_HUB_PRINTF("part of a compound device\n"); + else + USB_HUB_PRINTF("standalone hub\n"); + + switch (hub->desc.wHubCharacteristics & HUB_CHAR_OCPM) { + case 0x00: + USB_HUB_PRINTF("global over-current protection\n"); + break; + case 0x08: + USB_HUB_PRINTF("individual port over-current protection\n"); + break; + case 0x10: + case 0x18: + USB_HUB_PRINTF("no over-current protection\n"); + break; + } + USB_HUB_PRINTF("power on to power good time: %dms\n", descriptor->bPwrOn2PwrGood * 2); + USB_HUB_PRINTF("hub controller current requirement: %dmA\n", descriptor->bHubContrCurrent); + for (i = 0; i < dev->maxchild; i++) + USB_HUB_PRINTF("port %d is%s removable\n", i + 1, + hub->desc.DeviceRemovable[(i + 1)/8] & (1 << ((i + 1)%8)) ? " not" : ""); + if (usb_get_hub_status(dev, buffer) < 0) { + USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n",dev->status); + return -1; + } + hubsts = (struct usb_hub_status *)buffer; + USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n", + swap_16(hubsts->wHubStatus),swap_16(hubsts->wHubChange)); + USB_HUB_PRINTF("local power source is %s\n", + (swap_16(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); + USB_HUB_PRINTF("%sover-current condition exists\n", + (swap_16(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no "); + usb_hub_power_on(hub); + for (i = 0; i < dev->maxchild; i++) { + struct usb_port_status portsts; + unsigned short portstatus, portchange; + + if (usb_get_port_status(dev, i + 1, &portsts) < 0) { + USB_HUB_PRINTF("get_port_status failed\n"); + continue; + } + portstatus = swap_16(portsts.wPortStatus); + portchange = swap_16(portsts.wPortChange); + USB_HUB_PRINTF("Port %d Status %X Change %X\n",i+1,portstatus,portchange); + if (portchange & USB_PORT_STAT_C_CONNECTION) { + USB_HUB_PRINTF("port %d connection change\n", i + 1); + usb_hub_port_connect_change(dev, i); + } + if (portchange & USB_PORT_STAT_C_ENABLE) { + USB_HUB_PRINTF("port %d enable change, status %x\n", i + 1, portstatus); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); + + /* EM interference sometimes causes bad shielded USB devices to + * be shutdown by the hub, this hack enables them again. + * Works at least with mouse driver */ + if (!(portstatus & USB_PORT_STAT_ENABLE) && + (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { + USB_HUB_PRINTF("already running port %i disabled by hub (EMI?), re-enabling...\n", + i + 1); + usb_hub_port_connect_change(dev, i); + } + } + if (portstatus & USB_PORT_STAT_SUSPEND) { + USB_HUB_PRINTF("port %d suspend change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + } + + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + USB_HUB_PRINTF("port %d over-current change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); + usb_hub_power_on(hub); + } + + if (portchange & USB_PORT_STAT_C_RESET) { + USB_HUB_PRINTF("port %d reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); + } + } /* end for i all ports */ + + return 0; +} + +int usb_hub_probe(struct usb_device *dev, int ifnum) +{ + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int ret; + + iface = &dev->config.if_desc[ifnum]; + /* Is it a hub? */ + if (iface->bInterfaceClass != USB_CLASS_HUB) + return 0; + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ + if ((iface->bInterfaceSubClass != 0) && + (iface->bInterfaceSubClass != 1)) + return 0; + /* Multiple endpoints? What kind of mutant ninja-hub is this? */ + if (iface->bNumEndpoints != 1) + return 0; + ep = &iface->ep_desc[0]; + /* Output endpoint? Curiousier and curiousier.. */ + if (!(ep->bEndpointAddress & USB_DIR_IN)) + return 0; + /* If it's not an interrupt endpoint, we'd better punt! */ + if ((ep->bmAttributes & 3) != 3) + return 0; + /* We found a hub */ + USB_HUB_PRINTF("USB hub found\n"); + ret=usb_hub_configure(dev); + return ret; +} + +#endif /* (CONFIG_COMMANDS & CFG_CMD_USB) */ + +/* EOF */ diff --git a/common/usb_kbd.c b/common/usb_kbd.c new file mode 100644 index 00000000000..ad7e6100e2c --- /dev/null +++ b/common/usb_kbd.c @@ -0,0 +1,734 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Part of this source has been derived from the Linux USB + * project. + * + * 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 + * + */ +#include <common.h> +#include <devices.h> + +#ifdef CONFIG_USB_KEYBOARD + +#include <usb.h> + +#undef USB_KBD_DEBUG +/* + * if overwrite_console returns 1, the stdin, stderr and stdout + * are switched to the serial port, else the settings in the + * environment are used + */ +#ifdef CFG_CONSOLE_OVERWRITE_ROUTINE +extern int overwrite_console (void); +#else +int overwrite_console (void) +{ + return (0); +} +#endif + +#ifdef USB_KBD_DEBUG +#define USB_KBD_PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define USB_KBD_PRINTF(fmt,args...) +#endif + + +#define REPEAT_RATE 40/4 /* 40msec -> 25cps */ +#define REPEAT_DELAY 10 /* 10 x REAPEAT_RATE = 400msec */ + +#define NUM_LOCK 0x53 +#define CAPS_LOCK 0x39 +#define SCROLL_LOCK 0x47 + + +/* Modifier bits */ +#define LEFT_CNTR 0 +#define LEFT_SHIFT 1 +#define LEFT_ALT 2 +#define LEFT_GUI 3 +#define RIGHT_CNTR 4 +#define RIGHT_SHIFT 5 +#define RIGHT_ALT 6 +#define RIGHT_GUI 7 + +#define USB_KBD_BUFFER_LEN 0x20 /* size of the keyboardbuffer */ + +static volatile char usb_kbd_buffer[USB_KBD_BUFFER_LEN]; +static volatile int usb_in_pointer = 0; +static volatile int usb_out_pointer = 0; + +unsigned char new[8]; +unsigned char old[8]; +int repeat_delay; +#define DEVNAME "usbkbd" +static unsigned char num_lock = 0; +static unsigned char caps_lock = 0; +static unsigned char scroll_lock = 0; + +static unsigned char leds __attribute__ ((aligned (0x4))); + +static unsigned char usb_kbd_numkey[] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0','\r',0x1b,'\b','\t',' ', '-', + '=', '[', ']','\\', '#', ';', '\'', '`', ',', '.', '/' +}; +static unsigned char usb_kbd_numkey_shifted[] = { + '!', '@', '#', '$', '%', '^', '&', '*', '(', ')','\r',0x1b,'\b','\t',' ', '_', + '+', '{', '}', '|', '~', ':', '"', '~', '<', '>', '?' +}; + +/****************************************************************** + * Queue handling + ******************************************************************/ +/* puts character in the queue and sets up the in and out pointer */ +static void usb_kbd_put_queue(char data) +{ + if((usb_in_pointer+1)==USB_KBD_BUFFER_LEN) { + if(usb_out_pointer==0) { + return; /* buffer full */ + } else{ + usb_in_pointer=0; + } + } else { + if((usb_in_pointer+1)==usb_out_pointer) + return; /* buffer full */ + usb_in_pointer++; + } + usb_kbd_buffer[usb_in_pointer]=data; + return; +} + +/* test if a character is in the queue */ +static int usb_kbd_testc(void) +{ + if(usb_in_pointer==usb_out_pointer) + return(0); /* no data */ + else + return(1); +} +/* gets the character from the queue */ +static int usb_kbd_getc(void) +{ + char c; + while(usb_in_pointer==usb_out_pointer); + if((usb_out_pointer+1)==USB_KBD_BUFFER_LEN) + usb_out_pointer=0; + else + usb_out_pointer++; + c=usb_kbd_buffer[usb_out_pointer]; + return (int)c; + +} + +/* forward decleration */ +static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum); + +/* search for keyboard and register it if found */ +int drv_usb_kbd_init(void) +{ + int error,i,index; + device_t usb_kbd_dev,*old_dev; + struct usb_device *dev; + char *stdinname = getenv ("stdin"); + + usb_in_pointer=0; + usb_out_pointer=0; + /* scan all USB Devices */ + for(i=0;i<USB_MAX_DEVICE;i++) { + dev=usb_get_dev_index(i); /* get device */ + if(dev->devnum!=-1) { + if(usb_kbd_probe(dev,0)==1) { /* Ok, we found a keyboard */ + /* check, if it is already registered */ + USB_KBD_PRINTF("USB KBD found set up device.\n"); + for (index=1; index<=ListNumItems(devlist); index++) { + old_dev = ListGetPtrToItem(devlist, index); + if(strcmp(old_dev->name,DEVNAME)==0) { + /* ok, already registered, just return ok */ + USB_KBD_PRINTF("USB KBD is already registered.\n"); + return 1; + } + } + /* register the keyboard */ + USB_KBD_PRINTF("USB KBD register.\n"); + memset (&usb_kbd_dev, 0, sizeof(device_t)); + strcpy(usb_kbd_dev.name, DEVNAME); + usb_kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + usb_kbd_dev.putc = NULL; + usb_kbd_dev.puts = NULL; + usb_kbd_dev.getc = usb_kbd_getc; + usb_kbd_dev.tstc = usb_kbd_testc; + error = device_register (&usb_kbd_dev); + if(error==0) { + /* check if this is the standard input device */ + if(strcmp(stdinname,DEVNAME)==0) { + /* reassign the console */ + if(overwrite_console()) { + return 1; + } + error=console_assign(stdin,DEVNAME); + if(error==0) + return 1; + else + return error; + } + return 1; + } + return error; + } + } + } + /* no USB Keyboard found */ + return -1; +} + + +/* deregistering the keyboard */ +int usb_kbd_deregister(void) +{ + return device_deregister(DEVNAME); +} + +/************************************************************************** + * Low Level drivers + */ + +/* set the LEDs. Since this is used in the irq routine, the control job + is issued with a timeout of 0. This means, that the job is queued without + waiting for job completion */ + +static void usb_kbd_setled(struct usb_device *dev) +{ + struct usb_interface_descriptor *iface; + iface = &dev->config.if_desc[0]; + leds=0; + if(scroll_lock!=0) + leds|=1; + leds<<=1; + if(caps_lock!=0) + leds|=1; + leds<<=1; + if(num_lock!=0) + leds|=1; + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x200, iface->bInterfaceNumber,(void *)&leds, 1, 0); + +} + + +#define CAPITAL_MASK 0x20 +/* Translate the scancode in ASCII */ +static int usb_kbd_translate(unsigned char scancode,unsigned char modifier,int pressed) +{ + unsigned char keycode; + + if(pressed==0) { + /* key released */ + repeat_delay=0; + return 0; + } + if(pressed==2) { + repeat_delay++; + if(repeat_delay<REPEAT_DELAY) + return 0; + repeat_delay=REPEAT_DELAY; + } + keycode=0; + if((scancode>3) && (scancode<0x1d)) { /* alpha numeric values */ + keycode=scancode-4 + 0x61; + if(caps_lock) + keycode&=~CAPITAL_MASK; /* switch to capital Letters */ + if(((modifier&(1<<LEFT_SHIFT))!=0)||((modifier&(1<<RIGHT_SHIFT))!=0)) { + if(keycode & CAPITAL_MASK) + keycode&=~CAPITAL_MASK; /* switch to capital Letters */ + else + keycode|=CAPITAL_MASK; /* switch to non capital Letters */ + } + } + if((scancode>0x1d) && (scancode<0x3A)) { + if(((modifier&(1<<LEFT_SHIFT))!=0)||((modifier&(1<<RIGHT_SHIFT))!=0)) /* shifted */ + keycode=usb_kbd_numkey_shifted[scancode-0x1e]; + else /* non shifted */ + keycode=usb_kbd_numkey[scancode-0x1e]; + } + if(pressed==1) { + if(scancode==NUM_LOCK) { + num_lock=~num_lock; + return 1; + } + if(scancode==CAPS_LOCK) { + caps_lock=~caps_lock; + return 1; + } + if(scancode==SCROLL_LOCK) { + scroll_lock=~scroll_lock; + return 1; + } + } + if(keycode!=0) { + USB_KBD_PRINTF("%c",keycode); + usb_kbd_put_queue(keycode); + } + return 0; +} + +/* Interrupt service routine */ +static int usb_kbd_irq(struct usb_device *dev) +{ + int i,res; + + if((dev->irq_status!=0)||(dev->irq_act_len!=8)) + { + USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n",dev->irq_status,dev->irq_act_len); + return 1; + } + res=0; + for (i = 2; i < 8; i++) { + if (old[i] > 3 && memscan(&new[2], old[i], 6) == &new[8]) { + res|=usb_kbd_translate(old[i],new[0],0); + } + if (new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8]) { + res|=usb_kbd_translate(new[i],new[0],1); + } + } + if((new[2]>3) && (old[2]==new[2])) /* still pressed */ + res|=usb_kbd_translate(new[2],new[0],2); + if(res==1) + usb_kbd_setled(dev); + memcpy(&old[0],&new[0], 8); + return 1; /* install IRQ Handler again */ +} + +/* probes the USB device dev for keyboard type */ +static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe,maxp; + + if (dev->descriptor.bNumConfigurations != 1) return 0; + iface = &dev->config.if_desc[ifnum]; + + if (iface->bInterfaceClass != 3) return 0; + if (iface->bInterfaceSubClass != 1) return 0; + if (iface->bInterfaceProtocol != 1) return 0; + if (iface->bNumEndpoints != 1) return 0; + + ep = &iface->ep_desc[0]; + + if (!(ep->bEndpointAddress & 0x80)) return 0; + if ((ep->bmAttributes & 3) != 3) return 0; + USB_KBD_PRINTF("USB KBD found set protocol...\n"); + /* ok, we found a USB Keyboard, install it */ + /* usb_kbd_get_hid_desc(dev); */ + usb_set_protocol(dev, iface->bInterfaceNumber, 0); + USB_KBD_PRINTF("USB KBD found set idle...\n"); + usb_set_idle(dev, iface->bInterfaceNumber, REPEAT_RATE, 0); + memset(&new[0], 0, 8); + memset(&old[0], 0, 8); + repeat_delay=0; + pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe); + dev->irq_handle=usb_kbd_irq; + USB_KBD_PRINTF("USB KBD enable interrupt pipe...\n"); + usb_submit_int_msg(dev,pipe,&new[0], maxp > 8 ? 8 : maxp,ep->bInterval); + return 1; +} + + +#if 0 +struct usb_hid_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; /* 0x21 for HID */ + unsigned short bcdHID; /* release number */ + unsigned char bCountryCode; + unsigned char bNumDescriptors; + unsigned char bReportDescriptorType; + unsigned short wDescriptorLength; +} __attribute__ ((packed)); + +/* + * We parse each description item into this structure. Short items data + * values are expanded to 32-bit signed int, long items contain a pointer + * into the data area. + */ + +struct hid_item { + unsigned char format; + unsigned char size; + unsigned char type; + unsigned char tag; + union { + unsigned char u8; + char s8; + unsigned short u16; + short s16; + unsigned long u32; + long s32; + unsigned char *longdata; + } data; +}; + +/* + * HID report item format + */ + +#define HID_ITEM_FORMAT_SHORT 0 +#define HID_ITEM_FORMAT_LONG 1 + +/* + * Special tag indicating long items + */ + +#define HID_ITEM_TAG_LONG 15 + + + +static struct usb_hid_descriptor usb_kbd_hid_desc; + +void usb_kbd_display_hid(struct usb_hid_descriptor *hid) +{ + printf("USB_HID_DESC:\n"); + printf(" bLenght 0x%x\n",hid->bLength); + printf(" bcdHID 0x%x\n",hid->bcdHID); + printf(" bCountryCode %d\n",hid->bCountryCode); + printf(" bNumDescriptors 0x%x\n",hid->bNumDescriptors); + printf(" bReportDescriptorType 0x%x\n",hid->bReportDescriptorType); + printf(" wDescriptorLength 0x%x\n",hid->wDescriptorLength); +} + + +/* + * Fetch a report description item from the data stream. We support long + * items, though they are not used yet. + */ + +static int fetch_item(unsigned char *start,unsigned char *end, struct hid_item *item) +{ + if((end - start) > 0) { + unsigned char b = *start++; + item->type = (b >> 2) & 3; + item->tag = (b >> 4) & 15; + if (item->tag == HID_ITEM_TAG_LONG) { + item->format = HID_ITEM_FORMAT_LONG; + if ((end - start) >= 2) { + item->size = *start++; + item->tag = *start++; + if ((end - start) >= item->size) { + item->data.longdata = start; + start += item->size; + return item->size; + } + } + } else { + item->format = HID_ITEM_FORMAT_SHORT; + item->size = b & 3; + switch (item->size) { + case 0: + return item->size; + case 1: + if ((end - start) >= 1) { + item->data.u8 = *start++; + return item->size; + } + break; + case 2: + if ((end - start) >= 2) { + item->data.u16 = swap_16((unsigned short *)start); + start+=2; + return item->size; + } + case 3: + item->size++; + if ((end - start) >= 4) { + item->data.u32 = swap_32((unsigned long *)start); + start+=4; + return item->size; + } + } + } + } + return -1; +} + +/* + * HID report descriptor item type (prefix bit 2,3) + */ + +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 +/* + * HID report descriptor main item tags + */ + +#define HID_MAIN_ITEM_TAG_INPUT 8 +#define HID_MAIN_ITEM_TAG_OUTPUT 9 +#define HID_MAIN_ITEM_TAG_FEATURE 11 +#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 +#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 +/* + * HID report descriptor main item contents + */ + +#define HID_MAIN_ITEM_CONSTANT 0x001 +#define HID_MAIN_ITEM_VARIABLE 0x002 +#define HID_MAIN_ITEM_RELATIVE 0x004 +#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_NONLINEAR 0x010 +#define HID_MAIN_ITEM_NO_PREFERRED 0x020 +#define HID_MAIN_ITEM_NULL_STATE 0x040 +#define HID_MAIN_ITEM_VOLATILE 0x080 +#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 + +/* + * HID report descriptor collection item types + */ + +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 +#define HID_COLLECTION_LOGICAL 2 +/* + * HID report descriptor global item tags + */ + +#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 +#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 +#define HID_GLOBAL_ITEM_TAG_UNIT 6 +#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 +#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 +#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 +#define HID_GLOBAL_ITEM_TAG_PUSH 10 +#define HID_GLOBAL_ITEM_TAG_POP 11 + +/* + * HID report descriptor local item tags + */ + +#define HID_LOCAL_ITEM_TAG_USAGE 0 +#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 +#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 +#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 +#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 +#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 +#define HID_LOCAL_ITEM_TAG_DELIMITER 10 + + + +static void usb_kbd_show_item(struct hid_item *item) +{ + switch(item->type) { + case HID_ITEM_TYPE_MAIN: + switch(item->tag) { + case HID_MAIN_ITEM_TAG_INPUT: + printf("Main Input"); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + printf("Main Output"); + break; + case HID_MAIN_ITEM_TAG_FEATURE: + printf("Main Feature"); + break; + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + printf("Main Begin Collection"); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + printf("Main End Collection"); + break; + default: + printf("Main reserved %d",item->tag); + break; + } + break; + case HID_ITEM_TYPE_GLOBAL: + switch(item->tag) { + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: + printf("- Global Usage Page"); + break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: + printf("- Global Logical Minimum"); + break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + printf("- Global Logical Maximum"); + break; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: + printf("- Global physical Minimum"); + break; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: + printf("- Global physical Maximum"); + break; + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: + printf("- Global Unit Exponent"); + break; + case HID_GLOBAL_ITEM_TAG_UNIT: + printf("- Global Unit"); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: + printf("- Global Report Size"); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_ID: + printf("- Global Report ID"); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: + printf("- Global Report Count"); + break; + case HID_GLOBAL_ITEM_TAG_PUSH: + printf("- Global Push"); + break; + case HID_GLOBAL_ITEM_TAG_POP: + printf("- Global Pop"); + break; + default: + printf("- Global reserved %d",item->tag); + break; + } + break; + case HID_ITEM_TYPE_LOCAL: + switch(item->tag) { + case HID_LOCAL_ITEM_TAG_USAGE: + printf("-- Local Usage"); + break; + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + printf("-- Local Usage Minimum"); + break; + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + printf("-- Local Usage Maximum"); + break; + case HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX: + printf("-- Local Designator Index"); + break; + case HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM: + printf("-- Local Designator Minimum"); + break; + case HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM: + printf("-- Local Designator Maximum"); + break; + case HID_LOCAL_ITEM_TAG_STRING_INDEX: + printf("-- Local String Index"); + break; + case HID_LOCAL_ITEM_TAG_STRING_MINIMUM: + printf("-- Local String Minimum"); + break; + case HID_LOCAL_ITEM_TAG_STRING_MAXIMUM: + printf("-- Local String Maximum"); + break; + case HID_LOCAL_ITEM_TAG_DELIMITER: + printf("-- Local Delimiter"); + break; + default: + printf("-- Local reserved %d",item->tag); + break; + } + break; + default: + printf("--- reserved %d",item->type); + break; + } + switch(item->size) { + case 1: + printf(" %d",item->data.u8); + break; + case 2: + printf(" %d",item->data.u16); + break; + case 4: + printf(" %ld",item->data.u32); + break; + } + printf("\n"); +} + + + +static int usb_kbd_get_hid_desc(struct usb_device *dev) +{ + unsigned char buffer[256]; + struct usb_descriptor_header *head; + struct usb_config_descriptor *config; + int index,len,i; + unsigned char *start, *end; + struct hid_item item; + + if(usb_get_configuration_no(dev,&buffer[0],0)==-1) + return -1; + head =(struct usb_descriptor_header *)&buffer[0]; + if(head->bDescriptorType!=USB_DT_CONFIG) { + printf(" ERROR: NOT USB_CONFIG_DESC %x\n",head->bDescriptorType); + return -1; + } + index=head->bLength; + config=(struct usb_config_descriptor *)&buffer[0]; + len=swap_16(config->wTotalLength); + /* Ok the first entry must be a configuration entry, now process the others */ + head=(struct usb_descriptor_header *)&buffer[index]; + while(index+1 < len) { + if(head->bDescriptorType==USB_DT_HID) { + printf("HID desc found\n"); + memcpy(&usb_kbd_hid_desc,&buffer[index],buffer[index]); + usb_kbd_hid_desc.bcdHID=swap_16(usb_kbd_hid_desc.bcdHID); + usb_kbd_hid_desc.wDescriptorLength=swap_16(usb_kbd_hid_desc.wDescriptorLength); + usb_kbd_display_hid(&usb_kbd_hid_desc); + len=0; + break; + } + index+=head->bLength; + head=(struct usb_descriptor_header *)&buffer[index]; + } + if(len>0) + return -1; + len=usb_kbd_hid_desc.wDescriptorLength; + if((index = usb_get_class_descriptor(dev, 0, USB_DT_REPORT, 0, &buffer[0], len)) < 0) { + printf("reading report descriptor failed\n"); + return -1; + } + printf(" report descriptor (size %u, read %d)\n", len, index); + start=&buffer[0]; + end=&buffer[len]; + i=0; + do { + index=fetch_item(start,end,&item); + i+=index; + i++; + if(index>=0) + usb_kbd_show_item(&item); + + start+=index; + start++; + } while(index>=0); + +} + + +#endif + +#endif /* CONFIG_USB_KEYBOARD */ + +/* eof */ + diff --git a/common/usb_storage.c b/common/usb_storage.c new file mode 100644 index 00000000000..b1347219af4 --- /dev/null +++ b/common/usb_storage.c @@ -0,0 +1,895 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Most of this source has been derived from the Linux USB + * project. + * + * 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 + * + */ + +/* Note: + * Currently only the CBI transport protocoll has been implemented, and it + * is only tested with a TEAC USB Floppy. Other Massstorages with CBI or CB + * transport protocoll may work as well. + */ + + + +#include <common.h> +#include <command.h> +#include <asm/processor.h> + + +#if (CONFIG_COMMANDS & CFG_CMD_USB) +#include <usb.h> + +#ifdef CONFIG_USB_STORAGE + +#undef USB_STOR_DEBUG + +#ifdef USB_STOR_DEBUG +#define USB_STOR_PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define USB_STOR_PRINTF(fmt,args...) +#endif + +#include <scsi.h> +/* direction table -- this indicates the direction of the data + * transfer for each command code -- a 1 indicates input + */ +unsigned char us_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +static unsigned char usb_stor_buf[512]; +static ccb usb_ccb; + +/* + * CBI style + */ + +#define US_CBI_ADSC 0 + + +#define USB_MAX_STOR_DEV 5 +static int usb_max_devs; /* number of highest available usb device */ + +static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV]; + +struct us_data; +typedef int (*trans_cmnd)(ccb*, struct us_data*); +typedef int (*trans_reset)(struct us_data*); + +struct us_data { + struct usb_device *pusb_dev; /* this usb_device */ + unsigned int flags; /* from filter initially */ + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ + unsigned char attention_done; /* force attn on first cmd */ + unsigned short ip_data; /* interrupt data */ + int action; /* what to do */ + int ip_wanted; /* needed */ + int *irq_handle; /* for USB int requests */ + unsigned int irqpipe; /* pipe for release_irq */ + unsigned char irqmaxp; /* max packed for irq Pipe */ + unsigned char irqinterval; /* Intervall for IRQ Pipe */ + ccb *srb; /* current srb */ + trans_reset transport_reset; /* reset routine */ + trans_cmnd transport; /* transport routine */ +}; + +static struct us_data usb_stor[USB_MAX_STOR_DEV]; + + + +#define USB_STOR_TRANSPORT_GOOD 0 +#define USB_STOR_TRANSPORT_FAILED -1 +#define USB_STOR_TRANSPORT_ERROR -2 + + + + + + +int usb_stor_get_info(struct usb_device *dev, struct us_data *us, block_dev_desc_t *dev_desc); +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data *ss); +unsigned long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, unsigned long *buffer); +struct usb_device * usb_get_dev_index(int index); +void uhci_show_temp_int_td(void); + +block_dev_desc_t *usb_stor_get_dev(int index) +{ + return &usb_dev_desc[index]; +} + + +void usb_show_progress(void) +{ + printf("."); +} + +/********************************************************************************* + * (re)-scan the usb and reports device info + * to the user if mode = 1 + * returns current device or -1 if no + */ +int usb_stor_scan(int mode) +{ + unsigned char i; + struct usb_device *dev; + + if(mode==1) { + printf("scanning bus for storage devices...\n"); + } + usb_disable_asynch(1); /* asynch transfer not allowed */ + + for(i=0;i<USB_MAX_STOR_DEV;i++) { + memset(&usb_dev_desc[i],0,sizeof(block_dev_desc_t)); + usb_dev_desc[i].target=0xff; + usb_dev_desc[i].if_type=IF_TYPE_USB; + usb_dev_desc[i].dev=i; + usb_dev_desc[i].part_type=PART_TYPE_UNKNOWN; + usb_dev_desc[i].block_read=usb_stor_read; + } + usb_max_devs=0; + for(i=0;i<USB_MAX_DEVICE;i++) { + dev=usb_get_dev_index(i); /* get device */ + USB_STOR_PRINTF("i=%d\n",i); + if(dev==NULL) { + break; /* no more devices avaiable */ + } + if(usb_storage_probe(dev,0,&usb_stor[usb_max_devs])) { /* ok, it is a storage devices */ + /* get info and fill it in */ + + if(usb_stor_get_info(dev, &usb_stor[usb_max_devs], &usb_dev_desc[usb_max_devs])) { + if(mode==1) { + printf (" Device %d: ", usb_max_devs); + dev_print(&usb_dev_desc[usb_max_devs]); + } /* if mode */ + usb_max_devs++; + } /* if get info ok */ + } /* if storage device */ + if(usb_max_devs==USB_MAX_STOR_DEV) { + printf("max USB Storage Device reached: %d stopping\n",usb_max_devs); + break; + } + } /* for */ + usb_disable_asynch(0); /* asynch transfer allowed */ + if(usb_max_devs>0) + return 0; + else + return-1; +} + +static int usb_stor_irq(struct usb_device *dev) +{ + struct us_data *us; + us=(struct us_data *)dev->privptr; + + if(us->ip_wanted) { + us->ip_wanted=0; + } + return 0; +} + + +#ifdef USB_STOR_DEBUG + +static void usb_show_srb(ccb * pccb) +{ + int i; + printf("SRB: len %d datalen 0x%lX\n ",pccb->cmdlen,pccb->datalen); + for(i=0;i<12;i++) { + printf("%02X ",pccb->cmd[i]); + } + printf("\n"); +} + +static void display_int_status(unsigned long tmp) +{ + printf("Status: %s %s %s %s %s %s %s\n", + (tmp & USB_ST_ACTIVE) ? "Active" : "", + (tmp & USB_ST_STALLED) ? "Stalled" : "", + (tmp & USB_ST_BUF_ERR) ? "Buffer Error" : "", + (tmp & USB_ST_BABBLE_DET) ? "Babble Det" : "", + (tmp & USB_ST_NAK_REC) ? "NAKed" : "", + (tmp & USB_ST_CRC_ERR) ? "CRC Error" : "", + (tmp & USB_ST_BIT_ERR) ? "Bitstuff Error" : ""); +} +#endif +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) +{ + int max_size; + int this_xfer; + int result; + int partial; + int maxtry; + int stat; + + /* determine the maximum packet size for these transfers */ + max_size = usb_maxpacket(us->pusb_dev, pipe) * 16; + + /* while we have data left to transfer */ + while (length) { + + /* calculate how long this will be -- maximum or a remainder */ + this_xfer = length > max_size ? max_size : length; + length -= this_xfer; + + /* setup the retry counter */ + maxtry = 10; + + /* set up the transfer loop */ + do { + /* transfer the data */ + USB_STOR_PRINTF("Bulk xfer 0x%x(%d) try #%d\n", + (unsigned int)buf, this_xfer, 11 - maxtry); + result = usb_bulk_msg(us->pusb_dev, pipe, buf, + this_xfer, &partial, USB_CNTL_TIMEOUT*5); + USB_STOR_PRINTF("bulk_msg returned %d xferred %d/%d\n", + result, partial, this_xfer); + if(us->pusb_dev->status!=0) { + /* if we stall, we need to clear it before we go on */ +#ifdef USB_STOR_DEBUG + display_int_status(us->pusb_dev->status); +#endif + if (us->pusb_dev->status & USB_ST_STALLED) { + USB_STOR_PRINTF("stalled ->clearing endpoint halt for pipe 0x%x\n", pipe); + stat = us->pusb_dev->status; + usb_clear_halt(us->pusb_dev, pipe); + us->pusb_dev->status=stat; + if(this_xfer == partial) { + USB_STOR_PRINTF("bulk transferred with error %X, but data ok\n",us->pusb_dev->status); + return 0; + } + else + return result; + } + if (us->pusb_dev->status & USB_ST_NAK_REC) { + USB_STOR_PRINTF("Device NAKed bulk_msg\n"); + return result; + } + if(this_xfer == partial) { + USB_STOR_PRINTF("bulk transferred with error %d, but data ok\n",us->pusb_dev->status); + return 0; + } + /* if our try counter reaches 0, bail out */ + USB_STOR_PRINTF("bulk transferred with error %d, data %d\n",us->pusb_dev->status,partial); + if (!maxtry--) + return result; + } + /* update to show what data was transferred */ + this_xfer -= partial; + buf += partial; + /* continue until this transfer is done */ + } while ( this_xfer ); + } + + /* if we get here, we're done and successful */ + return 0; +} + +/* FIXME: this reset function doesn't really reset the port, and it + * should. Actually it should probably do what it's doing here, and + * reset the port physically + */ +static int usb_stor_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + int result; + + USB_STOR_PRINTF("CB_reset\n"); + memset(cmd, 0xFF, sizeof(cmd)); + cmd[0] = SCSI_SEND_DIAG; + cmd[1] = 4; + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, cmd, sizeof(cmd), USB_CNTL_TIMEOUT*5); + + /* long wait for reset */ + wait_ms(1500); + USB_STOR_PRINTF("CB_reset result %d: status %X clearing endpoint halt\n",result,us->pusb_dev->status); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + + USB_STOR_PRINTF("CB_reset done\n"); + return 0; +} + +/* FIXME: we also need a CBI_command which sets up the completion + * interrupt, and waits for it + */ +int usb_stor_CB_comdat(ccb *srb, struct us_data *us) +{ + int result; + int dir_in,retry; + unsigned int pipe; + unsigned long status; + + retry=5; + dir_in=US_DIRECTION(srb->cmd[0]); + + if(dir_in) + pipe=usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe=usb_sndbulkpipe(us->pusb_dev, us->ep_out); + while(retry--) { + USB_STOR_PRINTF("CBI gets a command: Try %d\n",5-retry); +#ifdef USB_STOR_DEBUG + usb_show_srb(srb); +#endif + /* let's send the command via the control pipe */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, + srb->cmd, srb->cmdlen, USB_CNTL_TIMEOUT*5); + USB_STOR_PRINTF("CB_transport: control msg returned %d, status %X\n",result,us->pusb_dev->status); + /* check the return code for the command */ + if (result < 0) { + if(us->pusb_dev->status & USB_ST_STALLED) { + status=us->pusb_dev->status; + USB_STOR_PRINTF(" stall during command found, clear pipe\n"); + usb_clear_halt(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0)); + us->pusb_dev->status=status; + } + USB_STOR_PRINTF(" error during command %02X Stat = %X\n",srb->cmd[0],us->pusb_dev->status); + return result; + } + /* transfer the data payload for this command, if one exists*/ + + USB_STOR_PRINTF("CB_transport: control msg returned %d, direction is %s to go 0x%lx\n",result,dir_in ? "IN" : "OUT",srb->datalen); + if (srb->datalen) { + result = us_one_transfer(us, pipe, srb->pdata,srb->datalen); + USB_STOR_PRINTF("CBI attempted to transfer data, result is %d status %lX, len %d\n", result,us->pusb_dev->status,us->pusb_dev->act_len); + if(!(us->pusb_dev->status & USB_ST_NAK_REC)) + break; + } /* if (srb->datalen) */ + else + break; + } + /* return result */ + + return result; +} + + +int usb_stor_CBI_get_status(ccb *srb, struct us_data *us) +{ + int timeout; + + us->ip_wanted=1; + submit_int_msg(us->pusb_dev,us->irqpipe, + (void *)&us->ip_data,us->irqmaxp ,us->irqinterval); + timeout=1000; + while(timeout--) { + if((volatile int *)us->ip_wanted==0) + break; + wait_ms(10); + } + if (us->ip_wanted) { + printf(" Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ERROR; + } + USB_STOR_PRINTF("Got interrupt data 0x%x, transfered %d status 0x%lX\n", us->ip_data,us->pusb_dev->irq_act_len,us->pusb_dev->irq_status); + /* UFI gives us ASC and ASCQ, like a request sense */ + if (us->subclass == US_SC_UFI) { + if (srb->cmd[0] == SCSI_REQ_SENSE || + srb->cmd[0] == SCSI_INQUIRY) + return USB_STOR_TRANSPORT_GOOD; /* Good */ + else + if (us->ip_data) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + /* otherwise, we interpret the data normally */ + switch (us->ip_data) { + case 0x0001: + return USB_STOR_TRANSPORT_GOOD; + case 0x0002: + return USB_STOR_TRANSPORT_FAILED; + default: + return USB_STOR_TRANSPORT_ERROR; + } /* switch */ + return USB_STOR_TRANSPORT_ERROR; +} + +#define USB_TRANSPORT_UNKNOWN_RETRY 5 +#define USB_TRANSPORT_NOT_READY_RETRY 10 + +int usb_stor_CB_transport(ccb *srb, struct us_data *us) +{ + int result,status; + ccb *psrb; + ccb reqsrb; + int retry,notready; + + psrb=&reqsrb; + status=USB_STOR_TRANSPORT_GOOD; + retry=0; + notready=0; + /* issue the command */ +do_retry: + result=usb_stor_CB_comdat(srb,us); + USB_STOR_PRINTF("command / Data returned %d, status %X\n",result,us->pusb_dev->status); + /* if this is an CBI Protocol, get IRQ */ + if(us->protocol==US_PR_CBI) { + status=usb_stor_CBI_get_status(srb,us); + /* if the status is error, report it */ + if(status==USB_STOR_TRANSPORT_ERROR) { + USB_STOR_PRINTF(" USB CBI Command Error\n"); + return status; + } + srb->sense_buf[12]=(unsigned char)(us->ip_data>>8); + srb->sense_buf[13]=(unsigned char)(us->ip_data&0xff); + if(!us->ip_data) { + /* if the status is good, report it */ + if(status==USB_STOR_TRANSPORT_GOOD) { + USB_STOR_PRINTF(" USB CBI Command Good\n"); + return status; + } + } + } + /* do we have to issue an auto request? */ + /* HERE we have to check the result */ + if((result<0) && !(us->pusb_dev->status & USB_ST_STALLED)) { + USB_STOR_PRINTF("ERROR %X\n",us->pusb_dev->status); + us->transport_reset(us); + return USB_STOR_TRANSPORT_ERROR; + } + if((us->protocol==US_PR_CBI) && + ((srb->cmd[0]==SCSI_REQ_SENSE) || + (srb->cmd[0]==SCSI_INQUIRY))) { /* do not issue an autorequest after request sense */ + USB_STOR_PRINTF("No auto request and good\n"); + return USB_STOR_TRANSPORT_GOOD; + } + /* issue an request_sense */ + memset(&psrb->cmd[0],0,12); + psrb->cmd[0]=SCSI_REQ_SENSE; + psrb->cmd[1]=srb->lun<<5; + psrb->cmd[4]=18; + psrb->datalen=18; + psrb->pdata=&srb->sense_buf[0]; + psrb->cmdlen=12; + /* issue the command */ + result=usb_stor_CB_comdat(psrb,us); + USB_STOR_PRINTF("auto request returned %d\n",result); + /* if this is an CBI Protocol, get IRQ */ + if(us->protocol==US_PR_CBI) { + status=usb_stor_CBI_get_status(psrb,us); + } + if((result<0)&&!(us->pusb_dev->status & USB_ST_STALLED)) { + USB_STOR_PRINTF(" AUTO REQUEST ERROR %d\n",us->pusb_dev->status); + return USB_STOR_TRANSPORT_ERROR; + } + USB_STOR_PRINTF("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\n",srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + /* Check the auto request result */ + if((srb->sense_buf[2]==0) && + (srb->sense_buf[12]==0) && + (srb->sense_buf[13]==0)) /* ok, no sense */ + return USB_STOR_TRANSPORT_GOOD; + /* Check the auto request result */ + switch(srb->sense_buf[2]) { + case 0x01: /* Recovered Error */ + return USB_STOR_TRANSPORT_GOOD; + break; + case 0x02: /* Not Ready */ + if(notready++ > USB_TRANSPORT_NOT_READY_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X (NOT READY)\n", + srb->cmd[0],srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + return USB_STOR_TRANSPORT_FAILED; + } + else { + wait_ms(100); + goto do_retry; + } + break; + default: + if(retry++ > USB_TRANSPORT_UNKNOWN_RETRY) { + printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X\n", + srb->cmd[0],srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + return USB_STOR_TRANSPORT_FAILED; + } + else + goto do_retry; + break; + } + return USB_STOR_TRANSPORT_FAILED; +} + + + +static int usb_inquiry(ccb *srb,struct us_data *ss) +{ + int retry,i; + retry=3; + do { + memset(&srb->cmd[0],0,12); + srb->cmd[0]=SCSI_INQUIRY; + srb->cmd[1]=srb->lun<<5; + srb->cmd[4]=36; + srb->datalen=36; + srb->cmdlen=12; + i=ss->transport(srb,ss); + USB_STOR_PRINTF("inquiry returns %d\n",i); + if(i==0) + break; + }while(retry--); + if(!retry) { + printf("error in inquiry\n"); + return -1; + } + return 0; +} + +static int usb_request_sense(ccb *srb,struct us_data *ss) +{ + char *ptr; + return 0; + ptr=srb->pdata; + memset(&srb->cmd[0],0,12); + srb->cmd[0]=SCSI_REQ_SENSE; + srb->cmd[1]=srb->lun<<5; + srb->cmd[4]=18; + srb->datalen=18; + srb->pdata=&srb->sense_buf[0]; + srb->cmdlen=12; + ss->transport(srb,ss); + USB_STOR_PRINTF("Request Sense returned %02X %02X %02X\n",srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + srb->pdata=ptr; + return 0; +} + +static int usb_test_unit_ready(ccb *srb,struct us_data *ss) +{ + int retries=10; + do { + memset(&srb->cmd[0],0,12); + srb->cmd[0]=SCSI_TST_U_RDY; + srb->cmd[1]=srb->lun<<5; + srb->datalen=0; + srb->cmdlen=12; + if(ss->transport(srb,ss)==USB_STOR_TRANSPORT_GOOD) + { + return 0; + } + } while(retries--); + return -1; +} + +static int usb_read_capacity(ccb *srb,struct us_data *ss) +{ + int retry; + retry=2; /* retries */ + do { + memset(&srb->cmd[0],0,12); + srb->cmd[0]=SCSI_RD_CAPAC; + srb->cmd[1]=srb->lun<<5; + srb->datalen=8; + srb->cmdlen=12; + if(ss->transport(srb,ss)==USB_STOR_TRANSPORT_GOOD) { + return 0; + } + }while(retry--); + return -1; +} + +static int usb_read_10(ccb *srb,struct us_data *ss, unsigned long start, unsigned short blocks) +{ + memset(&srb->cmd[0],0,12); + srb->cmd[0]=SCSI_READ10; + srb->cmd[1]=srb->lun<<5; + srb->cmd[2]=((unsigned char) (start>>24))&0xff; + srb->cmd[3]=((unsigned char) (start>>16))&0xff; + srb->cmd[4]=((unsigned char) (start>>8))&0xff; + srb->cmd[5]=((unsigned char) (start))&0xff; + srb->cmd[7]=((unsigned char) (blocks>>8))&0xff; + srb->cmd[8]=(unsigned char) blocks & 0xff; + srb->cmdlen=12; + USB_STOR_PRINTF("read10: start %lx blocks %x\n",start,blocks); + return ss->transport(srb,ss); +} + + +#define USB_MAX_READ_BLK 20 + +unsigned long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, unsigned long *buffer) +{ + unsigned long start,blks, buf_addr; + unsigned short smallblks; + struct usb_device *dev; + int retry,i; + ccb *srb=&usb_ccb; + device&=0xff; + /* Setup device + */ + USB_STOR_PRINTF("\nusb_read: dev %d \n",device); + dev=NULL; + for(i=0;i<USB_MAX_DEVICE;i++) { + dev=usb_get_dev_index(i); + if(dev==NULL) { + return 0; + } + if(dev->devnum==usb_dev_desc[device].target) + break; + } + + usb_disable_asynch(1); /* asynch transfer not allowed */ + srb->lun=usb_dev_desc[device].lun; + buf_addr=(unsigned long)buffer; + start=blknr; + blks=blkcnt; + if(usb_test_unit_ready(srb,(struct us_data *)dev->privptr)) { + printf("Device NOT ready\n Request Sense returned %02X %02X %02X\n", + srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]); + return 0; + } + USB_STOR_PRINTF("\nusb_read: dev %d startblk %lx, blccnt %lx buffer %lx\n",device,start,blks, buf_addr); + do { + retry=2; + srb->pdata=(unsigned char *)buf_addr; + if(blks>USB_MAX_READ_BLK) { + smallblks=USB_MAX_READ_BLK; + } + else { + smallblks=(unsigned short) blks; + } +retry_it: + if(smallblks==USB_MAX_READ_BLK) + usb_show_progress(); + srb->datalen=usb_dev_desc[device].blksz * smallblks; + srb->pdata=(unsigned char *)buf_addr; + if(usb_read_10(srb,(struct us_data *)dev->privptr, start, smallblks)) { + USB_STOR_PRINTF("Read ERROR\n"); + usb_request_sense(srb,(struct us_data *)dev->privptr); + if(retry--) + goto retry_it; + blkcnt-=blks; + break; + } + start+=smallblks; + blks-=smallblks; + buf_addr+=srb->datalen; + } while(blks!=0); + USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\n",start,smallblks,buf_addr); + usb_disable_asynch(0); /* asynch transfer allowed */ + if(blkcnt>=USB_MAX_READ_BLK) + printf("\n"); + return(blkcnt); +} + + +/* Probe to see if a new device is actually a Storage device */ +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data *ss) +{ + struct usb_interface_descriptor *iface; + int i; + unsigned int flags = 0; + + int protocol = 0; + int subclass = 0; + + + memset(ss, 0, sizeof(struct us_data)); + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + +#if 0 + /* this is the place to patch some storage devices */ + USB_STOR_PRINTF("iVendor %X iProduct %X\n",dev->descriptor.idVendor,dev->descriptor.idProduct); + if ((dev->descriptor.idVendor) == 0x066b && (dev->descriptor.idProduct) == 0x0103) { + USB_STOR_PRINTF("patched for E-USB\n"); + protocol = US_PR_CB; + subclass = US_SC_UFI; /* an assumption */ + } +#endif + + if (dev->descriptor.bDeviceClass != 0 || + iface->bInterfaceClass != USB_CLASS_MASS_STORAGE || + iface->bInterfaceSubClass < US_SC_MIN || + iface->bInterfaceSubClass > US_SC_MAX) { + /* if it's not a mass storage, we go no further */ + return 0; + } + + /* At this point, we know we've got a live one */ + USB_STOR_PRINTF("\n\nUSB Mass Storage device detected\n"); + + /* Initialize the us_data structure with some useful info */ + ss->flags = flags; + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->attention_done = 0; + + /* If the device has subclass and protocol, then use that. Otherwise, + * take data from the specific interface. + */ + if (subclass) { + ss->subclass = subclass; + ss->protocol = protocol; + } else { + ss->subclass = iface->bInterfaceSubClass; + ss->protocol = iface->bInterfaceProtocol; + } + + /* set the handler pointers based on the protocol */ + USB_STOR_PRINTF("Transport: "); + switch (ss->protocol) { + case US_PR_CB: + USB_STOR_PRINTF("Control/Bulk\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + + case US_PR_CBI: + USB_STOR_PRINTF("Control/Bulk/Interrupt\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + default: + printf("USB Starage Transport unknown / not yet implemented\n"); + return 0; + break; + } + + /* + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for (i = 0; i < iface->bNumEndpoints; i++) { + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK) { + if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN) + ss->ep_in = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else + ss->ep_out = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT) { + ss->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = iface->ep_desc[i].bInterval; + } + } + USB_STOR_PRINTF("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || + (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { + USB_STOR_PRINTF("Problems with device\n"); + return 0; + } + /* set class specific stuff */ + /* We only handle certain protocols. Currently, this is + * the only one. + */ + if (ss->subclass != US_SC_UFI) { + printf("Sorry, protocol %d not yet supported.\n",ss->subclass); + return 0; + } + if(ss->ep_int) /* we had found an interrupt endpoint, prepare irq pipe */ + { + /* set up the IRQ pipe and handler */ + + ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255; + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe); + dev->irq_handle=usb_stor_irq; + dev->privptr=(void *)ss; + } + return 1; +} + +int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t *dev_desc) +{ + unsigned char perq,modi; + unsigned long cap[2]; + unsigned long *capacity,*blksz; + ccb *pccb=&usb_ccb; + + ss->transport_reset(ss); + pccb->pdata=usb_stor_buf; + + dev_desc->target=dev->devnum; + pccb->lun=dev_desc->lun; + USB_STOR_PRINTF(" address %d\n",dev_desc->target); + + if(usb_inquiry(pccb,ss)) + return -1; + perq=usb_stor_buf[0]; + modi=usb_stor_buf[1]; + if((perq & 0x1f)==0x1f) { + return 0; /* skip unknown devices */ + } + if((modi&0x80)==0x80) {/* drive is removable */ + dev_desc->removable=1; + } + memcpy(&dev_desc->vendor[0], &usb_stor_buf[8], 8); + memcpy(&dev_desc->product[0], &usb_stor_buf[16], 16); + memcpy(&dev_desc->revision[0], &usb_stor_buf[32], 4); + dev_desc->vendor[8]=0; + dev_desc->product[16]=0; + dev_desc->revision[4]=0; + USB_STOR_PRINTF("ISO Vers %X, Response Data %X\n",usb_stor_buf[2],usb_stor_buf[3]); + if(usb_test_unit_ready(pccb,ss)) { + printf("Device NOT ready\n Request Sense returned %02X %02X %02X\n",pccb->sense_buf[2],pccb->sense_buf[12],pccb->sense_buf[13]); + if(dev_desc->removable==1) { + dev_desc->type=perq; + return 1; + } + else + return 0; + } + pccb->pdata=(unsigned char *)&cap[0]; + memset(pccb->pdata,0,8); + if(usb_read_capacity(pccb,ss)!=0) { + printf("READ_CAP ERROR\n"); + cap[0]=2880; + cap[1]=0x200; + } + USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\n",cap[0],cap[1]); +#if 0 + if(cap[0]>(0x200000 * 10)) /* greater than 10 GByte */ + cap[0]>>=16; +#endif + cap[0]+=1; + capacity=&cap[0]; + blksz=&cap[1]; + USB_STOR_PRINTF("Capacity = 0x%lx, blocksz = 0x%lx\n",*capacity,*blksz); + dev_desc->lba=*capacity; + dev_desc->blksz=*blksz; + dev_desc->type=perq; + USB_STOR_PRINTF(" address %d\n",dev_desc->target); + USB_STOR_PRINTF("partype: %d\n",dev_desc->part_type); + + init_part(dev_desc); + + USB_STOR_PRINTF("partype: %d\n",dev_desc->part_type); + return 1; +} + +#endif +#endif /* CONFIG_USB_STORAGE */ + + + diff --git a/cpu/74xx_7xx/traps.c b/cpu/74xx_7xx/traps.c new file mode 100644 index 00000000000..26db0974025 --- /dev/null +++ b/cpu/74xx_7xx/traps.c @@ -0,0 +1,234 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (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 handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +int (*debugger_exception_handler)(struct pt_regs *) = 0; +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void +show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS:" + " %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP:" + " %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x0000F000) + { + case (1<<12) : + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (1<<13) : + printf("Transfer error ack signal\n"); + break; + case (1<<14) : + printf("Data parity signal\n"); + break; + case (1<<15) : + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/cpu/mpc824x/drivers/epic/epic1.c b/cpu/mpc824x/drivers/epic/epic1.c new file mode 100644 index 00000000000..362e12970d2 --- /dev/null +++ b/cpu/mpc824x/drivers/epic/epic1.c @@ -0,0 +1,513 @@ +/************************************************** + * + * copyright @ motorola, 1999 + * + *************************************************/ +#include <mpc824x.h> +#include <common.h> +#include "epic.h" + + +#define PRINT(format, args...) printf(format , ## args) + +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); + 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 */ +} + +/**************************************************************************** + * 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 /* Interrupt Vector Number */ + ) + { + ULONG tmp; + ULONG srAddr; + + srAddr = SrcVecTable[intVec].srcAddr; /* Retrieve src Vec/Prio register */ + tmp = sysEUMBBARRead(srAddr); + tmp &= 0x7fffffff; /* Clear the mask bit */ + 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 + ) + { + ULONG temp; + + temp = *(ULONG *) (CFG_EUMB_ADDR + regNum); + return ( LONGSWAP(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 */ + ULONG regVal /* Value to be written */ + ) + { + + *(ULONG *) (CFG_EUMB_ADDR + regNum) = LONGSWAP(regVal); + 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); + PRINT("%s\n", SrcVecTable[Vect].srcName); + PRINT("Address = 0x%lx\n", SrcVecTable[Vect].srcAddr); + PRINT("Vector = %ld\n", (srcVal & 0x000000FF) ); + PRINT("Mask = %ld\n", srcVal >> 31); + PRINT("Activitiy = %ld\n", (srcVal & 40000000) >> 30); + PRINT("Polarity = %ld\n", (srcVal & 0x00800000) >> 23); + PRINT("Sense = %ld\n", (srcVal & 0x00400000) >> 22); + PRINT("Priority = %ld\n", (srcVal & 0x000F0000) >> 16); + } diff --git a/cpu/mpc824x/traps.c b/cpu/mpc824x/traps.c new file mode 100644 index 00000000000..11f51c25011 --- /dev/null +++ b/cpu/mpc824x/traps.c @@ -0,0 +1,205 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (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 handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <asm/processor.h> + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x00400000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x0000F000) + { + case (1<<12) : + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (1<<13) : + printf("Transfer error ack signal\n"); + break; + case (1<<14) : + printf("Data parity signal\n"); + break; + case (1<<15) : + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/cpu/mpc8260/ether_fcc.c b/cpu/mpc8260/ether_fcc.c new file mode 100644 index 00000000000..01ddf5ccba4 --- /dev/null +++ b/cpu/mpc8260/ether_fcc.c @@ -0,0 +1,347 @@ +/* + * MPC8260 FCC Fast Ethernet + * + * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.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 + */ + +/* + * MPC8260 FCC Fast Ethernet + * Basic ET HW initialization and packet RX/TX routines + * + * This code will not perform the IO port configuration. This should be + * done in the iop_conf_t structure specific for the board. + * + * TODO: + * add a PHY driver to do the negotiation + * reflect negotiation results in FPSMR + * look for ways to configure the board specific stuff elsewhere, eg. + * config_xxx.h or the board directory + */ + +#include <common.h> +#include <asm/cpm_8260.h> +#include <mpc8260.h> +#include <net.h> +#include <command.h> +#include <config.h> + +#if defined(CONFIG_ETHER_ON_FCC) && (CONFIG_COMMANDS & CFG_CMD_NET) + +/*---------------------------------------------------------------------*/ +#if (CONFIG_ETHER_INDEX == 1) + +#define PROFF_ENET PROFF_FCC1 +#define CPM_CR_ENET_SBLOCK CPM_CR_FCC1_SBLOCK +#define CPM_CR_ENET_SBLOCK CPM_CR_FCC1_SBLOCK +#define CPM_CR_ENET_PAGE CPM_CR_FCC1_PAGE + +/*---------------------------------------------------------------------*/ +#elif (CONFIG_ETHER_INDEX == 2) + +#define PROFF_ENET PROFF_FCC2 +#define CPM_CR_ENET_SBLOCK CPM_CR_FCC2_SBLOCK +#define CPM_CR_ENET_PAGE CPM_CR_FCC2_PAGE + +/*---------------------------------------------------------------------*/ +#elif (CONFIG_ETHER_INDEX == 3) + +#define PROFF_ENET PROFF_FCC3 +#define CPM_CR_ENET_SBLOCK CPM_CR_FCC3_SBLOCK +#define CPM_CR_ENET_PAGE CPM_CR_FCC3_PAGE + +/*---------------------------------------------------------------------*/ +#else +#error "FCC Ethernet not correctly defined" +#endif +/*---------------------------------------------------------------------*/ + +/* Maximum input DMA size. Must be a should(?) be a multiple of 4. */ +#define PKT_MAXDMA_SIZE 1520 + +/* The FCC stores dest/src/type, data, and checksum for receive packets. */ +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 + +/* Maximum input buffer size. Must be a multiple of 32. */ +#define PKT_MAXBLR_SIZE 1536 + +#define TOUT_LOOP 1000000 + +#define TX_BUF_CNT 2 +#ifdef __GNUC__ +static char txbuf[TX_BUF_CNT][PKT_MAXBLR_SIZE] __attribute__ ((aligned(8))); +#else +#error "txbuf must be 64-bit aligned" +#endif + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +/* + * FCC Ethernet Tx and Rx buffer descriptors. + * Provide for Double Buffering + * Note: PKTBUFSRX is defined in net.h + */ + +typedef volatile struct rtxbd { + cbd_t rxbd[PKTBUFSRX]; + cbd_t txbd[TX_BUF_CNT]; +} RTXBD; + +/* Good news: the FCC supports external BDs! */ +#ifdef __GNUC__ +static RTXBD rtx __attribute__ ((aligned(8))); +#else +#error "rtx must be 64-bit aligned" +#endif + +int eth_send(volatile void *packet, int length) +{ + int i; + int result = 0; + + if (length <= 0) { + printf("fec: bad packet size: %d\n", length); + goto out; + } + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + printf("fec: tx buffer not ready\n"); + goto out; + } + } + + rtx.txbd[txIdx].cbd_bufaddr = (uint)packet; + rtx.txbd[txIdx].cbd_datlen = length; + rtx.txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST | + BD_ENET_TX_WRAP); + + for(i=0; rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) { + if (i >= TOUT_LOOP) { + printf("fec: tx error\n"); + goto out; + } + } + +#ifdef ET_DEBUG + printf("cycles: %d status: %04x\n", i, rtx.txbd[txIdx].cbd_sc); +#endif + + /* return only status bits */ + result = rtx.txbd[txIdx].cbd_sc & BD_ENET_TX_STATS; + +out: + return result; +} + +int eth_rx(void) +{ + int length; + + for (;;) + { + if (rtx.rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + length = rtx.rxbd[rxIdx].cbd_datlen; + + if (rtx.rxbd[rxIdx].cbd_sc & 0x003f) { + printf("fec: rx error %04x\n", rtx.rxbd[rxIdx].cbd_sc); + } + else { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[rxIdx], length - 4); + } + + + /* Give the buffer back to the FCC. */ + rtx.rxbd[rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if ((rxIdx + 1) >= PKTBUFSRX) { + rtx.rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + rxIdx = 0; + } + else { + rtx.rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + rxIdx++; + } + } + return length; +} + + +int eth_init(bd_t *bis) +{ + int i; + volatile immap_t *immr = (immap_t *)CFG_IMMR; + volatile cpm8260_t *cp = &(immr->im_cpm); + fcc_enet_t *pram_ptr; + unsigned long mem_addr; + +#if 0 + mii_discover_phy(); +#endif + + /* 28.9 - (1-2): ioports have been set up already */ + + /* 28.9 - (3): connect FCC's tx and rx clocks */ + immr->im_cpmux.cmx_uar = 0; + immr->im_cpmux.cmx_fcr = (immr->im_cpmux.cmx_fcr & ~CFG_CMXFCR_MASK) | + CFG_CMXFCR_VALUE; + + /* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, Mode Ethernet */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_gfmr = + FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32; + + /* 28.9 - (5): FPSMR: enable full duplex, select CCITT CRC for Ethernet */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_fpsmr = CFG_FCC_PSMR | FCC_PSMR_ENCRC; + + /* 28.9 - (6): FDSR: Ethernet Syn */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_fdsr = 0xD555; + + /* reset indeces to current rx/tx bd (see eth_send()/eth_rx()) */ + rxIdx = 0; + txIdx = 0; + + /* Setup Receiver Buffer Descriptors */ + for (i = 0; i < PKTBUFSRX; i++) + { + rtx.rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + rtx.rxbd[i].cbd_datlen = 0; + rtx.rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i]; + } + rtx.rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* Setup Ethernet Transmitter Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) + { + rtx.txbd[i].cbd_sc = (BD_ENET_TX_PAD | BD_ENET_TX_LAST | BD_ENET_TX_TC); + rtx.txbd[i].cbd_datlen = 0; + rtx.txbd[i].cbd_bufaddr = (uint)&txbuf[i][0]; + } + rtx.txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* 28.9 - (7): initialise parameter ram */ + pram_ptr = (fcc_enet_t *)&(immr->im_dprambase[PROFF_ENET]); + + /* clear whole structure to make sure all reserved fields are zero */ + memset((void*)pram_ptr, 0, sizeof(fcc_enet_t)); + + /* + * common Parameter RAM area + * + * Allocate space in the reserved FCC area of DPRAM for the + * internal buffers. No one uses this space (yet), so we + * can do this. Later, we will add resource management for + * this area. + */ + mem_addr = CPM_FCC_SPECIAL_BASE + ((CONFIG_ETHER_INDEX-1) * 64); + pram_ptr->fen_genfcc.fcc_riptr = mem_addr; + pram_ptr->fen_genfcc.fcc_tiptr = mem_addr+32; + /* + * Set maximum bytes per receive buffer. + * It must be a multiple of 32. + */ + pram_ptr->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; + pram_ptr->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB | + CFG_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_rbase = (unsigned int)(&rtx.rxbd[rxIdx]); + pram_ptr->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB | + CFG_CPMFCR_RAMTYPE) << 24; + pram_ptr->fen_genfcc.fcc_tbase = (unsigned int)(&rtx.txbd[txIdx]); + + /* protocol-specific area */ + pram_ptr->fen_cmask = 0xdebb20e3; /* CRC mask */ + pram_ptr->fen_cpres = 0xffffffff; /* CRC preset */ + pram_ptr->fen_retlim = 15; /* Retry limit threshold */ + pram_ptr->fen_mflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ + /* + * Set Ethernet station address. + * + * This is supplied in the board information structure, so we + * copy that into the controller. + * So, far we have only been given one Ethernet address. We make + * it unique by setting a few bits in the upper byte of the + * non-static part of the address. + */ +#define ea bis->bi_enetaddr + pram_ptr->fen_paddrh = (ea[5] << 8) + ea[4]; + pram_ptr->fen_paddrm = (ea[3] << 8) + ea[2]; + pram_ptr->fen_paddrl = (ea[1] << 8) + ea[0]; +#undef ea + pram_ptr->fen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ + /* pad pointer. use tiptr since we don't need a specific padding char */ + pram_ptr->fen_padptr = pram_ptr->fen_genfcc.fcc_tiptr; + pram_ptr->fen_maxd1 = PKT_MAXDMA_SIZE; /* maximum DMA1 length */ + pram_ptr->fen_maxd2 = PKT_MAXDMA_SIZE; /* maximum DMA2 length */ + pram_ptr->fen_rfthr = 1; + pram_ptr->fen_rfcnt = 1; +#if 0 + printf("pram_ptr->fen_genfcc.fcc_rbase %08lx\n", + pram_ptr->fen_genfcc.fcc_rbase); + printf("pram_ptr->fen_genfcc.fcc_tbase %08lx\n", + pram_ptr->fen_genfcc.fcc_tbase); +#endif + + /* 28.9 - (8): clear out events in FCCE */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_fcce = ~0x0; + + /* 28.9 - (9): FCCM: mask all events */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_fccm = 0; + + /* 28.9 - (10-12): we don't use ethernet interrupts */ + + /* 28.9 - (13) + * + * Let's re-initialize the channel now. We have to do it later + * than the manual describes because we have just now finished + * the BD initialization. + */ + cp->cp_cpcr = mk_cr_cmd(CPM_CR_ENET_PAGE, + CPM_CR_ENET_SBLOCK, + 0x0c, + CPM_CR_INIT_TRX) | CPM_CR_FLG; + do { + __asm__ __volatile__ ("eieio"); + } while (cp->cp_cpcr & CPM_CR_FLG); + + /* 28.9 - (14): enable tx/rx in gfmr */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR; + + return 1; +} + +void eth_halt(void) +{ + volatile immap_t *immr = (immap_t *)CFG_IMMR; + + /* write GFMR: disable tx/rx */ + immr->im_fcc[CONFIG_ETHER_INDEX-1].fcc_gfmr &= + ~(FCC_GFMR_ENT | FCC_GFMR_ENR); +} + +#endif /* CONFIG_ETHER_ON_FCC && CFG_CMD_NET */ diff --git a/cpu/mpc8260/traps.c b/cpu/mpc8260/traps.c new file mode 100644 index 00000000000..a7f6f7e1a78 --- /dev/null +++ b/cpu/mpc8260/traps.c @@ -0,0 +1,246 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (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 handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +int (*debugger_exception_handler)(struct pt_regs *) = 0; +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x0000F000) + { + case (1<<12) : + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (1<<13) : + printf("Transfer error ack signal\n"); + break; + case (1<<14) : + printf("Data parity signal\n"); + break; + case (1<<15) : + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +void +DebugException(struct pt_regs *regs) +{ + + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/cpu/mpc8xx/traps.c b/cpu/mpc8xx/traps.c new file mode 100644 index 00000000000..c432be6030c --- /dev/null +++ b/cpu/mpc8xx/traps.c @@ -0,0 +1,245 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (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 handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +int (*debugger_exception_handler)(struct pt_regs *) = 0; +#endif + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. +*/ +#define END_OF_MEM 0x02000000 + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x0000F000) + { + case (1<<12) : + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (1<<13) : + printf("Transfer error ack signal\n"); + break; + case (1<<14) : + printf("Data parity signal\n"); + break; + case (1<<15) : + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +SoftEmuException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Software Emulation Exception"); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/cpu/mpc8xx/upatch.c b/cpu/mpc8xx/upatch.c new file mode 100644 index 00000000000..99bdf53e27a --- /dev/null +++ b/cpu/mpc8xx/upatch.c @@ -0,0 +1,102 @@ +#include <common.h> +#include <commproc.h> + +#if defined(CFG_I2C_UCODE_PATCH) || defined(CFG_SPI_UCODE_PATCH) + +static void UcodeCopy (volatile cpm8xx_t *cpm); + +void cpm_load_patch (volatile immap_t *immr) +{ + immr->im_cpm.cp_rccr &= ~0x0003; /* Disable microcode program area */ + + UcodeCopy ((cpm8xx_t *)&immr->im_cpm); /* Copy ucode patch to DPRAM */ +#ifdef CFG_SPI_UCODE_PATCH + { + volatile spi_t *spi = (spi_t *) & immr->im_cpm.cp_dparam[PROFF_SPI]; + /* Activate the microcode per the instructions in the microcode manual */ + /* NOTE: We're only relocating the SPI parameters (not I2C). */ + immr->im_cpm.cp_cpmcr1 = 0x802a; /* Write Trap register 1 value */ + immr->im_cpm.cp_cpmcr2 = 0x8028; /* Write Trap register 2 value */ + spi->spi_rpbase = CFG_SPI_DPMEM_OFFSET; /* Where to relocte SPI params */ + } +#endif + +#ifdef CFG_I2C_UCODE_PATCH + { + volatile iic_t *iip = (iic_t *) & immr->im_cpm.cp_dparam[PROFF_IIC]; + /* Activate the microcode per the instructions in the microcode manual */ + /* NOTE: We're only relocating the I2C parameters (not SPI). */ + immr->im_cpm.cp_cpmcr3 = 0x802e; /* Write Trap register 3 value */ + immr->im_cpm.cp_cpmcr4 = 0x802c; /* Write Trap register 4 value */ + iip->iic_rpbase = CFG_I2C_DPMEM_OFFSET; /* Where to relocte I2C params */ + } +#endif + + /* + * Enable DPRAM microcode to execute from the first 512 bytes + * and a 256 byte extension of DPRAM. + */ + immr->im_cpm.cp_rccr |= 0x0001; +} + +static ulong patch_2000[] = { + 0x7FFFEFD9, 0x3FFD0000, 0x7FFB49F7, 0x7FF90000, + 0x5FEFADF7, 0x5F88ADF7, 0x5FEFAFF7, 0x5F88AFF7, + 0x3A9CFBC8, 0x77CAE1BB, 0xF4DE7FAD, 0xABAE9330, + 0x4E08FDCF, 0x6E0FAFF8, 0x7CCF76CF, 0xFDAFF9CF, + 0xABF88DC8, 0xAB5879F7, 0xB0927383, 0xDFD079F7, + 0xB090E6BB, 0xE5BBE74F, 0xB3FA6F0F, 0x6FFB76CE, + 0xEE0CF9CF, 0x2BFBEFEF, 0xCFEEF9CF, 0x76CEAD23, + 0x90B3DF99, 0x7FDDD0C1, 0x4BF847FD, 0x7CCF76CE, + 0xCFEF77CA, 0x7EAF7FAD, 0x7DFDF0B7, 0xEF7A7FCA, + 0x77CAFBC8, 0x6079E722, 0xFBC85FFF, 0xDFFF5FB3, + 0xFFFBFBC8, 0xF3C894A5, 0xE7C9EDF9, 0x7F9A7FAD, + 0x5F36AFE8, 0x5F5BFFDF, 0xDF95CB9E, 0xAF7D5FC3, + 0xAFED8C1B, 0x5FC3AFDD, 0x5FC5DF99, 0x7EFDB0B3, + 0x5FB3FFFE, 0xABAE5FB3, 0xFFFE5FD0, 0x600BE6BB, + 0x600B5FD0, 0xDFC827FB, 0xEFDF5FCA, 0xCFDE3A9C, + 0xE7C9EDF9, 0xF3C87F9E, 0x54CA7FED, 0x2D3A3637, + 0x756F7E9A, 0xF1CE37EF, 0x2E677FEE, 0x10EBADF8, + 0xEFDECFEA, 0xE52F7D9F, 0xE12BF1CE, 0x5F647E9A, + 0x4DF8CFEA, 0x5F717D9B, 0xEFEECFEA, 0x5F73E522, + 0xEFDE5F73, 0xCFDA0B61, 0x7385DF61, 0xE7C9EDF9, + 0x7E9A30D5, 0x1458BFFF, 0xF3C85FFF, 0xDFFFA7F8, + 0x5F5BBFFE, 0x7F7D10D0, 0x144D5F33, 0xBFFFAF78, + 0x5F5BBFFD, 0xA7F85F33, 0xBFFE77FD, 0x30BD4E08, + 0xFDCFE5FF, 0x6E0FAFF8, 0x7EEF7E9F, 0xFDEFF1CF, + 0x5F17ABF8, 0x0D5B5F5B, 0xFFEF79F7, 0x309EAFDD, + 0x5F3147F8, 0x5F31AFED, 0x7FDD50AF, 0x497847FD, + 0x7F9E7FED, 0x7DFD70A9, 0xEF7E7ECE, 0x6BA07F9E, + 0x2D227EFD, 0x30DB5F5B, 0xFFFD5F5B, 0xFFEF5F5B, + 0xFFDF0C9C, 0xAFED0A9A, 0xAFDD0C37, 0x5F37AFBD, + 0x7FBDB081, 0x5F8147F8, +}; + +static ulong patch_2F00[] = { + 0x3E303430, 0x34343737, 0xABBF9B99, 0x4B4FBDBD, + 0x59949334, 0x9FFF37FB, 0x9B177DD9, 0x936956BB, + 0xFBDD697B, 0xDD2FD113, 0x1DB9F7BB, 0x36313963, + 0x79373369, 0x3193137F, 0x7331737A, 0xF7BB9B99, + 0x9BB19795, 0x77FDFD3D, 0x573B773F, 0x737933F7, + 0xB991D115, 0x31699315, 0x31531694, 0xBF4FBDBD, + 0x35931497, 0x35376956, 0xBD697B9D, 0x96931313, + 0x19797937, 0x69350000, +}; + +static void UcodeCopy (volatile cpm8xx_t *cpm) +{ + vu_long *p; + int i; + + p = (vu_long *)&(cpm->cp_dpmem[0x0000]); + for (i=0; i < sizeof(patch_2000)/4; ++i) { + p[i] = patch_2000[i]; + } + + p = (vu_long *)&(cpm->cp_dpmem[0x0F00]); + for (i=0; i < sizeof(patch_2F00)/4; ++i) { + p[i] = patch_2F00[i]; + } +} + +#endif /* CFG_I2C_UCODE_PATCH, CFG_SPI_UCODE_PATCH */ diff --git a/cpu/ppc4xx/miiphy.c b/cpu/ppc4xx/miiphy.c new file mode 100644 index 00000000000..51ba47111b5 --- /dev/null +++ b/cpu/ppc4xx/miiphy.c @@ -0,0 +1,182 @@ +/*-----------------------------------------------------------------------------+ + | + | This source code has been made available to you by IBM on an AS-IS + | basis. Anyone receiving this source is licensed under IBM + | copyrights to use it in any way he or she deems fit, including + | copying it, modifying it, compiling it, and redistributing it either + | with or without modifications. No license under IBM patents or + | patent applications is to be implied by the copyright license. + | + | Any user of this software should understand that IBM cannot provide + | technical support for this software and will not be responsible for + | any consequences resulting from the use of this software. + | + | Any person who transfers this source code or any derivative work + | must include the IBM copyright notice, this paragraph, and the + | preceding two paragraphs in the transferred software. + | + | COPYRIGHT I B M CORPORATION 1995 + | LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + +-----------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------+ + | + | File Name: miiphy.c + | + | Function: This module has utilities for accessing the MII PHY through + | the EMAC3 macro. + | + | Author: Mark Wisner + | + | Change Activity- + | + | Date Description of Change BY + | --------- --------------------- --- + | 05-May-99 Created MKW + | 01-Jul-99 Changed clock setting of sta_reg from 66Mhz to 50Mhz to + | better match OPB speed. Also modified delay times. JWB + | 29-Jul-99 Added Full duplex support MKW + | 24-Aug-99 Removed printf from dp83843_duplex() JWB + | 19-Jul-00 Ported to esd cpci405 sr + | + +-----------------------------------------------------------------------------*/ + +#include <common.h> +#include <asm/processor.h> +#include <ppc_asm.tmpl> +#include <commproc.h> +#include <405gp_enet.h> +#include <405_mal.h> +#include <miiphy.h> + +#if defined(CONFIG_405GP) || defined(CONFIG_440) + + +/***********************************************************/ +/* Dump out to the screen PHY regs */ +/***********************************************************/ + +void miiphy_dump (unsigned char addr) +{ + unsigned long i; + unsigned short data; + + + for (i = 0; i < 0x1A; i++) { + if (miiphy_read (addr, i, &data)) { + printf ("read error for reg %lx\n", i); + return; + } + printf ("Phy reg %lx ==> %4x\n", i, data); + + /* jump to the next set of regs */ + if (i == 0x07) + i = 0x0f; + + } /* end for loop */ +} /* end dump */ + + + +/***********************************************************/ +/* read a phy reg and return the value with a rc */ +/***********************************************************/ + +int miiphy_read (unsigned char addr, unsigned char reg, + unsigned short *value) +{ + unsigned long sta_reg; /* STA scratch area */ + unsigned long i; + + /* see if it is ready for 1000 nsec */ + i = 0; + + /* see if it is ready for sec */ + while ((in32 (EMAC_STACR) & EMAC_STACR_OC) == 0) { + udelay (7); + if (i > 5) { + printf ("read err 1\n"); + return -1; + } + i++; + } + sta_reg = reg; /* reg address */ + /* set clock (50Mhz) and read flags */ + sta_reg = (sta_reg | EMAC_STACR_READ) & ~EMAC_STACR_CLK_100MHZ; + sta_reg = sta_reg | (addr << 5); /* Phy address */ + + out32 (EMAC_STACR, sta_reg); +#if 0 /* test-only */ + printf ("a2: write: EMAC_STACR=0x%0x\n", sta_reg); /* test-only */ +#endif + + sta_reg = in32 (EMAC_STACR); + i = 0; + while ((sta_reg & EMAC_STACR_OC) == 0) { + udelay (7); + if (i > 5) { + printf ("read err 2\n"); + return -1; + } + i++; + sta_reg = in32 (EMAC_STACR); + } + if ((sta_reg & EMAC_STACR_PHYE) != 0) { + printf ("read err 3\n"); + printf ("a2: read: EMAC_STACR=0x%0lx, i=%d\n", + sta_reg, (int) i); /* test-only */ + return -1; + } + + *value = *(short *) (&sta_reg); + return 0; + + +} /* phy_read */ + + +/***********************************************************/ +/* write a phy reg and return the value with a rc */ +/***********************************************************/ + +int miiphy_write (unsigned char addr, unsigned char reg, + unsigned short value) +{ + unsigned long sta_reg; /* STA scratch area */ + unsigned long i; + + /* see if it is ready for 1000 nsec */ + i = 0; + + while ((in32 (EMAC_STACR) & EMAC_STACR_OC) == 0) { + if (i > 5) + return -1; + udelay (7); + i++; + } + sta_reg = 0; + sta_reg = reg; /* reg address */ + /* set clock (50Mhz) and read flags */ + sta_reg = (sta_reg | EMAC_STACR_WRITE) & ~EMAC_STACR_CLK_100MHZ; + sta_reg = sta_reg | ((unsigned long) addr << 5); /* Phy address */ + memcpy (&sta_reg, &value, 2); /* put in data */ + + out32 (EMAC_STACR, sta_reg); + + /* wait for completion */ + i = 0; + sta_reg = in32 (EMAC_STACR); + while ((sta_reg & EMAC_STACR_OC) == 0) { + udelay (7); + if (i > 5) + return -1; + i++; + sta_reg = in32 (EMAC_STACR); + } + + if ((sta_reg & EMAC_STACR_PHYE) != 0) + return -1; + return 0; + +} /* phy_read */ + +#endif /* CONFIG_405GP */ diff --git a/cpu/ppc4xx/traps.c b/cpu/ppc4xx/traps.c new file mode 100644 index 00000000000..85f2ea4cce9 --- /dev/null +++ b/cpu/ppc4xx/traps.c @@ -0,0 +1,291 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) + * + * (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 handles the architecture-dependent parts of hardware exceptions + */ + +#include <common.h> +#include <command.h> +#include <asm/processor.h> + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +int (*debugger_exception_handler)(struct pt_regs *) = 0; +#endif + +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); + +/* THIS NEEDS CHANGING to use the board info structure. + */ +#define END_OF_MEM 0x00400000 + + +static __inline__ void set_tsr(unsigned long val) +{ +#if defined(CONFIG_440) + asm volatile("mtspr 0x150, %0" : : "r" (val)); +#else + asm volatile("mttsr %0" : : "r" (val)); +#endif +} + +static __inline__ unsigned long get_esr(void) +{ + unsigned long val; + +#if defined(CONFIG_440) + asm volatile("mfspr %0, 0x03e" : "=r" (val) :); +#else + asm volatile("mfesr %0" : "=r" (val) :); +#endif + return val; +} + +#define ESR_MCI 0x80000000 +#define ESR_PIL 0x08000000 +#define ESR_PPR 0x04000000 +#define ESR_PTR 0x02000000 +#define ESR_DST 0x00800000 +#define ESR_DIZ 0x00400000 +#define ESR_U0F 0x00008000 + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) +extern void do_bedbug_breakpoint(struct pt_regs *); +#endif + +/* + * Trap & Exception support + */ + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printf("Call backtrace: "); + while (sp) { + if ((uint)sp > END_OF_MEM) + break; + + i = sp[1]; + if (cnt++ % 7 == 0) + printf("\n"); + printf("%08lX ", i); + if (cnt > 32) break; + sp = (unsigned long *)*sp; + } + printf("\n"); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printf("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx DAR: %08lX\n", + regs->nip, regs->xer, regs->link, regs, regs->trap, regs->dar); + printf("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, + regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, + regs->msr&MSR_IR ? 1 : 0, + regs->msr&MSR_DR ? 1 : 0); + + printf("\n"); + for (i = 0; i < 32; i++) { + if ((i % 8) == 0) + { + printf("GPR%02d: ", i); + } + + printf("%08lX ", regs->gpr[i]); + if ((i % 8) == 7) + { + printf("\n"); + } + } +} + + +void +_exception(int signr, struct pt_regs *regs) +{ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +} + +void +MachineCheckException(struct pt_regs *regs) +{ + unsigned long fixup; + + /* Probing PCI using config cycles cause this exception + * when a device is not present. Catch it and return to + * the PCI exception handler. + */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Machine check in kernel mode.\n"); + printf("Caused by (from msr): "); + printf("regs %p ",regs); + switch( regs->msr & 0x0000F000) + { + case (1<<12) : + printf("Machine check signal - probably due to mm fault\n" + "with mmu off\n"); + break; + case (1<<13) : + printf("Transfer error ack signal\n"); + break; + case (1<<14) : + printf("Data parity signal\n"); + break; + case (1<<15) : + printf("Address parity signal\n"); + break; + default: + printf("Unknown values in msr\n"); + } + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); +} + +void +AlignmentException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Alignment Exception"); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ + long esr_val; + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + show_regs(regs); + + esr_val = get_esr(); + if( esr_val & ESR_PIL ) + printf( "** Illegal Instruction **\n" ); + else if( esr_val & ESR_PPR ) + printf( "** Privileged Instruction **\n" ); + else if( esr_val & ESR_PTR ) + printf( "** Trap Instruction **\n" ); + + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Program Check Exception"); +} + +void +PITException(struct pt_regs *regs) +{ + /* + * Reset PIT interrupt + */ + set_tsr(0x08000000); + + /* + * Call timer_interrupt routine in interrupts.c + */ + timer_interrupt(NULL); +} + + +void +UnknownException(struct pt_regs *regs) +{ +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + if (debugger_exception_handler && (*debugger_exception_handler)(regs)) + return; +#endif + + printf("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(0, regs); +} + +void +DebugException(struct pt_regs *regs) +{ + printf("Debugger trap at @ %lx\n", regs->nip ); + show_regs(regs); +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) + do_bedbug_breakpoint( regs ); +#endif +} + +/* Probe an address by reading. If not present, return -1, otherwise + * return 0. + */ +int +addr_probe(uint *addr) +{ +#if 0 + int retval; + + __asm__ __volatile__( \ + "1: lwz %0,0(%1)\n" \ + " eieio\n" \ + " li %0,0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %0,-1\n" \ + " b 2b\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 2\n" \ + " .long 1b,3b\n" \ + ".text" \ + : "=r" (retval) : "r"(addr)); + + return (retval); +#endif + return 0; +} diff --git a/disk/part.c b/disk/part.c new file mode 100644 index 00000000000..99fe0db9fa7 --- /dev/null +++ b/disk/part.c @@ -0,0 +1,227 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +#include <common.h> +#include <command.h> +#include <ide.h> +#include <cmd_disk.h> + +#undef PART_DEBUG + +#ifdef PART_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +#if (CONFIG_COMMANDS & CFG_CMD_IDE) || (CONFIG_COMMANDS & CFG_CMD_SCSI) + +/* ------------------------------------------------------------------------- */ +/* + * reports device info to the user + */ +void dev_print (block_dev_desc_t *dev_desc) +{ + ulong lba512; /* number of blocks if 512bytes block size */ + + if (dev_desc->type==DEV_TYPE_UNKNOWN) { + puts ("not available\n"); + return; + } + if (dev_desc->if_type==IF_TYPE_SCSI) { + printf ("(%d:%d) ", dev_desc->target,dev_desc->lun); + } + if (dev_desc->if_type==IF_TYPE_IDE) { + printf ("Model: %s Firm: %s Ser#: %s\n", + dev_desc->vendor, + dev_desc->revision, + dev_desc->product); + } else { + printf ("Vendor: %s Prod.: %s Rev: %s\n", + dev_desc->vendor, + dev_desc->product, + dev_desc->revision); + } + puts (" Type: "); + if (dev_desc->removable) + puts ("Removable "); + switch (dev_desc->type & 0x1F) { + case DEV_TYPE_HARDDISK: puts ("Hard Disk"); + break; + case DEV_TYPE_CDROM: puts ("CD ROM"); + break; + case DEV_TYPE_OPDISK: puts ("Optical Device"); + break; + case DEV_TYPE_TAPE: puts ("Tape"); + break; + default: printf ("# %02X #", dev_desc->type & 0x1F); + break; + } + puts ("\n"); + if ((dev_desc->lba * dev_desc->blksz)>0L) { + ulong mb, mb_quot, mb_rem, gb, gb_quot, gb_rem; + + lba512 = (dev_desc->lba * (dev_desc->blksz/512)); + + mb = (10 * lba512) / 2048; /* 2048 = (1024 * 1024) / 512 MB */ + /* round to 1 digit */ + mb_quot = mb / 10; + mb_rem = mb - (10 * mb_quot); + + gb = mb / 1024; + gb_quot = gb / 10; + gb_rem = gb - (10 * gb_quot); + + printf (" Capacity: %ld.%ld MB = %ld.%ld GB (%ld x %ld)\n", + mb_quot, mb_rem, + gb_quot, gb_rem, + dev_desc->lba, + dev_desc->blksz); + } else { + puts (" Capacity: not available\n"); + } +} + + + +#if defined(CONFIG_MAC_PARTITION) || \ + defined(CONFIG_DOS_PARTITION) || \ + defined(CONFIG_ISO_PARTITION) + +void init_part (block_dev_desc_t * dev_desc) +{ +#ifdef CONFIG_ISO_PARTITION + if (test_part_iso(dev_desc) == 0) { + dev_desc->part_type = PART_TYPE_ISO; + return; + } +#endif + +#ifdef CONFIG_MAC_PARTITION + if (test_part_mac(dev_desc) == 0) { + dev_desc->part_type = PART_TYPE_MAC; + return; + } +#endif + +#ifdef CONFIG_DOS_PARTITION + if (test_part_dos(dev_desc) == 0) { + dev_desc->part_type = PART_TYPE_DOS; + return; + } +#endif +} + + +int get_partition_info (block_dev_desc_t *dev_desc, int part, disk_partition_t *info) +{ + switch (dev_desc->part_type) { +#ifdef CONFIG_MAC_PARTITION + case PART_TYPE_MAC: + if (get_partition_info_mac(dev_desc,part,info) == 0) { + PRINTF ("## Valid MAC partition found ##\n"); + return (0); + } + break; +#endif + +#ifdef CONFIG_DOS_PARTITION + case PART_TYPE_DOS: + if (get_partition_info_dos(dev_desc,part,info) == 0) { + PRINTF ("## Valid DOS partition found ##\n"); + return (0); + } + break; +#endif + +#ifdef CONFIG_ISO_PARTITION + case PART_TYPE_ISO: + if (get_partition_info_iso(dev_desc,part,info) == 0) { + PRINTF ("## Valid ISO boot partition found ##\n"); + return (0); + } + break; +#endif + default: + break; + } + return (-1); +} + +static void print_part_header (const char *type, block_dev_desc_t * dev_desc) +{ + puts ("\nPartition Map for "); + switch (dev_desc->if_type) { + case IF_TYPE_IDE: puts ("IDE"); + break; + case IF_TYPE_SCSI: puts ("SCSI"); + break; + case IF_TYPE_ATAPI: puts ("ATAPI"); + break; + case IF_TYPE_USB: puts ("USB"); + break; + case IF_TYPE_DOC: puts ("DOC"); + break; + default: puts ("UNKNOWN"); + break; + } + printf (" device %d -- Partition Type: %s\n\n", + dev_desc->dev, type); +} + +void print_part (block_dev_desc_t * dev_desc) +{ + + switch (dev_desc->part_type) { +#ifdef CONFIG_MAC_PARTITION + case PART_TYPE_MAC: + PRINTF ("## Testing for valid MAC partition ##\n"); + print_part_header ("MAC", dev_desc); + print_part_mac (dev_desc); + return; +#endif +#ifdef CONFIG_DOS_PARTITION + case PART_TYPE_DOS: + PRINTF ("## Testing for valid DOS partition ##\n"); + print_part_header ("DOS", dev_desc); + print_part_dos (dev_desc); + return; +#endif + +#ifdef CONFIG_ISO_PARTITION + case PART_TYPE_ISO: + PRINTF ("## Testing for valid ISO Boot partition ##\n"); + print_part_header ("ISO", dev_desc); + print_part_iso (dev_desc); + return; +#endif + } + puts ("## Unknown partition table\n"); +} + + +#else /* neither MAC nor DOS nor ISO partition configured */ +# error neither CONFIG_MAC_PARTITION nor CONFIG_DOS_PARTITION nor CONFIG_ISO_PARTITION configured! +#endif + +#endif /* (CONFIG_COMMANDS & CFG_CMD_IDE) || CONFIG_COMMANDS & CFG_CMD_SCSI) */ diff --git a/drivers/pci_indirect.c b/drivers/pci_indirect.c new file mode 100644 index 00000000000..cb76ef23be3 --- /dev/null +++ b/drivers/pci_indirect.c @@ -0,0 +1,74 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * 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. + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +#define cfg_read(val, addr, type, op) *val = op((type)(addr)) +#define cfg_write(val, addr, type, op) op((type *)(addr), (val)) + +#define INDIRECT_PCI_OP(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + return 0; \ +} + +#define INDIRECT_PCI_OP_ERRATA6(rw, size, type, op, mask) \ +static int \ +indirect_##rw##_config_##size(struct pci_controller *hose, \ + pci_dev_t dev, int offset, type val) \ +{ \ + unsigned int msr = mfmsr(); \ + mtmsr(msr & ~(MSR_EE | MSR_CE)); \ + out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \ + cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \ + out_le32(hose->cfg_addr, 0x00000000); \ + mtmsr(msr); \ + return 0; \ +} + +INDIRECT_PCI_OP(read, byte, u8 *, in_8, 3) +INDIRECT_PCI_OP(read, word, u16 *, in_le16, 2) +INDIRECT_PCI_OP(read, dword, u32 *, in_le32, 0) +#ifdef CONFIG_405GP +INDIRECT_PCI_OP_ERRATA6(write, byte, u8, out_8, 3) +INDIRECT_PCI_OP_ERRATA6(write, word, u16, out_le16, 2) +INDIRECT_PCI_OP_ERRATA6(write, dword, u32, out_le32, 0) +#else +INDIRECT_PCI_OP(write, byte, u8, out_8, 3) +INDIRECT_PCI_OP(write, word, u16, out_le16, 2) +INDIRECT_PCI_OP(write, dword, u32, out_le32, 0) +#endif + +void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) +{ + pci_set_ops(hose, + indirect_read_config_byte, + indirect_read_config_word, + indirect_read_config_dword, + indirect_write_config_byte, + indirect_write_config_word, + indirect_write_config_dword); + + hose->cfg_addr = (unsigned int *) cfg_addr; + hose->cfg_data = (unsigned char *) cfg_data; +} + +#endif diff --git a/examples/eepro100_eeprom.c b/examples/eepro100_eeprom.c new file mode 100644 index 00000000000..656b2382b8e --- /dev/null +++ b/examples/eepro100_eeprom.c @@ -0,0 +1,210 @@ +/* + * Copyright 1998-2001 by Donald Becker. + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by reference. + * Contact the author for use under other terms. + * + * This program must be compiled with "-O"! + * See the bottom of this file for the suggested compile-command. + * + * The author may be reached as becker@scyld.com, or C/O + * Scyld Computing Corporation + * 410 Severn Ave., Suite 210 + * Annapolis MD 21403 + * + * Common-sense licensing statement: Using any portion of this program in + * your own program means that you must give credit to the original author + * and release the resulting code under the GPL. + */ + +#define _PPC_STRING_H_ /* avoid unnecessary str/mem functions */ +#define _LINUX_STRING_H_ /* avoid unnecessary str/mem functions */ + +#include <common.h> +#include <syscall.h> + +static int reset_eeprom(unsigned long ioaddr, unsigned char *hwaddr); + +int eepro100_eeprom(void) +{ + int ret = 0; + + unsigned char hwaddr1[6] = { 0x00, 0x00, 0x02, 0x03, 0x04, 0x05 }; + unsigned char hwaddr2[6] = { 0x00, 0x00, 0x02, 0x03, 0x04, 0x06 }; + +#if defined(CONFIG_OXC) + ret |= reset_eeprom(0x80000000, hwaddr1); + ret |= reset_eeprom(0x81000000, hwaddr2); +#endif + + return ret; +} + +/* Default EEPROM for i82559 */ +static unsigned short default_eeprom[64] = { + 0x0100, 0x0302, 0x0504, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0x40c0, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + +static unsigned short eeprom[256]; + +static int eeprom_size = 64; +static int eeprom_addr_size = 6; + +static int debug = 0; + +static inline unsigned short swap16(unsigned short x) +{ + return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); +} + +static inline void outw(short data, long addr) +{ + *(volatile short *)(addr) = swap16(data); +} + +static inline short inw(long addr) +{ + return swap16(*(volatile short *)(addr)); +} + +static inline void *memcpy(void *dst, const void *src, unsigned int len) +{ + void * ret = dst; + while (len-- > 0) *((char *)dst)++ = *((char *)src)++; + return ret; +} + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +/* Serial EEPROM section. */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) +#define EE_WRITE_0 0x4802 +#define EE_WRITE_1 0x4806 +#define EE_OFFSET 14 + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay(ee_addr) inw(ee_addr) + +/* Wait for the EEPROM to finish the previous operation. */ +static int eeprom_busy_poll(long ee_ioaddr) +{ + int i; + outw(EE_ENB, ee_ioaddr); + for (i = 0; i < 10000; i++) /* Typical 2000 ticks */ + if (inw(ee_ioaddr) & EE_DATA_READ) + break; + return i; +} + +/* This executes a generic EEPROM command, typically a write or write enable. + It returns the data output from the EEPROM, and thus may also be used for + reads. */ +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len) +{ + unsigned retval = 0; + long ee_addr = ioaddr + EE_OFFSET; + + if (debug > 1) + mon_printf(" EEPROM op 0x%x: ", cmd); + + outw(EE_ENB | EE_SHIFT_CLK, ee_addr); + + /* Shift the command bits out. */ + do { + short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; + outw(dataval, ee_addr); + eeprom_delay(ee_addr); + if (debug > 2) + mon_printf("%X", inw(ee_addr) & 15); + outw(dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); + } while (--cmd_len >= 0); +#if 0 + outw(EE_ENB, ee_addr); +#endif + /* Terminate the EEPROM access. */ + outw(EE_ENB & ~EE_CS, ee_addr); + if (debug > 1) + mon_printf(" EEPROM result is 0x%5.5x.\n", retval); + return retval; +} + +static int read_eeprom(long ioaddr, int location, int addr_len) +{ + return do_eeprom_cmd(ioaddr, ((EE_READ_CMD << addr_len) | location) + << 16 , 3 + addr_len + 16) & 0xffff; +} + +static void write_eeprom(long ioaddr, int index, int value, int addr_len) +{ + long ee_ioaddr = ioaddr + EE_OFFSET; + int i; + + /* Poll for previous op finished. */ + eeprom_busy_poll(ee_ioaddr); /* Typical 0 ticks */ + /* Enable programming modes. */ + do_eeprom_cmd(ioaddr, (0x4f << (addr_len-4)), 3 + addr_len); + /* Do the actual write. */ + do_eeprom_cmd(ioaddr, + (((EE_WRITE_CMD<<addr_len) | index)<<16) | (value & 0xffff), + 3 + addr_len + 16); + /* Poll for write finished. */ + i = eeprom_busy_poll(ee_ioaddr); /* Typical 2000 ticks */ + if (debug) + mon_printf(" Write finished after %d ticks.\n", i); + /* Disable programming. This command is not instantaneous, so we check + for busy before the next op. */ + do_eeprom_cmd(ioaddr, (0x40 << (addr_len-4)), 3 + addr_len); + eeprom_busy_poll(ee_ioaddr); +} + +static int reset_eeprom(unsigned long ioaddr, unsigned char *hwaddr) +{ + unsigned short checksum = 0; + int size_test; + int i; + + mon_printf("Resetting i82559 EEPROM @ 0x%08x ... ", ioaddr); + + size_test = do_eeprom_cmd(ioaddr, (EE_READ_CMD << 8) << 16, 27); + eeprom_addr_size = (size_test & 0xffe0000) == 0xffe0000 ? 8 : 6; + eeprom_size = 1 << eeprom_addr_size; + + memcpy(eeprom, default_eeprom, sizeof default_eeprom); + + for (i = 0; i < 3; i++) + eeprom[i] = (hwaddr[i*2+1]<<8) + hwaddr[i*2]; + + /* Recalculate the checksum. */ + for (i = 0; i < eeprom_size - 1; i++) + checksum += eeprom[i]; + eeprom[i] = 0xBABA - checksum; + + for (i = 0; i < eeprom_size; i++) + write_eeprom(ioaddr, i, eeprom[i], eeprom_addr_size); + + for (i = 0; i < eeprom_size; i++) + if (read_eeprom(ioaddr, i, eeprom_addr_size) != eeprom[i]) { + mon_printf("failed\n"); + return 1; + } + + mon_printf("done\n"); + return 0; +} + diff --git a/include/pcmcia.h b/include/pcmcia.h new file mode 100644 index 00000000000..ee2c63c978f --- /dev/null +++ b/include/pcmcia.h @@ -0,0 +1,301 @@ +/* + * (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 + */ + +#ifndef _PCMCIA_H +#define _PCMCIA_H + +#include <common.h> +#include <config.h> + +/* + * Allow configuration to select PCMCIA slot, + * or try to generate a useful default + */ +#if ( CONFIG_COMMANDS & CFG_CMD_PCMCIA) || \ + ((CONFIG_COMMANDS & CFG_CMD_IDE) && \ + (defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) ) ) + +#if !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B) + + /* The RPX series use SLOT_B */ +#if defined(CONFIG_RPXCLASSIC) || defined(CONFIG_RPXLITE) +# define CONFIG_PCMCIA_SLOT_B +#elif defined(CONFIG_ADS) /* The ADS board use SLOT_A */ +# define CONFIG_PCMCIA_SLOT_A +#elif defined(CONFIG_FADS) /* The FADS series are a mess */ +# if defined(CONFIG_MPC860T) || defined(CONFIG_MPC860) || defined(CONFIG_MPC821) +# define CONFIG_PCMCIA_SLOT_A +# else +# define CONFIG_PCMCIA_SLOT_B +# endif +#elif defined(CONFIG_TQM8xxL) +# define CONFIG_PCMCIA_SLOT_B /* The TQM8xxL use SLOT_B */ +#elif defined(CONFIG_SPD823TS) /* The SPD8xx use SLOT_B */ +# define CONFIG_PCMCIA_SLOT_B +#elif defined(CONFIG_IVMS8) || defined(CONFIG_IVML24) /* The IVM* use SLOT_A */ +# define CONFIG_PCMCIA_SLOT_A +#elif defined(CONFIG_LWMON) /* The LWMON use SLOT_B */ +# define CONFIG_PCMCIA_SLOT_B +#elif defined(CONFIG_ICU862) /* The ICU862 use SLOT_B */ +# define CONFIG_PCMCIA_SLOT_B +#elif defined(CONFIG_C2MON) /* The C2MON use SLOT_B */ +# define CONFIG_PCMCIA_SLOT_B +#elif defined(CONFIG_R360MPI) /* The R360MPI use SLOT_B */ +# define CONFIG_PCMCIA_SLOT_B +#else +# error "PCMCIA Slot not configured" +#endif + +#endif /* !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B) */ + +/* Make sure exactly one slot is defined - we support only one for now */ +#if !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B) +#error Neither CONFIG_PCMCIA_SLOT_A nor CONFIG_PCMCIA_SLOT_B configured +#endif +#if defined(CONFIG_PCMCIA_SLOT_A) && defined(CONFIG_PCMCIA_SLOT_B) +#error Both CONFIG_PCMCIA_SLOT_A and CONFIG_PCMCIA_SLOT_B configured +#endif + +#define PCMCIA_SOCKETS_NO 1 +#define PCMCIA_MEM_WIN_NO 4 +#define PCMCIA_IO_WIN_NO 2 + +/* define _slot_ to be able to optimize macros */ +#ifdef CONFIG_PCMCIA_SLOT_A +# define _slot_ 0 +# define PCMCIA_SLOT_MSG "slot A" +# define PCMCIA_SLOT_x PCMCIA_PSLOT_A +#else +# define _slot_ 1 +# define PCMCIA_SLOT_MSG "slot B" +# define PCMCIA_SLOT_x PCMCIA_PSLOT_B +#endif + +/* + * The TQM850L hardware has two pins swapped! Grrrrgh! + */ +#ifdef CONFIG_TQM850L +#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXOE +#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXRESET +#else +#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXRESET +#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXOE +#endif + +/* + * This structure is used to address each window in the PCMCIA controller. + * + * Keep in mind that we assume that pcmcia_win_t[n+1] is mapped directly + * after pcmcia_win_t[n]... + */ + +typedef struct { + ulong br; + ulong or; +} pcmcia_win_t; + +/* + * Definitions for PCMCIA control registers to operate in IDE mode + * + * All timing related setup (PCMCIA_SHT, PCMCIA_SST, PCMCIA_SL) + * to be done later (depending on CPU clock) + */ + +/* Window 0: + * Base: 0xFE100000 CS1 + * Port Size: 2 Bytes + * Port Size: 16 Bit + * Common Memory Space + */ + +#define CFG_PCMCIA_PBR0 0xFE100000 +#define CFG_PCMCIA_POR0 ( PCMCIA_BSIZE_2 \ + | PCMCIA_PPS_16 \ + | PCMCIA_PRS_MEM \ + | PCMCIA_SLOT_x \ + | PCMCIA_PV \ + ) + +/* Window 1: + * Base: 0xFE100080 CS1 + * Port Size: 8 Bytes + * Port Size: 8 Bit + * Common Memory Space + */ + +#define CFG_PCMCIA_PBR1 0xFE100080 +#define CFG_PCMCIA_POR1 ( PCMCIA_BSIZE_8 \ + | PCMCIA_PPS_8 \ + | PCMCIA_PRS_MEM \ + | PCMCIA_SLOT_x \ + | PCMCIA_PV \ + ) + +/* Window 2: + * Base: 0xFE100100 CS2 + * Port Size: 8 Bytes + * Port Size: 8 Bit + * Common Memory Space + */ + +#define CFG_PCMCIA_PBR2 0xFE100100 +#define CFG_PCMCIA_POR2 ( PCMCIA_BSIZE_8 \ + | PCMCIA_PPS_8 \ + | PCMCIA_PRS_MEM \ + | PCMCIA_SLOT_x \ + | PCMCIA_PV \ + ) + +/* Window 3: + * not used + */ +#define CFG_PCMCIA_PBR3 0 +#define CFG_PCMCIA_POR3 0 + +/* Window 4: + * Base: 0xFE100C00 CS1 + * Port Size: 2 Bytes + * Port Size: 16 Bit + * Common Memory Space + */ + +#define CFG_PCMCIA_PBR4 0xFE100C00 +#define CFG_PCMCIA_POR4 ( PCMCIA_BSIZE_2 \ + | PCMCIA_PPS_16 \ + | PCMCIA_PRS_MEM \ + | PCMCIA_SLOT_x \ + | PCMCIA_PV \ + ) + +/* Window 5: + * Base: 0xFE100C80 CS1 + * Port Size: 8 Bytes + * Port Size: 8 Bit + * Common Memory Space + */ + +#define CFG_PCMCIA_PBR5 0xFE100C80 +#define CFG_PCMCIA_POR5 ( PCMCIA_BSIZE_8 \ + | PCMCIA_PPS_8 \ + | PCMCIA_PRS_MEM \ + | PCMCIA_SLOT_x \ + | PCMCIA_PV \ + ) + +/* Window 6: + * Base: 0xFE100D00 CS2 + * Port Size: 8 Bytes + * Port Size: 8 Bit + * Common Memory Space + */ + +#define CFG_PCMCIA_PBR6 0xFE100D00 +#define CFG_PCMCIA_POR6 ( PCMCIA_BSIZE_8 \ + | PCMCIA_PPS_8 \ + | PCMCIA_PRS_MEM \ + | PCMCIA_SLOT_x \ + | PCMCIA_PV \ + ) + +/* Window 7: + * not used + */ +#define CFG_PCMCIA_PBR7 0 +#define CFG_PCMCIA_POR7 0 + +/**********************************************************************/ + +/* + * CIS Tupel codes + */ +#define CISTPL_NULL 0x00 +#define CISTPL_DEVICE 0x01 +#define CISTPL_LONGLINK_CB 0x02 +#define CISTPL_INDIRECT 0x03 +#define CISTPL_CONFIG_CB 0x04 +#define CISTPL_CFTABLE_ENTRY_CB 0x05 +#define CISTPL_LONGLINK_MFC 0x06 +#define CISTPL_BAR 0x07 +#define CISTPL_PWR_MGMNT 0x08 +#define CISTPL_EXTDEVICE 0x09 +#define CISTPL_CHECKSUM 0x10 +#define CISTPL_LONGLINK_A 0x11 +#define CISTPL_LONGLINK_C 0x12 +#define CISTPL_LINKTARGET 0x13 +#define CISTPL_NO_LINK 0x14 +#define CISTPL_VERS_1 0x15 +#define CISTPL_ALTSTR 0x16 +#define CISTPL_DEVICE_A 0x17 +#define CISTPL_JEDEC_C 0x18 +#define CISTPL_JEDEC_A 0x19 +#define CISTPL_CONFIG 0x1a +#define CISTPL_CFTABLE_ENTRY 0x1b +#define CISTPL_DEVICE_OC 0x1c +#define CISTPL_DEVICE_OA 0x1d +#define CISTPL_DEVICE_GEO 0x1e +#define CISTPL_DEVICE_GEO_A 0x1f +#define CISTPL_MANFID 0x20 +#define CISTPL_FUNCID 0x21 +#define CISTPL_FUNCE 0x22 +#define CISTPL_SWIL 0x23 +#define CISTPL_END 0xff + +/* + * CIS Function ID codes + */ +#define CISTPL_FUNCID_MULTI 0x00 +#define CISTPL_FUNCID_MEMORY 0x01 +#define CISTPL_FUNCID_SERIAL 0x02 +#define CISTPL_FUNCID_PARALLEL 0x03 +#define CISTPL_FUNCID_FIXED 0x04 +#define CISTPL_FUNCID_VIDEO 0x05 +#define CISTPL_FUNCID_NETWORK 0x06 +#define CISTPL_FUNCID_AIMS 0x07 +#define CISTPL_FUNCID_SCSI 0x08 + +/* + * Fixed Disk FUNCE codes + */ +#define CISTPL_IDE_INTERFACE 0x01 + +#define CISTPL_FUNCE_IDE_IFACE 0x01 +#define CISTPL_FUNCE_IDE_MASTER 0x02 +#define CISTPL_FUNCE_IDE_SLAVE 0x03 + +/* First feature byte */ +#define CISTPL_IDE_SILICON 0x04 +#define CISTPL_IDE_UNIQUE 0x08 +#define CISTPL_IDE_DUAL 0x10 + +/* Second feature byte */ +#define CISTPL_IDE_HAS_SLEEP 0x01 +#define CISTPL_IDE_HAS_STANDBY 0x02 +#define CISTPL_IDE_HAS_IDLE 0x04 +#define CISTPL_IDE_LOW_POWER 0x08 +#define CISTPL_IDE_REG_INHIBIT 0x10 +#define CISTPL_IDE_HAS_INDEX 0x20 +#define CISTPL_IDE_IOIS16 0x40 + +#endif /* CFG_CMD_PCMCIA || CFG_CMD_IDE && (CONFIG_IDE_8xx_PCCARD || CONFIG_IDE_8xx_DIRECT) */ + +#endif /* _PCMCIA_H */ diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 00000000000..f80d550ee8d --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,37 @@ +#ifndef __MON_SYS_CALL_H__ +#define __MON_SYS_CALL_H__ + +#ifndef __ASSEMBLY__ + +#include <common.h> + +/* These are declarations of system calls available in C code */ +int mon_getc(void); +int mon_tstc(void); +void mon_putc(const char); +void mon_puts(const char*); +void mon_printf(const char* fmt, ...); +void mon_install_hdlr(int, interrupt_handler_t*, void*); +void mon_free_hdlr(int); +void *mon_malloc(size_t); +void mon_free(void*); + +#endif /* ifndef __ASSEMBLY__ */ + +#define NR_SYSCALLS 9 /* number of syscalls */ + +/* + * Make sure these functions are in the same order as they + * appear in the "examples/syscall.S" file !!! + */ +#define SYSCALL_GETC 0 +#define SYSCALL_TSTC 1 +#define SYSCALL_PUTC 2 +#define SYSCALL_PUTS 3 +#define SYSCALL_PRINTF 4 +#define SYSCALL_INSTALL_HDLR 5 +#define SYSCALL_FREE_HDLR 6 +#define SYSCALL_MALLOC 7 +#define SYSCALL_FREE 8 + +#endif diff --git a/lib_generic/crc32.c b/lib_generic/crc32.c new file mode 100644 index 00000000000..2de39c7db71 --- /dev/null +++ b/lib_generic/crc32.c @@ -0,0 +1,199 @@ +/* + * This file is derived from crc32.c from the zlib-1.1.3 distribution + * by Jean-loup Gailly and Mark Adler. + */ + +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifndef USE_HOSTCC /* Shut down "ANSI does not permit..." warnings */ +#include <common.h> /* to get command definitions like CFG_CMD_JFFS2 */ +#endif + +#include "zlib.h" + +#define local static +#define ZEXPORT /* empty */ +unsigned long crc32 (unsigned long, const unsigned char *, unsigned int); + +#ifdef DYNAMIC_CRC_TABLE + +local int crc_table_empty = 1; +local uLongf crc_table[256]; +local void make_crc_table OF((void)); + +/* + Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The table is simply the CRC of all possible eight bit values. This is all + the information needed to generate CRC's on data a byte at a time for all + combinations of CRC register values and incoming bytes. +*/ +local void make_crc_table() +{ + uLong c; + int n, k; + uLong poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* make exclusive-or pattern from polynomial (0xedb88320L) */ + poly = 0L; + for (n = 0; n < sizeof(p)/sizeof(Byte); n++) + poly |= 1L << (31 - p[n]); + + for (n = 0; n < 256; n++) + { + c = (uLong)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[n] = c; + } + crc_table_empty = 0; +} +#else +/* ======================================================================== + * Table of CRC-32's of all single-byte values (made by make_crc_table) + */ +local const uLongf crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#endif + +#if 0 +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const uLongf * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) make_crc_table(); +#endif + return (const uLongf *)crc_table; +} +#endif + +/* ========================================================================= */ +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +/* ========================================================================= */ +uLong ZEXPORT crc32(crc, buf, len) + uLong crc; + const Bytef *buf; + uInt len; +{ + if (buf == Z_NULL) return 0L; +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8(buf); + len -= 8; + } + if (len) do { + DO1(buf); + } while (--len); + return crc ^ 0xffffffffL; +} + +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) + +/* No ones complement version. JFFS2 (and other things ?) + * don't use ones compliment in their CRC calculations. + */ +uLong ZEXPORT crc32_no_comp(uLong crc, const Bytef *buf, uInt len) +{ + if (buf == Z_NULL) return 0L; +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif + while (len >= 8) + { + DO8(buf); + len -= 8; + } + if (len) do { + DO1(buf); + } while (--len); + + return crc; +} + +#endif /* CFG_CMD_JFFS2 */ diff --git a/lib_ppc/bat_rw.c b/lib_ppc/bat_rw.c new file mode 100644 index 00000000000..912efa72b4b --- /dev/null +++ b/lib_ppc/bat_rw.c @@ -0,0 +1,133 @@ +/* + * (C) Copyright 2002 + * Rich Ireland, Enterasys Networks, rireland@enterasys.com. + * + * 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 + * + */ + +#include <common.h> +#include <asm/processor.h> +#include <asm/mmu.h> + +int write_bat (ppc_bat_t bat, unsigned long upper, unsigned long lower) +{ + switch (bat) { + case IBAT0: + mtspr (IBAT0L, lower); + mtspr (IBAT0U, upper); + break; + + case IBAT1: + mtspr (IBAT1L, lower); + mtspr (IBAT1U, upper); + break; + + case IBAT2: + mtspr (IBAT2L, lower); + mtspr (IBAT2U, upper); + break; + + case IBAT3: + mtspr (IBAT3L, lower); + mtspr (IBAT3U, upper); + break; + + case DBAT0: + mtspr (DBAT0L, lower); + mtspr (DBAT0U, upper); + break; + + case DBAT1: + mtspr (DBAT1L, lower); + mtspr (DBAT1U, upper); + break; + + case DBAT2: + mtspr (DBAT2L, lower); + mtspr (DBAT2U, upper); + break; + + case DBAT3: + mtspr (DBAT3L, lower); + mtspr (DBAT3U, upper); + break; + + default: + return (-1); + } + + return (0); +} + +int read_bat (ppc_bat_t bat, unsigned long *upper, unsigned long *lower) +{ + unsigned long register u; + unsigned long register l; + + switch (bat) { + case IBAT0: + l = mfspr (IBAT0L); + u = mfspr (IBAT0U); + break; + + case IBAT1: + l = mfspr (IBAT1L); + u = mfspr (IBAT1U); + break; + + case IBAT2: + l = mfspr (IBAT2L); + u = mfspr (IBAT2U); + break; + + case IBAT3: + l = mfspr (IBAT3L); + u = mfspr (IBAT3U); + break; + + case DBAT0: + l = mfspr (DBAT0L); + u = mfspr (DBAT0U); + break; + + case DBAT1: + l = mfspr (DBAT1L); + u = mfspr (DBAT1U); + break; + + case DBAT2: + l = mfspr (DBAT2L); + u = mfspr (DBAT2U); + break; + + case DBAT3: + l = mfspr (DBAT3L); + u = mfspr (DBAT3U); + break; + + default: + return (-1); + } + + *upper = u; + *lower = l; + + return (0); +} diff --git a/lib_ppc/cache.c b/lib_ppc/cache.c new file mode 100644 index 00000000000..bec092e4ceb --- /dev/null +++ b/lib_ppc/cache.c @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2002 + * 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 + */ + +#include <common.h> + +void flush_cache (ulong start_addr, ulong size) +{ + ulong addr, end_addr = start_addr + size; + + if (CFG_CACHELINE_SIZE) { + addr = start_addr & (CFG_CACHELINE_SIZE - 1); + for (addr = start_addr; + addr < end_addr; + addr += CFG_CACHELINE_SIZE) { + asm ("dcbst 0,%0": :"r" (addr)); + } + asm ("sync"); /* Wait for all dcbst to complete on bus */ + + for (addr = start_addr; + addr < end_addr; + addr += CFG_CACHELINE_SIZE) { + asm ("icbi 0,%0": :"r" (addr)); + } + } + asm ("sync"); /* Always flush prefetch queue in any case */ + asm ("isync"); +} diff --git a/net/rarp.c b/net/rarp.c new file mode 100644 index 00000000000..279e650fcd4 --- /dev/null +++ b/net/rarp.c @@ -0,0 +1,106 @@ +/* + * (C) Copyright 2000-2002 + * 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 + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include "bootp.h" +#include "rarp.h" +#include "tftp.h" + +#if (CONFIG_COMMANDS & CFG_CMD_NET) + +#define TIMEOUT 5 /* Seconds before trying BOOTP again */ +#ifndef CONFIG_NET_RETRY_COUNT +# define TIMEOUT_COUNT 5 /* # of timeouts before giving up */ +#else +# define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT) +#endif + + +int RarpTry; + +/* + * Handle a RARP received packet. + */ +static void +RarpHandler(uchar * dummi0, unsigned dummi1, unsigned dummi2, unsigned dummi3) +{ +#ifdef DEBUG + printf("Got good RARP\n"); +#endif + TftpStart (); +} + + +/* + * Timeout on BOOTP request. + */ +static void +RarpTimeout(void) +{ + if (RarpTry >= TIMEOUT_COUNT) { + puts ("\nRetry count exceeded; starting again\n"); + NetStartAgain (); + } else { + NetSetTimeout (TIMEOUT * CFG_HZ, RarpTimeout); + RarpRequest (); + } +} + + +void +RarpRequest (void) +{ + int i; + volatile uchar *pkt; + ARP_t * rarp; + + printf("RARP broadcast %d\n", ++RarpTry); + pkt = NetTxPacket; + + NetSetEther(pkt, NetBcastAddr, PROT_RARP); + pkt += ETHER_HDR_SIZE; + + rarp = (ARP_t *)pkt; + + rarp->ar_hrd = ARP_ETHER; + rarp->ar_pro = PROT_IP; + rarp->ar_hln = 6; + rarp->ar_pln = 4; + rarp->ar_op = RARPOP_REQUEST; + memcpy (&rarp->ar_data[0], NetOurEther, 6); /* source ET addr */ + memcpy (&rarp->ar_data[6], &NetOurIP, 4); /* source IP addr */ + memcpy (&rarp->ar_data[10], NetOurEther, 6); /* dest ET addr = source ET addr ??*/ + /* dest. IP addr set to broadcast */ + for (i = 0; i <= 3; i++) { + rarp->ar_data[16 + i] = 0xff; + } + + NetSendPacket(NetTxPacket, ETHER_HDR_SIZE + ARP_HDR_SIZE); + + NetSetTimeout(TIMEOUT * CFG_HZ, RarpTimeout); + NetSetHandler(RarpHandler); +} + +#endif /* CFG_CMD_NET */ diff --git a/rtc/ds1306.c b/rtc/ds1306.c new file mode 100644 index 00000000000..9c11c600d69 --- /dev/null +++ b/rtc/ds1306.c @@ -0,0 +1,274 @@ +/* + * (C) Copyright 2002 SIXNET, dge@sixnetio.com. + * + * 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 + */ + +/* + * Date & Time support for DS1306 RTC using software SPI + */ + +#include <common.h> +#include <command.h> +#include <rtc.h> + +#if defined(CONFIG_RTC_DS1306) && (CONFIG_COMMANDS & CFG_CMD_DATE) + +static unsigned int bin2bcd(unsigned int n); +static unsigned char bcd2bin(unsigned char c); +static void soft_spi_send(unsigned char n); +static unsigned char soft_spi_read(void); +static void init_spi(void); + +/*----------------------------------------------------------------------- + * Definitions + */ + +#define PB_SPISCK 0x00000002 /* PB 30 */ +#define PB_SPIMOSI 0x00000004 /* PB 29 */ +#define PB_SPIMISO 0x00000008 /* PB 28 */ +#define PB_SPI_CE 0x00010000 /* PB 15 */ + +/* ------------------------------------------------------------------------- */ + +/* read clock time from DS1306 and return it in *tmp */ +void rtc_get(struct rtc_time *tmp) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + unsigned char spi_byte; /* Data Byte */ + + init_spi(); /* set port B for software SPI */ + + /* Now we can enable the DS1306 RTC */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; + udelay(10); + + /* Shift out the address (0) of the time in the Clock Chip */ + soft_spi_send(0); + + /* Put the clock readings into the rtc_time structure */ + tmp->tm_sec = bcd2bin(soft_spi_read()); /* Read seconds */ + tmp->tm_min = bcd2bin(soft_spi_read()); /* Read minutes */ + + /* Hours are trickier */ + spi_byte = soft_spi_read(); /* Read Hours into temporary value */ + if (spi_byte & 0x40) { + /* 12 hour mode bit is set (time is in 1-12 format) */ + if (spi_byte & 0x20) { + /* since PM we add 11 to get 0-23 for hours */ + tmp->tm_hour = (bcd2bin(spi_byte & 0x1F)) + 11; + } + else { + /* since AM we subtract 1 to get 0-23 for hours */ + tmp->tm_hour = (bcd2bin(spi_byte & 0x1F)) - 1; + } + } + else { + /* Otherwise, 0-23 hour format */ + tmp->tm_hour = (bcd2bin(spi_byte & 0x3F)); + } + + soft_spi_read(); /* Read and discard Day of week */ + tmp->tm_mday = bcd2bin(soft_spi_read()); /* Read Day of the Month */ + tmp->tm_mon = bcd2bin(soft_spi_read()); /* Read Month */ + + /* Read Year and convert to this century */ + tmp->tm_year = bcd2bin(soft_spi_read()) + 2000; + + /* Now we can disable the DS1306 RTC */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ + udelay(10); + + GregorianDay(tmp); /* Determine the day of week */ + + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +/* ------------------------------------------------------------------------- */ + +/* set clock time in DS1306 RTC and in MPC8xx RTC */ +void rtc_set(struct rtc_time *tmp) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + + init_spi(); /* set port B for software SPI */ + + /* Now we can enable the DS1306 RTC */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */ + udelay(10); + + /* First disable write protect in the clock chip control register */ + soft_spi_send(0x8F); /* send address of the control register */ + soft_spi_send(0x00); /* send control register contents */ + + /* Now disable the DS1306 to terminate the write */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; + udelay(10); + + /* Now enable the DS1306 to initiate a new write */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; + udelay(10); + + /* Next, send the address of the clock time write registers */ + soft_spi_send(0x80); /* send address of the first time register */ + + /* Use Burst Mode to send all of the time data to the clock */ + bin2bcd(tmp->tm_sec); + soft_spi_send(bin2bcd(tmp->tm_sec)); /* Send Seconds */ + soft_spi_send(bin2bcd(tmp->tm_min)); /* Send Minutes */ + soft_spi_send(bin2bcd(tmp->tm_hour)); /* Send Hour */ + soft_spi_send(bin2bcd(tmp->tm_wday)); /* Send Day of the Week */ + soft_spi_send(bin2bcd(tmp->tm_mday)); /* Send Day of Month */ + soft_spi_send(bin2bcd(tmp->tm_mon)); /* Send Month */ + soft_spi_send(bin2bcd(tmp->tm_year - 2000)); /* Send Year */ + + /* Now we can disable the Clock chip to terminate the burst write */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ + udelay(10); + + /* Now we can enable the Clock chip to initiate a new write */ + immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */ + udelay(10); + + /* First we Enable write protect in the clock chip control register */ + soft_spi_send(0x8F); /* send address of the control register */ + soft_spi_send(0x40); /* send out Control Register contents */ + + /* Now disable the DS1306 */ + immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ + udelay(10); + + /* Set standard MPC8xx clock to the same time so Linux will + * see the time even if it doesn't have a DS1306 clock driver. + * This helps with experimenting with standard kernels. + */ + { + ulong tim; + + tim = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + immap->im_sitk.sitk_rtck = KAPWR_KEY; + immap->im_sit.sit_rtc = tim; + } + + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +/* ------------------------------------------------------------------------- */ + +void rtc_reset(void) +{ + return; /* nothing to do */ +} + +/* ------------------------------------------------------------------------- */ + +static unsigned char bcd2bin(unsigned char n) +{ + return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F)); +} + +/* ------------------------------------------------------------------------- */ + +static unsigned int bin2bcd(unsigned int n) +{ + return (((n / 10) << 4) | (n % 10)); +} + +/* ------------------------------------------------------------------------- */ + +/* Initialize Port B for software SPI */ +static void init_spi(void) { + volatile immap_t *immap = (immap_t *)CFG_IMMR; + + /* Force output pins to begin at logic 0 */ + immap->im_cpm.cp_pbdat &= ~(PB_SPI_CE | PB_SPIMOSI | PB_SPISCK); + + /* Set these 3 signals as outputs */ + immap->im_cpm.cp_pbdir |= (PB_SPIMOSI | PB_SPI_CE | PB_SPISCK); + + immap->im_cpm.cp_pbdir &= ~PB_SPIMISO; /* Make MISO pin an input */ + udelay(10); +} + +/* ------------------------------------------------------------------------- */ + +/* NOTE: soft_spi_send() assumes that the I/O lines are configured already */ +static void soft_spi_send(unsigned char n) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + unsigned char bitpos; /* bit position to receive */ + unsigned char i; /* Loop Control */ + + /* bit position to send, start with most significant bit */ + bitpos = 0x80; + + /* Send 8 bits to software SPI */ + for (i = 0; i < 8; i++) { /* Loop for 8 bits */ + immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */ + + if (n & bitpos) + immap->im_cpm.cp_pbdat |= PB_SPIMOSI; /* Set MOSI to 1 */ + else + immap->im_cpm.cp_pbdat &= ~PB_SPIMOSI; /* Set MOSI to 0 */ + udelay(10); + + immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */ + udelay(10); + + bitpos >>= 1; /* Shift for next bit position */ + } +} + +/* ------------------------------------------------------------------------- */ + +/* NOTE: soft_spi_read() assumes that the I/O lines are configured already */ +static unsigned char soft_spi_read(void) +{ + volatile immap_t *immap = (immap_t *)CFG_IMMR; + + unsigned char spi_byte = 0; /* Return value, assume success */ + unsigned char bitpos; /* bit position to receive */ + unsigned char i; /* Loop Control */ + + /* bit position to receive, start with most significant bit */ + bitpos = 0x80; + + /* Read 8 bits here */ + for (i = 0; i < 8; i++) { /* Do 8 bits in loop */ + immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */ + udelay(10); + if (immap->im_cpm.cp_pbdat & PB_SPIMISO) /* Get a bit of data */ + spi_byte |= bitpos; /* Set data accordingly */ + immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */ + udelay(10); + bitpos >>= 1; /* Shift for next bit position */ + } + + return spi_byte; /* Return the byte read */ +} + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/rtc/m41t11.c b/rtc/m41t11.c new file mode 100644 index 00000000000..30a101c34dc --- /dev/null +++ b/rtc/m41t11.c @@ -0,0 +1,202 @@ +/* + * (C) Copyright 2002 + * Andrew May, Viasat Inc, amay@viasat.com + * + * 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 + */ + +/* + * M41T11 Serial Access Timekeeper(R) SRAM + * can you believe a trademark on that? + */ + +#include <common.h> +#include <command.h> +#include <rtc.h> +#include <i2c.h> + +/* + I Don't have an example config file but this + is what should be done. + +#define CONFIG_RTC_M41T11 1 +#define CFG_I2C_RTC_ADDR 0x68 +#if 0 +#define CFG_M41T11_EXT_CENTURY_DATA +#else +#define CFG_M41T11_BASE_YEAR 2000 +#endif +*/ + +#if defined(CONFIG_RTC_M41T11) && defined(CFG_I2C_RTC_ADDR) && (CONFIG_COMMANDS & CFG_CMD_DATE) + +#define DEBUG 1 + +static unsigned bcd2bin (uchar n) +{ + return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F)); +} + +static unsigned char bin2bcd (unsigned int n) +{ + return (((n / 10) << 4) | (n % 10)); +} + + +/* ------------------------------------------------------------------------- */ +/* + these are simple defines for the chip local to here so they aren't too + verbose + DAY/DATE aren't nice but that is how they are on the data sheet +*/ +#define RTC_SEC_ADDR 0x0 +#define RTC_MIN_ADDR 0x1 +#define RTC_HOUR_ADDR 0x2 +#define RTC_DAY_ADDR 0x3 +#define RTC_DATE_ADDR 0x4 +#define RTC_MONTH_ADDR 0x5 +#define RTC_YEARS_ADDR 0x6 + +#define RTC_REG_CNT 7 + +#define RTC_CONTROL_ADDR 0x7 + + +#ifndef CFG_M41T11_EXT_CENTURY_DATA + +#define REG_CNT (RTC_REG_CNT+1) + +/* + you only get 00-99 for the year we will asume you + want from the year 2000 if you don't set the config +*/ +#ifndef CFG_M41T11_BASE_YEAR +#define CFG_M41T11_BASE_YEAR 2000 +#endif + +#else +/* we will store extra year info in byte 9*/ +#define M41T11_YEAR_DATA 0x8 +#define M41T11_YEAR_SIZE 1 +#define REG_CNT (RTC_REG_CNT+1+M41T11_YEAR_SIZE) +#endif + +#define M41T11_STORAGE_SZ (64-REG_CNT) + +void rtc_get (struct rtc_time *tmp) +{ + uchar data[RTC_REG_CNT]; + + i2c_read(CFG_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, data, RTC_REG_CNT); + + if( data[RTC_SEC_ADDR] & 0x80 ){ + printf( "m41t11 RTC Clock stopped!!!\n" ); + } + tmp->tm_sec = bcd2bin (data[RTC_SEC_ADDR] & 0x7F); + tmp->tm_min = bcd2bin (data[RTC_MIN_ADDR] & 0x7F); + tmp->tm_hour = bcd2bin (data[RTC_HOUR_ADDR] & 0x3F); + tmp->tm_mday = bcd2bin (data[RTC_DATE_ADDR] & 0x3F); + tmp->tm_mon = bcd2bin (data[RTC_MONTH_ADDR]& 0x1F); +#ifndef CFG_M41T11_EXT_CENTURY_DATA + tmp->tm_year = CFG_M41T11_BASE_YEAR + + bcd2bin(data[RTC_YEARS_ADDR]) + + ((data[RTC_HOUR_ADDR]&0x40) ? 100 : 0); +#else + { + unsigned char cent; + i2c_read(CFG_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, ¢, M41T11_YEAR_SIZE); + if( !(data[RTC_HOUR_ADDR] & 0x80) ){ + printf( "m41t11 RTC: cann't keep track of years without CEB set\n" ); + } + if( (cent & 0x1) != ((data[RTC_HOUR_ADDR]&0x40)>>7) ){ + /*century flip store off new year*/ + cent += 1; + i2c_write(CFG_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, ¢, M41T11_YEAR_SIZE); + } + tmp->tm_year =((int)cent*100)+bcd2bin(data[RTC_YEARS_ADDR]); + } +#endif + tmp->tm_wday = bcd2bin (data[RTC_DAY_ADDR] & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +void rtc_set (struct rtc_time *tmp) +{ + uchar data[RTC_REG_CNT]; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + data[RTC_SEC_ADDR] = bin2bcd(tmp->tm_sec) & 0x7F;/*just in case*/ + data[RTC_MIN_ADDR] = bin2bcd(tmp->tm_min); + data[RTC_HOUR_ADDR] = bin2bcd(tmp->tm_hour) & 0x3F;/*handle cent stuff later*/ + data[RTC_DATE_ADDR] = bin2bcd(tmp->tm_mday) & 0x3F; + data[RTC_MONTH_ADDR] = bin2bcd(tmp->tm_mon); + data[RTC_DAY_ADDR] = bin2bcd(tmp->tm_wday) & 0x07; + + data[RTC_HOUR_ADDR] |= 0x80;/*we will always use CEB*/ + + data[RTC_YEARS_ADDR] = bin2bcd(tmp->tm_year%100);/*same thing either way*/ +#ifndef CFG_M41T11_EXT_CENTURY_DATA + if( ((tmp->tm_year - CFG_M41T11_BASE_YEAR) > 200) || + (tmp->tm_year < CFG_M41T11_BASE_YEAR) ){ + printf( "m41t11 RTC setting year out of range!!need recompile\n" ); + } + data[RTC_HOUR_ADDR] |= (tmp->tm_year - CFG_M41T11_BASE_YEAR) > 100 ? 0x40 : 0; +#else + { + unsigned char cent; + cent = tmp->tm_year ? tmp->tm_year / 100 : 0; + data[RTC_HOUR_ADDR] |= (cent & 0x1) ? 0x40 : 0; + i2c_write(CFG_I2C_RTC_ADDR, M41T11_YEAR_DATA, 1, ¢, M41T11_YEAR_SIZE); + } +#endif + i2c_write(CFG_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, data, RTC_REG_CNT); +} + +void rtc_reset (void) +{ + unsigned char val; + /* clear all control & status registers */ + i2c_read(CFG_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, &val, 1); + val = val & 0x7F;/*make sure we are running*/ + i2c_write(CFG_I2C_RTC_ADDR, RTC_SEC_ADDR, 1, &val, RTC_REG_CNT); + + i2c_read(CFG_I2C_RTC_ADDR, RTC_CONTROL_ADDR, 1, &val, 1); + val = val & 0x3F;/*turn off freq test keep calibration*/ + i2c_write(CFG_I2C_RTC_ADDR, RTC_CONTROL_ADDR, 1, &val, 1); +} + +int rtc_store(int addr, unsigned char* data, int size) +{ + /*don't let things wrap onto the time on a write*/ + if( (addr+size) >= M41T11_STORAGE_SZ ) + return 1; + return i2c_write( CFG_I2C_RTC_ADDR, REG_CNT+addr, 1, data, size ); +} + +int rtc_recall(int addr, unsigned char* data, int size) +{ + return i2c_read( CFG_I2C_RTC_ADDR, REG_CNT+addr, 1, data, size ); +} + +#endif /* CONFIG_RTC_M41T11 && CFG_I2C_RTC_ADDR && CFG_CMD_DATE */ diff --git a/rtc/m48t35ax.c b/rtc/m48t35ax.c new file mode 100644 index 00000000000..6c38a538b9f --- /dev/null +++ b/rtc/m48t35ax.c @@ -0,0 +1,166 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * 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 + */ + +/* + * Date & Time support for ST Electronics M48T35Ax RTC + */ + +/*#define DEBUG */ + + +#include <common.h> +#include <command.h> +#include <rtc.h> +#include <config.h> + +#if defined(CONFIG_RTC_M48T35A) && (CONFIG_COMMANDS & CFG_CMD_DATE) + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); +static uchar bin2bcd (unsigned int n); +static unsigned bcd2bin(uchar c); + +/* ------------------------------------------------------------------------- */ + +void rtc_get (struct rtc_time *tmp) +{ + uchar sec, min, hour, cent_day, date, month, year; + uchar ccr; /* Clock control register */ + + /* Lock RTC for read using clock control register */ + ccr = rtc_read(0); + ccr = ccr | 0x40; + rtc_write(0, ccr); + + sec = rtc_read (0x1); + min = rtc_read (0x2); + hour = rtc_read (0x3); + cent_day= rtc_read (0x4); + date = rtc_read (0x5); + month = rtc_read (0x6); + year = rtc_read (0x7); + + /* UNLock RTC */ + ccr = rtc_read(0); + ccr = ccr & 0xBF; + rtc_write(0, ccr); + + debug ( "Get RTC year: %02x month: %02x date: %02x cent_day: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, month, date, cent_day, + hour, min, sec ); + + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (date & 0x3F); + tmp->tm_mon = bcd2bin (month & 0x1F); + tmp->tm_year = bcd2bin (year) + ((cent_day & 0x10) ? 2000 : 1900); + tmp->tm_wday = bcd2bin (cent_day & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst= 0; + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +void rtc_set (struct rtc_time *tmp) +{ + uchar ccr; /* Clock control register */ + uchar century; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + /* Lock RTC for write using clock control register */ + ccr = rtc_read(0); + ccr = ccr | 0x80; + rtc_write(0, ccr); + + rtc_write (0x07, bin2bcd(tmp->tm_year % 100)); + rtc_write (0x06, bin2bcd(tmp->tm_mon)); + rtc_write (0x05, bin2bcd(tmp->tm_mday)); + + century = ((tmp->tm_year >= 2000) ? 0x10 : 0) | 0x20; + rtc_write (0x04, bin2bcd(tmp->tm_wday) | century); + + rtc_write (0x03, bin2bcd(tmp->tm_hour)); + rtc_write (0x02, bin2bcd(tmp->tm_min )); + rtc_write (0x01, bin2bcd(tmp->tm_sec )); + + /* UNLock RTC */ + ccr = rtc_read(0); + ccr = ccr & 0x7F; + rtc_write(0, ccr); +} + +void rtc_reset (void) +{ + uchar val; + + /* Clear all clock control registers */ + rtc_write (0x0, 0x80); /* No Read Lock or calibration */ + + /* Clear stop bit */ + val = rtc_read (0x1); + val &= 0x7f; + rtc_write(0x1, val); + + /* Enable century / disable frequency test */ + val = rtc_read (0x4); + val = (val & 0xBF) | 0x20; + rtc_write(0x4, val); + + /* Clear write lock */ + rtc_write(0x0, 0); +} + +/* ------------------------------------------------------------------------- */ + +static uchar rtc_read (uchar reg) +{ + uchar val; + val = *(unsigned char *) + ((CFG_NVRAM_BASE_ADDR + CFG_NVRAM_SIZE - 8) + reg); + return val; +} + +static void rtc_write (uchar reg, uchar val) +{ + *(unsigned char *) + ((CFG_NVRAM_BASE_ADDR + CFG_NVRAM_SIZE - 8) + reg) = val; +} + +static unsigned bcd2bin (uchar n) +{ + return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F)); +} + +static unsigned char bin2bcd (unsigned int n) +{ + return (((n / 10) << 4) | (n % 10)); +} + +#endif /* CONFIG_RTC_M48T35A && CFG_CMD_DATE */ diff --git a/rtc/mc146818.c b/rtc/mc146818.c new file mode 100644 index 00000000000..1d65808f79d --- /dev/null +++ b/rtc/mc146818.c @@ -0,0 +1,172 @@ +/* + * (C) Copyright 2001 + * Denis Peter MPL AG Switzerland. d.peter@mpl.ch + * + * 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 + */ + +/* + * Date & Time support for the MC146818 (PIXX4) RTC + */ + +/*#define DEBUG*/ + +#include <common.h> +#include <command.h> +#include <rtc.h> + +#if defined(CONFIG_RTC_MC146818) && (CONFIG_COMMANDS & CFG_CMD_DATE) + +static uchar rtc_read (uchar reg); +static void rtc_write (uchar reg, uchar val); +static uchar bin2bcd (unsigned int n); +static unsigned bcd2bin(uchar c); + +#define RTC_PORT_MC146818 CFG_ISA_IO_BASE_ADDRESS + 0x70 +#define RTC_SECONDS 0x00 +#define RTC_SECONDS_ALARM 0x01 +#define RTC_MINUTES 0x02 +#define RTC_MINUTES_ALARM 0x03 +#define RTC_HOURS 0x04 +#define RTC_HOURS_ALARM 0x05 +#define RTC_DAY_OF_WEEK 0x06 +#define RTC_DATE_OF_MONTH 0x07 +#define RTC_MONTH 0x08 +#define RTC_YEAR 0x09 +#define RTC_CONFIG_A 0x0A +#define RTC_CONFIG_B 0x0B +#define RTC_CONFIG_C 0x0C +#define RTC_CONFIG_D 0x0D + + +/* ------------------------------------------------------------------------- */ + +void rtc_get (struct rtc_time *tmp) +{ + uchar sec, min, hour, mday, wday, mon, year; + /* here check if rtc can be accessed */ + while((rtc_read(RTC_CONFIG_A)&0x80)==0x80); + sec = rtc_read (RTC_SECONDS); + min = rtc_read (RTC_MINUTES); + hour = rtc_read (RTC_HOURS); + mday = rtc_read (RTC_DATE_OF_MONTH); + wday = rtc_read (RTC_DAY_OF_WEEK); + mon = rtc_read (RTC_MONTH); + year = rtc_read (RTC_YEAR); +#ifdef RTC_DEBUG + printf ( "Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x\n", + year, mon_cent, mday, wday, + hour, min, sec ); + printf ( "Alarms: month: %02x hour: %02x min: %02x sec: %02x\n", + rtc_read (RTC_CONFIG_D) & 0x3F, + rtc_read (RTC_HOURS_ALARM), + rtc_read (RTC_MINUTES_ALARM), + rtc_read (RTC_SECONDS_ALARM) ); +#endif + tmp->tm_sec = bcd2bin (sec & 0x7F); + tmp->tm_min = bcd2bin (min & 0x7F); + tmp->tm_hour = bcd2bin (hour & 0x3F); + tmp->tm_mday = bcd2bin (mday & 0x3F); + tmp->tm_mon = bcd2bin (mon & 0x1F); + tmp->tm_year = bcd2bin (year); + tmp->tm_wday = bcd2bin (wday & 0x07); + if(tmp->tm_year<70) + tmp->tm_year+=2000; + else + tmp->tm_year+=1900; + tmp->tm_yday = 0; + tmp->tm_isdst= 0; +#ifdef RTC_DEBUG + printf ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif +} + +void rtc_set (struct rtc_time *tmp) +{ +#ifdef RTC_DEBUG + printf ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +#endif + rtc_write(RTC_CONFIG_B,0x82); /* disables the RTC to update the regs */ + + rtc_write (RTC_YEAR, bin2bcd(tmp->tm_year % 100)); + rtc_write (RTC_MONTH, bin2bcd(tmp->tm_mon)); + + rtc_write (RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday)); + rtc_write (RTC_DATE_OF_MONTH, bin2bcd(tmp->tm_mday)); + rtc_write (RTC_HOURS, bin2bcd(tmp->tm_hour)); + rtc_write (RTC_MINUTES, bin2bcd(tmp->tm_min )); + rtc_write (RTC_SECONDS, bin2bcd(tmp->tm_sec )); + rtc_write(RTC_CONFIG_B,0x02); /* enables the RTC to update the regs */ + +} + +void rtc_reset (void) +{ + rtc_write(RTC_CONFIG_B,0x82); /* disables the RTC to update the regs */ + rtc_write(RTC_CONFIG_A,0x20); /* Normal OP */ + rtc_write(RTC_CONFIG_B,0x00); + rtc_write(RTC_CONFIG_B,0x00); + rtc_write(RTC_CONFIG_B,0x02); /* enables the RTC to update the regs */ +} + +/* ------------------------------------------------------------------------- */ + +#ifdef CFG_RTC_REG_BASE_ADDR +/* + * use direct memory access + */ +static uchar rtc_read (uchar reg) +{ + return(in8(CFG_RTC_REG_BASE_ADDR+reg)); +} + +static void rtc_write (uchar reg, uchar val) +{ + out8(CFG_RTC_REG_BASE_ADDR+reg, val); +} +#else +static uchar rtc_read (uchar reg) +{ + out8(RTC_PORT_MC146818,reg); + return(in8(RTC_PORT_MC146818+1)); +} + +static void rtc_write (uchar reg, uchar val) +{ + out8(RTC_PORT_MC146818,reg); + out8(RTC_PORT_MC146818+1,val); +} +#endif + +static unsigned bcd2bin (uchar n) +{ + return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F)); +} + +static unsigned char bin2bcd (unsigned int n) +{ + return (((n / 10) << 4) | (n % 10)); +} + +#endif /* CONFIG_RTC_MC146818 && CFG_CMD_DATE */ diff --git a/rtc/mpc8xx.c b/rtc/mpc8xx.c new file mode 100644 index 00000000000..830e56e17c5 --- /dev/null +++ b/rtc/mpc8xx.c @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2001 + * 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 + */ + +/* + * Date & Time support for internal RTC of MPC8xx + */ + +/*#define DEBUG*/ + +#include <common.h> +#include <command.h> +#include <rtc.h> + +#if defined(CONFIG_RTC_MPC8xx) && (CONFIG_COMMANDS & CFG_CMD_DATE) + +/* ------------------------------------------------------------------------- */ + +void rtc_get (struct rtc_time *tmp) +{ + volatile immap_t *immr = (immap_t *)CFG_IMMR; + ulong tim; + + tim = immr->im_sit.sit_rtc; + + to_tm (tim, tmp); + + debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +} + +void rtc_set (struct rtc_time *tmp) +{ + volatile immap_t *immr = (immap_t *)CFG_IMMR; + ulong tim; + + debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + tim = mktime (tmp->tm_year, tmp->tm_mon, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + immr->im_sitk.sitk_rtck = KAPWR_KEY; + immr->im_sit.sit_rtc = tim; +} + +void rtc_reset (void) +{ + return; /* nothing to do */ +} + +/* ------------------------------------------------------------------------- */ + +#endif /* CONFIG_RTC_MPC8xx && CFG_CMD_DATE */ |