powerpc: Partialy restore core of mpc8xx
CS Systemes d'Information (CSSI) manufactures 8xx boards for
critical communication systems. Those boards have been
running U-Boot since 2010 and will have to be maintained
until at least 2027.
commit 5b8e76c35ec312a3f73126bd1a2d2c0965b98a9f
("powerpc, 8xx: remove support for 8xx") orphaned those boards
by removing support for the mpc8xx CPU.
This commit partially restores support for the 8xx, with the
following limitations:
- Restores support for MPC866 and MPC885 only
- Does not restore IDE, PCMCIA, I2C, USB
- Does not restore examples
- Does not restore POST
- Does not restore Ethernet on SCC
- Does not restore console on SCC
- Does not restore bedbug and kgdb support
As the 866 and 885 do not support the following features,
they are not restored either:
- VIDEO / LCD
- RTC clock
The CPM uCODE patch is not restored either, because:
- 866 and 885 already have support for I2C and SPI relocation
without a uCODE patch
- relocation of SMC, I2C or SPI is only needed for using SCCs
for Ethernet or QMC
The dynamic setup/calculation of clocks is removed, we
expect the target being use with the clock and PLPRCR register
defined in the configuration.
All the clock settings for 8xx prior to 866 is removed as
well as we now only support 866 and 885.
This code is mature and addresses mature boards. Therefore
all code enclosed in '#if 0/#endif' and '#if XX_DEBUG/#endif'
is unneeded.
The following files are not restored by this patch:
- arch/powerpc/cpu/mpc8xx/bedbug_860.c
- arch/powerpc/cpu/mpc8xx/fec.h
- arch/powerpc/cpu/mpc8xx/kgdb.S
- arch/powerpc/cpu/mpc8xx/plprcr_write.S
- arch/powerpc/cpu/mpc8xx/scc.c
- arch/powerpc/cpu/mpc8xx/upatch.c
- arch/powerpc/cpu/mpc8xx/video.c
- arch/powerpc/include/asm/status_led.h
- arch/powerpc/lib/ide.c
- arch/powerpc/lib/ide.h
- doc/README.MPC866
- drivers/pcmcia/mpc8xx_pcmcia.c
- drivers/rtc/mpc8xx.c
- drivers/usb/gadget/mpc8xx_udc.c
- drivers/video/mpc8xx_lcd.c
- examples/standalone/test_burst.c
- examples/standalone/test_burst.h
- examples/standalone/test_burst_lib.S
- examples/standalone/timer.c
- include/mpc823_lcd.h
- include/usb/mpc8xx_udc.h
- post/cpu/mpc8xx/Makefile
- post/cpu/mpc8xx/cache.c
- post/cpu/mpc8xx/cache_8xx.S
- post/cpu/mpc8xx/ether.c
- post/cpu/mpc8xx/spr.c
- post/cpu/mpc8xx/uart.c
- post/cpu/mpc8xx/usb.c
- post/cpu/mpc8xx/watchdog.c
Some of the restored files are not located in a proper location.
In order to keep traceability of the changes, they will be
moved to their correct location and moved to Kconfig in a
followup patch.
This patch also declares CSSI as point of contact for the update
of the 8xx platform, as those boards are the only ones still
being maintained on the 8xx area. A later patch will add
those boards to the tree.
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index c3dba89..a7558d5 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -29,10 +29,16 @@
select SYS_FSL_DDR
select SYS_FSL_DDR_BE
+config 8xx
+ bool "MPC8xx"
+
endchoice
+source "arch/powerpc/lib/Kconfig"
+
source "arch/powerpc/cpu/mpc83xx/Kconfig"
source "arch/powerpc/cpu/mpc85xx/Kconfig"
source "arch/powerpc/cpu/mpc86xx/Kconfig"
+source "arch/powerpc/cpu/mpc8xx/Kconfig"
endmenu
diff --git a/arch/powerpc/cpu/mpc8xx/Kconfig b/arch/powerpc/cpu/mpc8xx/Kconfig
new file mode 100644
index 0000000..a425cba
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/Kconfig
@@ -0,0 +1,13 @@
+menu "mpc8xx CPU"
+ depends on 8xx
+
+config SYS_CPU
+ default "mpc8xx"
+
+choice
+ prompt "Target select"
+ optional
+
+endchoice
+
+endmenu
diff --git a/arch/powerpc/cpu/mpc8xx/Makefile b/arch/powerpc/cpu/mpc8xx/Makefile
new file mode 100644
index 0000000..5dd801d
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/Makefile
@@ -0,0 +1,17 @@
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+extra-y += start.o
+extra-y += traps.o
+obj-y += cpu.o
+obj-y += cpu_init.o
+obj-y += fec.o
+obj-$(CONFIG_OF_LIBFDT) += fdt.o
+obj-y += interrupts.o
+obj-y += serial.o
+obj-y += speed.o
+obj-y += spi.o
diff --git a/arch/powerpc/cpu/mpc8xx/config.mk b/arch/powerpc/cpu/mpc8xx/config.mk
new file mode 100644
index 0000000..485e43d
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/config.mk
@@ -0,0 +1,8 @@
+#
+# (C) Copyright 2000-2010
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+PLATFORM_CPPFLAGS += -mstring -mcpu=860 -msoft-float
diff --git a/arch/powerpc/cpu/mpc8xx/cpu.c b/arch/powerpc/cpu/mpc8xx/cpu.c
new file mode 100644
index 0000000..80b9596
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/cpu.c
@@ -0,0 +1,331 @@
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * m8xx.c
+ *
+ * CPU specific code
+ *
+ * written or collected and sometimes rewritten by
+ * Magnus Damm <damm@bitsmart.com>
+ *
+ * minor modifications by
+ * Wolfgang Denk <wd@denx.de>
+ */
+
+#include <common.h>
+#include <watchdog.h>
+#include <command.h>
+#include <mpc8xx.h>
+#include <commproc.h>
+#include <netdev.h>
+#include <asm/cache.h>
+#include <linux/compiler.h>
+#include <asm/io.h>
+
+#if defined(CONFIG_OF_LIBFDT)
+#include <libfdt.h>
+#include <fdt_support.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static char *cpu_warning = "\n " \
+ "*** Warning: CPU Core has Silicon Bugs -- Check the Errata ***";
+
+static int check_CPU (long clock, uint pvr, uint immr)
+{
+ char *id_str =
+ NULL;
+ volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000);
+ uint k, m;
+ char buf[32];
+ char pre = 'X';
+ char *mid = "xx";
+ char *suf;
+
+ /* the highest 16 bits should be 0x0050 for a 860 */
+
+ if ((pvr >> 16) != 0x0050)
+ return -1;
+
+ k = (immr << 16) |
+ immap->im_cpm.cp_dparam16[PROFF_REVNUM / sizeof(u16)];
+ m = 0;
+ suf = "";
+
+ /*
+ * Some boards use sockets so different CPUs can be used.
+ * We have to check chip version in run time.
+ */
+ switch (k) {
+ /* MPC866P/MPC866T/MPC859T/MPC859DSL/MPC852T */
+ case 0x08010004: /* Rev. A.0 */
+ suf = "A";
+ /* fall through */
+ case 0x08000003: /* Rev. 0.3 */
+ pre = 'M'; m = 1;
+ if (id_str == NULL)
+ id_str =
+ "PC866x"; /* Unknown chip from MPC866 family */
+ break;
+ case 0x09000000: pre = 'M'; mid = suf = ""; m = 1;
+ if (id_str == NULL)
+ id_str = "PC885"; /* 870/875/880/885 */
+ break;
+
+ default: suf = NULL; break;
+ }
+
+ if (id_str == NULL)
+ id_str = "PC86x"; /* Unknown 86x chip */
+ if (suf)
+ printf ("%c%s%sZPnn%s", pre, id_str, mid, suf);
+ else
+ printf ("unknown M%s (0x%08x)", id_str, k);
+
+ printf (" at %s MHz: ", strmhz (buf, clock));
+
+ print_size(checkicache(), " I-Cache ");
+ print_size(checkdcache(), " D-Cache");
+
+ /* do we have a FEC (860T/P or 852/859/866/885)? */
+
+ immap->im_cpm.cp_fec.fec_addr_low = 0x12345678;
+ if (immap->im_cpm.cp_fec.fec_addr_low == 0x12345678) {
+ printf (" FEC present");
+ }
+
+ if (!m) {
+ puts (cpu_warning);
+ }
+
+ putc ('\n');
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int checkcpu (void)
+{
+ ulong clock = gd->cpu_clk;
+ uint immr = get_immr (0); /* Return full IMMR contents */
+ uint pvr = get_pvr ();
+
+ puts ("CPU: ");
+
+ return check_CPU (clock, pvr, immr);
+}
+
+/* ------------------------------------------------------------------------- */
+/* L1 i-cache */
+
+int checkicache (void)
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+ volatile memctl8xx_t *memctl = &immap->im_memctl;
+ u32 cacheon = rd_ic_cst () & IDC_ENABLED;
+
+ u32 k = memctl->memc_br0 & ~0x00007fff; /* probe in flash memoryarea */
+ u32 m;
+ u32 lines = -1;
+
+ wr_ic_cst (IDC_UNALL);
+ wr_ic_cst (IDC_INVALL);
+ wr_ic_cst (IDC_DISABLE);
+ __asm__ volatile ("isync");
+
+ while (!((m = rd_ic_cst ()) & IDC_CERR2)) {
+ wr_ic_adr (k);
+ wr_ic_cst (IDC_LDLCK);
+ __asm__ volatile ("isync");
+
+ lines++;
+ k += 0x10; /* the number of bytes in a cacheline */
+ }
+
+ wr_ic_cst (IDC_UNALL);
+ wr_ic_cst (IDC_INVALL);
+
+ if (cacheon)
+ wr_ic_cst (IDC_ENABLE);
+ else
+ wr_ic_cst (IDC_DISABLE);
+
+ __asm__ volatile ("isync");
+
+ return lines << 4;
+};
+
+/* ------------------------------------------------------------------------- */
+/* L1 d-cache */
+/* call with cache disabled */
+
+int checkdcache (void)
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+ volatile memctl8xx_t *memctl = &immap->im_memctl;
+ u32 cacheon = rd_dc_cst () & IDC_ENABLED;
+
+ u32 k = memctl->memc_br0 & ~0x00007fff; /* probe in flash memoryarea */
+ u32 m;
+ u32 lines = -1;
+
+ wr_dc_cst (IDC_UNALL);
+ wr_dc_cst (IDC_INVALL);
+ wr_dc_cst (IDC_DISABLE);
+
+ while (!((m = rd_dc_cst ()) & IDC_CERR2)) {
+ wr_dc_adr (k);
+ wr_dc_cst (IDC_LDLCK);
+ lines++;
+ k += 0x10; /* the number of bytes in a cacheline */
+ }
+
+ wr_dc_cst (IDC_UNALL);
+ wr_dc_cst (IDC_INVALL);
+
+ if (cacheon)
+ wr_dc_cst (IDC_ENABLE);
+ else
+ wr_dc_cst (IDC_DISABLE);
+
+ return lines << 4;
+};
+
+/* ------------------------------------------------------------------------- */
+
+void upmconfig (uint upm, uint * table, uint size)
+{
+ uint i;
+ uint addr = 0;
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+ volatile memctl8xx_t *memctl = &immap->im_memctl;
+
+ for (i = 0; i < size; i++) {
+ memctl->memc_mdr = table[i]; /* (16-15) */
+ memctl->memc_mcr = addr | upm; /* (16-16) */
+ addr++;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ ulong msr, addr;
+
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ immap->im_clkrst.car_plprcr |= PLPRCR_CSR; /* Checkstop Reset enable */
+
+ /* Interrupts and MMU off */
+ __asm__ volatile ("mtspr 81, 0");
+ __asm__ volatile ("mfmsr %0":"=r" (msr));
+
+ msr &= ~0x1030;
+ __asm__ volatile ("mtmsr %0"::"r" (msr));
+
+ /*
+ * Trying to execute the next instruction at a non-existing address
+ * should cause a machine check, resulting in reset
+ */
+#ifdef CONFIG_SYS_RESET_ADDRESS
+ addr = CONFIG_SYS_RESET_ADDRESS;
+#else
+ /*
+ * note: when CONFIG_SYS_MONITOR_BASE points to a RAM address, CONFIG_SYS_MONITOR_BASE
+ * - sizeof (ulong) is usually a valid address. Better pick an address
+ * known to be invalid on your system and assign it to CONFIG_SYS_RESET_ADDRESS.
+ * "(ulong)-1" used to be a good choice for many systems...
+ */
+ addr = CONFIG_SYS_MONITOR_BASE - sizeof (ulong);
+#endif
+ ((void (*)(void)) addr) ();
+ return 1;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Get timebase clock frequency (like cpu_clk in Hz)
+ *
+ * See sections 14.2 and 14.6 of the User's Manual
+ */
+unsigned long get_tbclk (void)
+{
+ uint immr = get_immr (0); /* Return full IMMR contents */
+ volatile immap_t *immap = (volatile immap_t *)(immr & 0xFFFF0000);
+ ulong oscclk, factor, pll;
+
+ if (immap->im_clkrst.car_sccr & SCCR_TBS) {
+ return (gd->cpu_clk / 16);
+ }
+
+ pll = immap->im_clkrst.car_plprcr;
+
+#define PLPRCR_val(a) ((pll & PLPRCR_ ## a ## _MSK) >> PLPRCR_ ## a ## _SHIFT)
+
+ /*
+ * For newer PQ1 chips (MPC866/87x/88x families), PLL multiplication
+ * factor is calculated as follows:
+ *
+ * MFN
+ * MFI + -------
+ * MFD + 1
+ * factor = -----------------
+ * (PDF + 1) * 2^S
+ *
+ */
+ factor = (PLPRCR_val(MFI) + PLPRCR_val(MFN)/(PLPRCR_val(MFD)+1))/
+ (PLPRCR_val(PDF)+1) / (1<<PLPRCR_val(S));
+
+ oscclk = gd->cpu_clk / factor;
+
+ if ((immap->im_clkrst.car_sccr & SCCR_RTSEL) == 0 || factor > 2) {
+ return (oscclk / 4);
+ }
+ return (oscclk / 16);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#if defined(CONFIG_WATCHDOG)
+void watchdog_reset (void)
+{
+ int re_enable = disable_interrupts ();
+
+ reset_8xx_watchdog ((immap_t *) CONFIG_SYS_IMMR);
+ if (re_enable)
+ enable_interrupts ();
+}
+#endif /* CONFIG_WATCHDOG */
+
+#if defined(CONFIG_WATCHDOG)
+
+void reset_8xx_watchdog (volatile immap_t * immr)
+{
+ /*
+ * All other boards use the MPC8xx Internal Watchdog
+ */
+ immr->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */
+ immr->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */
+}
+#endif /* CONFIG_WATCHDOG */
+
+/*
+ * Initializes on-chip ethernet controllers.
+ * to override, implement board_eth_init()
+ */
+int cpu_eth_init(bd_t *bis)
+{
+#if defined(FEC_ENET)
+ fec_initialize(bis);
+#endif
+ return 0;
+}
diff --git a/arch/powerpc/cpu/mpc8xx/cpu_init.c b/arch/powerpc/cpu/mpc8xx/cpu_init.c
new file mode 100644
index 0000000..0f935af
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/cpu_init.c
@@ -0,0 +1,180 @@
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <watchdog.h>
+
+#include <mpc8xx.h>
+#include <commproc.h>
+
+/*
+ * Breath some life into the CPU...
+ *
+ * Set up the memory map,
+ * initialize a bunch of registers,
+ * initialize the UPM's
+ */
+void cpu_init_f (volatile immap_t * immr)
+{
+ volatile memctl8xx_t *memctl = &immr->im_memctl;
+# ifdef CONFIG_SYS_PLPRCR
+ ulong mfmask;
+# endif
+ ulong reg;
+
+ /* SYPCR - contains watchdog control (11-9) */
+
+ immr->im_siu_conf.sc_sypcr = CONFIG_SYS_SYPCR;
+
+#if defined(CONFIG_WATCHDOG)
+ reset_8xx_watchdog (immr);
+#endif /* CONFIG_WATCHDOG */
+
+ /* SIUMCR - contains debug pin configuration (11-6) */
+ immr->im_siu_conf.sc_siumcr |= CONFIG_SYS_SIUMCR;
+ /* initialize timebase status and control register (11-26) */
+ /* unlock TBSCRK */
+
+ immr->im_sitk.sitk_tbscrk = KAPWR_KEY;
+ immr->im_sit.sit_tbscr = CONFIG_SYS_TBSCR;
+
+ /* initialize the PIT (11-31) */
+
+ immr->im_sitk.sitk_piscrk = KAPWR_KEY;
+ immr->im_sit.sit_piscr = CONFIG_SYS_PISCR;
+
+ /* System integration timers. Don't change EBDF! (15-27) */
+
+ immr->im_clkrstk.cark_sccrk = KAPWR_KEY;
+ reg = immr->im_clkrst.car_sccr;
+ reg &= SCCR_MASK;
+ reg |= CONFIG_SYS_SCCR;
+ immr->im_clkrst.car_sccr = reg;
+
+ /* PLL (CPU clock) settings (15-30) */
+
+ immr->im_clkrstk.cark_plprcrk = KAPWR_KEY;
+
+ /* If CONFIG_SYS_PLPRCR (set in the various *_config.h files) tries to
+ * set the MF field, then just copy CONFIG_SYS_PLPRCR over car_plprcr,
+ * otherwise OR in CONFIG_SYS_PLPRCR so we do not change the current MF
+ * field value.
+ *
+ * For newer (starting MPC866) chips PLPRCR layout is different.
+ */
+#ifdef CONFIG_SYS_PLPRCR
+ mfmask = PLPRCR_MFACT_MSK;
+
+ if ((CONFIG_SYS_PLPRCR & mfmask) != 0)
+ reg = CONFIG_SYS_PLPRCR; /* reset control bits */
+ else {
+ reg = immr->im_clkrst.car_plprcr;
+ reg &= mfmask; /* isolate MF-related fields */
+ reg |= CONFIG_SYS_PLPRCR; /* reset control bits */
+ }
+ immr->im_clkrst.car_plprcr = reg;
+#endif
+
+ /*
+ * Memory Controller:
+ */
+
+ /* perform BR0 reset that MPC850 Rev. A can't guarantee */
+ reg = memctl->memc_br0;
+ reg &= BR_PS_MSK; /* Clear everything except Port Size bits */
+ reg |= BR_V; /* then add just the "Bank Valid" bit */
+ memctl->memc_br0 = reg;
+
+ /* Map banks 0 (and maybe 1) to the FLASH banks 0 (and 1) at
+ * preliminary addresses - these have to be modified later
+ * when FLASH size has been determined
+ *
+ * Depending on the size of the memory region defined by
+ * CONFIG_SYS_OR0_REMAP some boards (wide address mask) allow to map the
+ * CONFIG_SYS_MONITOR_BASE, while others (narrower address mask) can't
+ * map CONFIG_SYS_MONITOR_BASE.
+ *
+ * For example, for CONFIG_IVMS8, the CONFIG_SYS_MONITOR_BASE is
+ * 0xff000000, but CONFIG_SYS_OR0_REMAP's address mask is 0xfff80000.
+ *
+ * If BR0 wasn't loaded with address base 0xff000000, then BR0's
+ * base address remains as 0x00000000. However, the address mask
+ * have been narrowed to 512Kb, so CONFIG_SYS_MONITOR_BASE wasn't mapped
+ * into the Bank0.
+ *
+ * This is why CONFIG_IVMS8 and similar boards must load BR0 with
+ * CONFIG_SYS_BR0_PRELIM in advance.
+ *
+ * [Thanks to Michael Liao for this explanation.
+ * I owe him a free beer. - wd]
+ */
+
+#if defined(CONFIG_SYS_OR0_REMAP)
+ memctl->memc_or0 = CONFIG_SYS_OR0_REMAP;
+#endif
+#if defined(CONFIG_SYS_OR1_REMAP)
+ memctl->memc_or1 = CONFIG_SYS_OR1_REMAP;
+#endif
+#if defined(CONFIG_SYS_OR5_REMAP)
+ memctl->memc_or5 = CONFIG_SYS_OR5_REMAP;
+#endif
+
+ /* now restrict to preliminary range */
+ memctl->memc_br0 = CONFIG_SYS_BR0_PRELIM;
+ memctl->memc_or0 = CONFIG_SYS_OR0_PRELIM;
+
+#if (defined(CONFIG_SYS_OR1_PRELIM) && defined(CONFIG_SYS_BR1_PRELIM))
+ memctl->memc_or1 = CONFIG_SYS_OR1_PRELIM;
+ memctl->memc_br1 = CONFIG_SYS_BR1_PRELIM;
+#endif
+
+#if defined(CONFIG_SYS_OR2_PRELIM) && defined(CONFIG_SYS_BR2_PRELIM)
+ memctl->memc_or2 = CONFIG_SYS_OR2_PRELIM;
+ memctl->memc_br2 = CONFIG_SYS_BR2_PRELIM;
+#endif
+
+#if defined(CONFIG_SYS_OR3_PRELIM) && defined(CONFIG_SYS_BR3_PRELIM)
+ memctl->memc_or3 = CONFIG_SYS_OR3_PRELIM;
+ memctl->memc_br3 = CONFIG_SYS_BR3_PRELIM;
+#endif
+
+#if defined(CONFIG_SYS_OR4_PRELIM) && defined(CONFIG_SYS_BR4_PRELIM)
+ memctl->memc_or4 = CONFIG_SYS_OR4_PRELIM;
+ memctl->memc_br4 = CONFIG_SYS_BR4_PRELIM;
+#endif
+
+#if defined(CONFIG_SYS_OR5_PRELIM) && defined(CONFIG_SYS_BR5_PRELIM)
+ memctl->memc_or5 = CONFIG_SYS_OR5_PRELIM;
+ memctl->memc_br5 = CONFIG_SYS_BR5_PRELIM;
+#endif
+
+#if defined(CONFIG_SYS_OR6_PRELIM) && defined(CONFIG_SYS_BR6_PRELIM)
+ memctl->memc_or6 = CONFIG_SYS_OR6_PRELIM;
+ memctl->memc_br6 = CONFIG_SYS_BR6_PRELIM;
+#endif
+
+#if defined(CONFIG_SYS_OR7_PRELIM) && defined(CONFIG_SYS_BR7_PRELIM)
+ memctl->memc_or7 = CONFIG_SYS_OR7_PRELIM;
+ memctl->memc_br7 = CONFIG_SYS_BR7_PRELIM;
+#endif
+
+ /*
+ * Reset CPM
+ */
+ immr->im_cpm.cp_cpcr = CPM_CR_RST | CPM_CR_FLG;
+ do { /* Spin until command processed */
+ __asm__ ("eieio");
+ } while (immr->im_cpm.cp_cpcr & CPM_CR_FLG);
+}
+
+/*
+ * initialize higher level parts of CPU like timers
+ */
+int cpu_init_r (void)
+{
+ return (0);
+}
diff --git a/arch/powerpc/cpu/mpc8xx/fdt.c b/arch/powerpc/cpu/mpc8xx/fdt.c
new file mode 100644
index 0000000..34d3647
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/fdt.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008 (C) Bryan O'Donoghue
+ *
+ * Code copied & edited from Freescale mpc85xx stuff.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <libfdt.h>
+#include <fdt_support.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void ft_cpu_setup(void *blob, bd_t *bd)
+{
+ do_fixup_by_prop_u32(blob, "device_type", "cpu", 4,
+ "timebase-frequency", get_tbclk(), 1);
+ do_fixup_by_prop_u32(blob, "device_type", "cpu", 4,
+ "bus-frequency", bd->bi_busfreq, 1);
+ do_fixup_by_prop_u32(blob, "device_type", "cpu", 4,
+ "clock-frequency", bd->bi_intfreq, 1);
+ do_fixup_by_compat_u32(blob, "fsl,cpm-brg", "clock-frequency",
+ gd->arch.brg_clk, 1);
+
+ fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize);
+}
diff --git a/arch/powerpc/cpu/mpc8xx/fec.c b/arch/powerpc/cpu/mpc8xx/fec.c
new file mode 100644
index 0000000..7aa526d
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/fec.c
@@ -0,0 +1,847 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <command.h>
+#include <commproc.h>
+#include <malloc.h>
+#include <net.h>
+
+#include <phy.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if defined(CONFIG_CMD_NET) && \
+ (defined(FEC_ENET) || defined(CONFIG_ETHER_ON_FEC1) || defined(CONFIG_ETHER_ON_FEC2))
+
+/* compatibility test, if only FEC_ENET defined assume ETHER on FEC1 */
+#if defined(FEC_ENET) && !defined(CONFIG_ETHER_ON_FEC1) && !defined(CONFIG_ETHER_ON_FEC2)
+#define CONFIG_ETHER_ON_FEC1 1
+#endif
+
+/* define WANT_MII when MII support is required */
+#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_FEC1_PHY) || defined(CONFIG_FEC2_PHY)
+#define WANT_MII
+#else
+#undef WANT_MII
+#endif
+
+#if defined(WANT_MII)
+#include <miiphy.h>
+
+#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII))
+#error "CONFIG_MII has to be defined!"
+#endif
+
+#endif
+
+#if defined(CONFIG_RMII) && !defined(WANT_MII)
+#error RMII support is unusable without a working PHY.
+#endif
+
+#ifdef CONFIG_SYS_DISCOVER_PHY
+static int mii_discover_phy(struct eth_device *dev);
+#endif
+
+int fec8xx_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg);
+int fec8xx_miiphy_write(struct mii_dev *bus, int addr, int devad, int reg,
+ u16 value);
+
+static struct ether_fcc_info_s
+{
+ int ether_index;
+ int fecp_offset;
+ int phy_addr;
+ int actual_phy_addr;
+ int initialized;
+}
+ ether_fcc_info[] = {
+#if defined(CONFIG_ETHER_ON_FEC1)
+ {
+ 0,
+ offsetof(immap_t, im_cpm.cp_fec1),
+#if defined(CONFIG_FEC1_PHY)
+ CONFIG_FEC1_PHY,
+#else
+ -1, /* discover */
+#endif
+ -1,
+ 0,
+
+ },
+#endif
+#if defined(CONFIG_ETHER_ON_FEC2)
+ {
+ 1,
+ offsetof(immap_t, im_cpm.cp_fec2),
+#if defined(CONFIG_FEC2_PHY)
+ CONFIG_FEC2_PHY,
+#else
+ -1,
+#endif
+ -1,
+ 0,
+ },
+#endif
+};
+
+/* Ethernet Transmit and Receive Buffers */
+#define DBUF_LENGTH 1520
+
+#define TX_BUF_CNT 2
+
+#define TOUT_LOOP 100
+
+#define PKT_MAXBUF_SIZE 1518
+#define PKT_MINBUF_SIZE 64
+#define PKT_MAXBLR_SIZE 1520
+
+#ifdef __GNUC__
+static char txbuf[DBUF_LENGTH] __attribute__ ((aligned(8)));
+#else
+#error txbuf must be aligned.
+#endif
+
+static uint rxIdx; /* index of the current RX buffer */
+static uint txIdx; /* index of the current TX buffer */
+
+/*
+ * FEC Ethernet Tx and Rx buffer descriptors allocated at the
+ * immr->udata_bd address on Dual-Port RAM
+ * Provide for Double Buffering
+ */
+
+typedef volatile struct CommonBufferDescriptor {
+ cbd_t rxbd[PKTBUFSRX]; /* Rx BD */
+ cbd_t txbd[TX_BUF_CNT]; /* Tx BD */
+} RTXBD;
+
+static RTXBD *rtx = NULL;
+
+static int fec_send(struct eth_device *dev, void *packet, int length);
+static int fec_recv(struct eth_device* dev);
+static int fec_init(struct eth_device* dev, bd_t * bd);
+static void fec_halt(struct eth_device* dev);
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+static void __mii_init(void);
+#endif
+
+int fec_initialize(bd_t *bis)
+{
+ struct eth_device* dev;
+ struct ether_fcc_info_s *efis;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ether_fcc_info); i++) {
+
+ dev = malloc(sizeof(*dev));
+ if (dev == NULL)
+ hang();
+
+ memset(dev, 0, sizeof(*dev));
+
+ /* for FEC1 make sure that the name of the interface is the same
+ as the old one for compatibility reasons */
+ if (i == 0) {
+ strcpy(dev->name, "FEC");
+ } else {
+ sprintf (dev->name, "FEC%d",
+ ether_fcc_info[i].ether_index + 1);
+ }
+
+ efis = ðer_fcc_info[i];
+
+ /*
+ * reset actual phy addr
+ */
+ efis->actual_phy_addr = -1;
+
+ dev->priv = efis;
+ dev->init = fec_init;
+ dev->halt = fec_halt;
+ dev->send = fec_send;
+ dev->recv = fec_recv;
+
+ eth_register(dev);
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+ int retval;
+ struct mii_dev *mdiodev = mdio_alloc();
+ if (!mdiodev)
+ return -ENOMEM;
+ strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN);
+ mdiodev->read = fec8xx_miiphy_read;
+ mdiodev->write = fec8xx_miiphy_write;
+
+ retval = mdio_register(mdiodev);
+ if (retval < 0)
+ return retval;
+#endif
+ }
+ return 1;
+}
+
+static int fec_send(struct eth_device *dev, void *packet, int length)
+{
+ int j, rc;
+ struct ether_fcc_info_s *efis = dev->priv;
+ volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset);
+
+ /* section 16.9.23.3
+ * Wait for ready
+ */
+ j = 0;
+ while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) {
+ udelay(1);
+ j++;
+ }
+ if (j>=TOUT_LOOP) {
+ printf("TX not ready\n");
+ }
+
+ 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;
+ __asm__ ("eieio");
+
+ /* Activate transmit Buffer Descriptor polling */
+ fecp->fec_x_des_active = 0x01000000; /* Descriptor polling active */
+
+ j = 0;
+ while ((rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY) && (j<TOUT_LOOP)) {
+ udelay(1);
+ j++;
+ }
+ if (j>=TOUT_LOOP) {
+ printf("TX timeout\n");
+ }
+ /* return only status bits */;
+ rc = (rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS);
+
+ txIdx = (txIdx + 1) % TX_BUF_CNT;
+
+ return rc;
+}
+
+static int fec_recv (struct eth_device *dev)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ volatile fec_t *fecp =
+ (volatile fec_t *) (CONFIG_SYS_IMMR + efis->fecp_offset);
+ int length;
+
+ for (;;) {
+ /* section 16.9.23.2 */
+ 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) {
+ } else {
+ uchar *rx = net_rx_packets[rxIdx];
+
+ length -= 4;
+
+#if defined(CONFIG_CMD_CDP)
+ if ((rx[0] & 1) != 0 &&
+ memcmp((uchar *)rx, net_bcast_ethaddr, 6) != 0 &&
+ !is_cdp_packet((uchar *)rx))
+ rx = NULL;
+#endif
+ /*
+ * Pass the packet up to the protocol layers.
+ */
+ if (rx != NULL)
+ net_process_received_packet(rx, length);
+ }
+
+ /* Give the buffer back to the FEC. */
+ 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++;
+ }
+
+ __asm__ ("eieio");
+
+ /* Try to fill Buffer Descriptors */
+ fecp->fec_r_des_active = 0x01000000; /* Descriptor polling active */
+ }
+
+ return length;
+}
+
+/**************************************************************
+ *
+ * FEC Ethernet Initialization Routine
+ *
+ *************************************************************/
+
+#define FEC_ECNTRL_PINMUX 0x00000004
+#define FEC_ECNTRL_ETHER_EN 0x00000002
+#define FEC_ECNTRL_RESET 0x00000001
+
+#define FEC_RCNTRL_BC_REJ 0x00000010
+#define FEC_RCNTRL_PROM 0x00000008
+#define FEC_RCNTRL_MII_MODE 0x00000004
+#define FEC_RCNTRL_DRT 0x00000002
+#define FEC_RCNTRL_LOOP 0x00000001
+
+#define FEC_TCNTRL_FDEN 0x00000004
+#define FEC_TCNTRL_HBC 0x00000002
+#define FEC_TCNTRL_GTS 0x00000001
+
+#define FEC_RESET_DELAY 50
+
+#if defined(CONFIG_RMII)
+
+static inline void fec_10Mbps(struct eth_device *dev)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ int fecidx = efis->ether_index;
+ uint mask = (fecidx == 0) ? 0x0000010 : 0x0000008;
+
+ if ((unsigned int)fecidx >= 2)
+ hang();
+
+ ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_cptr |= mask;
+}
+
+static inline void fec_100Mbps(struct eth_device *dev)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ int fecidx = efis->ether_index;
+ uint mask = (fecidx == 0) ? 0x0000010 : 0x0000008;
+
+ if ((unsigned int)fecidx >= 2)
+ hang();
+
+ ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_cptr &= ~mask;
+}
+
+#endif
+
+static inline void fec_full_duplex(struct eth_device *dev)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset);
+
+ fecp->fec_r_cntrl &= ~FEC_RCNTRL_DRT;
+ fecp->fec_x_cntrl |= FEC_TCNTRL_FDEN; /* FD enable */
+}
+
+static inline void fec_half_duplex(struct eth_device *dev)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset);
+
+ fecp->fec_r_cntrl |= FEC_RCNTRL_DRT;
+ fecp->fec_x_cntrl &= ~FEC_TCNTRL_FDEN; /* FD disable */
+}
+
+static void fec_pin_init(int fecidx)
+{
+ bd_t *bd = gd->bd;
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+
+ /*
+ * Set MII speed to 2.5 MHz or slightly below.
+ *
+ * According to the MPC860T (Rev. D) Fast ethernet controller user
+ * manual (6.2.14),
+ * the MII management interface clock must be less than or equal
+ * to 2.5 MHz.
+ * This MDC frequency is equal to system clock / (2 * MII_SPEED).
+ * Then MII_SPEED = system_clock / 2 * 2,5 MHz.
+ *
+ * All MII configuration is done via FEC1 registers:
+ */
+ immr->im_cpm.cp_fec1.fec_mii_speed = ((bd->bi_intfreq + 4999999) / 5000000) << 1;
+
+#if defined(CONFIG_MPC885_FAMILY) && defined(WANT_MII)
+ /* use MDC for MII */
+ immr->im_ioport.iop_pdpar |= 0x0080;
+ immr->im_ioport.iop_pddir &= ~0x0080;
+#endif
+
+ if (fecidx == 0) {
+#if defined(CONFIG_ETHER_ON_FEC1)
+
+#if defined(CONFIG_MPC885_FAMILY) /* MPC87x/88x have got 2 FECs and different pinout */
+
+#if !defined(CONFIG_RMII)
+
+ immr->im_ioport.iop_papar |= 0xf830;
+ immr->im_ioport.iop_padir |= 0x0830;
+ immr->im_ioport.iop_padir &= ~0xf000;
+
+ immr->im_cpm.cp_pbpar |= 0x00001001;
+ immr->im_cpm.cp_pbdir &= ~0x00001001;
+
+ immr->im_ioport.iop_pcpar |= 0x000c;
+ immr->im_ioport.iop_pcdir &= ~0x000c;
+
+ immr->im_cpm.cp_pepar |= 0x00000003;
+ immr->im_cpm.cp_pedir |= 0x00000003;
+ immr->im_cpm.cp_peso &= ~0x00000003;
+
+ immr->im_cpm.cp_cptr &= ~0x00000100;
+
+#else
+
+#if !defined(CONFIG_FEC1_PHY_NORXERR)
+ immr->im_ioport.iop_papar |= 0x1000;
+ immr->im_ioport.iop_padir &= ~0x1000;
+#endif
+ immr->im_ioport.iop_papar |= 0xe810;
+ immr->im_ioport.iop_padir |= 0x0810;
+ immr->im_ioport.iop_padir &= ~0xe000;
+
+ immr->im_cpm.cp_pbpar |= 0x00000001;
+ immr->im_cpm.cp_pbdir &= ~0x00000001;
+
+ immr->im_cpm.cp_cptr |= 0x00000100;
+ immr->im_cpm.cp_cptr &= ~0x00000050;
+
+#endif /* !CONFIG_RMII */
+
+#else
+ /*
+ * Configure all of port D for MII.
+ */
+ immr->im_ioport.iop_pdpar = 0x1fff;
+
+ immr->im_ioport.iop_pddir = 0x1fff; /* Rev. D and later */
+#endif
+
+#endif /* CONFIG_ETHER_ON_FEC1 */
+ } else if (fecidx == 1) {
+
+#if defined(CONFIG_ETHER_ON_FEC2)
+
+#if defined(CONFIG_MPC885_FAMILY) /* MPC87x/88x have got 2 FECs and different pinout */
+
+#if !defined(CONFIG_RMII)
+ immr->im_cpm.cp_pepar |= 0x0003fffc;
+ immr->im_cpm.cp_pedir |= 0x0003fffc;
+ immr->im_cpm.cp_peso &= ~0x000087fc;
+ immr->im_cpm.cp_peso |= 0x00037800;
+
+ immr->im_cpm.cp_cptr &= ~0x00000080;
+#else
+
+#if !defined(CONFIG_FEC2_PHY_NORXERR)
+ immr->im_cpm.cp_pepar |= 0x00000010;
+ immr->im_cpm.cp_pedir |= 0x00000010;
+ immr->im_cpm.cp_peso &= ~0x00000010;
+#endif
+ immr->im_cpm.cp_pepar |= 0x00039620;
+ immr->im_cpm.cp_pedir |= 0x00039620;
+ immr->im_cpm.cp_peso |= 0x00031000;
+ immr->im_cpm.cp_peso &= ~0x00008620;
+
+ immr->im_cpm.cp_cptr |= 0x00000080;
+ immr->im_cpm.cp_cptr &= ~0x00000028;
+#endif /* CONFIG_RMII */
+
+#endif /* CONFIG_MPC885_FAMILY */
+
+#endif /* CONFIG_ETHER_ON_FEC2 */
+
+ }
+}
+
+static int fec_reset(volatile fec_t *fecp)
+{
+ int i;
+
+ /* Whack a reset.
+ * A delay is required between a reset of the FEC block and
+ * initialization of other FEC registers because the reset takes
+ * some time to complete. If you don't delay, subsequent writes
+ * to FEC registers might get killed by the reset routine which is
+ * still in progress.
+ */
+
+ fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET;
+ for (i = 0;
+ (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY);
+ ++i) {
+ udelay (1);
+ }
+ if (i == FEC_RESET_DELAY)
+ return -1;
+
+ return 0;
+}
+
+static int fec_init (struct eth_device *dev, bd_t * bd)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+ volatile fec_t *fecp =
+ (volatile fec_t *) (CONFIG_SYS_IMMR + efis->fecp_offset);
+ int i;
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+ /* the MII interface is connected to FEC1
+ * so for the miiphy_xxx function to work we must
+ * call mii_init since fec_halt messes the thing up
+ */
+ if (efis->ether_index != 0)
+ __mii_init();
+#endif
+
+ if (fec_reset(fecp) < 0)
+ printf ("FEC_RESET_DELAY timeout\n");
+
+ /* We use strictly polling mode only
+ */
+ fecp->fec_imask = 0;
+
+ /* Clear any pending interrupt
+ */
+ fecp->fec_ievent = 0xffc0;
+
+ /* No need to set the IVEC register */
+
+ /* Set station address
+ */
+#define ea dev->enetaddr
+ fecp->fec_addr_low = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]);
+ fecp->fec_addr_high = (ea[4] << 8) | (ea[5]);
+#undef ea
+
+#if defined(CONFIG_CMD_CDP)
+ /*
+ * Turn on multicast address hash table
+ */
+ fecp->fec_hash_table_high = 0xffffffff;
+ fecp->fec_hash_table_low = 0xffffffff;
+#else
+ /* Clear multicast address hash table
+ */
+ fecp->fec_hash_table_high = 0;
+ fecp->fec_hash_table_low = 0;
+#endif
+
+ /* Set maximum receive buffer size.
+ */
+ fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
+
+ /* Set maximum frame length
+ */
+ fecp->fec_r_hash = PKT_MAXBUF_SIZE;
+
+ /*
+ * Setup Buffers and Buffer Desriptors
+ */
+ rxIdx = 0;
+ txIdx = 0;
+
+ if (!rtx)
+ rtx = (RTXBD *)(immr->im_cpm.cp_dpmem + CPM_FEC_BASE);
+ /*
+ * Setup Receiver Buffer Descriptors (13.14.24.18)
+ * Settings:
+ * Empty, Wrap
+ */
+ for (i = 0; i < PKTBUFSRX; i++) {
+ rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY;
+ rtx->rxbd[i].cbd_datlen = 0; /* Reset */
+ rtx->rxbd[i].cbd_bufaddr = (uint) net_rx_packets[i];
+ }
+ rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP;
+
+ /*
+ * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19)
+ * Settings:
+ * Last, Tx CRC
+ */
+ for (i = 0; i < TX_BUF_CNT; i++) {
+ rtx->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC;
+ rtx->txbd[i].cbd_datlen = 0; /* Reset */
+ rtx->txbd[i].cbd_bufaddr = (uint) (&txbuf[0]);
+ }
+ rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP;
+
+ /* Set receive and transmit descriptor base
+ */
+ fecp->fec_r_des_start = (unsigned int) (&rtx->rxbd[0]);
+ fecp->fec_x_des_start = (unsigned int) (&rtx->txbd[0]);
+
+ /* Enable MII mode
+ */
+ /* Half duplex mode */
+ fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT;
+ fecp->fec_x_cntrl = 0;
+
+ /* Enable big endian and don't care about SDMA FC.
+ */
+ fecp->fec_fun_code = 0x78000000;
+
+ /*
+ * Setup the pin configuration of the FEC
+ */
+ fec_pin_init (efis->ether_index);
+
+ rxIdx = 0;
+ txIdx = 0;
+
+ /*
+ * Now enable the transmit and receive processing
+ */
+ fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN;
+
+ if (efis->phy_addr == -1) {
+#ifdef CONFIG_SYS_DISCOVER_PHY
+ /*
+ * wait for the PHY to wake up after reset
+ */
+ efis->actual_phy_addr = mii_discover_phy (dev);
+
+ if (efis->actual_phy_addr == -1) {
+ printf ("Unable to discover phy!\n");
+ return -1;
+ }
+#else
+ efis->actual_phy_addr = -1;
+#endif
+ } else {
+ efis->actual_phy_addr = efis->phy_addr;
+ }
+
+#if defined(CONFIG_MII) && defined(CONFIG_RMII)
+ /*
+ * adapt the RMII speed to the speed of the phy
+ */
+ if (miiphy_speed (dev->name, efis->actual_phy_addr) == _100BASET) {
+ fec_100Mbps (dev);
+ } else {
+ fec_10Mbps (dev);
+ }
+#endif
+
+#if defined(CONFIG_MII)
+ /*
+ * adapt to the half/full speed settings
+ */
+ if (miiphy_duplex (dev->name, efis->actual_phy_addr) == FULL) {
+ fec_full_duplex (dev);
+ } else {
+ fec_half_duplex (dev);
+ }
+#endif
+
+ /* And last, try to fill Rx Buffer Descriptors */
+ fecp->fec_r_des_active = 0x01000000; /* Descriptor polling active */
+
+ efis->initialized = 1;
+
+ return 0;
+}
+
+
+static void fec_halt(struct eth_device* dev)
+{
+ struct ether_fcc_info_s *efis = dev->priv;
+ volatile fec_t *fecp = (volatile fec_t *)(CONFIG_SYS_IMMR + efis->fecp_offset);
+ int i;
+
+ /* avoid halt if initialized; mii gets stuck otherwise */
+ if (!efis->initialized)
+ return;
+
+ /* Whack a reset.
+ * A delay is required between a reset of the FEC block and
+ * initialization of other FEC registers because the reset takes
+ * some time to complete. If you don't delay, subsequent writes
+ * to FEC registers might get killed by the reset routine which is
+ * still in progress.
+ */
+
+ fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET;
+ for (i = 0;
+ (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY);
+ ++i) {
+ udelay (1);
+ }
+ if (i == FEC_RESET_DELAY) {
+ printf ("FEC_RESET_DELAY timeout\n");
+ return;
+ }
+
+ efis->initialized = 0;
+}
+
+#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+
+/* Make MII read/write commands for the FEC.
+*/
+
+#define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \
+ (REG & 0x1f) << 18))
+
+#define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \
+ (REG & 0x1f) << 18) | \
+ (VAL & 0xffff))
+
+/* Interrupt events/masks.
+*/
+#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
+#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
+#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
+#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
+#define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */
+#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
+#define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */
+#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
+#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
+#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
+
+/* send command to phy using mii, wait for result */
+static uint
+mii_send(uint mii_cmd)
+{
+ uint mii_reply;
+ volatile fec_t *ep;
+ int cnt;
+
+ ep = &(((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_fec);
+
+ ep->fec_mii_data = mii_cmd; /* command to phy */
+
+ /* wait for mii complete */
+ cnt = 0;
+ while (!(ep->fec_ievent & FEC_ENET_MII)) {
+ if (++cnt > 1000) {
+ printf("mii_send STUCK!\n");
+ break;
+ }
+ }
+ mii_reply = ep->fec_mii_data; /* result from phy */
+ ep->fec_ievent = FEC_ENET_MII; /* clear MII complete */
+ return (mii_reply & 0xffff); /* data read from phy */
+}
+#endif
+
+#if defined(CONFIG_SYS_DISCOVER_PHY)
+static int mii_discover_phy(struct eth_device *dev)
+{
+#define MAX_PHY_PASSES 11
+ uint phyno;
+ int pass;
+ uint phytype;
+ int phyaddr;
+
+ phyaddr = -1; /* didn't find a PHY yet */
+ for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) {
+ if (pass > 1) {
+ /* PHY may need more time to recover from reset.
+ * The LXT970 needs 50ms typical, no maximum is
+ * specified, so wait 10ms before try again.
+ * With 11 passes this gives it 100ms to wake up.
+ */
+ udelay(10000); /* wait 10ms */
+ }
+ for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) {
+ phytype = mii_send(mk_mii_read(phyno, MII_PHYSID2));
+ if (phytype != 0xffff) {
+ phyaddr = phyno;
+ phytype |= mii_send(mk_mii_read(phyno,
+ MII_PHYSID1)) << 16;
+ }
+ }
+ }
+ if (phyaddr < 0) {
+ printf("No PHY device found.\n");
+ }
+ return phyaddr;
+}
+#endif /* CONFIG_SYS_DISCOVER_PHY */
+
+#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) && !defined(CONFIG_BITBANGMII)
+
+/****************************************************************************
+ * mii_init -- Initialize the MII via FEC 1 for MII command without ethernet
+ * This function is a subset of eth_init
+ ****************************************************************************
+ */
+static void __mii_init(void)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+ volatile fec_t *fecp = &(immr->im_cpm.cp_fec);
+
+ if (fec_reset(fecp) < 0)
+ printf ("FEC_RESET_DELAY timeout\n");
+
+ /* We use strictly polling mode only
+ */
+ fecp->fec_imask = 0;
+
+ /* Clear any pending interrupt
+ */
+ fecp->fec_ievent = 0xffc0;
+
+ /* Now enable the transmit and receive processing
+ */
+ fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN;
+}
+
+void mii_init (void)
+{
+ int i;
+
+ __mii_init();
+
+ /* Setup the pin configuration of the FEC(s)
+ */
+ for (i = 0; i < ARRAY_SIZE(ether_fcc_info); i++)
+ fec_pin_init(ether_fcc_info[i].ether_index);
+}
+
+/*****************************************************************************
+ * Read and write a MII PHY register, routines used by MII Utilities
+ *
+ * FIXME: These routines are expected to return 0 on success, but mii_send
+ * does _not_ return an error code. Maybe 0xFFFF means error, i.e.
+ * no PHY connected...
+ * For now always return 0.
+ * FIXME: These routines only work after calling eth_init() at least once!
+ * Otherwise they hang in mii_send() !!! Sorry!
+ *****************************************************************************/
+
+int fec8xx_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ unsigned short value = 0;
+ short rdreg; /* register working value */
+
+ rdreg = mii_send(mk_mii_read(addr, reg));
+
+ value = rdreg;
+ return value;
+}
+
+int fec8xx_miiphy_write(struct mii_dev *bus, int addr, int devad, int reg,
+ u16 value)
+{
+ (void)mii_send(mk_mii_write(addr, reg, value));
+
+ return 0;
+}
+#endif
+
+#endif
diff --git a/arch/powerpc/cpu/mpc8xx/interrupts.c b/arch/powerpc/cpu/mpc8xx/interrupts.c
new file mode 100644
index 0000000..f090ad9
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/interrupts.c
@@ -0,0 +1,259 @@
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <mpc8xx.h>
+#include <mpc8xx_irq.h>
+#include <asm/processor.h>
+#include <commproc.h>
+
+/************************************************************************/
+
+/*
+ * CPM interrupt vector functions.
+ */
+struct interrupt_action {
+ interrupt_handler_t *handler;
+ void *arg;
+};
+
+static struct interrupt_action cpm_vecs[CPMVEC_NR];
+static struct interrupt_action irq_vecs[NR_IRQS];
+
+static void cpm_interrupt_init (void);
+static void cpm_interrupt (void *regs);
+
+/************************************************************************/
+
+int interrupt_init_cpu (unsigned *decrementer_count)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+
+ *decrementer_count = get_tbclk () / CONFIG_SYS_HZ;
+
+ /* disable all interrupts */
+ immr->im_siu_conf.sc_simask = 0;
+
+ /* Configure CPM interrupts */
+ cpm_interrupt_init ();
+
+ return (0);
+}
+
+/************************************************************************/
+
+/*
+ * Handle external interrupts
+ */
+void external_interrupt (struct pt_regs *regs)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+ int irq;
+ ulong simask, newmask;
+ ulong vec, v_bit;
+
+ /*
+ * read the SIVEC register and shift the bits down
+ * to get the irq number
+ */
+ vec = immr->im_siu_conf.sc_sivec;
+ irq = vec >> 26;
+ v_bit = 0x80000000UL >> irq;
+
+ /*
+ * Read Interrupt Mask Register and Mask Interrupts
+ */
+ simask = immr->im_siu_conf.sc_simask;
+ newmask = simask & (~(0xFFFF0000 >> irq));
+ immr->im_siu_conf.sc_simask = newmask;
+
+ if (!(irq & 0x1)) { /* External Interrupt ? */
+ ulong siel;
+
+ /*
+ * Read Interrupt Edge/Level Register
+ */
+ siel = immr->im_siu_conf.sc_siel;
+
+ if (siel & v_bit) { /* edge triggered interrupt ? */
+ /*
+ * Rewrite SIPEND Register to clear interrupt
+ */
+ immr->im_siu_conf.sc_sipend = v_bit;
+ }
+ }
+
+ if (irq_vecs[irq].handler != NULL) {
+ irq_vecs[irq].handler (irq_vecs[irq].arg);
+ } else {
+ printf ("\nBogus External Interrupt IRQ %d Vector %ld\n",
+ irq, vec);
+ /* turn off the bogus interrupt to avoid it from now */
+ simask &= ~v_bit;
+ }
+ /*
+ * Re-Enable old Interrupt Mask
+ */
+ immr->im_siu_conf.sc_simask = simask;
+}
+
+/************************************************************************/
+
+/*
+ * CPM interrupt handler
+ */
+static void cpm_interrupt (void *regs)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+ uint vec;
+
+ /*
+ * Get the vector by setting the ACK bit
+ * and then reading the register.
+ */
+ immr->im_cpic.cpic_civr = 1;
+ vec = immr->im_cpic.cpic_civr;
+ vec >>= 11;
+
+ if (cpm_vecs[vec].handler != NULL) {
+ (*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
+ } else {
+ immr->im_cpic.cpic_cimr &= ~(1 << vec);
+ printf ("Masking bogus CPM interrupt vector 0x%x\n", vec);
+ }
+ /*
+ * After servicing the interrupt,
+ * we have to remove the status indicator.
+ */
+ immr->im_cpic.cpic_cisr |= (1 << vec);
+}
+
+/*
+ * The CPM can generate the error interrupt when there is a race
+ * condition between generating and masking interrupts. All we have
+ * to do is ACK it and return. This is a no-op function so we don't
+ * need any special tests in the interrupt handler.
+ */
+static void cpm_error_interrupt (void *dummy)
+{
+}
+
+/************************************************************************/
+/*
+ * Install and free an interrupt handler
+ */
+void irq_install_handler (int vec, interrupt_handler_t * handler,
+ void *arg)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+
+ if ((vec & CPMVEC_OFFSET) != 0) {
+ /* CPM interrupt */
+ vec &= 0xffff;
+ if (cpm_vecs[vec].handler != NULL) {
+ printf ("CPM interrupt 0x%x replacing 0x%x\n",
+ (uint) handler,
+ (uint) cpm_vecs[vec].handler);
+ }
+ cpm_vecs[vec].handler = handler;
+ cpm_vecs[vec].arg = arg;
+ immr->im_cpic.cpic_cimr |= (1 << vec);
+ } else {
+ /* SIU interrupt */
+ if (irq_vecs[vec].handler != NULL) {
+ printf ("SIU interrupt %d 0x%x replacing 0x%x\n",
+ vec,
+ (uint) handler,
+ (uint) cpm_vecs[vec].handler);
+ }
+ irq_vecs[vec].handler = handler;
+ irq_vecs[vec].arg = arg;
+ immr->im_siu_conf.sc_simask |= 1 << (31 - vec);
+ }
+}
+
+void irq_free_handler (int vec)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+
+ if ((vec & CPMVEC_OFFSET) != 0) {
+ /* CPM interrupt */
+ vec &= 0xffff;
+ immr->im_cpic.cpic_cimr &= ~(1 << vec);
+ cpm_vecs[vec].handler = NULL;
+ cpm_vecs[vec].arg = NULL;
+ } else {
+ /* SIU interrupt */
+ immr->im_siu_conf.sc_simask &= ~(1 << (31 - vec));
+ irq_vecs[vec].handler = NULL;
+ irq_vecs[vec].arg = NULL;
+ }
+}
+
+/************************************************************************/
+
+static void cpm_interrupt_init (void)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+
+ /*
+ * Initialize the CPM interrupt controller.
+ */
+
+ immr->im_cpic.cpic_cicr =
+ (CICR_SCD_SCC4 |
+ CICR_SCC_SCC3 |
+ CICR_SCB_SCC2 |
+ CICR_SCA_SCC1) | ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
+
+ immr->im_cpic.cpic_cimr = 0;
+
+ /*
+ * Install the error handler.
+ */
+ irq_install_handler (CPMVEC_ERROR, cpm_error_interrupt, NULL);
+
+ immr->im_cpic.cpic_cicr |= CICR_IEN;
+
+ /*
+ * Install the cpm interrupt handler
+ */
+ irq_install_handler (CPM_INTERRUPT, cpm_interrupt, NULL);
+}
+
+/************************************************************************/
+
+/*
+ * timer_interrupt - gets called when the decrementer overflows,
+ * with interrupts disabled.
+ * Trivial implementation - no need to be really accurate.
+ */
+void timer_interrupt_cpu (struct pt_regs *regs)
+{
+ volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
+
+ /* Reset Timer Expired and Timers Interrupt Status */
+ immr->im_clkrstk.cark_plprcrk = KAPWR_KEY;
+ __asm__ ("nop");
+ /*
+ Clear TEXPS (and TMIST on older chips). SPLSS (on older
+ chips) is cleared too.
+
+ Bitwise OR is a read-modify-write operation so ALL bits
+ which are cleared by writing `1' would be cleared by
+ operations like
+
+ immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
+
+ The same can be achieved by simple writing of the PLPRCR
+ to itself. If a bit value should be preserved, read the
+ register, ZERO the bit and write, not OR, the result back.
+ */
+ immr->im_clkrst.car_plprcr = immr->im_clkrst.car_plprcr;
+}
+
+/************************************************************************/
diff --git a/arch/powerpc/cpu/mpc8xx/serial.c b/arch/powerpc/cpu/mpc8xx/serial.c
new file mode 100644
index 0000000..d4f1a41
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/serial.c
@@ -0,0 +1,301 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <commproc.h>
+#include <command.h>
+#include <serial.h>
+#include <watchdog.h>
+#include <linux/compiler.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if !defined(CONFIG_8xx_CONS_NONE) /* No Console at all */
+
+#if defined(CONFIG_8xx_CONS_SMC1) /* Console on SMC1 */
+#define SMC_INDEX 0
+#define PROFF_SMC PROFF_SMC1
+#define CPM_CR_CH_SMC CPM_CR_CH_SMC1
+
+#elif defined(CONFIG_8xx_CONS_SMC2) /* Console on SMC2 */
+#define SMC_INDEX 1
+#define PROFF_SMC PROFF_SMC2
+#define CPM_CR_CH_SMC CPM_CR_CH_SMC2
+
+#endif /* CONFIG_8xx_CONS_SMCx */
+
+#if !defined(CONFIG_SYS_SMC_RXBUFLEN)
+#define CONFIG_SYS_SMC_RXBUFLEN 1
+#define CONFIG_SYS_MAXIDLE 0
+#else
+#if !defined(CONFIG_SYS_MAXIDLE)
+#error "you must define CONFIG_SYS_MAXIDLE"
+#endif
+#endif
+
+typedef volatile struct serialbuffer {
+ cbd_t rxbd; /* Rx BD */
+ cbd_t txbd; /* Tx BD */
+ uint rxindex; /* index for next character to read */
+ volatile uchar rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */
+ volatile uchar txbuf; /* tx buffers */
+} serialbuffer_t;
+
+static void serial_setdivisor(volatile cpm8xx_t *cp)
+{
+ int divisor=(gd->cpu_clk + 8*gd->baudrate)/16/gd->baudrate;
+
+ if(divisor/16>0x1000) {
+ /* bad divisor, assume 50MHz clock and 9600 baud */
+ divisor=(50*1000*1000 + 8*9600)/16/9600;
+ }
+
+#ifdef CONFIG_SYS_BRGCLK_PRESCALE
+ divisor /= CONFIG_SYS_BRGCLK_PRESCALE;
+#endif
+
+ if(divisor<=0x1000) {
+ cp->cp_brgc1=((divisor-1)<<1) | CPM_BRG_EN;
+ } else {
+ cp->cp_brgc1=((divisor/16-1)<<1) | CPM_BRG_EN | CPM_BRG_DIV16;
+ }
+}
+
+/*
+ * Minimal serial functions needed to use one of the SMC ports
+ * as serial console interface.
+ */
+
+static void smc_setbrg (void)
+{
+ volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ volatile cpm8xx_t *cp = &(im->im_cpm);
+
+ /* Set up the baud rate generator.
+ * See 8xx_io/commproc.c for details.
+ *
+ * Wire BRG1 to SMCx
+ */
+
+ cp->cp_simode = 0x00000000;
+
+ serial_setdivisor(cp);
+}
+
+static int smc_init (void)
+{
+ volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ volatile smc_t *sp;
+ volatile smc_uart_t *up;
+ volatile cpm8xx_t *cp = &(im->im_cpm);
+ uint dpaddr;
+ volatile serialbuffer_t *rtx;
+
+ /* initialize pointers to SMC */
+
+ sp = (smc_t *) &(cp->cp_smc[SMC_INDEX]);
+ up = (smc_uart_t *) &cp->cp_dparam[PROFF_SMC];
+ /* Disable relocation */
+ up->smc_rpbase = 0;
+
+ /* Disable transmitter/receiver. */
+ sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+
+ /* Enable SDMA. */
+ im->im_siu_conf.sc_sdcr = 1;
+
+ /* clear error conditions */
+#ifdef CONFIG_SYS_SDSR
+ im->im_sdma.sdma_sdsr = CONFIG_SYS_SDSR;
+#else
+ im->im_sdma.sdma_sdsr = 0x83;
+#endif
+
+ /* clear SDMA interrupt mask */
+#ifdef CONFIG_SYS_SDMR
+ im->im_sdma.sdma_sdmr = CONFIG_SYS_SDMR;
+#else
+ im->im_sdma.sdma_sdmr = 0x00;
+#endif
+
+#if defined(CONFIG_8xx_CONS_SMC1)
+ /* Use Port B for SMC1 instead of other functions. */
+ cp->cp_pbpar |= 0x000000c0;
+ cp->cp_pbdir &= ~0x000000c0;
+ cp->cp_pbodr &= ~0x000000c0;
+#else /* CONFIG_8xx_CONS_SMC2 */
+ /* Use Port B for SMC2 instead of other functions.
+ */
+ cp->cp_pbpar |= 0x00000c00;
+ cp->cp_pbdir &= ~0x00000c00;
+ cp->cp_pbodr &= ~0x00000c00;
+#endif
+
+ /* Set the physical address of the host memory buffers in
+ * the buffer descriptors.
+ */
+ dpaddr = CPM_SERIAL_BASE;
+
+ rtx = (serialbuffer_t *)&cp->cp_dpmem[dpaddr];
+ /* Allocate space for two buffer descriptors in the DP ram.
+ * For now, this address seems OK, but it may have to
+ * change with newer versions of the firmware.
+ * damm: allocating space after the two buffers for rx/tx data
+ */
+
+ rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf;
+ rtx->rxbd.cbd_sc = 0;
+
+ rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf;
+ rtx->txbd.cbd_sc = 0;
+
+ /* Set up the uart parameters in the parameter ram. */
+ up->smc_rbase = dpaddr;
+ up->smc_tbase = dpaddr+sizeof(cbd_t);
+ up->smc_rfcr = SMC_EB;
+ up->smc_tfcr = SMC_EB;
+
+ /* Set UART mode, 8 bit, no parity, one stop.
+ * Enable receive and transmit.
+ */
+ sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART;
+
+ /* Mask all interrupts and remove anything pending.
+ */
+ sp->smc_smcm = 0;
+ sp->smc_smce = 0xff;
+
+ /* Set up the baud rate generator */
+ smc_setbrg ();
+
+ /* Make the first buffer the only buffer. */
+ rtx->txbd.cbd_sc |= BD_SC_WRAP;
+ rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
+
+ /* single/multi character receive. */
+ up->smc_mrblr = CONFIG_SYS_SMC_RXBUFLEN;
+ up->smc_maxidl = CONFIG_SYS_MAXIDLE;
+ rtx->rxindex = 0;
+
+ /* Initialize Tx/Rx parameters. */
+ while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */
+ ;
+
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+ while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */
+ ;
+
+ /* Enable transmitter/receiver. */
+ sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
+
+ return (0);
+}
+
+static void
+smc_putc(const char c)
+{
+ volatile smc_uart_t *up;
+ volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ volatile cpm8xx_t *cpmp = &(im->im_cpm);
+ volatile serialbuffer_t *rtx;
+
+ if (c == '\n')
+ smc_putc ('\r');
+
+ up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
+
+ rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+ /* Wait for last character to go. */
+ rtx->txbuf = c;
+ rtx->txbd.cbd_datlen = 1;
+ rtx->txbd.cbd_sc |= BD_SC_READY;
+ __asm__("eieio");
+
+ while (rtx->txbd.cbd_sc & BD_SC_READY) {
+ WATCHDOG_RESET ();
+ __asm__("eieio");
+ }
+}
+
+static void
+smc_puts (const char *s)
+{
+ while (*s) {
+ smc_putc (*s++);
+ }
+}
+
+static int
+smc_getc(void)
+{
+ volatile smc_uart_t *up;
+ volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ volatile cpm8xx_t *cpmp = &(im->im_cpm);
+ volatile serialbuffer_t *rtx;
+ unsigned char c;
+
+ up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
+ rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+ /* Wait for character to show up. */
+ while (rtx->rxbd.cbd_sc & BD_SC_EMPTY)
+ WATCHDOG_RESET ();
+
+ /* the characters are read one by one,
+ * use the rxindex to know the next char to deliver
+ */
+ c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr+rtx->rxindex);
+ rtx->rxindex++;
+
+ /* check if all char are readout, then make prepare for next receive */
+ if (rtx->rxindex >= rtx->rxbd.cbd_datlen) {
+ rtx->rxindex = 0;
+ rtx->rxbd.cbd_sc |= BD_SC_EMPTY;
+ }
+ return(c);
+}
+
+static int
+smc_tstc(void)
+{
+ volatile smc_uart_t *up;
+ volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ volatile cpm8xx_t *cpmp = &(im->im_cpm);
+ volatile serialbuffer_t *rtx;
+
+ up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
+
+ rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+ return !(rtx->rxbd.cbd_sc & BD_SC_EMPTY);
+}
+
+struct serial_device serial_smc_device =
+{
+ .name = "serial_smc",
+ .start = smc_init,
+ .stop = NULL,
+ .setbrg = smc_setbrg,
+ .getc = smc_getc,
+ .tstc = smc_tstc,
+ .putc = smc_putc,
+ .puts = smc_puts,
+};
+
+__weak struct serial_device *default_serial_console(void)
+{
+ return &serial_smc_device;
+}
+
+void mpc8xx_serial_initialize(void)
+{
+ serial_register(&serial_smc_device);
+}
+
+#endif /* CONFIG_8xx_CONS_NONE */
diff --git a/arch/powerpc/cpu/mpc8xx/speed.c b/arch/powerpc/cpu/mpc8xx/speed.c
new file mode 100644
index 0000000..751c089
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/speed.c
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 2000-2004
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <mpc8xx.h>
+#include <asm/processor.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void get_brgclk(uint sccr)
+{
+ uint divider = 0;
+
+ switch((sccr&SCCR_DFBRG11)>>11){
+ case 0:
+ divider = 1;
+ break;
+ case 1:
+ divider = 4;
+ break;
+ case 2:
+ divider = 16;
+ break;
+ case 3:
+ divider = 64;
+ break;
+ }
+ gd->arch.brg_clk = gd->cpu_clk/divider;
+}
+
+/*
+ * get_clocks() fills in gd->cpu_clock depending on CONFIG_8xx_GCLK_FREQ
+ */
+int get_clocks (void)
+{
+ uint immr = get_immr (0); /* Return full IMMR contents */
+ volatile immap_t *immap = (immap_t *) (immr & 0xFFFF0000);
+ uint sccr = immap->im_clkrst.car_sccr;
+ /*
+ * If for some reason measuring the gclk frequency won't
+ * work, we return the hardwired value.
+ * (For example, the cogent CMA286-60 CPU module has no
+ * separate oscillator for PITRTCLK)
+ */
+ gd->cpu_clk = CONFIG_8xx_GCLK_FREQ;
+
+ if ((sccr & SCCR_EBDF11) == 0) {
+ /* No Bus Divider active */
+ gd->bus_clk = gd->cpu_clk;
+ } else {
+ /* The MPC8xx has only one BDF: half clock speed */
+ gd->bus_clk = gd->cpu_clk / 2;
+ }
+
+ get_brgclk(sccr);
+
+ return (0);
+}
diff --git a/arch/powerpc/cpu/mpc8xx/spi.c b/arch/powerpc/cpu/mpc8xx/spi.c
new file mode 100644
index 0000000..c7863ec
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/spi.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2001 Navin Boppuri / Prashant Patel
+ * <nboppuri@trinetcommunication.com>,
+ * <pmpatel@trinetcommunication.com>
+ * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de>
+ * Copyright (c) 2001 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * MPC8xx CPM SPI interface.
+ *
+ * Parts of this code are probably not portable and/or specific to
+ * the board which I used for the tests. Please send fixes/complaints
+ * to wd@denx.de
+ *
+ */
+
+#include <common.h>
+#include <mpc8xx.h>
+#include <commproc.h>
+#include <linux/ctype.h>
+#include <malloc.h>
+#include <post.h>
+#include <serial.h>
+
+#ifdef CONFIG_SPI
+
+#define SPI_EEPROM_WREN 0x06
+#define SPI_EEPROM_RDSR 0x05
+#define SPI_EEPROM_READ 0x03
+#define SPI_EEPROM_WRITE 0x02
+
+/* ---------------------------------------------------------------
+ * Offset for initial SPI buffers in DPRAM:
+ * We need a 520 byte scratch DPRAM area to use at an early stage.
+ * It is used between the two initialization calls (spi_init_f()
+ * and spi_init_r()).
+ * The value 0xb00 makes it far enough from the start of the data
+ * area (as well as from the stack pointer).
+ * --------------------------------------------------------------- */
+#ifndef CONFIG_SYS_SPI_INIT_OFFSET
+#define CONFIG_SYS_SPI_INIT_OFFSET 0xB00
+#endif
+
+/* -------------------
+ * Function prototypes
+ * ------------------- */
+void spi_init (void);
+
+ssize_t spi_read (uchar *, int, uchar *, int);
+ssize_t spi_write (uchar *, int, uchar *, int);
+ssize_t spi_xfer (size_t);
+
+/* -------------------
+ * Variables
+ * ------------------- */
+
+#define MAX_BUFFER 0x104
+
+/* ----------------------------------------------------------------------
+ * Initially we place the RX and TX buffers at a fixed location in DPRAM!
+ * ---------------------------------------------------------------------- */
+static uchar *rxbuf =
+ (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem
+ [CONFIG_SYS_SPI_INIT_OFFSET];
+static uchar *txbuf =
+ (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem
+ [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER];
+
+/* **************************************************************************
+ *
+ * Function: spi_init_f
+ *
+ * Description: Init SPI-Controller (ROM part)
+ *
+ * return: ---
+ *
+ * *********************************************************************** */
+void spi_init_f (void)
+{
+ unsigned int dpaddr;
+
+ volatile spi_t *spi;
+ volatile immap_t *immr;
+ volatile cpm8xx_t *cp;
+ volatile cbd_t *tbdf, *rbdf;
+
+ immr = (immap_t *) CONFIG_SYS_IMMR;
+ cp = (cpm8xx_t *) &immr->im_cpm;
+
+ spi = (spi_t *)&cp->cp_dparam[PROFF_SPI];
+ /* Disable relocation */
+ spi->spi_rpbase = 0;
+
+/* 1 */
+ /* ------------------------------------------------
+ * Initialize Port B SPI pins -> page 34-8 MPC860UM
+ * (we are only in Master Mode !)
+ * ------------------------------------------------ */
+
+ /* --------------------------------------------
+ * GPIO or per. Function
+ * PBPAR[28] = 1 [0x00000008] -> PERI: (SPIMISO)
+ * PBPAR[29] = 1 [0x00000004] -> PERI: (SPIMOSI)
+ * PBPAR[30] = 1 [0x00000002] -> PERI: (SPICLK)
+ * PBPAR[31] = 0 [0x00000001] -> GPIO: (CS for PCUE/CCM-EEPROM)
+ * -------------------------------------------- */
+ cp->cp_pbpar |= 0x0000000E; /* set bits */
+ cp->cp_pbpar &= ~0x00000001; /* reset bit */
+
+ /* ----------------------------------------------
+ * In/Out or per. Function 0/1
+ * PBDIR[28] = 1 [0x00000008] -> PERI1: SPIMISO
+ * PBDIR[29] = 1 [0x00000004] -> PERI1: SPIMOSI
+ * PBDIR[30] = 1 [0x00000002] -> PERI1: SPICLK
+ * PBDIR[31] = 1 [0x00000001] -> GPIO OUT: CS for PCUE/CCM-EEPROM
+ * ---------------------------------------------- */
+ cp->cp_pbdir |= 0x0000000F;
+
+ /* ----------------------------------------------
+ * open drain or active output
+ * PBODR[28] = 1 [0x00000008] -> open drain: SPIMISO
+ * PBODR[29] = 0 [0x00000004] -> active output SPIMOSI
+ * PBODR[30] = 0 [0x00000002] -> active output: SPICLK
+ * PBODR[31] = 0 [0x00000001] -> active output: GPIO OUT: CS for PCUE/CCM
+ * ---------------------------------------------- */
+
+ cp->cp_pbodr |= 0x00000008;
+ cp->cp_pbodr &= ~0x00000007;
+
+ /* Initialize the parameter ram.
+ * We need to make sure many things are initialized to zero
+ */
+ spi->spi_rstate = 0;
+ spi->spi_rdp = 0;
+ spi->spi_rbptr = 0;
+ spi->spi_rbc = 0;
+ spi->spi_rxtmp = 0;
+ spi->spi_tstate = 0;
+ spi->spi_tdp = 0;
+ spi->spi_tbptr = 0;
+ spi->spi_tbc = 0;
+ spi->spi_txtmp = 0;
+
+ dpaddr = CPM_SPI_BASE;
+
+/* 3 */
+ /* Set up the SPI parameters in the parameter ram */
+ spi->spi_rbase = dpaddr;
+ spi->spi_tbase = dpaddr + sizeof (cbd_t);
+
+ /***********IMPORTANT******************/
+
+ /*
+ * Setting transmit and receive buffer descriptor pointers
+ * initially to rbase and tbase. Only the microcode patches
+ * documentation talks about initializing this pointer. This
+ * is missing from the sample I2C driver. If you dont
+ * initialize these pointers, the kernel hangs.
+ */
+ spi->spi_rbptr = spi->spi_rbase;
+ spi->spi_tbptr = spi->spi_tbase;
+
+/* 4 */
+ /* Init SPI Tx + Rx Parameters */
+ while (cp->cp_cpcr & CPM_CR_FLG)
+ ;
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG)
+ ;
+
+/* 5 */
+ /* Set SDMA configuration register */
+ immr->im_siu_conf.sc_sdcr = 0x0001;
+
+/* 6 */
+ /* Set to big endian. */
+ spi->spi_tfcr = SMC_EB;
+ spi->spi_rfcr = SMC_EB;
+
+/* 7 */
+ /* Set maximum receive size. */
+ spi->spi_mrblr = MAX_BUFFER;
+
+/* 8 + 9 */
+ /* tx and rx buffer descriptors */
+ tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase];
+ rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase];
+
+ tbdf->cbd_sc &= ~BD_SC_READY;
+ rbdf->cbd_sc &= ~BD_SC_EMPTY;
+
+ /* Set the bd's rx and tx buffer address pointers */
+ rbdf->cbd_bufaddr = (ulong) rxbuf;
+ tbdf->cbd_bufaddr = (ulong) txbuf;
+
+/* 10 + 11 */
+ cp->cp_spim = 0; /* Mask all SPI events */
+ cp->cp_spie = SPI_EMASK; /* Clear all SPI events */
+
+ return;
+}
+
+/* **************************************************************************
+ *
+ * Function: spi_init_r
+ *
+ * Description: Init SPI-Controller (RAM part) -
+ * The malloc engine is ready and we can move our buffers to
+ * normal RAM
+ *
+ * return: ---
+ *
+ * *********************************************************************** */
+void spi_init_r (void)
+{
+ volatile cpm8xx_t *cp;
+ volatile spi_t *spi;
+ volatile immap_t *immr;
+ volatile cbd_t *tbdf, *rbdf;
+
+ immr = (immap_t *) CONFIG_SYS_IMMR;
+ cp = (cpm8xx_t *) &immr->im_cpm;
+
+ spi = (spi_t *)&cp->cp_dparam[PROFF_SPI];
+ /* Disable relocation */
+ spi->spi_rpbase = 0;
+
+ /* tx and rx buffer descriptors */
+ tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase];
+ rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase];
+
+ /* Allocate memory for RX and TX buffers */
+ rxbuf = (uchar *) malloc (MAX_BUFFER);
+ txbuf = (uchar *) malloc (MAX_BUFFER);
+
+ rbdf->cbd_bufaddr = (ulong) rxbuf;
+ tbdf->cbd_bufaddr = (ulong) txbuf;
+
+ return;
+}
+
+/****************************************************************************
+ * Function: spi_write
+ **************************************************************************** */
+ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len)
+{
+ int i;
+
+ memset(rxbuf, 0, MAX_BUFFER);
+ memset(txbuf, 0, MAX_BUFFER);
+ *txbuf = SPI_EEPROM_WREN; /* write enable */
+ spi_xfer(1);
+ memcpy(txbuf, addr, alen);
+ *txbuf = SPI_EEPROM_WRITE; /* WRITE memory array */
+ memcpy(alen + txbuf, buffer, len);
+ spi_xfer(alen + len);
+ /* ignore received data */
+ for (i = 0; i < 1000; i++) {
+ *txbuf = SPI_EEPROM_RDSR; /* read status */
+ txbuf[1] = 0;
+ spi_xfer(2);
+ if (!(rxbuf[1] & 1)) {
+ break;
+ }
+ udelay(1000);
+ }
+ if (i >= 1000) {
+ printf ("*** spi_write: Time out while writing!\n");
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ * Function: spi_read
+ **************************************************************************** */
+ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len)
+{
+ memset(rxbuf, 0, MAX_BUFFER);
+ memset(txbuf, 0, MAX_BUFFER);
+ memcpy(txbuf, addr, alen);
+ *txbuf = SPI_EEPROM_READ; /* READ memory array */
+
+ /*
+ * There is a bug in 860T (?) that cuts the last byte of input
+ * if we're reading into DPRAM. The solution we choose here is
+ * to always read len+1 bytes (we have one extra byte at the
+ * end of the buffer).
+ */
+ spi_xfer(alen + len + 1);
+ memcpy(buffer, alen + rxbuf, len);
+
+ return len;
+}
+
+/****************************************************************************
+ * Function: spi_xfer
+ **************************************************************************** */
+ssize_t spi_xfer (size_t count)
+{
+ volatile immap_t *immr;
+ volatile cpm8xx_t *cp;
+ volatile spi_t *spi;
+ cbd_t *tbdf, *rbdf;
+ ushort loop;
+ int tm;
+
+ immr = (immap_t *) CONFIG_SYS_IMMR;
+ cp = (cpm8xx_t *) &immr->im_cpm;
+
+ spi = (spi_t *)&cp->cp_dparam[PROFF_SPI];
+ /* Disable relocation */
+ spi->spi_rpbase = 0;
+
+ tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase];
+ rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase];
+
+ /* Set CS for device */
+ cp->cp_pbdat &= ~0x0001;
+
+ /* Setting tx bd status and data length */
+ tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP;
+ tbdf->cbd_datlen = count;
+
+ /* Setting rx bd status and data length */
+ rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
+ rbdf->cbd_datlen = 0; /* rx length has no significance */
+
+ loop = cp->cp_spmode & SPMODE_LOOP;
+ cp->cp_spmode = /*SPMODE_DIV16 |*/ /* BRG/16 mode not used here */
+ loop |
+ SPMODE_REV |
+ SPMODE_MSTR |
+ SPMODE_EN |
+ SPMODE_LEN(8) | /* 8 Bits per char */
+ SPMODE_PM(0x8) ; /* medium speed */
+ cp->cp_spim = 0; /* Mask all SPI events */
+ cp->cp_spie = SPI_EMASK; /* Clear all SPI events */
+
+ /* start spi transfer */
+ cp->cp_spcom |= SPI_STR; /* Start transmit */
+
+ /* --------------------------------
+ * Wait for SPI transmit to get out
+ * or time out (1 second = 1000 ms)
+ * -------------------------------- */
+ for (tm=0; tm<1000; ++tm) {
+ if (cp->cp_spie & SPI_TXB) { /* Tx Buffer Empty */
+ break;
+ }
+ if ((tbdf->cbd_sc & BD_SC_READY) == 0) {
+ break;
+ }
+ udelay (1000);
+ }
+ if (tm >= 1000) {
+ printf ("*** spi_xfer: Time out while xferring to/from SPI!\n");
+ }
+
+ /* Clear CS for device */
+ cp->cp_pbdat |= 0x0001;
+
+ return count;
+}
+#endif /* CONFIG_SPI */
diff --git a/arch/powerpc/cpu/mpc8xx/start.S b/arch/powerpc/cpu/mpc8xx/start.S
new file mode 100644
index 0000000..b00696f
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/start.S
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 1998 Dan Malek <dmalek@jlc.net>
+ * Copyright (C) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se>
+ * Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@denx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* U-Boot - Startup Code for PowerPC based Embedded Boards
+ *
+ *
+ * The processor starts at 0x00000100 and the code is executed
+ * from flash. The code is organized to be at an other address
+ * in memory, but as long we don't jump around before relocating,
+ * board_init lies at a quite high address and when the cpu has
+ * jumped there, everything is ok.
+ * This works because the cpu gives the FLASH (CS0) the whole
+ * address space at startup, and board_init lies as a echo of
+ * the flash somewhere up there in the memory map.
+ *
+ * board_init will change CS0 to be positioned at the correct
+ * address and (s)dram will be positioned at address 0
+ */
+#include <asm-offsets.h>
+#include <config.h>
+#include <mpc8xx.h>
+#include <version.h>
+
+#include <ppc_asm.tmpl>
+#include <ppc_defs.h>
+
+#include <asm/cache.h>
+#include <asm/mmu.h>
+#include <asm/u-boot.h>
+
+/* We don't want the MMU yet.
+*/
+#undef MSR_KERNEL
+#define MSR_KERNEL ( MSR_ME | MSR_RI ) /* Machine Check and Recoverable Interr. */
+
+/*
+ * Set up GOT: Global Offset Table
+ *
+ * Use r12 to access the GOT
+ */
+ START_GOT
+ GOT_ENTRY(_GOT2_TABLE_)
+ GOT_ENTRY(_FIXUP_TABLE_)
+
+ GOT_ENTRY(_start)
+ GOT_ENTRY(_start_of_vectors)
+ GOT_ENTRY(_end_of_vectors)
+ GOT_ENTRY(transfer_to_handler)
+
+ GOT_ENTRY(__init_end)
+ GOT_ENTRY(__bss_end)
+ GOT_ENTRY(__bss_start)
+ END_GOT
+
+/*
+ * r3 - 1st arg to board_init(): IMMP pointer
+ * r4 - 2nd arg to board_init(): boot flag
+ */
+ .text
+ .long 0x27051956 /* U-Boot Magic Number */
+ .globl version_string
+version_string:
+ .ascii U_BOOT_VERSION_STRING, "\0"
+
+ . = EXC_OFF_SYS_RESET
+ .globl _start
+_start:
+ lis r3, CONFIG_SYS_IMMR@h /* position IMMR */
+ mtspr 638, r3
+
+ /* Initialize machine status; enable machine check interrupt */
+ /*----------------------------------------------------------------------*/
+ li r3, MSR_KERNEL /* Set ME, RI flags */
+ mtmsr r3
+ mtspr SRR1, r3 /* Make SRR1 match MSR */
+
+ mfspr r3, ICR /* clear Interrupt Cause Register */
+
+ /* Initialize debug port registers */
+ /*----------------------------------------------------------------------*/
+ xor r0, r0, r0 /* Clear R0 */
+ mtspr LCTRL1, r0 /* Initialize debug port regs */
+ mtspr LCTRL2, r0
+ mtspr COUNTA, r0
+ mtspr COUNTB, r0
+
+ /* Reset the caches */
+ /*----------------------------------------------------------------------*/
+
+ mfspr r3, IC_CST /* Clear error bits */
+ mfspr r3, DC_CST
+
+ lis r3, IDC_UNALL@h /* Unlock all */
+ mtspr IC_CST, r3
+ mtspr DC_CST, r3
+
+ lis r3, IDC_INVALL@h /* Invalidate all */
+ mtspr IC_CST, r3
+ mtspr DC_CST, r3
+
+ lis r3, IDC_DISABLE@h /* Disable data cache */
+ mtspr DC_CST, r3
+
+ lis r3, IDC_ENABLE@h /* Enable instruction cache */
+ mtspr IC_CST, r3
+
+ /* invalidate all tlb's */
+ /*----------------------------------------------------------------------*/
+
+ tlbia
+ isync
+
+ /*
+ * Calculate absolute address in FLASH and jump there
+ *----------------------------------------------------------------------*/
+
+ lis r3, CONFIG_SYS_MONITOR_BASE@h
+ ori r3, r3, CONFIG_SYS_MONITOR_BASE@l
+ addi r3, r3, in_flash - _start + EXC_OFF_SYS_RESET
+ mtlr r3
+ blr
+
+in_flash:
+
+ /* initialize some SPRs that are hard to access from C */
+ /*----------------------------------------------------------------------*/
+
+ lis r3, CONFIG_SYS_IMMR@h /* pass IMMR as arg1 to C routine */
+ ori r1, r3, CONFIG_SYS_INIT_SP_OFFSET /* set up the stack in internal DPRAM */
+ /* Note: R0 is still 0 here */
+ stwu r0, -4(r1) /* clear final stack frame so that */
+ stwu r0, -4(r1) /* stack backtraces terminate cleanly */
+
+ /*
+ * Disable serialized ifetch and show cycles
+ * (i.e. set processor to normal mode).
+ * This is also a silicon bug workaround, see errata
+ */
+
+ li r2, 0x0007
+ mtspr ICTRL, r2
+
+ /* Set up debug mode entry */
+
+ lis r2, CONFIG_SYS_DER@h
+ ori r2, r2, CONFIG_SYS_DER@l
+ mtspr DER, r2
+
+ /* let the C-code set up the rest */
+ /* */
+ /* Be careful to keep code relocatable ! */
+ /*----------------------------------------------------------------------*/
+
+ GET_GOT /* initialize GOT access */
+
+ /* r3: IMMR */
+ bl cpu_init_f /* run low-level CPU init code (from Flash) */
+
+ bl board_init_f /* run 1st part of board init code (from Flash) */
+
+ /* NOTREACHED - board_init_f() does not return */
+
+
+ .globl _start_of_vectors
+_start_of_vectors:
+
+/* Machine check */
+ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
+
+/* Data Storage exception. "Never" generated on the 860. */
+ STD_EXCEPTION(0x300, DataStorage, UnknownException)
+
+/* Instruction Storage exception. "Never" generated on the 860. */
+ STD_EXCEPTION(0x400, InstStorage, UnknownException)
+
+/* External Interrupt exception. */
+ STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt)
+
+/* Alignment exception. */
+ . = 0x600
+Alignment:
+ EXCEPTION_PROLOG(SRR0, SRR1)
+ mfspr r4,DAR
+ stw r4,_DAR(r21)
+ mfspr r5,DSISR
+ stw r5,_DSISR(r21)
+ addi r3,r1,STACK_FRAME_OVERHEAD
+ EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE)
+
+/* Program check exception */
+ . = 0x700
+ProgramCheck:
+ EXCEPTION_PROLOG(SRR0, SRR1)
+ addi r3,r1,STACK_FRAME_OVERHEAD
+ EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException,
+ MSR_KERNEL, COPY_EE)
+
+ /* No FPU on MPC8xx. This exception is not supposed to happen.
+ */
+ STD_EXCEPTION(0x800, FPUnavailable, UnknownException)
+
+ /* I guess we could implement decrementer, and may have
+ * to someday for timekeeping.
+ */
+ STD_EXCEPTION(0x900, Decrementer, timer_interrupt)
+ STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
+ STD_EXCEPTION(0xb00, Trap_0b, UnknownException)
+ STD_EXCEPTION(0xc00, SystemCall, UnknownException)
+ STD_EXCEPTION(0xd00, SingleStep, UnknownException)
+
+ STD_EXCEPTION(0xe00, Trap_0e, UnknownException)
+ STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
+
+ /* On the MPC8xx, this is a software emulation interrupt. It occurs
+ * for all unimplemented and illegal instructions.
+ */
+ STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException)
+
+ STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException)
+ STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException)
+ STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException)
+ STD_EXCEPTION(0x1400, DataTLBError, UnknownException)
+
+ STD_EXCEPTION(0x1500, Reserved5, UnknownException)
+ STD_EXCEPTION(0x1600, Reserved6, UnknownException)
+ STD_EXCEPTION(0x1700, Reserved7, UnknownException)
+ STD_EXCEPTION(0x1800, Reserved8, UnknownException)
+ STD_EXCEPTION(0x1900, Reserved9, UnknownException)
+ STD_EXCEPTION(0x1a00, ReservedA, UnknownException)
+ STD_EXCEPTION(0x1b00, ReservedB, UnknownException)
+
+ STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException)
+ STD_EXCEPTION(0x1d00, InstructionBreakpoint, DebugException)
+ STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException)
+ STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException)
+
+
+ .globl _end_of_vectors
+_end_of_vectors:
+
+
+ . = 0x2000
+
+/*
+ * This code finishes saving the registers to the exception frame
+ * and jumps to the appropriate handler for the exception.
+ * Register r21 is pointer into trap frame, r1 has new stack pointer.
+ */
+ .globl transfer_to_handler
+transfer_to_handler:
+ stw r22,_NIP(r21)
+ lis r22,MSR_POW@h
+ andc r23,r23,r22
+ stw r23,_MSR(r21)
+ SAVE_GPR(7, r21)
+ SAVE_4GPRS(8, r21)
+ SAVE_8GPRS(12, r21)
+ SAVE_8GPRS(24, r21)
+ mflr r23
+ andi. r24,r23,0x3f00 /* get vector offset */
+ stw r24,TRAP(r21)
+ li r22,0
+ stw r22,RESULT(r21)
+ mtspr SPRG2,r22 /* r1 is now kernel sp */
+ lwz r24,0(r23) /* virtual address of handler */
+ lwz r23,4(r23) /* where to go when done */
+ mtspr SRR0,r24
+ mtspr SRR1,r20
+ mtlr r23
+ SYNC
+ rfi /* jump to handler, enable MMU */
+
+int_return:
+ mfmsr r28 /* Disable interrupts */
+ li r4,0
+ ori r4,r4,MSR_EE
+ andc r28,r28,r4
+ SYNC /* Some chip revs need this... */
+ mtmsr r28
+ SYNC
+ lwz r2,_CTR(r1)
+ lwz r0,_LINK(r1)
+ mtctr r2
+ mtlr r0
+ lwz r2,_XER(r1)
+ lwz r0,_CCR(r1)
+ mtspr XER,r2
+ mtcrf 0xFF,r0
+ REST_10GPRS(3, r1)
+ REST_10GPRS(13, r1)
+ REST_8GPRS(23, r1)
+ REST_GPR(31, r1)
+ lwz r2,_NIP(r1) /* Restore environment */
+ lwz r0,_MSR(r1)
+ mtspr SRR0,r2
+ mtspr SRR1,r0
+ lwz r0,GPR0(r1)
+ lwz r2,GPR2(r1)
+ lwz r1,GPR1(r1)
+ SYNC
+ rfi
+
+/* Cache functions.
+*/
+ .globl icache_enable
+icache_enable:
+ SYNC
+ lis r3, IDC_INVALL@h
+ mtspr IC_CST, r3
+ lis r3, IDC_ENABLE@h
+ mtspr IC_CST, r3
+ blr
+
+ .globl icache_disable
+icache_disable:
+ SYNC
+ lis r3, IDC_DISABLE@h
+ mtspr IC_CST, r3
+ blr
+
+ .globl icache_status
+icache_status:
+ mfspr r3, IC_CST
+ srwi r3, r3, 31 /* >>31 => select bit 0 */
+ blr
+
+ .globl dcache_enable
+dcache_enable:
+ lis r3, 0x0400 /* Set cache mode with MMU off */
+ mtspr MD_CTR, r3
+
+ lis r3, IDC_INVALL@h
+ mtspr DC_CST, r3
+ lis r3, IDC_ENABLE@h
+ mtspr DC_CST, r3
+ blr
+
+ .globl dcache_disable
+dcache_disable:
+ SYNC
+ lis r3, IDC_DISABLE@h
+ mtspr DC_CST, r3
+ lis r3, IDC_INVALL@h
+ mtspr DC_CST, r3
+ blr
+
+ .globl dcache_status
+dcache_status:
+ mfspr r3, DC_CST
+ srwi r3, r3, 31 /* >>31 => select bit 0 */
+ blr
+
+ .globl dc_read
+dc_read:
+ mtspr DC_ADR, r3
+ mfspr r3, DC_DAT
+ blr
+
+/*
+ * unsigned int get_immr (unsigned int mask)
+ *
+ * return (mask ? (IMMR & mask) : IMMR);
+ */
+ .globl get_immr
+get_immr:
+ mr r4,r3 /* save mask */
+ mfspr r3, IMMR /* IMMR */
+ cmpwi 0,r4,0 /* mask != 0 ? */
+ beq 4f
+ and r3,r3,r4 /* IMMR & mask */
+4:
+ blr
+
+ .globl get_pvr
+get_pvr:
+ mfspr r3, PVR
+ blr
+
+
+ .globl wr_ic_cst
+wr_ic_cst:
+ mtspr IC_CST, r3
+ blr
+
+ .globl rd_ic_cst
+rd_ic_cst:
+ mfspr r3, IC_CST
+ blr
+
+ .globl wr_ic_adr
+wr_ic_adr:
+ mtspr IC_ADR, r3
+ blr
+
+
+ .globl wr_dc_cst
+wr_dc_cst:
+ mtspr DC_CST, r3
+ blr
+
+ .globl rd_dc_cst
+rd_dc_cst:
+ mfspr r3, DC_CST
+ blr
+
+ .globl wr_dc_adr
+wr_dc_adr:
+ mtspr DC_ADR, r3
+ blr
+
+/*------------------------------------------------------------------------------*/
+
+/*
+ * void relocate_code (addr_sp, gd, addr_moni)
+ *
+ * This "function" does not return, instead it continues in RAM
+ * after relocating the monitor code.
+ *
+ * r3 = dest
+ * r4 = src
+ * r5 = length in bytes
+ * r6 = cachelinesize
+ */
+ .globl relocate_code
+relocate_code:
+ mr r1, r3 /* Set new stack pointer */
+ mr r9, r4 /* Save copy of Global Data pointer */
+ mr r10, r5 /* Save copy of Destination Address */
+
+ GET_GOT
+ mr r3, r5 /* Destination Address */
+ lis r4, CONFIG_SYS_MONITOR_BASE@h /* Source Address */
+ ori r4, r4, CONFIG_SYS_MONITOR_BASE@l
+ lwz r5, GOT(__init_end)
+ sub r5, r5, r4
+ li r6, CONFIG_SYS_CACHELINE_SIZE /* Cache Line Size */
+
+ /*
+ * Fix GOT pointer:
+ *
+ * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address
+ *
+ * Offset:
+ */
+ sub r15, r10, r4
+
+ /* First our own GOT */
+ add r12, r12, r15
+ /* then the one used by the C code */
+ add r30, r30, r15
+
+ /*
+ * Now relocate code
+ */
+
+ cmplw cr1,r3,r4
+ addi r0,r5,3
+ srwi. r0,r0,2
+ beq cr1,4f /* In place copy is not necessary */
+ beq 7f /* Protect against 0 count */
+ mtctr r0
+ bge cr1,2f
+
+ la r8,-4(r4)
+ la r7,-4(r3)
+1: lwzu r0,4(r8)
+ stwu r0,4(r7)
+ bdnz 1b
+ b 4f
+
+2: slwi r0,r0,2
+ add r8,r4,r0
+ add r7,r3,r0
+3: lwzu r0,-4(r8)
+ stwu r0,-4(r7)
+ bdnz 3b
+
+/*
+ * Now flush the cache: note that we must start from a cache aligned
+ * address. Otherwise we might miss one cache line.
+ */
+4: cmpwi r6,0
+ add r5,r3,r5
+ beq 7f /* Always flush prefetch queue in any case */
+ subi r0,r6,1
+ andc r3,r3,r0
+ mr r4,r3
+5: dcbst 0,r4
+ add r4,r4,r6
+ cmplw r4,r5
+ blt 5b
+ sync /* Wait for all dcbst to complete on bus */
+ mr r4,r3
+6: icbi 0,r4
+ add r4,r4,r6
+ cmplw r4,r5
+ blt 6b
+7: sync /* Wait for all icbi to complete on bus */
+ isync
+
+/*
+ * We are done. Do not return, instead branch to second part of board
+ * initialization, now running from RAM.
+ */
+
+ addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET
+ mtlr r0
+ blr
+
+in_ram:
+
+ /*
+ * Relocation Function, r12 point to got2+0x8000
+ *
+ * Adjust got2 pointers, no need to check for 0, this code
+ * already puts a few entries in the table.
+ */
+ li r0,__got2_entries@sectoff@l
+ la r3,GOT(_GOT2_TABLE_)
+ lwz r11,GOT(_GOT2_TABLE_)
+ mtctr r0
+ sub r11,r3,r11
+ addi r3,r3,-4
+1: lwzu r0,4(r3)
+ cmpwi r0,0
+ beq- 2f
+ add r0,r0,r11
+ stw r0,0(r3)
+2: bdnz 1b
+
+ /*
+ * Now adjust the fixups and the pointers to the fixups
+ * in case we need to move ourselves again.
+ */
+ li r0,__fixup_entries@sectoff@l
+ lwz r3,GOT(_FIXUP_TABLE_)
+ cmpwi r0,0
+ mtctr r0
+ addi r3,r3,-4
+ beq 4f
+3: lwzu r4,4(r3)
+ lwzux r0,r4,r11
+ cmpwi r0,0
+ add r0,r0,r11
+ stw r4,0(r3)
+ beq- 5f
+ stw r0,0(r4)
+5: bdnz 3b
+4:
+clear_bss:
+ /*
+ * Now clear BSS segment
+ */
+ lwz r3,GOT(__bss_start)
+ lwz r4,GOT(__bss_end)
+
+ cmplw 0, r3, r4
+ beq 6f
+
+ li r0, 0
+5:
+ stw r0, 0(r3)
+ addi r3, r3, 4
+ cmplw 0, r3, r4
+ bne 5b
+6:
+
+ mr r3, r9 /* Global Data pointer */
+ mr r4, r10 /* Destination Address */
+ bl board_init_r
+
+ /*
+ * Copy exception vector code to low memory
+ *
+ * r3: dest_addr
+ * r7: source address, r8: end address, r9: target address
+ */
+ .globl trap_init
+trap_init:
+ mflr r4 /* save link register */
+ GET_GOT
+ lwz r7, GOT(_start)
+ lwz r8, GOT(_end_of_vectors)
+
+ li r9, 0x100 /* reset vector always at 0x100 */
+
+ cmplw 0, r7, r8
+ bgelr /* return if r7>=r8 - just in case */
+1:
+ lwz r0, 0(r7)
+ stw r0, 0(r9)
+ addi r7, r7, 4
+ addi r9, r9, 4
+ cmplw 0, r7, r8
+ bne 1b
+
+ /*
+ * relocate `hdlr' and `int_return' entries
+ */
+ li r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET
+ li r8, Alignment - _start + EXC_OFF_SYS_RESET
+2:
+ bl trap_reloc
+ addi r7, r7, 0x100 /* next exception vector */
+ cmplw 0, r7, r8
+ blt 2b
+
+ li r7, .L_Alignment - _start + EXC_OFF_SYS_RESET
+ bl trap_reloc
+
+ li r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET
+ bl trap_reloc
+
+ li r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET
+ li r8, SystemCall - _start + EXC_OFF_SYS_RESET
+3:
+ bl trap_reloc
+ addi r7, r7, 0x100 /* next exception vector */
+ cmplw 0, r7, r8
+ blt 3b
+
+ li r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET
+ li r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET
+4:
+ bl trap_reloc
+ addi r7, r7, 0x100 /* next exception vector */
+ cmplw 0, r7, r8
+ blt 4b
+
+ mtlr r4 /* restore link register */
+ blr
diff --git a/arch/powerpc/cpu/mpc8xx/traps.c b/arch/powerpc/cpu/mpc8xx/traps.c
new file mode 100644
index 0000000..ec283d8
--- /dev/null
+++ b/arch/powerpc/cpu/mpc8xx/traps.c
@@ -0,0 +1,168 @@
+/*
+ * linux/arch/powerpc/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.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * This file handles the architecture-dependent parts of hardware exceptions
+ */
+
+#include <common.h>
+#include <command.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 0x02000000
+
+/*
+ * Trap & Exception support
+ */
+
+static 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");
+ }
+ }
+}
+
+
+static 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 & 0x000F0000) {
+ case (0x80000000>>12):
+ printf("Machine check signal - probably due to mm fault\n"
+ "with mmu off\n");
+ break;
+ case (0x80000000>>13):
+ printf("Transfer error ack signal\n");
+ break;
+ case (0x80000000>>14):
+ printf("Data parity signal\n");
+ break;
+ case (0x80000000>>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);
+}
+
+void DebugException(struct pt_regs *regs)
+{
+ printf("Debugger trap at @ %lx\n", regs->nip );
+ show_regs(regs);
+}
+
+/* Probe an address by reading. If not present, return -1, otherwise
+ * return 0.
+ */
+int addr_probe(uint *addr)
+{
+ return 0;
+}
diff --git a/arch/powerpc/include/asm/8xx_immap.h b/arch/powerpc/include/asm/8xx_immap.h
new file mode 100644
index 0000000..3999a02
--- /dev/null
+++ b/arch/powerpc/include/asm/8xx_immap.h
@@ -0,0 +1,468 @@
+/*
+ * MPC8xx Internal Memory Map
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * The I/O on the MPC860 is comprised of blocks of special registers
+ * and the dual port ram for the Communication Processor Module.
+ * Within this space are functional units such as the SIU, memory
+ * controller, system timers, and other control functions. It is
+ * a combination that I found difficult to separate into logical
+ * functional files.....but anyone else is welcome to try. -- Dan
+ */
+#ifndef __IMMAP_8XX__
+#define __IMMAP_8XX__
+
+/* System configuration registers.
+*/
+typedef struct sys_conf {
+ uint sc_siumcr;
+ uint sc_sypcr;
+ uint sc_swt;
+ char res1[2];
+ ushort sc_swsr;
+ uint sc_sipend;
+ uint sc_simask;
+ uint sc_siel;
+ uint sc_sivec;
+ uint sc_tesr;
+ char res2[0xc];
+ uint sc_sdcr;
+ char res3[0x4c];
+} sysconf8xx_t;
+
+/* PCMCIA configuration registers.
+*/
+typedef struct pcmcia_conf {
+ uint pcmc_pbr0;
+ uint pcmc_por0;
+ uint pcmc_pbr1;
+ uint pcmc_por1;
+ uint pcmc_pbr2;
+ uint pcmc_por2;
+ uint pcmc_pbr3;
+ uint pcmc_por3;
+ uint pcmc_pbr4;
+ uint pcmc_por4;
+ uint pcmc_pbr5;
+ uint pcmc_por5;
+ uint pcmc_pbr6;
+ uint pcmc_por6;
+ uint pcmc_pbr7;
+ uint pcmc_por7;
+ char res1[0x20];
+ uint pcmc_pgcra;
+ uint pcmc_pgcrb;
+ uint pcmc_pscr;
+ char res2[4];
+ uint pcmc_pipr;
+ char res3[4];
+ uint pcmc_per;
+ char res4[4];
+} pcmconf8xx_t;
+
+/* Memory controller registers.
+*/
+typedef struct mem_ctlr {
+ uint memc_br0;
+ uint memc_or0;
+ uint memc_br1;
+ uint memc_or1;
+ uint memc_br2;
+ uint memc_or2;
+ uint memc_br3;
+ uint memc_or3;
+ uint memc_br4;
+ uint memc_or4;
+ uint memc_br5;
+ uint memc_or5;
+ uint memc_br6;
+ uint memc_or6;
+ uint memc_br7;
+ uint memc_or7;
+ char res1[0x24];
+ uint memc_mar;
+ uint memc_mcr;
+ char res2[4];
+ uint memc_mamr;
+ uint memc_mbmr;
+ ushort memc_mstat;
+ ushort memc_mptpr;
+ uint memc_mdr;
+ char res3[0x80];
+} memctl8xx_t;
+
+/* System Integration Timers.
+*/
+typedef struct sys_int_timers {
+ ushort sit_tbscr;
+ char res0[0x02];
+ uint sit_tbreff0;
+ uint sit_tbreff1;
+ char res1[0x14];
+ ushort sit_rtcsc;
+ char res2[0x02];
+ uint sit_rtc;
+ uint sit_rtsec;
+ uint sit_rtcal;
+ char res3[0x10];
+ ushort sit_piscr;
+ char res4[2];
+ uint sit_pitc;
+ uint sit_pitr;
+ char res5[0x34];
+} sit8xx_t;
+
+#define TBSCR_TBIRQ_MASK ((ushort)0xff00)
+#define TBSCR_REFA ((ushort)0x0080)
+#define TBSCR_REFB ((ushort)0x0040)
+#define TBSCR_REFAE ((ushort)0x0008)
+#define TBSCR_REFBE ((ushort)0x0004)
+#define TBSCR_TBF ((ushort)0x0002)
+#define TBSCR_TBE ((ushort)0x0001)
+
+#define RTCSC_RTCIRQ_MASK ((ushort)0xff00)
+#define RTCSC_SEC ((ushort)0x0080)
+#define RTCSC_ALR ((ushort)0x0040)
+#define RTCSC_38K ((ushort)0x0010)
+#define RTCSC_SIE ((ushort)0x0008)
+#define RTCSC_ALE ((ushort)0x0004)
+#define RTCSC_RTF ((ushort)0x0002)
+#define RTCSC_RTE ((ushort)0x0001)
+
+#define PISCR_PIRQ_MASK ((ushort)0xff00)
+#define PISCR_PS ((ushort)0x0080)
+#define PISCR_PIE ((ushort)0x0004)
+#define PISCR_PTF ((ushort)0x0002)
+#define PISCR_PTE ((ushort)0x0001)
+
+/* Clocks and Reset.
+*/
+typedef struct clk_and_reset {
+ uint car_sccr;
+ uint car_plprcr;
+ uint car_rsr;
+ char res[0x74]; /* Reserved area */
+} car8xx_t;
+
+/* System Integration Timers keys.
+*/
+typedef struct sitk {
+ uint sitk_tbscrk;
+ uint sitk_tbreff0k;
+ uint sitk_tbreff1k;
+ uint sitk_tbk;
+ char res1[0x10];
+ uint sitk_rtcsck;
+ uint sitk_rtck;
+ uint sitk_rtseck;
+ uint sitk_rtcalk;
+ char res2[0x10];
+ uint sitk_piscrk;
+ uint sitk_pitck;
+ char res3[0x38];
+} sitk8xx_t;
+
+/* Clocks and reset keys.
+*/
+typedef struct cark {
+ uint cark_sccrk;
+ uint cark_plprcrk;
+ uint cark_rsrk;
+ char res[0x474];
+} cark8xx_t;
+
+/* The key to unlock registers maintained by keep-alive power.
+*/
+#define KAPWR_KEY ((unsigned int)0x55ccaa33)
+
+/* I2C
+*/
+typedef struct i2c {
+ u_char i2c_i2mod;
+ char res1[3];
+ u_char i2c_i2add;
+ char res2[3];
+ u_char i2c_i2brg;
+ char res3[3];
+ u_char i2c_i2com;
+ char res4[3];
+ u_char i2c_i2cer;
+ char res5[3];
+ u_char i2c_i2cmr;
+ char res6[0x8b];
+} i2c8xx_t;
+
+/* DMA control/status registers.
+*/
+typedef struct sdma_csr {
+ char res1[4];
+ uint sdma_sdar;
+ u_char sdma_sdsr;
+ char res3[3];
+ u_char sdma_sdmr;
+ char res4[3];
+ u_char sdma_idsr1;
+ char res5[3];
+ u_char sdma_idmr1;
+ char res6[3];
+ u_char sdma_idsr2;
+ char res7[3];
+ u_char sdma_idmr2;
+ char res8[0x13];
+} sdma8xx_t;
+
+/* Communication Processor Module Interrupt Controller.
+*/
+typedef struct cpm_ic {
+ ushort cpic_civr;
+ char res[0xe];
+ uint cpic_cicr;
+ uint cpic_cipr;
+ uint cpic_cimr;
+ uint cpic_cisr;
+} cpic8xx_t;
+
+/* Input/Output Port control/status registers.
+*/
+typedef struct io_port {
+ ushort iop_padir;
+ ushort iop_papar;
+ ushort iop_paodr;
+ ushort iop_padat;
+ char res1[8];
+ ushort iop_pcdir;
+ ushort iop_pcpar;
+ ushort iop_pcso;
+ ushort iop_pcdat;
+ ushort iop_pcint;
+ char res2[6];
+ ushort iop_pddir;
+ ushort iop_pdpar;
+ char res3[2];
+ ushort iop_pddat;
+ uint utmode;
+ char res4[4];
+} iop8xx_t;
+
+/* Communication Processor Module Timers
+*/
+typedef struct cpm_timers {
+ ushort cpmt_tgcr;
+ char res1[0xe];
+ ushort cpmt_tmr1;
+ ushort cpmt_tmr2;
+ ushort cpmt_trr1;
+ ushort cpmt_trr2;
+ ushort cpmt_tcr1;
+ ushort cpmt_tcr2;
+ ushort cpmt_tcn1;
+ ushort cpmt_tcn2;
+ ushort cpmt_tmr3;
+ ushort cpmt_tmr4;
+ ushort cpmt_trr3;
+ ushort cpmt_trr4;
+ ushort cpmt_tcr3;
+ ushort cpmt_tcr4;
+ ushort cpmt_tcn3;
+ ushort cpmt_tcn4;
+ ushort cpmt_ter1;
+ ushort cpmt_ter2;
+ ushort cpmt_ter3;
+ ushort cpmt_ter4;
+ char res2[8];
+} cpmtimer8xx_t;
+
+/* Finally, the Communication Processor stuff.....
+*/
+typedef struct scc { /* Serial communication channels */
+ uint scc_gsmrl;
+ uint scc_gsmrh;
+ ushort scc_psmr;
+ char res1[2];
+ ushort scc_todr;
+ ushort scc_dsr;
+ ushort scc_scce;
+ char res2[2];
+ ushort scc_sccm;
+ char res3;
+ u_char scc_sccs;
+ char res4[8];
+} scc_t;
+
+typedef struct smc { /* Serial management channels */
+ char res1[2];
+ ushort smc_smcmr;
+ char res2[2];
+ u_char smc_smce;
+ char res3[3];
+ u_char smc_smcm;
+ char res4[5];
+} smc_t;
+
+/* MPC860T Fast Ethernet Controller. It isn't part of the CPM, but
+ * it fits within the address space.
+ */
+
+typedef struct fec {
+ uint fec_addr_low; /* lower 32 bits of station address */
+ ushort fec_addr_high; /* upper 16 bits of station address */
+ ushort res1; /* reserved */
+ uint fec_hash_table_high; /* upper 32-bits of hash table */
+ uint fec_hash_table_low; /* lower 32-bits of hash table */
+ uint fec_r_des_start; /* beginning of Rx descriptor ring */
+ uint fec_x_des_start; /* beginning of Tx descriptor ring */
+ uint fec_r_buff_size; /* Rx buffer size */
+ uint res2[9]; /* reserved */
+ uint fec_ecntrl; /* ethernet control register */
+ uint fec_ievent; /* interrupt event register */
+ uint fec_imask; /* interrupt mask register */
+ uint fec_ivec; /* interrupt level and vector status */
+ uint fec_r_des_active; /* Rx ring updated flag */
+ uint fec_x_des_active; /* Tx ring updated flag */
+ uint res3[10]; /* reserved */
+ uint fec_mii_data; /* MII data register */
+ uint fec_mii_speed; /* MII speed control register */
+ uint res4[17]; /* reserved */
+ uint fec_r_bound; /* end of RAM (read-only) */
+ uint fec_r_fstart; /* Rx FIFO start address */
+ uint res5[6]; /* reserved */
+ uint fec_x_fstart; /* Tx FIFO start address */
+ uint res6[17]; /* reserved */
+ uint fec_fun_code; /* fec SDMA function code */
+ uint res7[3]; /* reserved */
+ uint fec_r_cntrl; /* Rx control register */
+ uint fec_r_hash; /* Rx hash register */
+ uint res8[14]; /* reserved */
+ uint fec_x_cntrl; /* Tx control register */
+ uint res9[0x1e]; /* reserved */
+} fec_t;
+
+typedef struct comm_proc {
+ /* General control and status registers.
+ */
+ ushort cp_cpcr;
+ u_char res1[2];
+ ushort cp_rccr;
+ u_char res2;
+ u_char cp_rmds;
+ u_char res3[4];
+ ushort cp_cpmcr1;
+ ushort cp_cpmcr2;
+ ushort cp_cpmcr3;
+ ushort cp_cpmcr4;
+ u_char res4[2];
+ ushort cp_rter;
+ u_char res5[2];
+ ushort cp_rtmr;
+ u_char res6[0x14];
+
+ /* Baud rate generators.
+ */
+ uint cp_brgc1;
+ uint cp_brgc2;
+ uint cp_brgc3;
+ uint cp_brgc4;
+
+ /* Serial Communication Channels.
+ */
+ scc_t cp_scc[4];
+
+ /* Serial Management Channels.
+ */
+ smc_t cp_smc[2];
+
+ /* Serial Peripheral Interface.
+ */
+ ushort cp_spmode;
+ u_char res7[4];
+ u_char cp_spie;
+ u_char res8[3];
+ u_char cp_spim;
+ u_char res9[2];
+ u_char cp_spcom;
+ u_char res10[2];
+
+ /* Parallel Interface Port.
+ */
+ u_char res11[2];
+ ushort cp_pipc;
+ u_char res12[2];
+ ushort cp_ptpr;
+ uint cp_pbdir;
+ uint cp_pbpar;
+ u_char res13[2];
+ ushort cp_pbodr;
+ uint cp_pbdat;
+
+ /* Port E - MPC87x/88x only.
+ */
+ uint cp_pedir;
+ uint cp_pepar;
+ uint cp_peso;
+ uint cp_peodr;
+ uint cp_pedat;
+
+ /* Communications Processor Timing Register -
+ Contains RMII Timing for the FECs on MPC87x/88x only.
+ */
+ uint cp_cptr;
+
+ /* Serial Interface and Time Slot Assignment.
+ */
+ uint cp_simode;
+ u_char cp_sigmr;
+ u_char res15;
+ u_char cp_sistr;
+ u_char cp_sicmr;
+ u_char res16[4];
+ uint cp_sicr;
+ uint cp_sirp;
+ u_char res17[0xc];
+
+ u_char res19[0x100];
+ u_char cp_siram[0x200];
+
+ /* The fast ethernet controller is not really part of the CPM,
+ * but it resides in the address space.
+ */
+ fec_t cp_fec;
+ char res18[0xE00];
+
+ /* The MPC885 family has a second FEC here */
+ fec_t cp_fec2;
+#define cp_fec1 cp_fec /* consistency macro */
+
+ /* Dual Ported RAM follows.
+ * There are many different formats for this memory area
+ * depending upon the devices used and options chosen.
+ * Some processors don't have all of it populated.
+ */
+ u_char cp_dpmem[0x1C00]; /* BD / Data / ucode */
+
+ /* Parameter RAM */
+ union {
+ u_char cp_dparam[0x400];
+ u16 cp_dparam16[0x200];
+ };
+} cpm8xx_t;
+
+/* Internal memory map.
+*/
+typedef struct immap {
+ sysconf8xx_t im_siu_conf; /* SIU Configuration */
+ pcmconf8xx_t im_pcmcia; /* PCMCIA Configuration */
+ memctl8xx_t im_memctl; /* Memory Controller */
+ sit8xx_t im_sit; /* System integration timers */
+ car8xx_t im_clkrst; /* Clocks and reset */
+ sitk8xx_t im_sitk; /* Sys int timer keys */
+ cark8xx_t im_clkrstk; /* Clocks and reset keys */
+ char res[96];
+ i2c8xx_t im_i2c; /* I2C control/status */
+ sdma8xx_t im_sdma; /* SDMA control/status */
+ cpic8xx_t im_cpic; /* CPM Interrupt Controller */
+ iop8xx_t im_ioport; /* IO Port control/status */
+ cpmtimer8xx_t im_cpmtimer; /* CPM timers */
+ cpm8xx_t im_cpm; /* Communication processor */
+} immap_t;
+
+#endif /* __IMMAP_8XX__ */
diff --git a/arch/powerpc/include/asm/cache.h b/arch/powerpc/include/asm/cache.h
index 20c52fc..d3a8391 100644
--- a/arch/powerpc/include/asm/cache.h
+++ b/arch/powerpc/include/asm/cache.h
@@ -7,7 +7,9 @@
#include <asm/processor.h>
/* bytes per L1 cache line */
-#if defined(CONFIG_PPC64BRIDGE)
+#if defined(CONFIG_8xx)
+#define L1_CACHE_SHIFT 4
+#elif defined(CONFIG_PPC64BRIDGE)
#define L1_CACHE_SHIFT 7
#elif defined(CONFIG_E500MC)
#define L1_CACHE_SHIFT 6
@@ -70,4 +72,41 @@
#define L2CACHE_NONE 0x03 /* NONE */
#define L2CACHE_PARITY 0x08 /* Mask for L2 Cache Parity Protected bit */
+#ifdef CONFIG_8xx
+/* Cache control on the MPC8xx is provided through some additional
+ * special purpose registers.
+ */
+#define IC_CST 560 /* Instruction cache control/status */
+#define IC_ADR 561 /* Address needed for some commands */
+#define IC_DAT 562 /* Read-only data register */
+#define DC_CST 568 /* Data cache control/status */
+#define DC_ADR 569 /* Address needed for some commands */
+#define DC_DAT 570 /* Read-only data register */
+
+/* Commands. Only the first few are available to the instruction cache.
+*/
+#define IDC_ENABLE 0x02000000 /* Cache enable */
+#define IDC_DISABLE 0x04000000 /* Cache disable */
+#define IDC_LDLCK 0x06000000 /* Load and lock */
+#define IDC_UNLINE 0x08000000 /* Unlock line */
+#define IDC_UNALL 0x0a000000 /* Unlock all */
+#define IDC_INVALL 0x0c000000 /* Invalidate all */
+
+#define DC_FLINE 0x0e000000 /* Flush data cache line */
+#define DC_SFWT 0x01000000 /* Set forced writethrough mode */
+#define DC_CFWT 0x03000000 /* Clear forced writethrough mode */
+#define DC_SLES 0x05000000 /* Set little endian swap mode */
+#define DC_CLES 0x07000000 /* Clear little endian swap mode */
+
+/* Status.
+*/
+#define IDC_ENABLED 0x80000000 /* Cache is enabled */
+#define IDC_CERR1 0x00200000 /* Cache error 1 */
+#define IDC_CERR2 0x00100000 /* Cache error 2 */
+#define IDC_CERR3 0x00080000 /* Cache error 3 */
+
+#define DC_DFWT 0x40000000 /* Data cache is forced write through */
+#define DC_LES 0x20000000 /* Caches are little endian mode */
+#endif /* CONFIG_8xx */
+
#endif
diff --git a/arch/powerpc/include/asm/global_data.h b/arch/powerpc/include/asm/global_data.h
index 1c4a82c..35a02b6 100644
--- a/arch/powerpc/include/asm/global_data.h
+++ b/arch/powerpc/include/asm/global_data.h
@@ -19,6 +19,9 @@
u8 sdhc_adapter;
#endif
#endif
+#if defined(CONFIG_8xx)
+ unsigned long brg_clk;
+#endif
#if defined(CONFIG_CPM2)
/* There are many clocks on the MPC8260 - see page 9-5 */
unsigned long vco_out;
diff --git a/arch/powerpc/include/asm/iopin_8xx.h b/arch/powerpc/include/asm/iopin_8xx.h
new file mode 100644
index 0000000..8db0fa2
--- /dev/null
+++ b/arch/powerpc/include/asm/iopin_8xx.h
@@ -0,0 +1,379 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * MPC8xx I/O port pin manipulation functions
+ * Roughly based on iopin_8260.h
+ */
+
+#ifndef _ASM_IOPIN_8XX_H_
+#define _ASM_IOPIN_8XX_H_
+
+#include <linux/types.h>
+#include <asm/8xx_immap.h>
+
+#ifdef __KERNEL__
+
+typedef struct {
+ u_char port:2; /* port number (A=0, B=1, C=2, D=3) */
+ u_char pin:5; /* port pin (0-31) */
+ u_char flag:1; /* for whatever */
+} iopin_t;
+
+#define IOPIN_PORTA 0
+#define IOPIN_PORTB 1
+#define IOPIN_PORTC 2
+#define IOPIN_PORTD 3
+
+static __inline__ void
+iopin_set_high(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padat;
+ *datp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat;
+ *datp |= (1 << (31 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat;
+ *datp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddat;
+ *datp |= (1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ void
+iopin_set_low(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padat;
+ *datp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat;
+ *datp &= ~(1 << (31 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat;
+ *datp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddat;
+ *datp &= ~(1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ uint
+iopin_is_high(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padat;
+ return (*datp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat;
+ return (*datp >> (31 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat;
+ return (*datp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddat;
+ return (*datp >> (15 - iopin->pin)) & 1;
+ }
+ return 0;
+}
+
+static __inline__ uint
+iopin_is_low(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padat;
+ return ((*datp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat;
+ return ((*datp >> (31 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat;
+ return ((*datp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *datp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddat;
+ return ((*datp >> (15 - iopin->pin)) & 1) ^ 1;
+ }
+ return 0;
+}
+
+static __inline__ void
+iopin_set_out(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padir;
+ *dirp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdir;
+ *dirp |= (1 << (31 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdir;
+ *dirp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddir;
+ *dirp |= (1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ void
+iopin_set_in(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padir;
+ *dirp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdir;
+ *dirp &= ~(1 << (31 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdir;
+ *dirp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddir;
+ *dirp &= ~(1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ uint
+iopin_is_out(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padir;
+ return (*dirp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdir;
+ return (*dirp >> (31 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdir;
+ return (*dirp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddir;
+ return (*dirp >> (15 - iopin->pin)) & 1;
+ }
+ return 0;
+}
+
+static __inline__ uint
+iopin_is_in(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_padir;
+ return ((*dirp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdir;
+ return ((*dirp >> (31 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdir;
+ return ((*dirp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *dirp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pddir;
+ return ((*dirp >> (15 - iopin->pin)) & 1) ^ 1;
+ }
+ return 0;
+}
+
+static __inline__ void
+iopin_set_odr(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_paodr;
+ *odrp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbodr;
+ *odrp |= (1 << (31 - iopin->pin));
+ }
+}
+
+static __inline__ void
+iopin_set_act(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_paodr;
+ *odrp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbodr;
+ *odrp &= ~(1 << (31 - iopin->pin));
+ }
+}
+
+static __inline__ uint
+iopin_is_odr(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_paodr;
+ return (*odrp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbodr;
+ return (*odrp >> (31 - iopin->pin)) & 1;
+ }
+ return 0;
+}
+
+static __inline__ uint
+iopin_is_act(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_paodr;
+ return ((*odrp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile ushort *odrp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbodr;
+ return ((*odrp >> (31 - iopin->pin)) & 1) ^ 1;
+ }
+ return 0;
+}
+
+static __inline__ void
+iopin_set_ded(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_papar;
+ *parp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbpar;
+ *parp |= (1 << (31 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcpar;
+ *parp |= (1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pdpar;
+ *parp |= (1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ void
+iopin_set_gen(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_papar;
+ *parp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbpar;
+ *parp &= ~(1 << (31 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcpar;
+ *parp &= ~(1 << (15 - iopin->pin));
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pdpar;
+ *parp &= ~(1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ uint
+iopin_is_ded(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_papar;
+ return (*parp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbpar;
+ return (*parp >> (31 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcpar;
+ return (*parp >> (15 - iopin->pin)) & 1;
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pdpar;
+ return (*parp >> (15 - iopin->pin)) & 1;
+ }
+ return 0;
+}
+
+static __inline__ uint
+iopin_is_gen(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTA) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_papar;
+ return ((*parp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTB) {
+ volatile uint *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbpar;
+ return ((*parp >> (31 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcpar;
+ return ((*parp >> (15 - iopin->pin)) & 1) ^ 1;
+ } else if (iopin->port == IOPIN_PORTD) {
+ volatile ushort *parp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pdpar;
+ return ((*parp >> (15 - iopin->pin)) & 1) ^ 1;
+ }
+ return 0;
+}
+
+static __inline__ void
+iopin_set_opt2(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *sorp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcso;
+ *sorp |= (1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ void
+iopin_set_opt1(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *sorp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcso;
+ *sorp &= ~(1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ uint
+iopin_is_opt2(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *sorp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcso;
+ return (*sorp >> (15 - iopin->pin)) & 1;
+ }
+ return 0;
+}
+
+static __inline__ uint
+iopin_is_opt1(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *sorp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcso;
+ return ((*sorp >> (15 - iopin->pin)) & 1) ^ 1;
+ }
+ return 0;
+}
+
+static __inline__ void
+iopin_set_falledge(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *intp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcint;
+ *intp |= (1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ void
+iopin_set_anyedge(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *intp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcint;
+ *intp &= ~(1 << (15 - iopin->pin));
+ }
+}
+
+static __inline__ uint
+iopin_is_falledge(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *intp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcint;
+ return (*intp >> (15 - iopin->pin)) & 1;
+ }
+ return 0;
+}
+
+static __inline__ uint
+iopin_is_anyedge(iopin_t *iopin)
+{
+ if (iopin->port == IOPIN_PORTC) {
+ volatile ushort *intp = &((immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcint;
+ return ((*intp >> (15 - iopin->pin)) & 1) ^ 1;
+ }
+ return 0;
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_IOPIN_8XX_H_ */
diff --git a/arch/powerpc/include/asm/ppc.h b/arch/powerpc/include/asm/ppc.h
index aa6c304..9a8afe1 100644
--- a/arch/powerpc/include/asm/ppc.h
+++ b/arch/powerpc/include/asm/ppc.h
@@ -13,6 +13,14 @@
#ifndef __ASSEMBLY__
+#if defined(CONFIG_8xx)
+#include <asm/8xx_immap.h>
+#if defined(CONFIG_MPC866)
+# define CONFIG_MPC866_FAMILY 1
+#elif defined(CONFIG_MPC885)
+# define CONFIG_MPC885_FAMILY 1
+#endif
+#endif
#ifdef CONFIG_MPC86xx
#include <mpc86xx.h>
#include <asm/immap_86xx.h>
@@ -35,6 +43,9 @@
#include <asm/arch/immap_lsch2.h>
#endif
+#if defined(CONFIG_8xx)
+uint get_immr(uint);
+#endif
uint get_pvr(void);
uint get_svr(void);
uint rd_ic_cst(void);
diff --git a/arch/powerpc/lib/Kconfig b/arch/powerpc/lib/Kconfig
new file mode 100644
index 0000000..7c8ea97
--- /dev/null
+++ b/arch/powerpc/lib/Kconfig
@@ -0,0 +1,7 @@
+config CMD_IMMAP
+ bool "Enable various commands to dump IMMR information"
+ help
+ This enables various commands such as:
+
+ siuinfo - print System Interface Unit (SIU) registers
+ memcinfo - print Memory Controller registers
diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index 9a3043a..4aa4183 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -32,6 +32,7 @@
obj-$(CONFIG_CMD_BOOTM) += bootm.o
obj-y += cache.o
obj-y += extable.o
+obj-$(CONFIG_CMD_IMMAP) += immap.o
obj-y += interrupts.o
obj-$(CONFIG_CMD_KGDB) += kgdb.o
obj-y += stack.o
diff --git a/arch/powerpc/lib/immap.c b/arch/powerpc/lib/immap.c
new file mode 100644
index 0000000..1beed1f
--- /dev/null
+++ b/arch/powerpc/lib/immap.c
@@ -0,0 +1,397 @@
+/*
+ * (C) Copyright 2000-2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * MPC8xx Internal Memory Map Functions
+ */
+
+#include <common.h>
+#include <command.h>
+
+#if defined(CONFIG_8xx)
+
+#include <asm/8xx_immap.h>
+#include <commproc.h>
+#include <asm/iopin_8xx.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int
+do_siuinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ volatile sysconf8xx_t *sc = &immap->im_siu_conf;
+
+ printf ("SIUMCR= %08x SYPCR = %08x\n", sc->sc_siumcr, sc->sc_sypcr);
+ printf ("SWT = %08x\n", sc->sc_swt);
+ printf ("SIPEND= %08x SIMASK= %08x\n", sc->sc_sipend, sc->sc_simask);
+ printf ("SIEL = %08x SIVEC = %08x\n", sc->sc_siel, sc->sc_sivec);
+ printf ("TESR = %08x SDCR = %08x\n", sc->sc_tesr, sc->sc_sdcr);
+ return 0;
+}
+
+int
+do_memcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ volatile memctl8xx_t *memctl = &immap->im_memctl;
+ int nbanks = 8;
+ volatile uint *p = &memctl->memc_br0;
+ int i;
+
+ for (i = 0; i < nbanks; i++, p += 2) {
+ if (i < 10) {
+ printf ("BR%d = %08x OR%d = %08x\n",
+ i, p[0], i, p[1]);
+ } else {
+ printf ("BR%d = %08x OR%d = %08x\n",
+ i, p[0], i, p[1]);
+ }
+ }
+
+ printf ("MAR = %08x", memctl->memc_mar);
+ printf (" MCR = %08x\n", memctl->memc_mcr);
+ printf ("MAMR = %08x MBMR = %08x",
+ memctl->memc_mamr, memctl->memc_mbmr);
+ printf ("\nMSTAT = %04x\n", memctl->memc_mstat);
+ printf ("MPTPR = %04x MDR = %08x\n",
+ memctl->memc_mptpr, memctl->memc_mdr);
+ return 0;
+}
+
+int
+do_carinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ volatile car8xx_t *car = &immap->im_clkrst;
+
+ printf ("SCCR = %08x\n", car->car_sccr);
+ printf ("PLPRCR= %08x\n", car->car_plprcr);
+ printf ("RSR = %08x\n", car->car_rsr);
+ return 0;
+}
+
+static int counter;
+
+static void
+header(void)
+{
+ char *data = "\
+ -------------------------------- --------------------------------\
+ 00000000001111111111222222222233 00000000001111111111222222222233\
+ 01234567890123456789012345678901 01234567890123456789012345678901\
+ -------------------------------- --------------------------------\
+ ";
+ int i;
+
+ if (counter % 2)
+ putc('\n');
+ counter = 0;
+
+ for (i = 0; i < 4; i++, data += 79)
+ printf("%.79s\n", data);
+}
+
+static void binary (char *label, uint value, int nbits)
+{
+ uint mask = 1 << (nbits - 1);
+ int i, second = (counter++ % 2);
+
+ if (second)
+ putc (' ');
+ puts (label);
+ for (i = 32 + 1; i != nbits; i--)
+ putc (' ');
+
+ while (mask != 0) {
+ if (value & mask)
+ putc ('1');
+ else
+ putc ('0');
+ mask >>= 1;
+ }
+
+ if (second)
+ putc ('\n');
+}
+
+#define PA_NBITS 16
+#define PA_NB_ODR 8
+#define PB_NBITS 18
+#define PB_NB_ODR 16
+#define PC_NBITS 12
+#define PD_NBITS 13
+
+int
+do_iopinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ volatile iop8xx_t *iop = &immap->im_ioport;
+ volatile ushort *l, *r;
+ volatile uint *R;
+
+ counter = 0;
+ header ();
+
+ /*
+ * Ports A & B
+ */
+
+ l = &iop->iop_padir;
+ R = &immap->im_cpm.cp_pbdir;
+ binary ("PA_DIR", *l++, PA_NBITS);
+ binary ("PB_DIR", *R++, PB_NBITS);
+ binary ("PA_PAR", *l++, PA_NBITS);
+ binary ("PB_PAR", *R++, PB_NBITS);
+ binary ("PA_ODR", *l++, PA_NB_ODR);
+ binary ("PB_ODR", *R++, PB_NB_ODR);
+ binary ("PA_DAT", *l++, PA_NBITS);
+ binary ("PB_DAT", *R++, PB_NBITS);
+
+ header ();
+
+ /*
+ * Ports C & D
+ */
+
+ l = &iop->iop_pcdir;
+ r = &iop->iop_pddir;
+ binary ("PC_DIR", *l++, PC_NBITS);
+ binary ("PD_DIR", *r++, PD_NBITS);
+ binary ("PC_PAR", *l++, PC_NBITS);
+ binary ("PD_PAR", *r++, PD_NBITS);
+ binary ("PC_SO ", *l++, PC_NBITS);
+ binary (" ", 0, 0);
+ r++;
+ binary ("PC_DAT", *l++, PC_NBITS);
+ binary ("PD_DAT", *r++, PD_NBITS);
+ binary ("PC_INT", *l++, PC_NBITS);
+
+ header ();
+ return 0;
+}
+
+/*
+ * set the io pins
+ * this needs a clean up for smaller tighter code
+ * use *uint and set the address based on cmd + port
+ */
+int
+do_iopset (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ uint rcode = 0;
+ iopin_t iopin;
+ static uint port = 0;
+ static uint pin = 0;
+ static uint value = 0;
+ static enum {
+ DIR,
+ PAR,
+ SOR,
+ ODR,
+ DAT,
+ INT
+ } cmd = DAT;
+
+ if (argc != 5) {
+ puts ("iopset PORT PIN CMD VALUE\n");
+ return 1;
+ }
+ port = argv[1][0] - 'A';
+ if (port > 3)
+ port -= 0x20;
+ if (port > 3)
+ rcode = 1;
+ pin = simple_strtol (argv[2], NULL, 10);
+ if (pin > 31)
+ rcode = 1;
+
+
+ switch (argv[3][0]) {
+ case 'd':
+ if (argv[3][1] == 'a')
+ cmd = DAT;
+ else if (argv[3][1] == 'i')
+ cmd = DIR;
+ else
+ rcode = 1;
+ break;
+ case 'p':
+ cmd = PAR;
+ break;
+ case 'o':
+ cmd = ODR;
+ break;
+ case 's':
+ cmd = SOR;
+ break;
+ case 'i':
+ cmd = INT;
+ break;
+ default:
+ printf ("iopset: unknown command %s\n", argv[3]);
+ rcode = 1;
+ }
+ if (argv[4][0] == '1')
+ value = 1;
+ else if (argv[4][0] == '0')
+ value = 0;
+ else
+ rcode = 1;
+ if (rcode == 0) {
+ iopin.port = port;
+ iopin.pin = pin;
+ iopin.flag = 0;
+ switch (cmd) {
+ case DIR:
+ if (value)
+ iopin_set_out (&iopin);
+ else
+ iopin_set_in (&iopin);
+ break;
+ case PAR:
+ if (value)
+ iopin_set_ded (&iopin);
+ else
+ iopin_set_gen (&iopin);
+ break;
+ case SOR:
+ if (value)
+ iopin_set_opt2 (&iopin);
+ else
+ iopin_set_opt1 (&iopin);
+ break;
+ case ODR:
+ if (value)
+ iopin_set_odr (&iopin);
+ else
+ iopin_set_act (&iopin);
+ break;
+ case DAT:
+ if (value)
+ iopin_set_high (&iopin);
+ else
+ iopin_set_low (&iopin);
+ break;
+ case INT:
+ if (value)
+ iopin_set_falledge (&iopin);
+ else
+ iopin_set_anyedge (&iopin);
+ break;
+ }
+
+ }
+ return rcode;
+}
+
+static void prbrg (int n, uint val)
+{
+ uint extc = (val >> 14) & 3;
+ uint cd = (val & CPM_BRG_CD_MASK) >> 1;
+ uint div16 = (val & CPM_BRG_DIV16) != 0;
+
+ ulong clock = gd->cpu_clk;
+
+ printf ("BRG%d:", n);
+
+ if (val & CPM_BRG_RST)
+ puts (" RESET");
+ else
+ puts (" ");
+
+ if (val & CPM_BRG_EN)
+ puts (" ENABLED");
+ else
+ puts (" DISABLED");
+
+ printf (" EXTC=%d", extc);
+
+ if (val & CPM_BRG_ATB)
+ puts (" ATB");
+ else
+ puts (" ");
+
+ printf (" DIVIDER=%4d", cd);
+ if (extc == 0 && cd != 0) {
+ uint baudrate;
+
+ if (div16)
+ baudrate = (clock / 16) / (cd + 1);
+ else
+ baudrate = clock / (cd + 1);
+
+ printf ("=%6d bps", baudrate);
+ } else {
+ puts (" ");
+ }
+
+ if (val & CPM_BRG_DIV16)
+ puts (" DIV16");
+ else
+ puts (" ");
+
+ putc ('\n');
+}
+
+int
+do_brginfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ volatile cpm8xx_t *cp = &immap->im_cpm;
+ volatile uint *p = &cp->cp_brgc1;
+ int i = 1;
+
+ while (i <= 4)
+ prbrg (i++, *p++);
+
+ return 0;
+}
+
+/***************************************************/
+
+U_BOOT_CMD(
+ siuinfo, 1, 1, do_siuinfo,
+ "print System Interface Unit (SIU) registers",
+ ""
+);
+
+U_BOOT_CMD(
+ memcinfo, 1, 1, do_memcinfo,
+ "print Memory Controller registers",
+ ""
+);
+
+U_BOOT_CMD(
+ carinfo, 1, 1, do_carinfo,
+ "print Clocks and Reset registers",
+ ""
+);
+
+U_BOOT_CMD(
+ iopinfo, 1, 1, do_iopinfo,
+ "print I/O Port registers",
+ ""
+);
+
+U_BOOT_CMD(
+ iopset, 5, 0, do_iopset,
+ "set I/O Port registers",
+ "PORT PIN CMD VALUE\nPORT: A-D, PIN: 0-31, CMD: [dat|dir|odr|sor], VALUE: 0|1"
+);
+
+U_BOOT_CMD(
+ brginfo, 1, 1, do_brginfo,
+ "print Baud Rate Generator (BRG) registers",
+ ""
+);
+#endif
diff --git a/arch/powerpc/lib/time.c b/arch/powerpc/lib/time.c
index 3a5ad4d..4cbb65e 100644
--- a/arch/powerpc/lib/time.c
+++ b/arch/powerpc/lib/time.c
@@ -64,10 +64,21 @@
{
unsigned long temp;
+#if defined(CONFIG_8xx)
+ volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
+
+ /* unlock */
+ immap->im_sitk.sitk_tbk = KAPWR_KEY;
+#endif
+
/* reset */
asm volatile("li %0,0 ; mttbu %0 ; mttbl %0;"
: "=&r"(temp) );
+#if defined(CONFIG_8xx)
+ /* enable */
+ immap->im_sit.sit_tbscr |= TBSCR_TBE;
+#endif
return (0);
}
/* ------------------------------------------------------------------------- */