summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/embedded6xx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
commitace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch)
treeb2d64bc10158fdd5497876388cd68142ca374ed3 /arch/powerpc/platforms/embedded6xx
parentInitial commit. (diff)
downloadlinux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz
linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/powerpc/platforms/embedded6xx')
-rw-r--r--arch/powerpc/platforms/embedded6xx/Kconfig94
-rw-r--r--arch/powerpc/platforms/embedded6xx/Makefile12
-rw-r--r--arch/powerpc/platforms/embedded6xx/flipper-pic.c244
-rw-r--r--arch/powerpc/platforms/embedded6xx/flipper-pic.h20
-rw-r--r--arch/powerpc/platforms/embedded6xx/gamecube.c89
-rw-r--r--arch/powerpc/platforms/embedded6xx/hlwd-pic.c235
-rw-r--r--arch/powerpc/platforms/embedded6xx/hlwd-pic.h17
-rw-r--r--arch/powerpc/platforms/embedded6xx/holly.c268
-rw-r--r--arch/powerpc/platforms/embedded6xx/linkstation.c162
-rw-r--r--arch/powerpc/platforms/embedded6xx/ls_uart.c147
-rw-r--r--arch/powerpc/platforms/embedded6xx/mpc10x.h159
-rw-r--r--arch/powerpc/platforms/embedded6xx/mvme5100.c208
-rw-r--r--arch/powerpc/platforms/embedded6xx/storcenter.c121
-rw-r--r--arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c306
-rw-r--r--arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h27
-rw-r--r--arch/powerpc/platforms/embedded6xx/wii.c180
16 files changed, 2289 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig
new file mode 100644
index 0000000000..c6adff216f
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/Kconfig
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: GPL-2.0
+config EMBEDDED6xx
+ bool "Embedded 6xx/7xx/7xxx-based boards"
+ depends on PPC_BOOK3S_32 && BROKEN_ON_SMP
+
+config LINKSTATION
+ bool "Linkstation / Kurobox(HG) from Buffalo"
+ depends on EMBEDDED6xx
+ select MPIC
+ select FSL_SOC
+ select PPC_UDBG_16550 if SERIAL_8250
+ select DEFAULT_UIMAGE
+ imply MPC10X_BRIDGE if PCI
+ help
+ Select LINKSTATION if configuring for one of PPC- (MPC8241)
+ based NAS systems from Buffalo Technology. So far only
+ KuroboxHG has been tested. In the future classical Kurobox,
+ Linkstation-I HD-HLAN and HD-HGLAN versions, and PPC-based
+ Terastation systems should be supported too.
+
+config STORCENTER
+ bool "IOMEGA StorCenter"
+ depends on EMBEDDED6xx
+ select MPIC
+ select FSL_SOC
+ select PPC_UDBG_16550 if SERIAL_8250
+ imply MPC10X_BRIDGE if PCI
+ help
+ Select STORCENTER if configuring for the iomega StorCenter
+ with an 8241 CPU in it.
+
+config PPC_HOLLY
+ bool "PPC750GX/CL with TSI10x bridge (Hickory/Holly)"
+ depends on EMBEDDED6xx
+ select TSI108_BRIDGE
+ select PPC_UDBG_16550
+ help
+ Select PPC_HOLLY if configuring for an IBM 750GX/CL Eval
+ Board with TSI108/9 bridge (Hickory/Holly)
+
+config MVME5100
+ bool "Motorola/Emerson MVME5100"
+ depends on EMBEDDED6xx
+ select MPIC
+ select FORCE_PCI
+ select PPC_INDIRECT_PCI
+ select PPC_I8259
+ select PPC_HASH_MMU_NATIVE
+ select PPC_UDBG_16550
+ help
+ This option enables support for the Motorola (now Emerson) MVME5100
+ board.
+
+config TSI108_BRIDGE
+ bool
+ select FORCE_PCI
+ select MPIC
+ select MPIC_WEIRD
+
+config MPC10X_BRIDGE
+ bool
+ select PPC_INDIRECT_PCI
+
+config GAMECUBE_COMMON
+ bool
+
+config USBGECKO_UDBG
+ bool "USB Gecko udbg console for the Nintendo GameCube/Wii"
+ depends on GAMECUBE_COMMON
+ help
+ If you say yes to this option, support will be included for the
+ USB Gecko adapter as an udbg console.
+ The USB Gecko is a EXI to USB Serial converter that can be plugged
+ into a memcard slot in the Nintendo GameCube/Wii.
+
+ This driver bypasses the EXI layer completely.
+
+ If in doubt, say N here.
+
+config GAMECUBE
+ bool "Nintendo-GameCube"
+ depends on EMBEDDED6xx
+ select GAMECUBE_COMMON
+ help
+ Select GAMECUBE if configuring for the Nintendo GameCube.
+ More information at: <http://gc-linux.sourceforge.net/>
+
+config WII
+ bool "Nintendo-Wii"
+ depends on EMBEDDED6xx
+ select GAMECUBE_COMMON
+ help
+ Select WII if configuring for the Nintendo Wii.
+ More information at: <http://gc-linux.sourceforge.net/>
diff --git a/arch/powerpc/platforms/embedded6xx/Makefile b/arch/powerpc/platforms/embedded6xx/Makefile
new file mode 100644
index 0000000000..7f2a8154e5
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the 6xx/7xx/7xxxx linux kernel.
+#
+obj-$(CONFIG_LINKSTATION) += linkstation.o ls_uart.o
+obj-$(CONFIG_STORCENTER) += storcenter.o
+obj-$(CONFIG_PPC_HOLLY) += holly.o
+obj-$(CONFIG_USBGECKO_UDBG) += usbgecko_udbg.o
+obj-$(CONFIG_GAMECUBE_COMMON) += flipper-pic.o
+obj-$(CONFIG_GAMECUBE) += gamecube.o
+obj-$(CONFIG_WII) += wii.o hlwd-pic.o
+obj-$(CONFIG_MVME5100) += mvme5100.o
diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c
new file mode 100644
index 0000000000..4d9200bdba
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * arch/powerpc/platforms/embedded6xx/flipper-pic.c
+ *
+ * Nintendo GameCube/Wii "Flipper" interrupt controller support.
+ * Copyright (C) 2004-2009 The GameCube Linux Team
+ * Copyright (C) 2007,2008,2009 Albert Herranz
+ */
+#define DRV_MODULE_NAME "flipper-pic"
+#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+
+#include "flipper-pic.h"
+
+#define FLIPPER_NR_IRQS 32
+
+/*
+ * Each interrupt has a corresponding bit in both
+ * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers.
+ *
+ * Enabling/disabling an interrupt line involves setting/clearing
+ * the corresponding bit in IMR.
+ * Except for the RSW interrupt, all interrupts get deasserted automatically
+ * when the source deasserts the interrupt.
+ */
+#define FLIPPER_ICR 0x00
+#define FLIPPER_ICR_RSS (1<<16) /* reset switch state */
+
+#define FLIPPER_IMR 0x04
+
+#define FLIPPER_RESET 0x24
+
+
+/*
+ * IRQ chip hooks.
+ *
+ */
+
+static void flipper_pic_mask_and_ack(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+ u32 mask = 1 << irq;
+
+ clrbits32(io_base + FLIPPER_IMR, mask);
+ /* this is at least needed for RSW */
+ out_be32(io_base + FLIPPER_ICR, mask);
+}
+
+static void flipper_pic_ack(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+
+ /* this is at least needed for RSW */
+ out_be32(io_base + FLIPPER_ICR, 1 << irq);
+}
+
+static void flipper_pic_mask(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+
+ clrbits32(io_base + FLIPPER_IMR, 1 << irq);
+}
+
+static void flipper_pic_unmask(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+
+ setbits32(io_base + FLIPPER_IMR, 1 << irq);
+}
+
+
+static struct irq_chip flipper_pic = {
+ .name = "flipper-pic",
+ .irq_ack = flipper_pic_ack,
+ .irq_mask_ack = flipper_pic_mask_and_ack,
+ .irq_mask = flipper_pic_mask,
+ .irq_unmask = flipper_pic_unmask,
+};
+
+/*
+ * IRQ host hooks.
+ *
+ */
+
+static struct irq_domain *flipper_irq_host;
+
+static int flipper_pic_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &flipper_pic, handle_level_irq);
+ return 0;
+}
+
+static const struct irq_domain_ops flipper_irq_domain_ops = {
+ .map = flipper_pic_map,
+};
+
+/*
+ * Platform hooks.
+ *
+ */
+
+static void __flipper_quiesce(void __iomem *io_base)
+{
+ /* mask and ack all IRQs */
+ out_be32(io_base + FLIPPER_IMR, 0x00000000);
+ out_be32(io_base + FLIPPER_ICR, 0xffffffff);
+}
+
+static struct irq_domain * __init flipper_pic_init(struct device_node *np)
+{
+ struct device_node *pi;
+ struct irq_domain *irq_domain = NULL;
+ struct resource res;
+ void __iomem *io_base;
+ int retval;
+
+ pi = of_get_parent(np);
+ if (!pi) {
+ pr_err("no parent found\n");
+ goto out;
+ }
+ if (!of_device_is_compatible(pi, "nintendo,flipper-pi")) {
+ pr_err("unexpected parent compatible\n");
+ goto out;
+ }
+
+ retval = of_address_to_resource(pi, 0, &res);
+ if (retval) {
+ pr_err("no io memory range found\n");
+ goto out;
+ }
+ io_base = ioremap(res.start, resource_size(&res));
+
+ pr_info("controller at 0x%pa mapped to 0x%p\n", &res.start, io_base);
+
+ __flipper_quiesce(io_base);
+
+ irq_domain = irq_domain_add_linear(np, FLIPPER_NR_IRQS,
+ &flipper_irq_domain_ops, io_base);
+ if (!irq_domain) {
+ pr_err("failed to allocate irq_domain\n");
+ return NULL;
+ }
+
+out:
+ return irq_domain;
+}
+
+unsigned int flipper_pic_get_irq(void)
+{
+ void __iomem *io_base = flipper_irq_host->host_data;
+ int irq;
+ u32 irq_status;
+
+ irq_status = in_be32(io_base + FLIPPER_ICR) &
+ in_be32(io_base + FLIPPER_IMR);
+ if (irq_status == 0)
+ return 0; /* no more IRQs pending */
+
+ irq = __ffs(irq_status);
+ return irq_linear_revmap(flipper_irq_host, irq);
+}
+
+/*
+ * Probe function.
+ *
+ */
+
+void __init flipper_pic_probe(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-pic");
+ BUG_ON(!np);
+
+ flipper_irq_host = flipper_pic_init(np);
+ BUG_ON(!flipper_irq_host);
+
+ irq_set_default_host(flipper_irq_host);
+
+ of_node_put(np);
+}
+
+/*
+ * Misc functions related to the flipper chipset.
+ *
+ */
+
+/**
+ * flipper_quiesce() - quiesce flipper irq controller
+ *
+ * Mask and ack all interrupt sources.
+ *
+ */
+void flipper_quiesce(void)
+{
+ void __iomem *io_base = flipper_irq_host->host_data;
+
+ __flipper_quiesce(io_base);
+}
+
+/*
+ * Resets the platform.
+ */
+void flipper_platform_reset(void)
+{
+ void __iomem *io_base;
+
+ if (flipper_irq_host && flipper_irq_host->host_data) {
+ io_base = flipper_irq_host->host_data;
+ out_8(io_base + FLIPPER_RESET, 0x00);
+ }
+}
+
+/*
+ * Returns non-zero if the reset button is pressed.
+ */
+int flipper_is_reset_button_pressed(void)
+{
+ void __iomem *io_base;
+ u32 icr;
+
+ if (flipper_irq_host && flipper_irq_host->host_data) {
+ io_base = flipper_irq_host->host_data;
+ icr = in_be32(io_base + FLIPPER_ICR);
+ return !(icr & FLIPPER_ICR_RSS);
+ }
+ return 0;
+}
+
diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.h b/arch/powerpc/platforms/embedded6xx/flipper-pic.h
new file mode 100644
index 0000000000..024ae70baa
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * arch/powerpc/platforms/embedded6xx/flipper-pic.h
+ *
+ * Nintendo GameCube/Wii "Flipper" interrupt controller support.
+ * Copyright (C) 2004-2009 The GameCube Linux Team
+ * Copyright (C) 2007,2008,2009 Albert Herranz
+ */
+
+#ifndef __FLIPPER_PIC_H
+#define __FLIPPER_PIC_H
+
+unsigned int flipper_pic_get_irq(void);
+void __init flipper_pic_probe(void);
+
+void flipper_quiesce(void);
+void flipper_platform_reset(void);
+int flipper_is_reset_button_pressed(void);
+
+#endif
diff --git a/arch/powerpc/platforms/embedded6xx/gamecube.c b/arch/powerpc/platforms/embedded6xx/gamecube.c
new file mode 100644
index 0000000000..e3b2c74647
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/gamecube.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * arch/powerpc/platforms/embedded6xx/gamecube.c
+ *
+ * Nintendo GameCube board-specific support
+ * Copyright (C) 2004-2009 The GameCube Linux Team
+ * Copyright (C) 2007,2008,2009 Albert Herranz
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kexec.h>
+#include <linux/seq_file.h>
+#include <linux/of_platform.h>
+
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+
+#include "flipper-pic.h"
+#include "usbgecko_udbg.h"
+
+
+static void __noreturn gamecube_spin(void)
+{
+ /* spin until power button pressed */
+ for (;;)
+ cpu_relax();
+}
+
+static void __noreturn gamecube_restart(char *cmd)
+{
+ local_irq_disable();
+ flipper_platform_reset();
+ gamecube_spin();
+}
+
+static void gamecube_power_off(void)
+{
+ local_irq_disable();
+ gamecube_spin();
+}
+
+static void __noreturn gamecube_halt(void)
+{
+ gamecube_restart(NULL);
+}
+
+static int __init gamecube_probe(void)
+{
+ pm_power_off = gamecube_power_off;
+
+ ug_udbg_init();
+
+ return 1;
+}
+
+static void gamecube_shutdown(void)
+{
+ flipper_quiesce();
+}
+
+define_machine(gamecube) {
+ .name = "gamecube",
+ .compatible = "nintendo,gamecube",
+ .probe = gamecube_probe,
+ .restart = gamecube_restart,
+ .halt = gamecube_halt,
+ .init_IRQ = flipper_pic_probe,
+ .get_irq = flipper_pic_get_irq,
+ .progress = udbg_progress,
+ .machine_shutdown = gamecube_shutdown,
+};
+
+
+static const struct of_device_id gamecube_of_bus[] = {
+ { .compatible = "nintendo,flipper", },
+ { },
+};
+
+static int __init gamecube_device_probe(void)
+{
+ of_platform_bus_probe(NULL, gamecube_of_bus, NULL);
+ return 0;
+}
+machine_device_initcall(gamecube, gamecube_device_probe);
+
diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
new file mode 100644
index 0000000000..4d2d92de30
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * arch/powerpc/platforms/embedded6xx/hlwd-pic.c
+ *
+ * Nintendo Wii "Hollywood" interrupt controller support.
+ * Copyright (C) 2009 The GameCube Linux Team
+ * Copyright (C) 2009 Albert Herranz
+ */
+#define DRV_MODULE_NAME "hlwd-pic"
+#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/io.h>
+
+#include "hlwd-pic.h"
+
+#define HLWD_NR_IRQS 32
+
+/*
+ * Each interrupt has a corresponding bit in both
+ * the Interrupt Cause (ICR) and Interrupt Mask (IMR) registers.
+ *
+ * Enabling/disabling an interrupt line involves asserting/clearing
+ * the corresponding bit in IMR. ACK'ing a request simply involves
+ * asserting the corresponding bit in ICR.
+ */
+#define HW_BROADWAY_ICR 0x00
+#define HW_BROADWAY_IMR 0x04
+#define HW_STARLET_ICR 0x08
+#define HW_STARLET_IMR 0x0c
+
+
+/*
+ * IRQ chip hooks.
+ *
+ */
+
+static void hlwd_pic_mask_and_ack(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+ u32 mask = 1 << irq;
+
+ clrbits32(io_base + HW_BROADWAY_IMR, mask);
+ out_be32(io_base + HW_BROADWAY_ICR, mask);
+}
+
+static void hlwd_pic_ack(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+
+ out_be32(io_base + HW_BROADWAY_ICR, 1 << irq);
+}
+
+static void hlwd_pic_mask(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+
+ clrbits32(io_base + HW_BROADWAY_IMR, 1 << irq);
+}
+
+static void hlwd_pic_unmask(struct irq_data *d)
+{
+ int irq = irqd_to_hwirq(d);
+ void __iomem *io_base = irq_data_get_irq_chip_data(d);
+
+ setbits32(io_base + HW_BROADWAY_IMR, 1 << irq);
+
+ /* Make sure the ARM (aka. Starlet) doesn't handle this interrupt. */
+ clrbits32(io_base + HW_STARLET_IMR, 1 << irq);
+}
+
+
+static struct irq_chip hlwd_pic = {
+ .name = "hlwd-pic",
+ .irq_ack = hlwd_pic_ack,
+ .irq_mask_ack = hlwd_pic_mask_and_ack,
+ .irq_mask = hlwd_pic_mask,
+ .irq_unmask = hlwd_pic_unmask,
+};
+
+/*
+ * IRQ host hooks.
+ *
+ */
+
+static struct irq_domain *hlwd_irq_host;
+
+static int hlwd_pic_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_chip_and_handler(virq, &hlwd_pic, handle_level_irq);
+ return 0;
+}
+
+static const struct irq_domain_ops hlwd_irq_domain_ops = {
+ .map = hlwd_pic_map,
+};
+
+static unsigned int __hlwd_pic_get_irq(struct irq_domain *h)
+{
+ void __iomem *io_base = h->host_data;
+ u32 irq_status;
+
+ irq_status = in_be32(io_base + HW_BROADWAY_ICR) &
+ in_be32(io_base + HW_BROADWAY_IMR);
+ if (irq_status == 0)
+ return 0; /* no more IRQs pending */
+
+ return __ffs(irq_status);
+}
+
+static void hlwd_pic_irq_cascade(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_domain *irq_domain = irq_desc_get_handler_data(desc);
+ unsigned int hwirq;
+
+ raw_spin_lock(&desc->lock);
+ chip->irq_mask(&desc->irq_data); /* IRQ_LEVEL */
+ raw_spin_unlock(&desc->lock);
+
+ hwirq = __hlwd_pic_get_irq(irq_domain);
+ if (hwirq)
+ generic_handle_domain_irq(irq_domain, hwirq);
+ else
+ pr_err("spurious interrupt!\n");
+
+ raw_spin_lock(&desc->lock);
+ chip->irq_ack(&desc->irq_data); /* IRQ_LEVEL */
+ if (!irqd_irq_disabled(&desc->irq_data) && chip->irq_unmask)
+ chip->irq_unmask(&desc->irq_data);
+ raw_spin_unlock(&desc->lock);
+}
+
+/*
+ * Platform hooks.
+ *
+ */
+
+static void __hlwd_quiesce(void __iomem *io_base)
+{
+ /* mask and ack all IRQs */
+ out_be32(io_base + HW_BROADWAY_IMR, 0);
+ out_be32(io_base + HW_BROADWAY_ICR, 0xffffffff);
+}
+
+static struct irq_domain *__init hlwd_pic_init(struct device_node *np)
+{
+ struct irq_domain *irq_domain;
+ struct resource res;
+ void __iomem *io_base;
+ int retval;
+
+ retval = of_address_to_resource(np, 0, &res);
+ if (retval) {
+ pr_err("no io memory range found\n");
+ return NULL;
+ }
+ io_base = ioremap(res.start, resource_size(&res));
+ if (!io_base) {
+ pr_err("ioremap failed\n");
+ return NULL;
+ }
+
+ pr_info("controller at 0x%pa mapped to 0x%p\n", &res.start, io_base);
+
+ __hlwd_quiesce(io_base);
+
+ irq_domain = irq_domain_add_linear(np, HLWD_NR_IRQS,
+ &hlwd_irq_domain_ops, io_base);
+ if (!irq_domain) {
+ pr_err("failed to allocate irq_domain\n");
+ iounmap(io_base);
+ return NULL;
+ }
+
+ return irq_domain;
+}
+
+unsigned int hlwd_pic_get_irq(void)
+{
+ unsigned int hwirq = __hlwd_pic_get_irq(hlwd_irq_host);
+ return hwirq ? irq_linear_revmap(hlwd_irq_host, hwirq) : 0;
+}
+
+/*
+ * Probe function.
+ *
+ */
+
+void __init hlwd_pic_probe(void)
+{
+ struct irq_domain *host;
+ struct device_node *np;
+ const u32 *interrupts;
+ int cascade_virq;
+
+ for_each_compatible_node(np, NULL, "nintendo,hollywood-pic") {
+ interrupts = of_get_property(np, "interrupts", NULL);
+ if (interrupts) {
+ host = hlwd_pic_init(np);
+ BUG_ON(!host);
+ cascade_virq = irq_of_parse_and_map(np, 0);
+ irq_set_handler_data(cascade_virq, host);
+ irq_set_chained_handler(cascade_virq,
+ hlwd_pic_irq_cascade);
+ hlwd_irq_host = host;
+ of_node_put(np);
+ break;
+ }
+ }
+}
+
+/**
+ * hlwd_quiesce() - quiesce hollywood irq controller
+ *
+ * Mask and ack all interrupt sources.
+ *
+ */
+void hlwd_quiesce(void)
+{
+ void __iomem *io_base = hlwd_irq_host->host_data;
+
+ __hlwd_quiesce(io_base);
+}
+
diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.h b/arch/powerpc/platforms/embedded6xx/hlwd-pic.h
new file mode 100644
index 0000000000..c2fa42e191
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * arch/powerpc/platforms/embedded6xx/hlwd-pic.h
+ *
+ * Nintendo Wii "Hollywood" interrupt controller support.
+ * Copyright (C) 2009 The GameCube Linux Team
+ * Copyright (C) 2009 Albert Herranz
+ */
+
+#ifndef __HLWD_PIC_H
+#define __HLWD_PIC_H
+
+extern unsigned int hlwd_pic_get_irq(void);
+void __init hlwd_pic_probe(void);
+extern void hlwd_quiesce(void);
+
+#endif
diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c
new file mode 100644
index 0000000000..ce9e58ee97
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/holly.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Board setup routines for the IBM 750GX/CL platform w/ TSI10x bridge
+ *
+ * Copyright 2007 IBM Corporation
+ *
+ * Stephen Winiecki <stevewin@us.ibm.com>
+ * Josh Boyer <jwboyer@linux.vnet.ibm.com>
+ *
+ * Based on code from mpc7448_hpc2.c
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/extable.h>
+
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/udbg.h>
+#include <asm/tsi108.h>
+#include <asm/pci-bridge.h>
+#include <asm/reg.h>
+#include <mm/mmu_decl.h>
+#include <asm/tsi108_irq.h>
+#include <asm/tsi108_pci.h>
+#include <asm/mpic.h>
+
+#undef DEBUG
+
+#define HOLLY_PCI_CFG_PHYS 0x7c000000
+
+static int holly_exclude_device(struct pci_controller *hose, u_char bus,
+ u_char devfn)
+{
+ if (bus == 0 && PCI_SLOT(devfn) == 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static void __init holly_remap_bridge(void)
+{
+ u32 lut_val, lut_addr;
+ int i;
+
+ printk(KERN_INFO "Remapping PCI bridge\n");
+
+ /* Re-init the PCI bridge and LUT registers to have mappings that don't
+ * rely on PIBS
+ */
+ lut_addr = 0x900;
+ for (i = 0; i < 31; i++) {
+ tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000201);
+ lut_addr += 4;
+ tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0);
+ lut_addr += 4;
+ }
+
+ /* Reserve the last LUT entry for PCI I/O space */
+ tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x00000241);
+ lut_addr += 4;
+ tsi108_write_reg(TSI108_PB_OFFSET + lut_addr, 0x0);
+
+ /* Map PCI I/O space */
+ tsi108_write_reg(TSI108_PCI_PFAB_IO_UPPER, 0x0);
+ tsi108_write_reg(TSI108_PCI_PFAB_IO, 0x1);
+
+ /* Map PCI CFG space */
+ tsi108_write_reg(TSI108_PCI_PFAB_BAR0_UPPER, 0x0);
+ tsi108_write_reg(TSI108_PCI_PFAB_BAR0, 0x7c000000 | 0x01);
+
+ /* We don't need MEM32 and PRM remapping so disable them */
+ tsi108_write_reg(TSI108_PCI_PFAB_MEM32, 0x0);
+ tsi108_write_reg(TSI108_PCI_PFAB_PFM3, 0x0);
+ tsi108_write_reg(TSI108_PCI_PFAB_PFM4, 0x0);
+
+ /* Set P2O_BAR0 */
+ tsi108_write_reg(TSI108_PCI_P2O_BAR0_UPPER, 0x0);
+ tsi108_write_reg(TSI108_PCI_P2O_BAR0, 0xc0000000);
+
+ /* Init the PCI LUTs to do no remapping */
+ lut_addr = 0x500;
+ lut_val = 0x00000002;
+
+ for (i = 0; i < 32; i++) {
+ tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, lut_val);
+ lut_addr += 4;
+ tsi108_write_reg(TSI108_PCI_OFFSET + lut_addr, 0x40000000);
+ lut_addr += 4;
+ lut_val += 0x02000000;
+ }
+ tsi108_write_reg(TSI108_PCI_P2O_PAGE_SIZES, 0x00007900);
+
+ /* Set 64-bit PCI bus address for system memory */
+ tsi108_write_reg(TSI108_PCI_P2O_BAR2_UPPER, 0x0);
+ tsi108_write_reg(TSI108_PCI_P2O_BAR2, 0x0);
+}
+
+static void __init holly_init_pci(void)
+{
+ struct device_node *np;
+
+ if (ppc_md.progress)
+ ppc_md.progress("holly_setup_arch():set_bridge", 0);
+
+ /* setup PCI host bridge */
+ holly_remap_bridge();
+
+ np = of_find_node_by_type(NULL, "pci");
+ if (np)
+ tsi108_setup_pci(np, HOLLY_PCI_CFG_PHYS, 1);
+
+ of_node_put(np);
+
+ ppc_md.pci_exclude_device = holly_exclude_device;
+ if (ppc_md.progress)
+ ppc_md.progress("tsi108: resources set", 0x100);
+}
+
+static void __init holly_setup_arch(void)
+{
+ tsi108_csr_vir_base = get_vir_csrbase();
+
+ printk(KERN_INFO "PPC750GX/CL Platform\n");
+}
+
+/*
+ * Interrupt setup and service. Interrupts on the holly come
+ * from the four external INT pins, PCI interrupts are routed via
+ * PCI interrupt control registers, it generates internal IRQ23
+ *
+ * Interrupt routing on the Holly Board:
+ * TSI108:PB_INT[0] -> CPU0:INT#
+ * TSI108:PB_INT[1] -> CPU0:MCP#
+ * TSI108:PB_INT[2] -> N/C
+ * TSI108:PB_INT[3] -> N/C
+ */
+static void __init holly_init_IRQ(void)
+{
+ struct mpic *mpic;
+#ifdef CONFIG_PCI
+ unsigned int cascade_pci_irq;
+ struct device_node *tsi_pci;
+ struct device_node *cascade_node = NULL;
+#endif
+
+ mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN |
+ MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108,
+ 24, 0,
+ "Tsi108_PIC");
+
+ BUG_ON(mpic == NULL);
+
+ mpic_assign_isu(mpic, 0, mpic->paddr + 0x100);
+
+ mpic_init(mpic);
+
+#ifdef CONFIG_PCI
+ tsi_pci = of_find_node_by_type(NULL, "pci");
+ if (tsi_pci == NULL) {
+ printk(KERN_ERR "%s: No tsi108 pci node found !\n", __func__);
+ return;
+ }
+
+ cascade_node = of_find_node_by_type(NULL, "pic-router");
+ if (cascade_node == NULL) {
+ printk(KERN_ERR "%s: No tsi108 pci cascade node found !\n", __func__);
+ return;
+ }
+
+ cascade_pci_irq = irq_of_parse_and_map(tsi_pci, 0);
+ pr_debug("%s: tsi108 cascade_pci_irq = 0x%x\n", __func__, (u32) cascade_pci_irq);
+ tsi108_pci_int_init(cascade_node);
+ irq_set_handler_data(cascade_pci_irq, mpic);
+ irq_set_chained_handler(cascade_pci_irq, tsi108_irq_cascade);
+
+ of_node_put(tsi_pci);
+ of_node_put(cascade_node);
+#endif
+ /* Configure MPIC outputs to CPU0 */
+ tsi108_write_reg(TSI108_MPIC_OFFSET + 0x30c, 0);
+}
+
+static void holly_show_cpuinfo(struct seq_file *m)
+{
+ seq_printf(m, "vendor\t\t: IBM\n");
+ seq_printf(m, "machine\t\t: PPC750 GX/CL\n");
+}
+
+static void __noreturn holly_restart(char *cmd)
+{
+ __be32 __iomem *ocn_bar1 = NULL;
+ unsigned long bar;
+ struct device_node *bridge = NULL;
+ struct resource res;
+ phys_addr_t addr = 0xc0000000;
+
+ local_irq_disable();
+
+ bridge = of_find_node_by_type(NULL, "tsi-bridge");
+ if (bridge) {
+ of_address_to_resource(bridge, 0, &res);
+ addr = res.start;
+ of_node_put(bridge);
+ }
+ addr += (TSI108_PB_OFFSET + 0x414);
+
+ ocn_bar1 = ioremap(addr, 0x4);
+
+ /* Turn on the BOOT bit so the addresses are correctly
+ * routed to the HLP interface */
+ bar = ioread32be(ocn_bar1);
+ bar |= 2;
+ iowrite32be(bar, ocn_bar1);
+ iosync();
+
+ /* Set SRR0 to the reset vector and turn on MSR_IP */
+ mtspr(SPRN_SRR0, 0xfff00100);
+ mtspr(SPRN_SRR1, MSR_IP);
+
+ /* Do an rfi to jump back to firmware. Somewhat evil,
+ * but it works
+ */
+ __asm__ __volatile__("rfi" : : : "memory");
+
+ /* Spin until reset happens. Shouldn't really get here */
+ for (;;) ;
+}
+
+static int ppc750_machine_check_exception(struct pt_regs *regs)
+{
+ const struct exception_table_entry *entry;
+
+ /* Are we prepared to handle this fault */
+ if ((entry = search_exception_tables(regs->nip)) != NULL) {
+ tsi108_clear_pci_cfg_error();
+ regs_set_recoverable(regs);
+ regs_set_return_ip(regs, extable_fixup(entry));
+ return 1;
+ }
+ return 0;
+}
+
+define_machine(holly){
+ .name = "PPC750 GX/CL TSI",
+ .compatible = "ibm,holly",
+ .setup_arch = holly_setup_arch,
+ .discover_phbs = holly_init_pci,
+ .init_IRQ = holly_init_IRQ,
+ .show_cpuinfo = holly_show_cpuinfo,
+ .get_irq = mpic_get_irq,
+ .restart = holly_restart,
+ .machine_check_exception = ppc750_machine_check_exception,
+ .progress = udbg_progress,
+};
diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c
new file mode 100644
index 0000000000..9c10aac40c
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/linkstation.c
@@ -0,0 +1,162 @@
+/*
+ * Board setup routines for the Buffalo Linkstation / Kurobox Platform.
+ *
+ * Copyright (C) 2006 G. Liakhovetski (g.liakhovetski@gmx.de)
+ *
+ * Based on sandpoint.c by Mark A. Greer
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/initrd.h>
+#include <linux/of_platform.h>
+
+#include <asm/time.h>
+#include <asm/mpic.h>
+#include <asm/pci-bridge.h>
+
+#include "mpc10x.h"
+
+static const struct of_device_id of_bus_ids[] __initconst = {
+ { .type = "soc", },
+ { .compatible = "simple-bus", },
+ {},
+};
+
+static int __init declare_of_platform_devices(void)
+{
+ of_platform_bus_probe(NULL, of_bus_ids, NULL);
+ return 0;
+}
+machine_device_initcall(linkstation, declare_of_platform_devices);
+
+static int __init linkstation_add_bridge(struct device_node *dev)
+{
+#ifdef CONFIG_PCI
+ int len;
+ struct pci_controller *hose;
+ const int *bus_range;
+
+ printk("Adding PCI host bridge %pOF\n", dev);
+
+ bus_range = of_get_property(dev, "bus-range", &len);
+ if (bus_range == NULL || len < 2 * sizeof(int))
+ printk(KERN_WARNING "Can't get bus-range for %pOF, assume"
+ " bus 0\n", dev);
+
+ hose = pcibios_alloc_controller(dev);
+ if (hose == NULL)
+ return -ENOMEM;
+ hose->first_busno = bus_range ? bus_range[0] : 0;
+ hose->last_busno = bus_range ? bus_range[1] : 0xff;
+ setup_indirect_pci(hose, 0xfec00000, 0xfee00000, 0);
+
+ /* Interpret the "ranges" property */
+ /* This also maps the I/O region and sets isa_io/mem_base */
+ pci_process_bridge_OF_ranges(hose, dev, 1);
+#endif
+ return 0;
+}
+
+static void __init linkstation_setup_arch(void)
+{
+ printk(KERN_INFO "BUFFALO Network Attached Storage Series\n");
+ printk(KERN_INFO "(C) 2002-2005 BUFFALO INC.\n");
+}
+
+static void __init linkstation_setup_pci(void)
+{
+ struct device_node *np;
+
+ /* Lookup PCI host bridges */
+ for_each_compatible_node(np, "pci", "mpc10x-pci")
+ linkstation_add_bridge(np);
+}
+
+/*
+ * Interrupt setup and service. Interrupts on the linkstation come
+ * from the four PCI slots plus onboard 8241 devices: I2C, DUART.
+ */
+static void __init linkstation_init_IRQ(void)
+{
+ struct mpic *mpic;
+
+ mpic = mpic_alloc(NULL, 0, 0, 4, 0, " EPIC ");
+ BUG_ON(mpic == NULL);
+
+ /* PCI IRQs */
+ mpic_assign_isu(mpic, 0, mpic->paddr + 0x10200);
+
+ /* I2C */
+ mpic_assign_isu(mpic, 1, mpic->paddr + 0x11000);
+
+ /* ttyS0, ttyS1 */
+ mpic_assign_isu(mpic, 2, mpic->paddr + 0x11100);
+
+ mpic_init(mpic);
+}
+
+extern void avr_uart_configure(void);
+extern void avr_uart_send(const char);
+
+static void __noreturn linkstation_restart(char *cmd)
+{
+ local_irq_disable();
+
+ /* Reset system via AVR */
+ avr_uart_configure();
+ /* Send reboot command */
+ avr_uart_send('C');
+
+ for(;;) /* Spin until reset happens */
+ avr_uart_send('G'); /* "kick" */
+}
+
+static void __noreturn linkstation_power_off(void)
+{
+ local_irq_disable();
+
+ /* Power down system via AVR */
+ avr_uart_configure();
+ /* send shutdown command */
+ avr_uart_send('E');
+
+ for(;;) /* Spin until power-off happens */
+ avr_uart_send('G'); /* "kick" */
+ /* NOTREACHED */
+}
+
+static void __noreturn linkstation_halt(void)
+{
+ linkstation_power_off();
+ /* NOTREACHED */
+}
+
+static void linkstation_show_cpuinfo(struct seq_file *m)
+{
+ seq_printf(m, "vendor\t\t: Buffalo Technology\n");
+ seq_printf(m, "machine\t\t: Linkstation I/Kurobox(HG)\n");
+}
+
+static int __init linkstation_probe(void)
+{
+ pm_power_off = linkstation_power_off;
+
+ return 1;
+}
+
+define_machine(linkstation){
+ .name = "Buffalo Linkstation",
+ .compatible = "linkstation",
+ .probe = linkstation_probe,
+ .setup_arch = linkstation_setup_arch,
+ .discover_phbs = linkstation_setup_pci,
+ .init_IRQ = linkstation_init_IRQ,
+ .show_cpuinfo = linkstation_show_cpuinfo,
+ .get_irq = mpic_get_irq,
+ .restart = linkstation_restart,
+ .halt = linkstation_halt,
+};
diff --git a/arch/powerpc/platforms/embedded6xx/ls_uart.c b/arch/powerpc/platforms/embedded6xx/ls_uart.c
new file mode 100644
index 0000000000..6c1dbf8ae7
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/ls_uart.c
@@ -0,0 +1,147 @@
+/*
+ * AVR power-management chip interface for the Buffalo Linkstation /
+ * Kurobox Platform.
+ *
+ * Author: 2006 (c) G. Liakhovetski
+ * g.liakhovetski@gmx.de
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_8250.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <asm/termbits.h>
+
+#include "mpc10x.h"
+
+static void __iomem *avr_addr;
+static unsigned long avr_clock;
+
+static struct work_struct wd_work;
+
+static void wd_stop(struct work_struct *unused)
+{
+ const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK";
+ int i = 0, rescue = 8;
+ int len = strlen(string);
+
+ while (rescue--) {
+ int j;
+ char lsr = in_8(avr_addr + UART_LSR);
+
+ if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) {
+ for (j = 0; j < 16 && i < len; j++, i++)
+ out_8(avr_addr + UART_TX, string[i]);
+ if (i == len) {
+ /* Read "OK" back: 4ms for the last "KKKK"
+ plus a couple bytes back */
+ msleep(7);
+ printk("linkstation: disarming the AVR watchdog: ");
+ while (in_8(avr_addr + UART_LSR) & UART_LSR_DR)
+ printk("%c", in_8(avr_addr + UART_RX));
+ break;
+ }
+ }
+ msleep(17);
+ }
+ printk("\n");
+}
+
+#define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600)
+
+void avr_uart_configure(void)
+{
+ unsigned char cval = UART_LCR_WLEN8;
+ unsigned int quot = AVR_QUOT(avr_clock);
+
+ if (!avr_addr || !avr_clock)
+ return;
+
+ out_8(avr_addr + UART_LCR, cval); /* initialise UART */
+ out_8(avr_addr + UART_MCR, 0);
+ out_8(avr_addr + UART_IER, 0);
+
+ cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR;
+
+ out_8(avr_addr + UART_LCR, cval); /* Set character format */
+
+ out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ out_8(avr_addr + UART_DLL, quot & 0xff); /* LS of divisor */
+ out_8(avr_addr + UART_DLM, quot >> 8); /* MS of divisor */
+ out_8(avr_addr + UART_LCR, cval); /* reset DLAB */
+ out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */
+}
+
+void avr_uart_send(const char c)
+{
+ if (!avr_addr || !avr_clock)
+ return;
+
+ out_8(avr_addr + UART_TX, c);
+ out_8(avr_addr + UART_TX, c);
+ out_8(avr_addr + UART_TX, c);
+ out_8(avr_addr + UART_TX, c);
+}
+
+static void __init ls_uart_init(void)
+{
+ local_irq_disable();
+
+#ifndef CONFIG_SERIAL_8250
+ out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */
+ out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* clear FIFOs */
+ out_8(avr_addr + UART_FCR, 0);
+ out_8(avr_addr + UART_IER, 0);
+
+ /* Clear up interrupts */
+ (void) in_8(avr_addr + UART_LSR);
+ (void) in_8(avr_addr + UART_RX);
+ (void) in_8(avr_addr + UART_IIR);
+ (void) in_8(avr_addr + UART_MSR);
+#endif
+ avr_uart_configure();
+
+ local_irq_enable();
+}
+
+static int __init ls_uarts_init(void)
+{
+ struct device_node *avr;
+ struct resource res;
+ int len, ret;
+
+ avr = of_find_node_by_path("/soc10x/serial@80004500");
+ if (!avr)
+ return -EINVAL;
+
+ avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len);
+ if (!avr_clock)
+ return -EINVAL;
+
+ ret = of_address_to_resource(avr, 0, &res);
+ if (ret)
+ return ret;
+
+ of_node_put(avr);
+
+ avr_addr = ioremap(res.start, 32);
+ if (!avr_addr)
+ return -EFAULT;
+
+ ls_uart_init();
+
+ INIT_WORK(&wd_work, wd_stop);
+ schedule_work(&wd_work);
+
+ return 0;
+}
+
+machine_late_initcall(linkstation, ls_uarts_init);
diff --git a/arch/powerpc/platforms/embedded6xx/mpc10x.h b/arch/powerpc/platforms/embedded6xx/mpc10x.h
new file mode 100644
index 0000000000..5ad12023e5
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/mpc10x.h
@@ -0,0 +1,159 @@
+/*
+ * Common routines for the Motorola SPS MPC106/8240/107 Host bridge/Mem
+ * ctlr/EPIC/etc.
+ *
+ * Author: Mark A. Greer
+ * mgreer@mvista.com
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef __PPC_KERNEL_MPC10X_H
+#define __PPC_KERNEL_MPC10X_H
+
+#include <linux/pci_ids.h>
+#include <asm/pci-bridge.h>
+
+/*
+ * The values here don't completely map everything but should work in most
+ * cases.
+ *
+ * MAP A (PReP Map)
+ * Processor: 0x80000000 - 0x807fffff -> PCI I/O: 0x00000000 - 0x007fffff
+ * Processor: 0xc0000000 - 0xdfffffff -> PCI MEM: 0x00000000 - 0x1fffffff
+ * PCI MEM: 0x80000000 -> Processor System Memory: 0x00000000
+ *
+ * MAP B (CHRP Map)
+ * Processor: 0xfe000000 - 0xfebfffff -> PCI I/O: 0x00000000 - 0x00bfffff
+ * Processor: 0x80000000 - 0xbfffffff -> PCI MEM: 0x80000000 - 0xbfffffff
+ * PCI MEM: 0x00000000 -> Processor System Memory: 0x00000000
+ */
+
+/*
+ * Define the vendor/device IDs for the various bridges--should be added to
+ * <linux/pci_ids.h>
+ */
+#define MPC10X_BRIDGE_106 ((PCI_DEVICE_ID_MOTOROLA_MPC106 << 16) | \
+ PCI_VENDOR_ID_MOTOROLA)
+#define MPC10X_BRIDGE_8240 ((0x0003 << 16) | PCI_VENDOR_ID_MOTOROLA)
+#define MPC10X_BRIDGE_107 ((0x0004 << 16) | PCI_VENDOR_ID_MOTOROLA)
+#define MPC10X_BRIDGE_8245 ((0x0006 << 16) | PCI_VENDOR_ID_MOTOROLA)
+
+/* Define the type of map to use */
+#define MPC10X_MEM_MAP_A 1
+#define MPC10X_MEM_MAP_B 2
+
+/* Map A (PReP Map) Defines */
+#define MPC10X_MAPA_CNFG_ADDR 0x80000cf8
+#define MPC10X_MAPA_CNFG_DATA 0x80000cfc
+
+#define MPC10X_MAPA_ISA_IO_BASE 0x80000000
+#define MPC10X_MAPA_ISA_MEM_BASE 0xc0000000
+#define MPC10X_MAPA_DRAM_OFFSET 0x80000000
+
+#define MPC10X_MAPA_PCI_INTACK_ADDR 0xbffffff0
+#define MPC10X_MAPA_PCI_IO_START 0x00000000
+#define MPC10X_MAPA_PCI_IO_END (0x00800000 - 1)
+#define MPC10X_MAPA_PCI_MEM_START 0x00000000
+#define MPC10X_MAPA_PCI_MEM_END (0x20000000 - 1)
+
+#define MPC10X_MAPA_PCI_MEM_OFFSET (MPC10X_MAPA_ISA_MEM_BASE - \
+ MPC10X_MAPA_PCI_MEM_START)
+
+/* Map B (CHRP Map) Defines */
+#define MPC10X_MAPB_CNFG_ADDR 0xfec00000
+#define MPC10X_MAPB_CNFG_DATA 0xfee00000
+
+#define MPC10X_MAPB_ISA_IO_BASE 0xfe000000
+#define MPC10X_MAPB_ISA_MEM_BASE 0x80000000
+#define MPC10X_MAPB_DRAM_OFFSET 0x00000000
+
+#define MPC10X_MAPB_PCI_INTACK_ADDR 0xfef00000
+#define MPC10X_MAPB_PCI_IO_START 0x00000000
+#define MPC10X_MAPB_PCI_IO_END (0x00c00000 - 1)
+#define MPC10X_MAPB_PCI_MEM_START 0x80000000
+#define MPC10X_MAPB_PCI_MEM_END (0xc0000000 - 1)
+
+#define MPC10X_MAPB_PCI_MEM_OFFSET (MPC10X_MAPB_ISA_MEM_BASE - \
+ MPC10X_MAPB_PCI_MEM_START)
+
+/* Miscellaneous Configuration register offsets */
+#define MPC10X_CFG_PIR_REG 0x09
+#define MPC10X_CFG_PIR_HOST_BRIDGE 0x00
+#define MPC10X_CFG_PIR_AGENT 0x01
+
+#define MPC10X_CFG_EUMBBAR 0x78
+
+#define MPC10X_CFG_PICR1_REG 0xa8
+#define MPC10X_CFG_PICR1_ADDR_MAP_MASK 0x00010000
+#define MPC10X_CFG_PICR1_ADDR_MAP_A 0x00010000
+#define MPC10X_CFG_PICR1_ADDR_MAP_B 0x00000000
+#define MPC10X_CFG_PICR1_SPEC_PCI_RD 0x00000004
+#define MPC10X_CFG_PICR1_ST_GATH_EN 0x00000040
+
+#define MPC10X_CFG_PICR2_REG 0xac
+#define MPC10X_CFG_PICR2_COPYBACK_OPT 0x00000001
+
+#define MPC10X_CFG_MAPB_OPTIONS_REG 0xe0
+#define MPC10X_CFG_MAPB_OPTIONS_CFAE 0x80 /* CPU_FD_ALIAS_EN */
+#define MPC10X_CFG_MAPB_OPTIONS_PFAE 0x40 /* PCI_FD_ALIAS_EN */
+#define MPC10X_CFG_MAPB_OPTIONS_DR 0x20 /* DLL_RESET */
+#define MPC10X_CFG_MAPB_OPTIONS_PCICH 0x08 /* PCI_COMPATIBILITY_HOLE */
+#define MPC10X_CFG_MAPB_OPTIONS_PROCCH 0x04 /* PROC_COMPATIBILITY_HOLE */
+
+/* Define offsets for the memory controller registers in the config space */
+#define MPC10X_MCTLR_MEM_START_1 0x80 /* Banks 0-3 */
+#define MPC10X_MCTLR_MEM_START_2 0x84 /* Banks 4-7 */
+#define MPC10X_MCTLR_EXT_MEM_START_1 0x88 /* Banks 0-3 */
+#define MPC10X_MCTLR_EXT_MEM_START_2 0x8c /* Banks 4-7 */
+
+#define MPC10X_MCTLR_MEM_END_1 0x90 /* Banks 0-3 */
+#define MPC10X_MCTLR_MEM_END_2 0x94 /* Banks 4-7 */
+#define MPC10X_MCTLR_EXT_MEM_END_1 0x98 /* Banks 0-3 */
+#define MPC10X_MCTLR_EXT_MEM_END_2 0x9c /* Banks 4-7 */
+
+#define MPC10X_MCTLR_MEM_BANK_ENABLES 0xa0
+
+/* Define some offset in the EUMB */
+#define MPC10X_EUMB_SIZE 0x00100000 /* Total EUMB size (1MB) */
+
+#define MPC10X_EUMB_MU_OFFSET 0x00000000 /* Msg Unit reg offset */
+#define MPC10X_EUMB_MU_SIZE 0x00001000 /* Msg Unit reg size */
+#define MPC10X_EUMB_DMA_OFFSET 0x00001000 /* DMA Unit reg offset */
+#define MPC10X_EUMB_DMA_SIZE 0x00001000 /* DMA Unit reg size */
+#define MPC10X_EUMB_ATU_OFFSET 0x00002000 /* Addr xlate reg offset */
+#define MPC10X_EUMB_ATU_SIZE 0x00001000 /* Addr xlate reg size */
+#define MPC10X_EUMB_I2C_OFFSET 0x00003000 /* I2C Unit reg offset */
+#define MPC10X_EUMB_I2C_SIZE 0x00001000 /* I2C Unit reg size */
+#define MPC10X_EUMB_DUART_OFFSET 0x00004000 /* DUART Unit reg offset (8245) */
+#define MPC10X_EUMB_DUART_SIZE 0x00001000 /* DUART Unit reg size (8245) */
+#define MPC10X_EUMB_EPIC_OFFSET 0x00040000 /* EPIC offset in EUMB */
+#define MPC10X_EUMB_EPIC_SIZE 0x00030000 /* EPIC size */
+#define MPC10X_EUMB_PM_OFFSET 0x000fe000 /* Performance Monitor reg offset (8245) */
+#define MPC10X_EUMB_PM_SIZE 0x00001000 /* Performance Monitor reg size (8245) */
+#define MPC10X_EUMB_WP_OFFSET 0x000ff000 /* Data path diagnostic, watchpoint reg offset */
+#define MPC10X_EUMB_WP_SIZE 0x00001000 /* Data path diagnostic, watchpoint reg size */
+
+enum ppc_sys_devices {
+ MPC10X_IIC1,
+ MPC10X_DMA0,
+ MPC10X_DMA1,
+ MPC10X_UART0,
+ MPC10X_UART1,
+ NUM_PPC_SYS_DEVS,
+};
+
+int mpc10x_bridge_init(struct pci_controller *hose,
+ uint current_map,
+ uint new_map,
+ uint phys_eumb_base);
+unsigned long mpc10x_get_mem_size(uint mem_map);
+int mpc10x_enable_store_gathering(struct pci_controller *hose);
+int mpc10x_disable_store_gathering(struct pci_controller *hose);
+
+/* For MPC107 boards that use the built-in openpic */
+void mpc10x_set_openpic(void);
+
+#endif /* __PPC_KERNEL_MPC10X_H */
diff --git a/arch/powerpc/platforms/embedded6xx/mvme5100.c b/arch/powerpc/platforms/embedded6xx/mvme5100.c
new file mode 100644
index 0000000000..00bec0f051
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/mvme5100.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Board setup routines for the Motorola/Emerson MVME5100.
+ *
+ * Copyright 2013 CSC Australia Pty. Ltd.
+ *
+ * Based on earlier code by:
+ *
+ * Matt Porter, MontaVista Software Inc.
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * Author: Stephen Chivers <schivers@csc.com>
+ */
+
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <asm/i8259.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpic.h>
+#include <mm/mmu_decl.h>
+#include <asm/udbg.h>
+
+#define HAWK_MPIC_SIZE 0x00040000U
+#define MVME5100_PCI_MEM_OFFSET 0x00000000
+
+/* Board register addresses. */
+#define BOARD_STATUS_REG 0xfef88080
+#define BOARD_MODFAIL_REG 0xfef88090
+#define BOARD_MODRST_REG 0xfef880a0
+#define BOARD_TBEN_REG 0xfef880c0
+#define BOARD_SW_READ_REG 0xfef880e0
+#define BOARD_GEO_ADDR_REG 0xfef880e8
+#define BOARD_EXT_FEATURE1_REG 0xfef880f0
+#define BOARD_EXT_FEATURE2_REG 0xfef88100
+
+static phys_addr_t pci_membase;
+static u_char *restart;
+
+static void mvme5100_8259_cascade(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int cascade_irq = i8259_irq();
+
+ if (cascade_irq)
+ generic_handle_irq(cascade_irq);
+
+ chip->irq_eoi(&desc->irq_data);
+}
+
+static void __init mvme5100_pic_init(void)
+{
+ struct mpic *mpic;
+ struct device_node *np;
+ struct device_node *cp = NULL;
+ unsigned int cirq;
+ unsigned long intack = 0;
+ const u32 *prop = NULL;
+
+ np = of_find_node_by_type(NULL, "open-pic");
+ if (!np) {
+ pr_err("Could not find open-pic node\n");
+ return;
+ }
+
+ mpic = mpic_alloc(np, pci_membase, 0, 16, 256, " OpenPIC ");
+
+ BUG_ON(mpic == NULL);
+ of_node_put(np);
+
+ mpic_assign_isu(mpic, 0, pci_membase + 0x10000);
+
+ mpic_init(mpic);
+
+ cp = of_find_compatible_node(NULL, NULL, "chrp,iic");
+ if (cp == NULL) {
+ pr_warn("mvme5100_pic_init: couldn't find i8259\n");
+ return;
+ }
+
+ cirq = irq_of_parse_and_map(cp, 0);
+ if (!cirq) {
+ pr_warn("mvme5100_pic_init: no cascade interrupt?\n");
+ return;
+ }
+
+ np = of_find_compatible_node(NULL, "pci", "mpc10x-pci");
+ if (np) {
+ prop = of_get_property(np, "8259-interrupt-acknowledge", NULL);
+
+ if (prop)
+ intack = prop[0];
+
+ of_node_put(np);
+ }
+
+ if (intack)
+ pr_debug("mvme5100_pic_init: PCI 8259 intack at 0x%016lx\n",
+ intack);
+
+ i8259_init(cp, intack);
+ of_node_put(cp);
+ irq_set_chained_handler(cirq, mvme5100_8259_cascade);
+}
+
+static int __init mvme5100_add_bridge(struct device_node *dev)
+{
+ const int *bus_range;
+ int len;
+ struct pci_controller *hose;
+ unsigned short devid;
+
+ pr_info("Adding PCI host bridge %pOF\n", dev);
+
+ bus_range = of_get_property(dev, "bus-range", &len);
+
+ hose = pcibios_alloc_controller(dev);
+ if (hose == NULL)
+ return -ENOMEM;
+
+ hose->first_busno = bus_range ? bus_range[0] : 0;
+ hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+ setup_indirect_pci(hose, 0xfe000cf8, 0xfe000cfc, 0);
+
+ pci_process_bridge_OF_ranges(hose, dev, 1);
+
+ early_read_config_word(hose, 0, 0, PCI_DEVICE_ID, &devid);
+
+ if (devid != PCI_DEVICE_ID_MOTOROLA_HAWK) {
+ pr_err("HAWK PHB not present?\n");
+ return 0;
+ }
+
+ early_read_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);
+
+ if (pci_membase == 0) {
+ pr_err("HAWK PHB mibar not correctly set?\n");
+ return 0;
+ }
+
+ pr_info("mvme5100_pic_init: pci_membase: %x\n", pci_membase);
+
+ return 0;
+}
+
+static const struct of_device_id mvme5100_of_bus_ids[] __initconst = {
+ { .compatible = "hawk-bridge", },
+ {},
+};
+
+/*
+ * Setup the architecture
+ */
+static void __init mvme5100_setup_arch(void)
+{
+ if (ppc_md.progress)
+ ppc_md.progress("mvme5100_setup_arch()", 0);
+
+ restart = ioremap(BOARD_MODRST_REG, 4);
+}
+
+static void __init mvme5100_setup_pci(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, "pci", "hawk-pci")
+ mvme5100_add_bridge(np);
+}
+
+static void mvme5100_show_cpuinfo(struct seq_file *m)
+{
+ seq_puts(m, "Vendor\t\t: Motorola/Emerson\n");
+ seq_puts(m, "Machine\t\t: MVME5100\n");
+}
+
+static void __noreturn mvme5100_restart(char *cmd)
+{
+
+ local_irq_disable();
+ mtmsr(mfmsr() | MSR_IP);
+
+ out_8((u_char *) restart, 0x01);
+
+ while (1)
+ ;
+}
+
+static int __init probe_of_platform_devices(void)
+{
+
+ of_platform_bus_probe(NULL, mvme5100_of_bus_ids, NULL);
+ return 0;
+}
+
+machine_device_initcall(mvme5100, probe_of_platform_devices);
+
+define_machine(mvme5100) {
+ .name = "MVME5100",
+ .compatible = "MVME5100",
+ .setup_arch = mvme5100_setup_arch,
+ .discover_phbs = mvme5100_setup_pci,
+ .init_IRQ = mvme5100_pic_init,
+ .show_cpuinfo = mvme5100_show_cpuinfo,
+ .get_irq = mpic_get_irq,
+ .restart = mvme5100_restart,
+ .progress = udbg_progress,
+};
diff --git a/arch/powerpc/platforms/embedded6xx/storcenter.c b/arch/powerpc/platforms/embedded6xx/storcenter.c
new file mode 100644
index 0000000000..e49880e8da
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/storcenter.c
@@ -0,0 +1,121 @@
+/*
+ * Board setup routines for the storcenter
+ *
+ * Copyright 2007 (C) Oyvind Repvik (nail@nslu2-linux.org)
+ * Copyright 2007 Andy Wilcox, Jon Loeliger
+ *
+ * Based on linkstation.c by G. Liakhovetski
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/initrd.h>
+#include <linux/of_platform.h>
+
+#include <asm/time.h>
+#include <asm/mpic.h>
+#include <asm/pci-bridge.h>
+
+#include "mpc10x.h"
+
+
+static const struct of_device_id storcenter_of_bus[] __initconst = {
+ { .name = "soc", },
+ {},
+};
+
+static int __init storcenter_device_probe(void)
+{
+ of_platform_bus_probe(NULL, storcenter_of_bus, NULL);
+ return 0;
+}
+machine_device_initcall(storcenter, storcenter_device_probe);
+
+
+static int __init storcenter_add_bridge(struct device_node *dev)
+{
+#ifdef CONFIG_PCI
+ int len;
+ struct pci_controller *hose;
+ const int *bus_range;
+
+ printk("Adding PCI host bridge %pOF\n", dev);
+
+ hose = pcibios_alloc_controller(dev);
+ if (hose == NULL)
+ return -ENOMEM;
+
+ bus_range = of_get_property(dev, "bus-range", &len);
+ hose->first_busno = bus_range ? bus_range[0] : 0;
+ hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+ setup_indirect_pci(hose, MPC10X_MAPB_CNFG_ADDR, MPC10X_MAPB_CNFG_DATA, 0);
+
+ /* Interpret the "ranges" property */
+ /* This also maps the I/O region and sets isa_io/mem_base */
+ pci_process_bridge_OF_ranges(hose, dev, 1);
+#endif
+
+ return 0;
+}
+
+static void __init storcenter_setup_arch(void)
+{
+ printk(KERN_INFO "IOMEGA StorCenter\n");
+}
+
+static void __init storcenter_setup_pci(void)
+{
+ struct device_node *np;
+
+ /* Lookup PCI host bridges */
+ for_each_compatible_node(np, "pci", "mpc10x-pci")
+ storcenter_add_bridge(np);
+}
+
+/*
+ * Interrupt setup and service. Interrupts on the turbostation come
+ * from the four PCI slots plus onboard 8241 devices: I2C, DUART.
+ */
+static void __init storcenter_init_IRQ(void)
+{
+ struct mpic *mpic;
+
+ mpic = mpic_alloc(NULL, 0, 0, 16, 0, " OpenPIC ");
+ BUG_ON(mpic == NULL);
+
+ /*
+ * 16 Serial Interrupts followed by 16 Internal Interrupts.
+ * I2C is the second internal, so it is at 17, 0x11020.
+ */
+ mpic_assign_isu(mpic, 0, mpic->paddr + 0x10200);
+ mpic_assign_isu(mpic, 1, mpic->paddr + 0x11000);
+
+ mpic_init(mpic);
+}
+
+static void __noreturn storcenter_restart(char *cmd)
+{
+ local_irq_disable();
+
+ /* Set exception prefix high - to the firmware */
+ mtmsr(mfmsr() | MSR_IP);
+ isync();
+
+ /* Wait for reset to happen */
+ for (;;) ;
+}
+
+define_machine(storcenter){
+ .name = "IOMEGA StorCenter",
+ .compatible = "iomega,storcenter",
+ .setup_arch = storcenter_setup_arch,
+ .discover_phbs = storcenter_setup_pci,
+ .init_IRQ = storcenter_init_IRQ,
+ .get_irq = mpic_get_irq,
+ .restart = storcenter_restart,
+};
diff --git a/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c
new file mode 100644
index 0000000000..221577f32b
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c
+ *
+ * udbg serial input/output routines for the USB Gecko adapter.
+ * Copyright (C) 2008-2009 The GameCube Linux Team
+ * Copyright (C) 2008,2009 Albert Herranz
+ */
+
+#include <linux/of_address.h>
+
+#include <mm/mmu_decl.h>
+
+#include <asm/io.h>
+#include <asm/udbg.h>
+#include <asm/fixmap.h>
+
+#include "usbgecko_udbg.h"
+
+
+#define EXI_CLK_32MHZ 5
+
+#define EXI_CSR 0x00
+#define EXI_CSR_CLKMASK (0x7<<4)
+#define EXI_CSR_CLK_32MHZ (EXI_CLK_32MHZ<<4)
+#define EXI_CSR_CSMASK (0x7<<7)
+#define EXI_CSR_CS_0 (0x1<<7) /* Chip Select 001 */
+
+#define EXI_CR 0x0c
+#define EXI_CR_TSTART (1<<0)
+#define EXI_CR_WRITE (1<<2)
+#define EXI_CR_READ_WRITE (2<<2)
+#define EXI_CR_TLEN(len) (((len)-1)<<4)
+
+#define EXI_DATA 0x10
+
+#define UG_READ_ATTEMPTS 100
+#define UG_WRITE_ATTEMPTS 100
+
+
+static void __iomem *ug_io_base;
+
+/*
+ * Performs one input/output transaction between the exi host and the usbgecko.
+ */
+static u32 ug_io_transaction(u32 in)
+{
+ u32 __iomem *csr_reg = ug_io_base + EXI_CSR;
+ u32 __iomem *data_reg = ug_io_base + EXI_DATA;
+ u32 __iomem *cr_reg = ug_io_base + EXI_CR;
+ u32 csr, data, cr;
+
+ /* select */
+ csr = EXI_CSR_CLK_32MHZ | EXI_CSR_CS_0;
+ out_be32(csr_reg, csr);
+
+ /* read/write */
+ data = in;
+ out_be32(data_reg, data);
+ cr = EXI_CR_TLEN(2) | EXI_CR_READ_WRITE | EXI_CR_TSTART;
+ out_be32(cr_reg, cr);
+
+ while (in_be32(cr_reg) & EXI_CR_TSTART)
+ barrier();
+
+ /* deselect */
+ out_be32(csr_reg, 0);
+
+ /* result */
+ data = in_be32(data_reg);
+
+ return data;
+}
+
+/*
+ * Returns true if an usbgecko adapter is found.
+ */
+static int ug_is_adapter_present(void)
+{
+ if (!ug_io_base)
+ return 0;
+
+ return ug_io_transaction(0x90000000) == 0x04700000;
+}
+
+/*
+ * Returns true if the TX fifo is ready for transmission.
+ */
+static int ug_is_txfifo_ready(void)
+{
+ return ug_io_transaction(0xc0000000) & 0x04000000;
+}
+
+/*
+ * Tries to transmit a character.
+ * If the TX fifo is not ready the result is undefined.
+ */
+static void ug_raw_putc(char ch)
+{
+ ug_io_transaction(0xb0000000 | (ch << 20));
+}
+
+/*
+ * Transmits a character.
+ * It silently fails if the TX fifo is not ready after a number of retries.
+ */
+static void ug_putc(char ch)
+{
+ int count = UG_WRITE_ATTEMPTS;
+
+ if (!ug_io_base)
+ return;
+
+ if (ch == '\n')
+ ug_putc('\r');
+
+ while (!ug_is_txfifo_ready() && count--)
+ barrier();
+ if (count >= 0)
+ ug_raw_putc(ch);
+}
+
+/*
+ * Returns true if the RX fifo is ready for transmission.
+ */
+static int ug_is_rxfifo_ready(void)
+{
+ return ug_io_transaction(0xd0000000) & 0x04000000;
+}
+
+/*
+ * Tries to receive a character.
+ * If a character is unavailable the function returns -1.
+ */
+static int ug_raw_getc(void)
+{
+ u32 data = ug_io_transaction(0xa0000000);
+ if (data & 0x08000000)
+ return (data >> 16) & 0xff;
+ else
+ return -1;
+}
+
+/*
+ * Receives a character.
+ * It fails if the RX fifo is not ready after a number of retries.
+ */
+static int ug_getc(void)
+{
+ int count = UG_READ_ATTEMPTS;
+
+ if (!ug_io_base)
+ return -1;
+
+ while (!ug_is_rxfifo_ready() && count--)
+ barrier();
+ return ug_raw_getc();
+}
+
+/*
+ * udbg functions.
+ *
+ */
+
+/*
+ * Transmits a character.
+ */
+static void ug_udbg_putc(char ch)
+{
+ ug_putc(ch);
+}
+
+/*
+ * Receives a character. Waits until a character is available.
+ */
+static int ug_udbg_getc(void)
+{
+ int ch;
+
+ while ((ch = ug_getc()) == -1)
+ barrier();
+ return ch;
+}
+
+/*
+ * Receives a character. If a character is not available, returns -1.
+ */
+static int ug_udbg_getc_poll(void)
+{
+ if (!ug_is_rxfifo_ready())
+ return -1;
+ return ug_getc();
+}
+
+/*
+ * Checks if a USB Gecko adapter is inserted in any memory card slot.
+ */
+static void __iomem *__init ug_udbg_probe(void __iomem *exi_io_base)
+{
+ int i;
+
+ /* look for a usbgecko on memcard slots A and B */
+ for (i = 0; i < 2; i++) {
+ ug_io_base = exi_io_base + 0x14 * i;
+ if (ug_is_adapter_present())
+ break;
+ }
+ if (i == 2)
+ ug_io_base = NULL;
+ return ug_io_base;
+
+}
+
+/*
+ * USB Gecko udbg support initialization.
+ */
+void __init ug_udbg_init(void)
+{
+ struct device_node *np;
+ void __iomem *exi_io_base;
+
+ if (ug_io_base)
+ udbg_printf("%s: early -> final\n", __func__);
+
+ np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-exi");
+ if (!np) {
+ udbg_printf("%s: EXI node not found\n", __func__);
+ goto out;
+ }
+
+ exi_io_base = of_iomap(np, 0);
+ if (!exi_io_base) {
+ udbg_printf("%s: failed to setup EXI io base\n", __func__);
+ goto done;
+ }
+
+ if (!ug_udbg_probe(exi_io_base)) {
+ udbg_printf("usbgecko_udbg: not found\n");
+ iounmap(exi_io_base);
+ } else {
+ udbg_putc = ug_udbg_putc;
+ udbg_getc = ug_udbg_getc;
+ udbg_getc_poll = ug_udbg_getc_poll;
+ udbg_printf("usbgecko_udbg: ready\n");
+ }
+
+done:
+ of_node_put(np);
+out:
+ return;
+}
+
+#ifdef CONFIG_PPC_EARLY_DEBUG_USBGECKO
+
+static phys_addr_t __init ug_early_grab_io_addr(void)
+{
+#if defined(CONFIG_GAMECUBE)
+ return 0x0c000000;
+#elif defined(CONFIG_WII)
+ return 0x0d000000;
+#else
+#error Invalid platform for USB Gecko based early debugging.
+#endif
+}
+
+/*
+ * USB Gecko early debug support initialization for udbg.
+ */
+void __init udbg_init_usbgecko(void)
+{
+ void __iomem *early_debug_area;
+ void __iomem *exi_io_base;
+
+ /*
+ * At this point we have a BAT already setup that enables I/O
+ * to the EXI hardware.
+ *
+ * The BAT uses a virtual address range reserved at the fixmap.
+ * This must match the virtual address configured in
+ * head_32.S:setup_usbgecko_bat().
+ */
+ early_debug_area = (void __iomem *)__fix_to_virt(FIX_EARLY_DEBUG_BASE);
+ exi_io_base = early_debug_area + 0x00006800;
+
+ /* try to detect a USB Gecko */
+ if (!ug_udbg_probe(exi_io_base))
+ return;
+
+ /* we found a USB Gecko, load udbg hooks */
+ udbg_putc = ug_udbg_putc;
+ udbg_getc = ug_udbg_getc;
+ udbg_getc_poll = ug_udbg_getc_poll;
+
+ /*
+ * Prepare again the same BAT for MMU_init.
+ * This allows udbg I/O to continue working after the MMU is
+ * turned on for real.
+ * It is safe to continue using the same virtual address as it is
+ * a reserved fixmap area.
+ */
+ setbat(1, (unsigned long)early_debug_area,
+ ug_early_grab_io_addr(), 128*1024, PAGE_KERNEL_NCG);
+}
+
+#endif /* CONFIG_PPC_EARLY_DEBUG_USBGECKO */
+
diff --git a/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h
new file mode 100644
index 0000000000..bceb11911e
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h
+ *
+ * udbg serial input/output routines for the USB Gecko adapter.
+ * Copyright (C) 2008-2009 The GameCube Linux Team
+ * Copyright (C) 2008,2009 Albert Herranz
+ */
+
+#ifndef __USBGECKO_UDBG_H
+#define __USBGECKO_UDBG_H
+
+#ifdef CONFIG_USBGECKO_UDBG
+
+extern void __init ug_udbg_init(void);
+
+#else
+
+static inline void __init ug_udbg_init(void)
+{
+}
+
+#endif /* CONFIG_USBGECKO_UDBG */
+
+void __init udbg_init_usbgecko(void);
+
+#endif /* __USBGECKO_UDBG_H */
diff --git a/arch/powerpc/platforms/embedded6xx/wii.c b/arch/powerpc/platforms/embedded6xx/wii.c
new file mode 100644
index 0000000000..cb3be6d6e3
--- /dev/null
+++ b/arch/powerpc/platforms/embedded6xx/wii.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * arch/powerpc/platforms/embedded6xx/wii.c
+ *
+ * Nintendo Wii board-specific support
+ * Copyright (C) 2008-2009 The GameCube Linux Team
+ * Copyright (C) 2008,2009 Albert Herranz
+ */
+#define DRV_MODULE_NAME "wii"
+#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+
+#include "flipper-pic.h"
+#include "hlwd-pic.h"
+#include "usbgecko_udbg.h"
+
+/* control block */
+#define HW_CTRL_COMPATIBLE "nintendo,hollywood-control"
+
+#define HW_CTRL_RESETS 0x94
+#define HW_CTRL_RESETS_SYS (1<<0)
+
+/* gpio */
+#define HW_GPIO_COMPATIBLE "nintendo,hollywood-gpio"
+
+#define HW_GPIO_BASE(idx) (idx * 0x20)
+#define HW_GPIO_OUT(idx) (HW_GPIO_BASE(idx) + 0)
+#define HW_GPIO_DIR(idx) (HW_GPIO_BASE(idx) + 4)
+#define HW_GPIO_OWNER (HW_GPIO_BASE(1) + 0x1c)
+
+#define HW_GPIO_SHUTDOWN (1<<1)
+#define HW_GPIO_SLOT_LED (1<<5)
+#define HW_GPIO_SENSOR_BAR (1<<8)
+
+
+static void __iomem *hw_ctrl;
+static void __iomem *hw_gpio;
+
+static void __noreturn wii_spin(void)
+{
+ local_irq_disable();
+ for (;;)
+ cpu_relax();
+}
+
+static void __iomem *__init wii_ioremap_hw_regs(char *name, char *compatible)
+{
+ void __iomem *hw_regs = NULL;
+ struct device_node *np;
+ struct resource res;
+ int error = -ENODEV;
+
+ np = of_find_compatible_node(NULL, NULL, compatible);
+ if (!np) {
+ pr_err("no compatible node found for %s\n", compatible);
+ goto out;
+ }
+ error = of_address_to_resource(np, 0, &res);
+ if (error) {
+ pr_err("no valid reg found for %pOFn\n", np);
+ goto out_put;
+ }
+
+ hw_regs = ioremap(res.start, resource_size(&res));
+ if (hw_regs) {
+ pr_info("%s at 0x%pa mapped to 0x%p\n", name,
+ &res.start, hw_regs);
+ }
+
+out_put:
+ of_node_put(np);
+out:
+ return hw_regs;
+}
+
+static void __init wii_setup_arch(void)
+{
+ hw_ctrl = wii_ioremap_hw_regs("hw_ctrl", HW_CTRL_COMPATIBLE);
+ hw_gpio = wii_ioremap_hw_regs("hw_gpio", HW_GPIO_COMPATIBLE);
+ if (hw_gpio) {
+ /* turn off the front blue led and IR light */
+ clrbits32(hw_gpio + HW_GPIO_OUT(0),
+ HW_GPIO_SLOT_LED | HW_GPIO_SENSOR_BAR);
+ }
+}
+
+static void __noreturn wii_restart(char *cmd)
+{
+ local_irq_disable();
+
+ if (hw_ctrl) {
+ /* clear the system reset pin to cause a reset */
+ clrbits32(hw_ctrl + HW_CTRL_RESETS, HW_CTRL_RESETS_SYS);
+ }
+ wii_spin();
+}
+
+static void wii_power_off(void)
+{
+ local_irq_disable();
+
+ if (hw_gpio) {
+ /*
+ * set the owner of the shutdown pin to ARM, because it is
+ * accessed through the registers for the ARM, below
+ */
+ clrbits32(hw_gpio + HW_GPIO_OWNER, HW_GPIO_SHUTDOWN);
+
+ /* make sure that the poweroff GPIO is configured as output */
+ setbits32(hw_gpio + HW_GPIO_DIR(1), HW_GPIO_SHUTDOWN);
+
+ /* drive the poweroff GPIO high */
+ setbits32(hw_gpio + HW_GPIO_OUT(1), HW_GPIO_SHUTDOWN);
+ }
+ wii_spin();
+}
+
+static void __noreturn wii_halt(void)
+{
+ if (ppc_md.restart)
+ ppc_md.restart(NULL);
+ wii_spin();
+}
+
+static void __init wii_pic_probe(void)
+{
+ flipper_pic_probe();
+ hlwd_pic_probe();
+}
+
+static int __init wii_probe(void)
+{
+ pm_power_off = wii_power_off;
+
+ ug_udbg_init();
+
+ return 1;
+}
+
+static void wii_shutdown(void)
+{
+ hlwd_quiesce();
+ flipper_quiesce();
+}
+
+static const struct of_device_id wii_of_bus[] = {
+ { .compatible = "nintendo,hollywood", },
+ { },
+};
+
+static int __init wii_device_probe(void)
+{
+ of_platform_populate(NULL, wii_of_bus, NULL, NULL);
+ return 0;
+}
+machine_device_initcall(wii, wii_device_probe);
+
+define_machine(wii) {
+ .name = "wii",
+ .compatible = "nintendo,wii",
+ .probe = wii_probe,
+ .setup_arch = wii_setup_arch,
+ .restart = wii_restart,
+ .halt = wii_halt,
+ .init_IRQ = wii_pic_probe,
+ .get_irq = flipper_pic_get_irq,
+ .progress = udbg_progress,
+ .machine_shutdown = wii_shutdown,
+};