/* * (C) Copyright 2009 * Matthias Fuchs, esd gmbh germany, matthias.fuchs@esd.eu * * 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 <libfdt.h> #include <fdt_support.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/gpio.h> #include <asm/4xx_pci.h> #include <command.h> #include <malloc.h> /* * PMC405-DE cpld registers * - all registers are 8 bit * - all registers are on 32 bit addesses */ struct pmc405de_cpld { /* cpld design version */ u8 version; u8 reserved0[3]; /* misc. status lines */ u8 status; u8 reserved1[3]; /* * gated control flags * gate bit(s) must be written with '1' to * access control flag */ u8 control; u8 reserved2[3]; }; #define CPLD_VERSION_MASK 0x0f #define CPLD_CONTROL_POSTLED_N 0x01 #define CPLD_CONTROL_POSTLED_GATE 0x02 #define CPLD_CONTROL_RESETOUT_N 0x40 #define CPLD_CONTROL_RESETOUT_N_GATE 0x80 DECLARE_GLOBAL_DATA_PTR; extern void __ft_board_setup(void *blob, bd_t *bd); extern void pll_write(u32 a, u32 b); static int wait_for_pci_ready_done; static int is_monarch(void); static int pci_is_66mhz(void); static int board_revision(void); static int cpld_revision(void); static void upd_plb_pci_div(u32 pllmr0, u32 pllmr1, u32 div); int board_early_init_f(void) { u32 pllmr0, pllmr1; /* * check M66EN and patch PLB:PCI divider for 66MHz PCI * * fCPU==333MHz && fPCI==66MHz (PLBDiv==3 && M66EN==1): PLB/PCI=1 * fCPU==333MHz && fPCI==33MHz (PLBDiv==3 && M66EN==0): PLB/PCI=2 * fCPU==133|266MHz && fPCI==66MHz (PLBDiv==1|2 && M66EN==1): PLB/PCI=2 * fCPU==133|266MHz && fPCI==33MHz (PLBDiv==1|2 && M66EN==0): PLB/PCI=3 * * calling upd_plb_pci_div() may end in calling pll_write() which will * do a chip reset and never return. */ pllmr0 = mfdcr(CPC0_PLLMR0); pllmr1 = mfdcr(CPC0_PLLMR1); if ((pllmr0 & PLLMR0_CPU_TO_PLB_MASK) == PLLMR0_CPU_PLB_DIV_3) { /* fCPU=333MHz, fPLB=111MHz */ if (pci_is_66mhz()) upd_plb_pci_div(pllmr0, pllmr1, PLLMR0_PCI_PLB_DIV_1); else upd_plb_pci_div(pllmr0, pllmr1, PLLMR0_PCI_PLB_DIV_2); } else { /* fCPU=133|266MHz, fPLB=133MHz */ if (pci_is_66mhz()) upd_plb_pci_div(pllmr0, pllmr1, PLLMR0_PCI_PLB_DIV_2); else upd_plb_pci_div(pllmr0, pllmr1, PLLMR0_PCI_PLB_DIV_3); } /* * IRQ 25 (EXT IRQ 0) PCI-INTA#; active low; level sensitive * IRQ 26 (EXT IRQ 1) PCI-INTB#; active low; level sensitive * IRQ 27 (EXT IRQ 2) PCI-INTC#; active low; level sensitive * IRQ 28 (EXT IRQ 3) PCI-INTD#; active low; level sensitive * IRQ 29 (EXT IRQ 4) ETH0-PHY-IRQ#; active low; level sensitive * IRQ 30 (EXT IRQ 5) ETH1-PHY-IRQ#; active low; level sensitive * IRQ 31 (EXT IRQ 6) PLD-IRQ#; active low; level sensitive */ mtdcr(uicsr, 0xFFFFFFFF); /* clear all ints */ mtdcr(uicer, 0x00000000); /* disable all ints */ mtdcr(uiccr, 0x00000000); /* set all to be non-critical*/ mtdcr(uicpr, 0xFFFFFF80); /* set int polarities */ mtdcr(uictr, 0x10000000); /* set int trigger levels */ mtdcr(uicvcr, 0x00000001); /* set vect base=0, INT0 highest prio */ mtdcr(uicsr, 0xFFFFFFFF); /* clear all ints */ /* * EBC Configuration Register: * - set ready timeout to 512 ebc-clks -> ca. 15 us * - EBC lines are always driven */ mtebc(epcr, 0xa8400000); return 0; } static void upd_plb_pci_div(u32 pllmr0, u32 pllmr1, u32 div) { if ((pllmr0 & PLLMR0_PCI_TO_PLB_MASK) != div) pll_write((pllmr0 & ~PLLMR0_PCI_TO_PLB_MASK) | div, pllmr1); } int misc_init_r(void) { int i; struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; struct pmc405de_cpld *cpld = (struct pmc405de_cpld *)CONFIG_SYS_CPLD_BASE; if (!is_monarch()) { /* PCI configuration done: release EREADY */ setbits_be32(&gpio0->or, CONFIG_SYS_GPIO_EREADY); setbits_be32(&gpio0->tcr, CONFIG_SYS_GPIO_EREADY); } /* turn off POST LED */ out_8(&cpld->control, CPLD_CONTROL_POSTLED_N | CPLD_CONTROL_POSTLED_GATE); /* turn on LEDs: RUN, A, B */ clrbits_be32(&gpio0->or, CONFIG_SYS_GPIO_LEDRUN_N | CONFIG_SYS_GPIO_LEDA_N | CONFIG_SYS_GPIO_LEDB_N); for (i=0; i < 200; i++) udelay(1000); /* turn off LEDs: A, B */ setbits_be32(&gpio0->or, CONFIG_SYS_GPIO_LEDA_N | CONFIG_SYS_GPIO_LEDB_N); return (0); } static int is_monarch(void) { struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; return (in_be32(&gpio0->ir) & CONFIG_SYS_GPIO_MONARCH_N) == 0; } static int pci_is_66mhz(void) { struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; return (in_be32(&gpio0->ir) & CONFIG_SYS_GPIO_M66EN); } static int board_revision(void) { struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; return ((in_be32(&gpio0->ir) & CONFIG_SYS_GPIO_HWREV_MASK) >> CONFIG_SYS_GPIO_HWREV_SHIFT); } static int cpld_revision(void) { struct pmc405de_cpld *cpld = (struct pmc405de_cpld *)CONFIG_SYS_CPLD_BASE; return ((in_8(&cpld->version) & CPLD_VERSION_MASK)); } /* * Check Board Identity */ int checkboard(void) { puts("Board: esd GmbH - PMC-CPU/405-DE"); gd->board_type = board_revision(); printf(", Rev 1.%ld, ", gd->board_type); if (!is_monarch()) puts("non-"); printf("monarch, PCI=%s MHz, PLD-Rev 1.%d\n", pci_is_66mhz() ? "66" : "33", cpld_revision()); return 0; } static void wait_for_pci_ready(void) { struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; int i; char *s = getenv("pcidelay"); /* only wait once */ if (wait_for_pci_ready_done) return; /* * We have our own handling of the pcidelay variable. * Using CONFIG_PCI_BOOTDELAY enables pausing for host * and adapter devices. For adapter devices we do not * want this. */ if (s) { int ms = simple_strtoul(s, NULL, 10); printf("PCI: Waiting for %d ms\n", ms); for (i=0; i<ms; i++) udelay(1000); } if (!(in_be32(&gpio0->ir) & CONFIG_SYS_GPIO_EREADY)) { printf("PCI: Waiting for EREADY (CTRL-C to skip) ... "); while (1) { if (ctrlc()) { puts("abort\n"); break; } if (in_be32(&gpio0->ir) & CONFIG_SYS_GPIO_EREADY) { printf("done\n"); break; } } } wait_for_pci_ready_done = 1; } /* * Overwrite weak is_pci_host() * * This routine is called to determine if a pci scan should be * performed. With various hardware environments (especially cPCI and * PPMC) it's insufficient to depend on the state of the arbiter enable * bit in the strap register, or generic host/adapter assumptions. * * Return 0 for adapter mode, non-zero for host (monarch) mode. */ int is_pci_host(struct pci_controller *hose) { char *s; if (!is_monarch()) { /* * Overwrite PCI identification when running in * non-monarch mode * This should be moved into pci_target_init() * when it is sometimes available for 405 CPUs */ pci_write_config_word(PCIDEVID_405GP, PCI_SUBSYSTEM_ID, CONFIG_SYS_PCI_SUBSYS_ID_NONMONARCH); pci_write_config_word(PCIDEVID_405GP, PCI_CLASS_SUB_CODE, CONFIG_SYS_PCI_CLASSCODE_NONMONARCH); } s = getenv("pciscan"); if (s == NULL) { if (is_monarch()) { wait_for_pci_ready(); return 1; } else { return 0; } } else { if (!strcmp(s, "yes")) return 1; } return 0; } /* * Overwrite weak pci_pre_init() * * The default implementation enables the 405EP * internal PCI arbiter. We do not want that * on a PMC module. */ int pci_pre_init(struct pci_controller *hose) { return 1; } #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) void ft_board_setup(void *blob, bd_t *bd) { int rc; __ft_board_setup(blob, bd); /* * Disable PCI in non-monarch mode. */ if (!is_monarch()) { rc = fdt_find_and_setprop(blob, "/plb/pci@ec000000", "status", "disabled", sizeof("disabled"), 1); if (rc) { printf("Unable to update property status in PCI node, " "err=%s\n", fdt_strerror(rc)); } } } #endif /* defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) */ #if defined(CONFIG_SYS_EEPROM_WREN) /* Input: <dev_addr> I2C address of EEPROM device to enable. * <state> -1: deliver current state * 0: disable write * 1: enable write * Returns: -1: wrong device address * 0: dis-/en- able done * 0/1: current state if <state> was -1. */ int eeprom_write_enable(unsigned dev_addr, int state) { struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; if (CONFIG_SYS_I2C_EEPROM_ADDR != dev_addr) { return -1; } else { switch (state) { case 1: /* Enable write access, clear bit GPIO0. */ clrbits_be32(&gpio0->or, CONFIG_SYS_GPIO_EEPROM_WP); state = 0; break; case 0: /* Disable write access, set bit GPIO0. */ setbits_be32(&gpio0->or, CONFIG_SYS_GPIO_EEPROM_WP); state = 0; break; default: /* Read current status back. */ state = (0 == (in_be32(&gpio0->or) & CONFIG_SYS_GPIO_EEPROM_WP)); break; } } return state; } int do_eep_wren(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { int query = argc == 1; int state = 0; if (query) { /* Query write access state. */ state = eeprom_write_enable(CONFIG_SYS_I2C_EEPROM_ADDR, - 1); if (state < 0) { puts("Query of write access state failed.\n"); } else { printf("Write access for device 0x%0x is %sabled.\n", CONFIG_SYS_I2C_EEPROM_ADDR, state ? "en" : "dis"); state = 0; } } else { if ('0' == argv[1][0]) { /* Disable write access. */ state = eeprom_write_enable( CONFIG_SYS_I2C_EEPROM_ADDR, 0); } else { /* Enable write access. */ state = eeprom_write_enable( CONFIG_SYS_I2C_EEPROM_ADDR, 1); } if (state < 0) puts ("Setup of write access state failed.\n"); } return state; } U_BOOT_CMD(eepwren, 2, 0, do_eep_wren, "Enable / disable / query EEPROM write access", "" ); #endif /* #if defined(CONFIG_SYS_EEPROM_WREN) */ #if defined(CONFIG_PRAM) #include <environment.h> extern env_t *env_ptr; int do_painit(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { u32 pram, nextbase, base; char *v; u32 param; ulong *lptr; v = getenv("pram"); if (v) pram = simple_strtoul(v, NULL, 10); else { printf("Error: pram undefined. Please define pram in KiB\n"); return 1; } base = gd->bd->bi_memsize; #if defined(CONFIG_LOGBUFFER) base -= LOGBUFF_LEN + LOGBUFF_OVERHEAD; #endif /* * gd->bd->bi_memsize == physical ram size - CONFIG_SYS_MM_TOP_HIDE */ param = base - (pram << 10); printf("PARAM: @%08x\n", param); debug("memsize=0x%08x, base=0x%08x\n", gd->bd->bi_memsize, base); /* clear entire PA ram */ memset((void*)param, 0, (pram << 10)); /* reserve 4k for pointer field */ nextbase = base - 4096; lptr = (ulong*)(base); /* * *(--lptr) = item_size; * *(--lptr) = base - item_base = distance from field top; */ /* env is first (4k aligned) */ nextbase -= ((CONFIG_ENV_SIZE + 4096 - 1) & ~(4096 - 1)); memcpy((void*)nextbase, env_ptr, CONFIG_ENV_SIZE); *(--lptr) = CONFIG_ENV_SIZE; /* size */ *(--lptr) = base - nextbase; /* offset | type=0 */ /* free section */ *(--lptr) = nextbase - param; /* size */ *(--lptr) = (base - param) | 126; /* offset | type=126 */ /* terminate pointer field */ *(--lptr) = crc32(0, (void*)(base - 0x10), 0x10); *(--lptr) = 0; /* offset=0 -> terminator */ return 0; } U_BOOT_CMD( painit, 1, 1, do_painit, "prepare PciAccess system", "" ); #endif /* CONFIG_PRAM */ int do_selfreset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { struct ppc4xx_gpio *gpio0 = (struct ppc4xx_gpio *)GPIO_BASE; setbits_be32(&gpio0->tcr, CONFIG_SYS_GPIO_SELFRST_N); return 0; } U_BOOT_CMD( selfreset, 1, 1, do_selfreset, "assert self-reset# signal", "" ); int do_resetout(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { struct pmc405de_cpld *cpld = (struct pmc405de_cpld *)CONFIG_SYS_CPLD_BASE; if (argc > 1) { if (argv[1][0] == '0') { /* assert */ printf("PMC-RESETOUT# asserted\n"); out_8(&cpld->control, CPLD_CONTROL_RESETOUT_N_GATE); } else { /* deassert */ printf("PMC-RESETOUT# deasserted\n"); out_8(&cpld->control, CPLD_CONTROL_RESETOUT_N | CPLD_CONTROL_RESETOUT_N_GATE); } } else { printf("PMC-RESETOUT# is %s\n", (in_8(&cpld->control) & CPLD_CONTROL_RESETOUT_N) ? "inactive" : "active"); } return 0; } U_BOOT_CMD( resetout, 2, 1, do_resetout, "assert PMC-RESETOUT# signal", "" );