commit 9dcfd8bd11db42d068ef3f3be105e78f15736de7 Author: Hector Martin Date: Thu Mar 5 04:25:35 2015 +0900 Starlet support diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 97d07ed..fb5f67a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -931,6 +931,8 @@ source "arch/arm/mach-socfpga/Kconfig" source "arch/arm/mach-spear/Kconfig" +source "arch/arm/mach-starlet/Kconfig" + source "arch/arm/mach-sti/Kconfig" source "arch/arm/mach-s3c24xx/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c1785ee..9721eb6 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -195,6 +195,7 @@ machine-$(CONFIG_ARCH_SA1100) += sa1100 machine-$(CONFIG_ARCH_SHMOBILE) += shmobile machine-$(CONFIG_ARCH_SIRF) += prima2 machine-$(CONFIG_ARCH_SOCFPGA) += socfpga +machine-$(CONFIG_ARCH_STARLET) += starlet machine-$(CONFIG_ARCH_STI) += sti machine-$(CONFIG_ARCH_SUNXI) += sunxi machine-$(CONFIG_ARCH_TEGRA) += tegra diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 91bd5bd..a4f67fa 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -435,6 +435,7 @@ dtb-$(CONFIG_ARCH_SPEAR3XX)+= spear300-evb.dtb \ spear320-evb.dtb \ spear320-hmi.dtb dtb-$(CONFIG_ARCH_SPEAR6XX)+= spear600-evb.dtb +dtb-$(CONFIG_ARCH_STARLET)+= starlet.dtb dtb-$(CONFIG_ARCH_STI)+= stih407-b2120.dtb \ stih410-b2120.dtb \ stih415-b2000.dtb \ diff --git a/arch/arm/boot/dts/starlet.dts b/arch/arm/boot/dts/starlet.dts new file mode 100644 index 0000000..adc4e79 --- /dev/null +++ b/arch/arm/boot/dts/starlet.dts @@ -0,0 +1,60 @@ +/* + * starlet.dts - Device tree file for the Nintendo Wii Starlet + * + * Copyright (C) 2015 Hector Martin + * + * Licensed under GPLv2 or later + */ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + model = "Nintendo Wii Starlet"; + compatible = "nintendo,starlet"; + + cpus { + #address-cells = <0>; + #size-cells = <0>; + + cpu { + device_type = "cpu"; + compatible = "arm,arm926ej-s"; + }; + }; + + memory { + device_type = "memory"; + reg = <0x10000000 0x4000000>; + }; + + aliases { }; + + chosen { + bootargs = "console=ttyPS0,115200 earlyprintk"; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + device_type = "soc"; + interrupt-parent = <&intc>; + ranges; + + intc: intc@d800038 { + compatible = "nintendo,hollywood-irq"; + #interrupt-cells = <1>; + interrupt-controller; + reg = <0xd800038 0x8>; + }; + + timer: timer@d800010 { + compatible = "nintendo,hollywood-timer"; + reg = <0xd800010 0x8>; + interrupts = <0>; + }; + }; + +}; diff --git a/arch/arm/mach-starlet/Kconfig b/arch/arm/mach-starlet/Kconfig new file mode 100644 index 0000000..6fc4503 --- /dev/null +++ b/arch/arm/mach-starlet/Kconfig @@ -0,0 +1,10 @@ +config ARCH_STARLET + bool "Nintendo Wii Starlet" if ARCH_MULTI_V5 + select ARCH_SUPPORTS_BIG_ENDIAN + select CPU_BIG_ENDIAN + select CPU_ARM926T + select SOC_BUS + select ARM_APPENDED_DTB + select HOLLYWOOD_IRQ + help + Support for Nintendo Wii Starlet diff --git a/arch/arm/mach-starlet/Makefile b/arch/arm/mach-starlet/Makefile new file mode 100644 index 0000000..fff88c4 --- /dev/null +++ b/arch/arm/mach-starlet/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Nintendo Wii Starlet mach +# + +# Common support +obj-y := starlet.o time.o diff --git a/arch/arm/mach-starlet/starlet.c b/arch/arm/mach-starlet/starlet.c new file mode 100644 index 0000000..dcb794e --- /dev/null +++ b/arch/arm/mach-starlet/starlet.c @@ -0,0 +1,65 @@ +/* + * Nintendo Wii Starlet platform. + * + * Copyright (C) 2015 Hector Martin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "starlet.h" + +void hollywood_dt_init_timer(void); + +static void __init starlet_init_machine(void) +{ +} + +static void __init starlet_timer_init(void) +{ + hollywood_dt_init_timer(); + of_clk_init(NULL); +} + +static void __init starlet_irq_init(void) +{ + irqchip_init(); +} + +static void starlet_system_reset(enum reboot_mode mode, const char *cmd) +{ + // TODO +} + +static void __init starlet_map_io(void) +{ +} + +static const char * const starlet_dt_match[] = { + "nintendo,starlet", + NULL +}; + +DT_MACHINE_START(STARLET, "Nintendo Wii Starlet") + .map_io = starlet_map_io, + .init_irq = starlet_irq_init, + .init_machine = starlet_init_machine, + .init_time = starlet_timer_init, + .dt_compat = starlet_dt_match, + .restart = starlet_system_reset, +MACHINE_END diff --git a/arch/arm/mach-starlet/starlet.h b/arch/arm/mach-starlet/starlet.h new file mode 100644 index 0000000..e69de29 diff --git a/arch/arm/mach-starlet/time.c b/arch/arm/mach-starlet/time.c new file mode 100644 index 0000000..092cffa --- /dev/null +++ b/arch/arm/mach-starlet/time.c @@ -0,0 +1,169 @@ +/* + * Support for clocksource and clockevents using the Hollywood timer + * + * Copyright (C) 2015 Hector Martin + * + * Based on arch/arm/mach-mmp/time.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define CLOCK_FREQ 1898437 + +#define MAX_DELTA (0xfffffffe) +#define MIN_DELTA (16) + +#define HOLLYWOOD_TIMER 0x00 +#define HOLLYWOOD_ALARM 0x04 + +static void __iomem *hollywood_timer_base; +static int hollywood_timer_irq; + +static inline uint32_t timer_read(void) +{ + return __raw_readl(hollywood_timer_base + HOLLYWOOD_TIMER); +} + +static u64 notrace hollywood_read_sched_clock(void) +{ + return timer_read(); +} + +static irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *c = dev_id; + + c->event_handler(c); + + return IRQ_HANDLED; +} + +static int timer_set_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + unsigned long flags; + u32 val; + + local_irq_save(flags); + + val = timer_read(); + __raw_writel(val + delta, hollywood_timer_base + HOLLYWOOD_ALARM); + + local_irq_restore(flags); + + return 0; +} + +static void timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + unsigned long flags; + + local_irq_save(flags); + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_PERIODIC: + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + } + local_irq_restore(flags); +} + +static struct clock_event_device ckevt = { + .name = "hollywood-timer-ckevt", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 200, + .set_next_event = timer_set_next_event, + .set_mode = timer_set_mode, +}; + +static cycle_t clksrc_read(struct clocksource *cs) +{ + return timer_read(); +} + +static struct clocksource cksrc = { + .name = "hollywood-timer-cksrc", + .rating = 200, + .read = clksrc_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void __init timer_config(void) +{ + __raw_writel(timer_read() - 1, hollywood_timer_base + HOLLYWOOD_ALARM); +} + +static struct irqaction timer_irq = { + .name = "timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = timer_interrupt, + .dev_id = &ckevt, +}; + +void __init timer_init(void) +{ + timer_config(); + + sched_clock_register(hollywood_read_sched_clock, 32, CLOCK_FREQ); + + ckevt.cpumask = cpumask_of(0); + + setup_irq(hollywood_timer_irq, &timer_irq); + + clocksource_register_hz(&cksrc, CLOCK_FREQ); + clockevents_config_and_register(&ckevt, CLOCK_FREQ, + MIN_DELTA, MAX_DELTA); +} + +static struct of_device_id hollywood_timer_dt_ids[] = { + { .compatible = "nintendo,hollywood-timer", }, + {} +}; + +void __init hollywood_dt_init_timer(void) +{ + struct device_node *np; + int ret; + + np = of_find_matching_node(NULL, hollywood_timer_dt_ids); + if (!np) { + ret = -ENODEV; + goto out; + } + + hollywood_timer_irq = irq_of_parse_and_map(np, 0); + if (!hollywood_timer_irq) { + ret = -EINVAL; + goto out; + } + hollywood_timer_base = of_iomap(np, 0); + if (!hollywood_timer_base) { + ret = -ENOMEM; + goto out; + } + timer_init(); + return; +out: + pr_err("Failed to get timer from device tree with error:%d\n", ret); +} diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index cc79d2a..c934732 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -88,6 +88,11 @@ config CLPS711X_IRQCHIP select SPARSE_IRQ default y +config HOLLYWOOD_IRQ + bool + depends on ARCH_STARLET + select IRQ_DOMAIN + config OR1K_PIC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 9516a32..499f376 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o +obj-$(CONFIG_ARCH_STARLET) += irq-hollywood.o diff --git a/drivers/irqchip/irq-hollywood.c b/drivers/irqchip/irq-hollywood.c new file mode 100644 index 0000000..101ac7f --- /dev/null +++ b/drivers/irqchip/irq-hollywood.c @@ -0,0 +1,121 @@ +/* + * Nintendo Wii Hollywood IRQ chip driver. + * + * Copyright (C) 2015 Hector Martin + * + * Based on irq-sun4i.c + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include "irqchip.h" + +#define HOLLYWOOD_IRQ_FLAG_REG 0x00 +#define HOLLYWOOD_IRQ_MASK_REG 0x04 + +static void __iomem *hollywood_irq_base; +static struct irq_domain *hollywood_irq_domain; + +static void __exception_irq_entry hollywood_handle_irq(struct pt_regs *regs); + +static void hollywood_irq_ack(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + __raw_writel(BIT(irq), hollywood_irq_base + HOLLYWOOD_IRQ_FLAG_REG); +} + +static void hollywood_irq_mask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + u32 val; + val = __raw_readl(hollywood_irq_base + HOLLYWOOD_IRQ_MASK_REG); + + __raw_writel(val & ~BIT(irq), hollywood_irq_base + HOLLYWOOD_IRQ_MASK_REG); +} + +static void hollywood_irq_unmask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + u32 val; + val = __raw_readl(hollywood_irq_base + HOLLYWOOD_IRQ_MASK_REG); + + __raw_writel(val | BIT(irq), hollywood_irq_base + HOLLYWOOD_IRQ_MASK_REG); +} + +static struct irq_chip hollywood_irq_chip = { + .name = "hollywood_irq", + .irq_eoi = hollywood_irq_ack, + .irq_mask = hollywood_irq_mask, + .irq_unmask = hollywood_irq_unmask, +}; + +static int hollywood_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &hollywood_irq_chip, handle_fasteoi_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +} + +static struct irq_domain_ops hollywood_irq_ops = { + .map = hollywood_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init hollywood_of_init(struct device_node *node, + struct device_node *parent) +{ + hollywood_irq_base = of_iomap(node, 0); + if (!hollywood_irq_base) + panic("%s: unable to map IC registers\n", + node->full_name); + + /* Disable all interrupts */ + writel(0, hollywood_irq_base + HOLLYWOOD_IRQ_MASK_REG); + + /* Clear all the pending interrupts */ + writel(0xffffffff, hollywood_irq_base + HOLLYWOOD_IRQ_FLAG_REG); + + hollywood_irq_domain = irq_domain_add_linear(node, 32, + &hollywood_irq_ops, NULL); + if (!hollywood_irq_domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + + set_handle_irq(hollywood_handle_irq); + + return 0; +} +IRQCHIP_DECLARE(nintendo_hollywood_ic, "nintendo,hollywood-irq", hollywood_of_init); + +static void __exception_irq_entry hollywood_handle_irq(struct pt_regs *regs) +{ + u32 stat; + int irqofs, handled; + + do { + handled = 0; + + stat = __raw_readl(hollywood_irq_base + HOLLYWOOD_IRQ_FLAG_REG) & + __raw_readl(hollywood_irq_base + HOLLYWOOD_IRQ_MASK_REG); + + while (stat) { + handled = 1; + irqofs = fls(stat) - 1; + handle_domain_irq(hollywood_irq_domain, irqofs, regs); + stat &= ~(1 << irqofs); + } + } while (handled); + +}