diff options
Diffstat (limited to 'arch/powerpc/platforms/embedded6xx')
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/Kconfig | 33 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/Makefile | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/flipper-pic.c | 263 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/flipper-pic.h | 25 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/gamecube.c | 118 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/hlwd-pic.c | 241 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/hlwd-pic.h | 22 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c | 328 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h | 32 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/wii.c | 268 |
10 files changed, 1334 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index 291ac9d8cbe..524d971a147 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig @@ -90,3 +90,36 @@ config MPC10X_OPENPIC config MPC10X_STORE_GATHERING bool "Enable MPC10x store gathering" depends on MPC10X_BRIDGE + +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 index 0773c08bd44..66c23e423f4 100644 --- a/arch/powerpc/platforms/embedded6xx/Makefile +++ b/arch/powerpc/platforms/embedded6xx/Makefile @@ -7,3 +7,7 @@ obj-$(CONFIG_STORCENTER) += storcenter.o obj-$(CONFIG_PPC_HOLLY) += holly.o obj-$(CONFIG_PPC_PRPMC2800) += prpmc2800.o obj-$(CONFIG_PPC_C2K) += c2k.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 diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c new file mode 100644 index 00000000000..c278bd3a8fe --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c @@ -0,0 +1,263 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ +#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/of.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(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + 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(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + + /* this is at least needed for RSW */ + out_be32(io_base + FLIPPER_ICR, 1 << irq); +} + +static void flipper_pic_mask(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + + clrbits32(io_base + FLIPPER_IMR, 1 << irq); +} + +static void flipper_pic_unmask(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + + setbits32(io_base + FLIPPER_IMR, 1 << irq); +} + + +static struct irq_chip flipper_pic = { + .name = "flipper-pic", + .ack = flipper_pic_ack, + .mask_ack = flipper_pic_mask_and_ack, + .mask = flipper_pic_mask, + .unmask = flipper_pic_unmask, +}; + +/* + * IRQ host hooks. + * + */ + +static struct irq_host *flipper_irq_host; + +static int flipper_pic_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hwirq) +{ + set_irq_chip_data(virq, h->host_data); + irq_to_desc(virq)->status |= IRQ_LEVEL; + set_irq_chip_and_handler(virq, &flipper_pic, handle_level_irq); + return 0; +} + +static void flipper_pic_unmap(struct irq_host *h, unsigned int irq) +{ + set_irq_chip_data(irq, NULL); + set_irq_chip(irq, NULL); +} + +static int flipper_pic_match(struct irq_host *h, struct device_node *np) +{ + return 1; +} + + +static struct irq_host_ops flipper_irq_host_ops = { + .map = flipper_pic_map, + .unmap = flipper_pic_unmap, + .match = flipper_pic_match, +}; + +/* + * 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); +} + +struct irq_host * __init flipper_pic_init(struct device_node *np) +{ + struct device_node *pi; + struct irq_host *irq_host = 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%08x mapped to 0x%p\n", res.start, io_base); + + __flipper_quiesce(io_base); + + irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, FLIPPER_NR_IRQS, + &flipper_irq_host_ops, -1); + if (!irq_host) { + pr_err("failed to allocate irq_host\n"); + return NULL; + } + + irq_host->host_data = io_base; + +out: + return irq_host; +} + +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 NO_IRQ; /* 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 00000000000..e339186b566 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.h @@ -0,0 +1,25 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#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 00000000000..1106fd99627 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/gamecube.c @@ -0,0 +1,118 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#include <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/prom.h> +#include <asm/time.h> +#include <asm/udbg.h> + +#include "flipper-pic.h" +#include "usbgecko_udbg.h" + + +static void gamecube_spin(void) +{ + /* spin until power button pressed */ + for (;;) + cpu_relax(); +} + +static void 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 gamecube_halt(void) +{ + gamecube_restart(NULL); +} + +static void __init gamecube_init_early(void) +{ + ug_udbg_init(); +} + +static int __init gamecube_probe(void) +{ + unsigned long dt_root; + + dt_root = of_get_flat_dt_root(); + if (!of_flat_dt_is_compatible(dt_root, "nintendo,gamecube")) + return 0; + + return 1; +} + +static void gamecube_shutdown(void) +{ + flipper_quiesce(); +} + +#ifdef CONFIG_KEXEC +static int gamecube_kexec_prepare(struct kimage *image) +{ + return 0; +} +#endif /* CONFIG_KEXEC */ + + +define_machine(gamecube) { + .name = "gamecube", + .probe = gamecube_probe, + .init_early = gamecube_init_early, + .restart = gamecube_restart, + .power_off = gamecube_power_off, + .halt = gamecube_halt, + .init_IRQ = flipper_pic_probe, + .get_irq = flipper_pic_get_irq, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, + .machine_shutdown = gamecube_shutdown, +#ifdef CONFIG_KEXEC + .machine_kexec_prepare = gamecube_kexec_prepare, +#endif +}; + + +static struct of_device_id gamecube_of_bus[] = { + { .compatible = "nintendo,flipper", }, + { }, +}; + +static int __init gamecube_device_probe(void) +{ + if (!machine_is(gamecube)) + return 0; + + of_platform_bus_probe(NULL, gamecube_of_bus, NULL); + return 0; +} +device_initcall(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 00000000000..a771f91e215 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c @@ -0,0 +1,241 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ +#define DRV_MODULE_NAME "hlwd-pic" +#define pr_fmt(fmt) DRV_MODULE_NAME ": " fmt + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/of.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 + + +/* + * IRQ chip hooks. + * + */ + +static void hlwd_pic_mask_and_ack(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + u32 mask = 1 << irq; + + clrbits32(io_base + HW_BROADWAY_IMR, mask); + out_be32(io_base + HW_BROADWAY_ICR, mask); +} + +static void hlwd_pic_ack(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + + out_be32(io_base + HW_BROADWAY_ICR, 1 << irq); +} + +static void hlwd_pic_mask(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + + clrbits32(io_base + HW_BROADWAY_IMR, 1 << irq); +} + +static void hlwd_pic_unmask(unsigned int virq) +{ + int irq = virq_to_hw(virq); + void __iomem *io_base = get_irq_chip_data(virq); + + setbits32(io_base + HW_BROADWAY_IMR, 1 << irq); +} + + +static struct irq_chip hlwd_pic = { + .name = "hlwd-pic", + .ack = hlwd_pic_ack, + .mask_ack = hlwd_pic_mask_and_ack, + .mask = hlwd_pic_mask, + .unmask = hlwd_pic_unmask, +}; + +/* + * IRQ host hooks. + * + */ + +static struct irq_host *hlwd_irq_host; + +static int hlwd_pic_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hwirq) +{ + set_irq_chip_data(virq, h->host_data); + irq_to_desc(virq)->status |= IRQ_LEVEL; + set_irq_chip_and_handler(virq, &hlwd_pic, handle_level_irq); + return 0; +} + +static void hlwd_pic_unmap(struct irq_host *h, unsigned int irq) +{ + set_irq_chip_data(irq, NULL); + set_irq_chip(irq, NULL); +} + +static struct irq_host_ops hlwd_irq_host_ops = { + .map = hlwd_pic_map, + .unmap = hlwd_pic_unmap, +}; + +static unsigned int __hlwd_pic_get_irq(struct irq_host *h) +{ + void __iomem *io_base = h->host_data; + int irq; + u32 irq_status; + + irq_status = in_be32(io_base + HW_BROADWAY_ICR) & + in_be32(io_base + HW_BROADWAY_IMR); + if (irq_status == 0) + return NO_IRQ; /* no more IRQs pending */ + + irq = __ffs(irq_status); + return irq_linear_revmap(h, irq); +} + +static void hlwd_pic_irq_cascade(unsigned int cascade_virq, + struct irq_desc *desc) +{ + struct irq_host *irq_host = get_irq_data(cascade_virq); + unsigned int virq; + + raw_spin_lock(&desc->lock); + desc->chip->mask(cascade_virq); /* IRQ_LEVEL */ + raw_spin_unlock(&desc->lock); + + virq = __hlwd_pic_get_irq(irq_host); + if (virq != NO_IRQ) + generic_handle_irq(virq); + else + pr_err("spurious interrupt!\n"); + + raw_spin_lock(&desc->lock); + desc->chip->ack(cascade_virq); /* IRQ_LEVEL */ + if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) + desc->chip->unmask(cascade_virq); + 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); +} + +struct irq_host *hlwd_pic_init(struct device_node *np) +{ + struct irq_host *irq_host; + 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%08x mapped to 0x%p\n", res.start, io_base); + + __hlwd_quiesce(io_base); + + irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, HLWD_NR_IRQS, + &hlwd_irq_host_ops, -1); + if (!irq_host) { + pr_err("failed to allocate irq_host\n"); + return NULL; + } + irq_host->host_data = io_base; + + return irq_host; +} + +unsigned int hlwd_pic_get_irq(void) +{ + return __hlwd_pic_get_irq(hlwd_irq_host); +} + +/* + * Probe function. + * + */ + +void hlwd_pic_probe(void) +{ + struct irq_host *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); + set_irq_data(cascade_virq, host); + set_irq_chained_handler(cascade_virq, + hlwd_pic_irq_cascade); + hlwd_irq_host = host; + 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 00000000000..d2e5a092761 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.h @@ -0,0 +1,22 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#ifndef __HLWD_PIC_H +#define __HLWD_PIC_H + +extern unsigned int hlwd_pic_get_irq(void); +extern void hlwd_pic_probe(void); +extern void hlwd_quiesce(void); + +#endif diff --git a/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c new file mode 100644 index 00000000000..20a8ed91962 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c @@ -0,0 +1,328 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#include <mm/mmu_decl.h> + +#include <asm/io.h> +#include <asm/prom.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. + */ +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(); +} + +/* + * Retrieves and prepares the virtual address needed to access the hardware. + */ +static void __iomem *ug_udbg_setup_exi_io_base(struct device_node *np) +{ + void __iomem *exi_io_base = NULL; + phys_addr_t paddr; + const unsigned int *reg; + + reg = of_get_property(np, "reg", NULL); + if (reg) { + paddr = of_translate_address(np, reg); + if (paddr) + exi_io_base = ioremap(paddr, reg[1]); + } + return exi_io_base; +} + +/* + * Checks if a USB Gecko adapter is inserted in any memory card slot. + */ +static void __iomem *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 done; + } + + exi_io_base = ug_udbg_setup_exi_io_base(np); + 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: + if (np) + of_node_put(np); + 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 00000000000..bb6cde4ad76 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.h @@ -0,0 +1,32 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ + +#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 00000000000..57e5b608fa1 --- /dev/null +++ b/arch/powerpc/platforms/embedded6xx/wii.c @@ -0,0 +1,268 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + */ +#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/kexec.h> +#include <linux/of_platform.h> +#include <linux/lmb.h> +#include <mm/mmu_decl.h> + +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/prom.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_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; + +unsigned long wii_hole_start; +unsigned long wii_hole_size; + + +static int __init page_aligned(unsigned long x) +{ + return !(x & (PAGE_SIZE-1)); +} + +void __init wii_memory_fixups(void) +{ + struct lmb_property *p = lmb.memory.region; + + /* + * This is part of a workaround to allow the use of two + * discontiguous RAM ranges on the Wii, even if this is + * currently unsupported on 32-bit PowerPC Linux. + * + * We coealesce the two memory ranges of the Wii into a + * single range, then create a reservation for the "hole" + * between both ranges. + */ + + BUG_ON(lmb.memory.cnt != 2); + BUG_ON(!page_aligned(p[0].base) || !page_aligned(p[1].base)); + + p[0].size = _ALIGN_DOWN(p[0].size, PAGE_SIZE); + p[1].size = _ALIGN_DOWN(p[1].size, PAGE_SIZE); + + wii_hole_start = p[0].base + p[0].size; + wii_hole_size = p[1].base - wii_hole_start; + + pr_info("MEM1: <%08llx %08llx>\n", p[0].base, p[0].size); + pr_info("HOLE: <%08lx %08lx>\n", wii_hole_start, wii_hole_size); + pr_info("MEM2: <%08llx %08llx>\n", p[1].base, p[1].size); + + p[0].size += wii_hole_size + p[1].size; + + lmb.memory.cnt = 1; + lmb_analyze(); + + /* reserve the hole */ + lmb_reserve(wii_hole_start, wii_hole_size); + + /* allow ioremapping the address space in the hole */ + __allow_ioremap_reserved = 1; +} + +unsigned long __init wii_mmu_mapin_mem2(unsigned long top) +{ + unsigned long delta, size, bl; + unsigned long max_size = (256<<20); + + /* MEM2 64MB@0x10000000 */ + delta = wii_hole_start + wii_hole_size; + size = top - delta; + for (bl = 128<<10; bl < max_size; bl <<= 1) { + if (bl * 2 > size) + break; + } + setbat(4, PAGE_OFFSET+delta, delta, bl, PAGE_KERNEL_X); + return delta + bl; +} + +static void wii_spin(void) +{ + local_irq_disable(); + for (;;) + cpu_relax(); +} + +static void __iomem *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 %s\n", np->name); + goto out_put; + } + + hw_regs = ioremap(res.start, resource_size(&res)); + if (hw_regs) { + pr_info("%s at 0x%08x 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 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) { + /* 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 wii_halt(void) +{ + if (ppc_md.restart) + ppc_md.restart(NULL); + wii_spin(); +} + +static void __init wii_init_early(void) +{ + ug_udbg_init(); +} + +static void __init wii_pic_probe(void) +{ + flipper_pic_probe(); + hlwd_pic_probe(); +} + +static int __init wii_probe(void) +{ + unsigned long dt_root; + + dt_root = of_get_flat_dt_root(); + if (!of_flat_dt_is_compatible(dt_root, "nintendo,wii")) + return 0; + + return 1; +} + +static void wii_shutdown(void) +{ + hlwd_quiesce(); + flipper_quiesce(); +} + +#ifdef CONFIG_KEXEC +static int wii_machine_kexec_prepare(struct kimage *image) +{ + return 0; +} +#endif /* CONFIG_KEXEC */ + +define_machine(wii) { + .name = "wii", + .probe = wii_probe, + .init_early = wii_init_early, + .setup_arch = wii_setup_arch, + .restart = wii_restart, + .power_off = wii_power_off, + .halt = wii_halt, + .init_IRQ = wii_pic_probe, + .get_irq = flipper_pic_get_irq, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, + .machine_shutdown = wii_shutdown, +#ifdef CONFIG_KEXEC + .machine_kexec_prepare = wii_machine_kexec_prepare, +#endif +}; + +static struct of_device_id wii_of_bus[] = { + { .compatible = "nintendo,hollywood", }, + { }, +}; + +static int __init wii_device_probe(void) +{ + if (!machine_is(wii)) + return 0; + + of_platform_bus_probe(NULL, wii_of_bus, NULL); + return 0; +} +device_initcall(wii_device_probe); + |