linux-sh.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* SH Core Linux 20110317
@ 2011-03-17  6:40 Magnus Damm
  2011-03-17  6:56 ` Mike Frysinger
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Magnus Damm @ 2011-03-17  6:40 UTC (permalink / raw)
  To: linux-sh

[-- Attachment #1: Type: text/plain, Size: 2772 bytes --]

This is the first public release of SH Core Linux, version 20110317.

SH Core Linux brings the Linux to the SH core included in sh7372 -
running in parallel with the ARM core.

Many modern SoCs contain multiple CPU cores in asymmetric
multiprocessing configurations. These asymmetric CPU configurations
often run multiple operating system instances; sometimes multiple
instances of Linux and sometimes Linux on the main CPU and some
customized software on the other CPU cores. Asymmetric CPU
configurations are different from symmetric multiprocessing
configurations which make use of a single Linux kernel together with
SMP to support multiple processor cores. To make things even more
complicated, come SoCs are configured to have some CPU cores operating
in SMP mode, and others in AMP.

This SH Core Linux prototype is written for the sh7372 SoC from
Renesas. The sh7372 contains one ARM Cortex-A8 and one SH4AL-DSP core.
This release includes all that is needed to run two instances of
Linux; one on the ARM core and one on the SH core. The ARM core is the
main SoC processor which runs a user space program to control the SH
core. The SH core does not make use of any special hardware except a
mailbox interface where Virtio serves as a communication link.

- - - Features - - -

- Console and network support via Virtio
- SH Core Linux boots in less than a second
- Early console printouts via Virtio
- Basic power management - clock stopping in the SH idle loop
- ARM User space program interfaces to the kernel with UIO
- The power domain can be off when the UIO device is closed
- Fully open system - no firmware or magic binaries

- - - Usage - - -

To start the SH Core Linux use the rtcpu-loader program on the ARM core:
# /rtcpu-loader /romImage 0x100000
To exit the SH Core Linux use the "poweroff" program inside the SH core:
# poweroff
To enable networking create the network interface "tap0" on the ARM core:
# tunctl -t tap0

- - - Source code - - -

This SH Core Linux release consists of 4 parts:

1) Virtio platform driver patches used on the SH core
http://lwn.net/Articles/432753/
https://patchwork.kernel.org/patch/623551/
https://patchwork.kernel.org/patch/623581/

2) sh7372 SH4AL-DSP kernel patch and configuration (attached)
linux-2.6.38-sh-shmobile-sh7372-sh-core-slave-20110317.patch
linux-2.6.38-sh-shmobile-sh7372-sh-core-slave-20110317.config.gz

3) sh7372 ARM kernel patch and configuration (attached)
linux-2.6.38-arm-shmobile-sh7372-sh-core-slave-20110317.patch
linux-2.6.38-arm-shmobile-mackerel-sh-core-slave-20110317.config.gz

4) User space prototype for the ARM core based on lguest.c (attached)
rtcpu-loader-20110228.c

Please note the experimental code quality of all contents above except 1).

Questions? Please ask!

/ magnus

[-- Attachment #2: linux-2.6.38-sh-shmobile-sh7372-sh-core-slave-20110317.patch --]
[-- Type: application/octet-stream, Size: 11951 bytes --]

From: Magnus Damm <damm@opensource.se>

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 arch/sh/Kconfig                            |    7 
 arch/sh/Makefile                           |    1 
 arch/sh/boards/Kconfig                     |    6 
 arch/sh/boards/mach-slave/Makefile         |    1 
 arch/sh/boards/mach-slave/setup.c          |  292 ++++++++++++++++++++++++++++
 arch/sh/include/mach-slave/mach/romimage.h |   62 +++++
 arch/sh/kernel/cpu/sh4a/Makefile           |    1 
 arch/sh/kernel/cpu/sh4a/setup-slave.c      |   37 +++
 arch/sh/mm/Kconfig                         |    2 
 9 files changed, 408 insertions(+), 1 deletion(-)

--- 0001/arch/sh/Kconfig
+++ work/arch/sh/Kconfig	2011-03-17 14:18:03.000000000 +0900
@@ -504,6 +504,12 @@ config CPU_SUBTYPE_SH7366
 	select SYS_SUPPORTS_NUMA
 	select SYS_SUPPORTS_CMT
 
+config CPU_SUBTYPE_SLAVE_SH4AL_DSP
+	bool "Support Slave operation of SH4AL-DSP processor"
+	select CPU_SH4AL_DSP
+	select CPU_SHX2
+	select ARCH_SHMOBILE
+	select ARCH_SPARSEMEM_ENABLE
 endchoice
 
 endif
@@ -767,6 +773,7 @@ config ZERO_PAGE_OFFSET
 
 config BOOT_LINK_OFFSET
 	hex
+	default "0x00800000" if CPU_SUBTYPE_SLAVE_SH4AL_DSP
 	default "0x00210000" if SH_SHMIN
 	default "0x00400000" if SH_CAYMAN
 	default "0x00810000" if SH_7780_SOLUTION_ENGINE
--- 0001/arch/sh/Makefile
+++ work/arch/sh/Makefile	2011-03-17 14:18:03.000000000 +0900
@@ -148,6 +148,7 @@ machdir-$(CONFIG_SH_LANDISK)			+= mach-l
 machdir-$(CONFIG_SH_LBOX_RE2)			+= mach-lboxre2
 machdir-$(CONFIG_SH_CAYMAN)			+= mach-cayman
 machdir-$(CONFIG_SH_RSK)			+= mach-rsk
+machdir-$(CONFIG_SH_SLAVE)			+= mach-slave
 
 ifneq ($(machdir-y),)
 core-y	+= $(addprefix arch/sh/boards/, \
--- 0001/arch/sh/boards/Kconfig
+++ work/arch/sh/boards/Kconfig	2011-03-17 14:18:03.000000000 +0900
@@ -138,6 +138,12 @@ config SH_RSK
 	 Select this option if configuring for any of the RSK+ MCU
 	 evaluation platforms.
 
+config SH_SLAVE
+	depends on CPU_SUBTYPE_SLAVE_SH4AL_DSP
+	bool "Slave CPU Support"
+	help
+	 Select this option if configuring for Slave CPU use.
+
 config SH_SDK7780
 	bool "SDK7780R3"
 	depends on CPU_SUBTYPE_SH7780
--- /dev/null
+++ work/arch/sh/boards/mach-slave/Makefile	2011-03-17 14:18:04.000000000 +0900
@@ -0,0 +1 @@
+obj-y	 := setup.o
--- /dev/null
+++ work/arch/sh/boards/mach-slave/setup.c	2011-03-17 14:22:24.000000000 +0900
@@ -0,0 +1,292 @@
+/*
+ * Slave CPU Setup
+ *
+ *  Copyright (C) 2010  Magnus Damm
+ *  Copyright (C) 2006 - 2008  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/virtio_platform.h>
+#include <linux/clockchips.h>
+#include <linux/console.h>
+#include <linux/virtio_console.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <asm/machvec.h>
+#include <asm/clock.h>
+#include <asm/mmzone.h>
+#include <asm/cacheflush.h>
+
+#define IRQ_DEMUX 0
+#define IRQ_EARLY_CONSOLE 0
+#define IRQ_POWER_OFF 1
+#define IRQ_TIMER 2
+#define IRQ_VIRTIO 3
+#define IRQ_NR 16
+
+#define MFRAM (void *)0xe6a70000
+
+static volatile unsigned long *a2s_enabled = (MFRAM + 0x100); /* 1 = enabled */
+static volatile unsigned long *a2s_pending = (MFRAM + 0x200); /* 1 = pending */
+static volatile unsigned long *s2a_pending = (MFRAM + 0x300); /* 1 = pending */
+
+static void s2a_pending_irq_set(int pending)
+{
+	__raw_writel(pending, 0xe6a60014); /* MFISEICR */
+}
+
+static void a2s_pending_irq_set(int pending)
+{
+	__raw_writel(pending, 0xe6a60010); /* MFISIICR */
+}
+
+static __init void early_put_char(const char c)
+{
+	s2a_pending[IRQ_EARLY_CONSOLE] = c;
+	s2a_pending_irq_set(1);
+	while (s2a_pending[IRQ_EARLY_CONSOLE])
+		;
+}
+
+static __init int early_put_chars(u32 vtermno, const char *buf, int count)
+{
+	int k;
+	char c;
+
+	for (k = 0; k < count; k++) {
+		c = *buf++;
+
+		if (c)
+			early_put_char(c);
+
+		if (c == '\n')
+			early_put_char('\r');
+	}
+
+	return count;
+}
+
+static void notify_host(int irq)
+{
+	s2a_pending[irq] = 1;
+	s2a_pending_irq_set(1);
+}
+
+static void slave_power_off(void)
+{
+	notify_host(IRQ_POWER_OFF);
+
+	/* busy loop that does _not_ use the sleep instruction */
+	while(1)
+	  ;
+}
+
+static void __init slave_setup(char **cmdline_p)
+{
+	add_preferred_console("hvc", 0, NULL);
+	virtio_cons_early_init(early_put_chars);
+
+	pm_power_off = slave_power_off;
+}
+
+
+static void notify_virtio(struct platform_device *pdev)
+{
+	notify_host(IRQ_VIRTIO);
+}
+
+static void notify_virtqueue(struct platform_device *pdev,
+			     struct lguest_vqconfig *config)
+{
+	notify_host(config->irq);
+}
+
+static struct virtio_platform_data virtio_pdata = {
+	.notify_virtio = notify_virtio,
+	.notify_virtqueue = notify_virtqueue,
+};
+
+static struct resource virtio_resources[1];
+
+static struct platform_device virtio_device = {
+	.name		= "virtio-platform",
+	.id		= 0,
+	.resource	= virtio_resources,
+	.num_resources	= ARRAY_SIZE(virtio_resources),
+	.dev		= {
+		.platform_data	= &virtio_pdata,
+	},
+};
+
+static int __init devices_setup(void)
+{
+	virtio_resources[0].start = max_pfn << PAGE_SHIFT;
+	virtio_resources[0].end = virtio_resources[0].start + (1 << 20) - 1;
+	virtio_resources[0].flags = IORESOURCE_MEM;
+
+	return platform_device_register(&virtio_device);
+}
+arch_initcall(devices_setup);
+
+static void disable_virt_irq(struct irq_data *data)
+{
+	a2s_enabled[data->irq] = 0;
+}
+
+static void enable_virt_irq(struct irq_data *data)
+{
+	a2s_enabled[data->irq] = 1;
+
+	if (a2s_pending[data->irq])
+		a2s_pending_irq_set(1);
+}
+
+static struct irq_chip virt_irq_controller = {
+	.name		= "virt",
+	.irq_mask	= disable_virt_irq,
+	.irq_mask_ack	= disable_virt_irq,
+	.irq_unmask	= enable_virt_irq,
+};
+
+static void slave_demux(unsigned int irq, struct irq_desc *desc)
+{
+	int k;
+
+	while (__raw_readl(0xe6a60010)) { /* if pending */
+		a2s_pending_irq_set(0); /* set not pending */
+
+		for (k = 0; k < IRQ_NR; k++) {
+			while (a2s_enabled[k] && a2s_pending[k]) {
+				a2s_pending[k] = 0; /* not pending anymore */
+				generic_handle_irq(k);
+			}
+		}
+	}
+}
+
+void __init slave_init_irq(void)
+{
+	int k;
+
+	irq_to_desc_alloc_node(IRQ_DEMUX, 0);
+	set_irq_chip_and_handler_name(IRQ_DEMUX, &dummy_irq_chip,
+				      handle_level_irq, "level");
+	set_irq_chained_handler(IRQ_DEMUX, slave_demux);
+
+	for (k = 1; k < IRQ_NR; k++) {
+		irq_to_desc_alloc_node(k, 0);
+		set_irq_chip_and_handler_name(k, &virt_irq_controller,
+					      handle_level_irq, "level");
+	}
+}
+
+
+static int slave_irq_demux(int irq)
+{
+	return IRQ_DEMUX;
+}
+
+struct clockevent_priv {
+	struct irqaction irqaction;
+	struct clock_event_device ced;
+};
+
+static struct clockevent_priv *ced_to_priv(struct clock_event_device *ced)
+{
+	return container_of(ced, struct clockevent_priv, ced);
+}
+
+static void clockevent_priv_event_start(struct clockevent_priv *p)
+{
+	struct clock_event_device *ced = &p->ced;
+
+	/* TODO: calculate good shift from rate and counter bit width */
+
+	ced->shift = 32;
+	ced->mult = div_sc(1000, NSEC_PER_SEC, ced->shift);
+	ced->max_delta_ns = clockevent_delta2ns(0xffffffff, ced);
+	ced->min_delta_ns = 5000;
+}
+
+static void clockevent_priv_clock_event_mode(enum clock_event_mode mode,
+					     struct clock_event_device *ced)
+{
+	struct clockevent_priv *p = ced_to_priv(ced);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		clockevent_priv_event_start(p);
+		printk("paravirtualized periodic clock events enabled\n");
+		break;
+	default:
+		break;
+	}
+}
+
+static irqreturn_t clockevent_priv_interrupt(int irq, void *dev_id)
+{
+	struct clockevent_priv *p = dev_id;
+	/* notify clockevent layer */
+	p->ced.event_handler(&p->ced);
+	return IRQ_HANDLED;
+}
+
+static void clockevent_priv_register_clockevent(struct clockevent_priv *p)
+{
+	struct clock_event_device *ced = &p->ced;
+	int ret;
+
+	memset(ced, 0, sizeof(*ced));
+
+	ced->name = "timer";
+	ced->features = CLOCK_EVT_FEAT_PERIODIC;
+	ced->rating = 100;
+	ced->cpumask = cpumask_of(0);
+	ced->set_mode = clockevent_priv_clock_event_mode;
+
+	clockevents_register_device(ced);
+
+	/* setup data for setup_irq() (too early for request_irq()) */
+	p->irqaction.name = ced->name;
+	p->irqaction.handler = clockevent_priv_interrupt;
+	p->irqaction.dev_id = p;
+	p->irqaction.irq = IRQ_TIMER;
+	p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | \
+			     IRQF_IRQPOLL  | IRQF_NOBALANCING;
+
+	ret = setup_irq(p->irqaction.irq, &p->irqaction);
+	if (ret) {
+		printk("timer failed to request irq %d\n",
+			p->irqaction.irq);
+		return;
+	}
+
+	notify_host(IRQ_TIMER);
+}
+
+static struct clockevent_priv p;
+
+static int __init arch_timer_init(void)
+{
+	clockevent_priv_register_clockevent(&p);
+	return 0;
+}
+arch_initcall(arch_timer_init);
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_sh4al_dsp_slave __initmv = {
+	.mv_name		= "SH4AL-DSP slave",
+	.mv_setup		= slave_setup,
+	.mv_init_irq		= slave_init_irq,
+	.mv_irq_demux		= slave_irq_demux,
+};
--- /dev/null
+++ work/arch/sh/include/mach-slave/mach/romimage.h	2011-03-17 14:18:04.000000000 +0900
@@ -0,0 +1,62 @@
+#ifdef __ASSEMBLY__
+	/* at this point we assume that we execute from U0/P0 */
+
+	/* setup PMB0 */
+	mov.l   pmb0_addr, r1
+	mov.l   pmb0_addr_value, r0
+	mov.l   r0, @r1
+	mov.l   pmb0_data, r1
+	mov.l   pmb0_data_value, r0
+	mov.l   r0, @r1
+
+	/* setup PMB1 */
+	mov.l   pmb1_addr, r1
+	mov.l   pmb1_addr_value, r0
+	mov.l   r0, @r1
+	mov.l   pmb1_data, r1
+	mov.l   pmb1_data_value, r0
+	mov.l   r0, @r1
+
+	/* enable cache */
+	mov.l   ccr_addr, r1
+	mov.l   ccr_data, r0
+	mov.l   r0, @r1
+	icbi	@r1
+
+	bra done
+	 nop
+
+	.align 2
+nmifcrs_addr:
+	.long   0xffd300c0
+pmb0_addr:
+	.long   0xf6100000
+pmb0_addr_value:
+	.long   0x80000100
+pmb0_data:
+	.long   0xf7100000
+pmb0_data_value:
+	.long   0x4c000319 /* 64M - must match CONFIG_MEMORY_START, C+WT+UB */
+
+pmb1_addr:
+	.long   0xf6100100
+pmb1_addr_value:
+	.long   0xa0000100
+pmb1_data:
+	.long   0xf7100100
+pmb1_data_value:
+	.long   0x4c000110 /* 64M */
+
+ccr_addr:
+	.long   0xff00001c
+ccr_data:
+	.long   0x0000090b
+done:
+
+#else /* __ASSEMBLY__ */
+
+extern inline void mmcif_update_progress(int nr)
+{
+}
+
+#endif /* __ASSEMBLY__ */
--- 0001/arch/sh/kernel/cpu/sh4a/Makefile
+++ work/arch/sh/kernel/cpu/sh4a/Makefile	2011-03-17 14:18:03.000000000 +0900
@@ -3,6 +3,7 @@
 #
 
 # CPU subtype setup
+obj-$(CONFIG_CPU_SUBTYPE_SLAVE_SH4AL_DSP)	+= setup-slave.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7757)	+= setup-sh7757.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7763)	+= setup-sh7763.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7770)	+= setup-sh7770.o
--- /dev/null
+++ work/arch/sh/kernel/cpu/sh4a/setup-slave.c	2011-03-17 14:18:04.000000000 +0900
@@ -0,0 +1,37 @@
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <asm/clock.h>
+
+static struct clk_ops cpu_clk_ops = {
+	.recalc		= followparent_recalc,
+};
+
+static struct clk cpu_clk = {
+	.rate		= 400000000,
+	.ops		= &cpu_clk_ops,
+	.flags		= CLK_ENABLE_ON_INIT,
+};
+
+#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
+
+static struct clk_lookup lookups[] = {
+	CLKDEV_CON_ID("cpu_clk", &cpu_clk),
+};
+
+int __init arch_clk_init(void)
+{
+	int ret;
+
+	ret = clk_register(&cpu_clk);
+	if (!ret)
+		clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	return ret;
+}
+
+void __init plat_irq_setup(void)
+{
+	/* nothing to do here */
+}
+
--- 0001/arch/sh/mm/Kconfig
+++ work/arch/sh/mm/Kconfig	2011-03-17 14:18:03.000000000 +0900
@@ -83,7 +83,7 @@ config 32BIT
 
 config PMB
 	bool "Support 32-bit physical addressing through PMB"
-	depends on MMU && EXPERIMENTAL && CPU_SH4A && !CPU_SH4AL_DSP
+	depends on MMU && EXPERIMENTAL && CPU_SH4A
 	select 32BIT
 	select UNCACHED_MAPPING
 	help

[-- Attachment #3: linux-2.6.38-sh-shmobile-sh7372-sh-core-slave-20110317.config.gz --]
[-- Type: application/x-gzip, Size: 4627 bytes --]

[-- Attachment #4: linux-2.6.38-arm-shmobile-sh7372-sh-core-slave-20110317.patch --]
[-- Type: application/octet-stream, Size: 10800 bytes --]

From: Magnus Damm <damm@opensource.se>

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 arch/arm/mach-shmobile/Makefile              |    1 
 arch/arm/mach-shmobile/board-ap4evb.c        |    1 
 arch/arm/mach-shmobile/board-mackerel.c      |    1 
 arch/arm/mach-shmobile/clock-sh7372.c        |   85 +++++++++++++++++++++++++-
 arch/arm/mach-shmobile/include/mach/common.h |    4 +
 arch/arm/mach-shmobile/memchunk.c            |   78 +++++++++++++++++++++++
 arch/arm/mach-shmobile/setup-sh7372.c        |   41 ++++++++++++
 7 files changed, 210 insertions(+), 1 deletion(-)

--- 0001/arch/arm/mach-shmobile/Makefile
+++ work/arch/arm/mach-shmobile/Makefile	2011-03-17 14:03:03.000000000 +0900
@@ -4,6 +4,7 @@
 
 # Common objects
 obj-y				:= timer.o console.o clock.o pm_runtime.o
+obj-y				+= memchunk.o
 
 # CPU objects
 obj-$(CONFIG_ARCH_SH7367)	+= setup-sh7367.o clock-sh7367.o intc-sh7367.o
--- 0001/arch/arm/mach-shmobile/board-ap4evb.c
+++ work/arch/arm/mach-shmobile/board-ap4evb.c	2011-03-17 14:03:03.000000000 +0900
@@ -1366,5 +1366,6 @@ MACHINE_START(AP4EVB, "ap4evb")
 	.init_irq	= sh7372_init_irq,
 	.handle_irq	= shmobile_handle_irq_intc,
 	.init_machine	= ap4evb_init,
+	.reserve	= sh7372_reserve_memory,
 	.timer		= &ap4evb_timer,
 MACHINE_END
--- 0001/arch/arm/mach-shmobile/board-mackerel.c
+++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-03-17 14:03:03.000000000 +0900
@@ -1216,5 +1216,6 @@ MACHINE_START(MACKEREL, "mackerel")
 	.init_irq	= sh7372_init_irq,
 	.handle_irq	= shmobile_handle_irq_intc,
 	.init_machine	= mackerel_init,
+	.reserve	= sh7372_reserve_memory,
 	.timer		= &mackerel_timer,
 MACHINE_END
--- 0001/arch/arm/mach-shmobile/clock-sh7372.c
+++ work/arch/arm/mach-shmobile/clock-sh7372.c	2011-03-17 14:03:03.000000000 +0900
@@ -16,11 +16,14 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
+#include <linux/compiler.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/sh_clk.h>
 #include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/sh_intc.h>
 #include <mach/common.h>
 
 /* SH7372 registers */
@@ -345,7 +348,7 @@ enum { DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1,
   SH_CLK_DIV4(&pllc1_clk, _reg, _bit, _mask, _flags)
 
 static struct clk div4_clks[DIV4_NR] = {
-	[DIV4_I] = DIV4(FRQCRA, 20, 0x6fff, CLK_ENABLE_ON_INIT),
+	[DIV4_I] = DIV4(FRQCRA, 20, 0x6fff, 0),
 	[DIV4_ZG] = DIV4(FRQCRA, 16, 0x6fff, CLK_ENABLE_ON_INIT),
 	[DIV4_B] = DIV4(FRQCRA, 8, 0x6fff, CLK_ENABLE_ON_INIT),
 	[DIV4_M1] = DIV4(FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT),
@@ -554,6 +557,82 @@ static struct clk mstp_clks[MSTP_NR] = {
 	[MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */
 };
 
+/* RT-CPU */
+
+#define RESCNT (void __iomem *)0xe618801c /* in SYSC */
+#define RBAR (void __iomem *)0xe618001c
+#define SRCR0 (void __iomem *)0xe61580a0
+#define SMFRAM 0xe6a70000 
+
+static unsigned char sh_boot_code[] =
+{
+	/* SH machine code to wait for MFI IRQ and jump to memory (bar3-3.S) */
+	0x09, 0x00, 0x09, 0x00,	0x04, 0xd1, 0x00, 0xe0,
+	0x02, 0x21, 0x09, 0x00,	0x12, 0x60, 0x00, 0x88, 
+	0xfb, 0x89, 0x02, 0xd1,	0x2b, 0x41, 0x09, 0x00,
+	0x10, 0x00, 0xa6, 0xe6, 0xff, 0xff, 0xff, 0xff
+};
+
+void set_rtcpu_base(unsigned long base)
+{
+	unsigned long *p = (void *)&sh_boot_code[28];
+
+	*p = base;
+}
+
+static int rtcpu_enable(struct clk *clk)
+{
+	/* copy code into MFRAM */
+	memcpy((void *)SMFRAM, sh_boot_code, sizeof(sh_boot_code));
+
+	/* set RT-CPU reset vector to MFRAM */
+	__raw_writel(SMFRAM | 0x01, RBAR);
+
+	 /* assert RT-CPU reset */
+	__raw_writel(__raw_readl(SRCR0) | (1 << 30), SRCR0);
+
+	/* enable RT-CPU clock */
+	__raw_writel(__raw_readl(RESCNT) & ~(1 << 9), RESCNT);
+
+	/* wait for reset */
+	mdelay(1);
+
+	/* enable MFIS clock */
+	__raw_writel(__raw_readl(SMSTPCR2) & ~(1 << 13), SMSTPCR2);
+
+	/* let go of RT-CPU reset */
+	__raw_writel(__raw_readl(SRCR0) & ~(1 << 30), SRCR0);
+
+	return 0;
+}
+
+static void rtcpu_disable(struct clk *clk)
+{
+	/* disable MFIS clock */
+	__raw_writel(__raw_readl(SMSTPCR2) & ~(1 << 13), SMSTPCR2);
+
+	/* disable RT-CPU clock */
+	__raw_writel(__raw_readl(RESCNT) | (1 << 9), RESCNT);
+}
+
+static struct clk_ops rtcpu_clk_ops = {
+	.recalc		= followparent_recalc,
+	.enable		= rtcpu_enable,
+	.disable	= rtcpu_disable,
+};
+
+struct clk rtcpu_clk = {
+	.ops		= &rtcpu_clk_ops,
+	.parent		= &div4_clks[DIV4_I],
+};
+
+enum { MISC_RTCPU,
+       MISC_NR };
+
+static struct clk *misc_clks[] = {
+	[MISC_RTCPU] = &rtcpu_clk,
+};
+
 #define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
 #define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
 #define CLKDEV_ICK_ID(_cid, _did, _clk) { .con_id = _cid, .dev_id = _did, .clk = _clk }
@@ -645,6 +724,7 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_DEV_ID("r8a66597_hcd.1", &mstp_clks[MSTP406]), /* USB1 */
 	CLKDEV_DEV_ID("r8a66597_udc.1", &mstp_clks[MSTP406]), /* USB1 */
 	CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */
+	CLKDEV_DEV_ID("uio_pdrv_genirq.8", &rtcpu_clk), /* RT-CPU */
 
 	CLKDEV_ICK_ID("ick", "sh-mobile-hdmi", &div6_reparent_clks[DIV6_HDMI]),
 	CLKDEV_ICK_ID("icka", "sh_fsi2", &div6_reparent_clks[DIV6_FSIA]),
@@ -673,6 +753,9 @@ void __init sh7372_clock_init(void)
 	for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
 		ret = clk_register(late_main_clks[k]);
 
+	for (k = 0; !ret && (k < ARRAY_SIZE(misc_clks)); k++)
+		ret = clk_register(misc_clks[k]);
+
 	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
 	if (!ret)
--- 0001/arch/arm/mach-shmobile/include/mach/common.h
+++ work/arch/arm/mach-shmobile/include/mach/common.h	2011-03-17 14:03:03.000000000 +0900
@@ -6,6 +6,9 @@ extern void shmobile_setup_console(void)
 extern void shmobile_secondary_vector(void);
 struct clk;
 extern int clk_init(void);
+struct platform_device;
+extern int shmobile_memchunk_setup(struct platform_device *pdev,
+				   char *name, unsigned long memsize);
 extern void shmobile_handle_irq_intc(struct pt_regs *);
 extern void shmobile_handle_irq_gic(struct pt_regs *);
 
@@ -28,6 +31,7 @@ extern struct clk sh7377_extal2_clk;
 extern void sh7372_init_irq(void);
 extern void sh7372_add_early_devices(void);
 extern void sh7372_add_standard_devices(void);
+extern void sh7372_reserve_memory(void);
 extern void sh7372_clock_init(void);
 extern void sh7372_pinmux_init(void);
 extern struct clk sh7372_extal1_clk;
--- /dev/null
+++ work/arch/arm/mach-shmobile/memchunk.c	2011-03-17 14:03:04.000000000 +0900
@@ -0,0 +1,78 @@
+/*
+ * SH-Mobile "memchunk" - Physically contiguous memory reservation code
+ *
+ * Copyright (C) 2010  Magnus Damm
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/memblock.h>
+#include <mach/common.h>
+
+static int __init memchunk_setup(char *str)
+{
+	return 1; /* accept anything that begins with "memchunk." */
+}
+__setup("memchunk.", memchunk_setup);
+
+static void __init memchunk_cmdline_override(char *name, unsigned long *sizep)
+{
+	char *p = boot_command_line;
+	int k = strlen(name);
+
+	while ((p = strstr(p, "memchunk."))) {
+		p += 9; /* strlen("memchunk.") */
+		if (!strncmp(name, p, k) && p[k] == '=') {
+			p += k + 1;
+			*sizep = memparse(p, NULL);
+			pr_info("%s: forcing memory chunk size to 0x%08lx\n",
+				name, *sizep);
+			break;
+		}
+	}
+}
+
+int __init shmobile_memchunk_setup(struct platform_device *pdev,
+				   char *name, unsigned long memsize)
+{
+	struct resource *r;
+	u64 addr;
+
+	r = pdev->resource + pdev->num_resources - 1;
+	if (r->flags) {
+		pr_warning("%s: unable to find empty space for resource\n",
+			name);
+		return -EINVAL;
+	}
+
+	memchunk_cmdline_override(name, &memsize);
+	if (!memsize)
+		return 0;
+
+	addr = memblock_alloc(memsize, PAGE_SIZE);
+	if (addr == ~(u64)0) {
+		pr_warning("%s: unable to allocate memory\n", name);
+		return -ENOMEM;
+	}
+	memblock_free(addr, memsize);
+	memblock_remove(addr, memsize);
+
+	r->flags = IORESOURCE_MEM;
+	r->start = addr;
+	r->end = r->start + memsize - 1;
+	r->name = name;
+	return 0;
+}
--- 0001/arch/arm/mach-shmobile/setup-sh7372.c
+++ work/arch/arm/mach-shmobile/setup-sh7372.c	2011-03-17 14:03:03.000000000 +0900
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/platform_device.h>
+#include <linux/uio_driver.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/io.h>
@@ -30,6 +31,7 @@
 #include <linux/sh_intc.h>
 #include <linux/sh_timer.h>
 #include <mach/hardware.h>
+#include <mach/common.h>
 #include <mach/sh7372.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -601,6 +603,35 @@ static struct platform_device dma2_devic
 	},
 };
 
+/* MFIS */
+static struct uio_info mfis_platform_data = {
+	.name = "MFIS",
+	.version = "0",
+	.irq = evt2irq(0x0920),
+};
+
+static struct resource mfis_resources[] = {
+	[0] = {
+		.name	= "MFIS",
+		.start	= 0xe6a60000,
+		.end	= 0xe6a7ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		/* place holder for contiguous memory */
+	},
+};
+
+static struct platform_device mfis_device = {
+	.name		= "uio_pdrv_genirq",
+	.id		= 8,
+	.dev = {
+		.platform_data	= &mfis_platform_data,
+	},
+	.resource	= mfis_resources,
+	.num_resources	= ARRAY_SIZE(mfis_resources),
+};
+
 static struct platform_device *sh7372_early_devices[] __initdata = {
 	&scif0_device,
 	&scif1_device,
@@ -620,8 +651,11 @@ static struct platform_device *sh7372_la
 	&dma0_device,
 	&dma1_device,
 	&dma2_device,
+	&mfis_device,
 };
 
+void set_rtcpu_base(unsigned long base);
+
 void __init sh7372_add_standard_devices(void)
 {
 	platform_add_devices(sh7372_early_devices,
@@ -631,6 +665,13 @@ void __init sh7372_add_standard_devices(
 			    ARRAY_SIZE(sh7372_late_devices));
 }
 
+void __init sh7372_reserve_memory(void)
+{
+	/* reserve MFIS memory first to put it at the top of memory */
+	shmobile_memchunk_setup(&mfis_device, "mfis", 64 << 20);
+	set_rtcpu_base((mfis_device.resource + 1)->start);
+}
+
 void __init sh7372_add_early_devices(void)
 {
 	early_platform_add_devices(sh7372_early_devices,

[-- Attachment #5: linux-2.6.38-arm-shmobile-mackerel-sh-core-slave-20110317.config.gz --]
[-- Type: application/x-gzip, Size: 10260 bytes --]

[-- Attachment #6: rtcpu-loader-20110228.c --]
[-- Type: text/x-csrc, Size: 17853 bytes --]

/*
 * rtcpu-loader - AP4 SH Core Linux backing code, 20110228 Magnus Damm
 *
 * A small Linux program that executes a SH Linux kernel on the RT-CPU.
 *
 * Compile for Linux running on the ARM side using:
 * $ arm-cross-gcc -o rtcpu-loader rtcpu-loader.c
 *
 * Includes code from the lguest, many thanks to Rusty Russell.
 * For more info see linux kernel source Documentation/lguest/lguest.c
 *
 * 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; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

#include <linux/if_tun.h>
#include <net/if.h>

#define IRQ_EARLY_CONSOLE 0 /* must match kernel irq setup */
#define IRQ_POWER_OFF 1 /* must match kernel irq setup */
#define IRQ_TIMER 2 /* must match kernel irq setup */
#define IRQ_VIRTIO 3 /* must match kernel irq setup */
#define IRQ_CONSOLE_RX 4
#define IRQ_CONSOLE_TX 5
#define IRQ_NET_RX 6
#define IRQ_NET_TX 7
#define IRQ_NR 16

static int fgets_with_openclose(char *fname, char *buf, size_t maxlen) {
       FILE *fp;

       if ((fp = fopen(fname, "r")) != NULL) {
               fgets(buf, maxlen, fp);
               fclose(fp);
               return strlen(buf);
       } else {
               return -1;
       }
}

struct uio_device {
       char *name;
       char *path;
       int fd;
};

#define MAXUIOIDS  100
#define MAXNAMELEN 256

static int locate_uio_device(char *name, struct uio_device *udp)
{
       char fname[MAXNAMELEN], buf[MAXNAMELEN];
       int uio_id, i;

       for (uio_id = 0; uio_id < MAXUIOIDS; uio_id++) {
               sprintf(fname, "/sys/class/uio/uio%d/name", uio_id);
               if (fgets_with_openclose(fname, buf, MAXNAMELEN) < 0)
                       continue;
               if (strncmp(name, buf, strlen(name)) == 0)
                       break;
       }

       if (uio_id >= MAXUIOIDS)
               return -1;

       udp->name = strdup(buf);
       udp->path = strdup(fname);
       udp->path[strlen(udp->path) - 4] = '\0';

       sprintf(buf, "/dev/uio%d", uio_id);
       udp->fd = open(buf, O_RDWR|O_SYNC /*| O_NONBLOCK*/);

       if (udp->fd < 0) {
               perror("open");
               return -1;
       }

       return 0;
}

struct uio_map {
       unsigned long address;
       unsigned long size;
       void *iomem;
};

static int setup_uio_map(struct uio_device *udp, int nr, struct uio_map *ump)
{
       char fname[MAXNAMELEN], buf[MAXNAMELEN];
 
       sprintf(fname, "%s/maps/map%d/addr", udp->path, nr);
       if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0)
               return -1;

       ump->address = strtoul(buf, NULL, 0);

       sprintf(fname, "%s/maps/map%d/size", udp->path, nr);
       if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0)
               return -1;

       ump->size = strtoul(buf, NULL, 0);

       ump->iomem = mmap(0, ump->size,
                         PROT_READ|PROT_WRITE, MAP_SHARED,
                         udp->fd, nr * getpagesize());

       if (ump->iomem == MAP_FAILED)
               return -1;

       return 0;
}

struct uio_device uio_dev;
struct uio_map uio_mmio, uio_mem;

typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned long ___u32;
typedef unsigned long long __u64;
struct lguest_device_desc {
	/* The device type: console, network, disk etc.  Type 0 terminates. */
	__u8 type;
	/* The number of virtqueues (first in config array) */
	__u8 num_vq;
	/*
	 * The number of bytes of feature bits.  Multiply by 2: one for host
	 * features and one for Guest acknowledgements.
	 */
	__u8 feature_len;
	/* The number of bytes of the config array after virtqueues. */
	__u8 config_len;
	/* A status byte, written by the Guest. */
	__u8 status;
	__u8 config[0];
};

/*D:135
 * This is how we expect the device configuration field for a virtqueue
 * to be laid out in config space.
 */
struct lguest_vqconfig {
	/* The number of entries in the virtio_ring */
	__u16 num;
	/* The interrupt we get when something happens. */
	__u16 irq;
	/* The page number of the virtio ring for this device. */
	___u32 pfn;
};
/*:*/

#define RING_NR 256

/* Virtio ring descriptors: 16 bytes.  These can chain together via "next". */
struct vring_desc {
	/* Address (guest-physical). */
	__u64 addr;
	/* Length. */
	___u32 len;
	/* The flags as indicated above. */
	__u16 flags;
	/* We chain unused descriptors via this, too */
	__u16 next;
};

struct vring_avail {
	__u16 flags;
	__u16 idx;
	__u16 ring[];
};

/* u32 is used here for ids for padding reasons. */
struct vring_used_elem {
	/* Index of start of used descriptor chain. */
	___u32 id;
	/* Total length of the descriptor chain which was used (written to) */
	___u32 len;
};

struct vring_used {
	__u16 flags;
	__u16 idx;
	volatile struct vring_used_elem ring[];
};

struct vring {
	unsigned int num;

	volatile struct vring_desc *desc;

	volatile struct vring_avail *avail;

	volatile struct vring_used *used;
};

static inline void vring_init(volatile struct vring *vr, unsigned int num,
			      void *p, unsigned long align)
{
	vr->num = num;
	vr->desc = p;
	vr->avail = p + num*sizeof(struct vring_desc);
	vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1)
			    & ~(align - 1));
}

static inline unsigned vring_size(unsigned int num, unsigned long align)
{
	return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (2 + num)
		 + align - 1) & ~(align - 1))
		+ sizeof(__u16) * 2 + sizeof(struct vring_used_elem) * num;
}

struct __vq {
	unsigned int local;
	volatile struct vring vring;
};

volatile struct __vq console_in;
volatile struct __vq console_out;

volatile struct __vq net_in;
volatile struct __vq net_out;

void setup_vq(volatile struct __vq *vq, unsigned long pfn)
{
	unsigned long offset;

	offset = (pfn << 12) - uio_mem.address;

	vq->local = 0;
	memset(uio_mem.iomem + offset, 0, vring_size(RING_NR, 4096));
	vring_init(&vq->vring, RING_NR, uio_mem.iomem + offset, 4096);
}

#define wmb() __asm__ __volatile__("" : : : "memory")

int vq_do(volatile struct __vq *vq, int input, int output)
{
	volatile struct vring *vring = &vq->vring;
	volatile struct vring_desc *desc;
	volatile struct vring_used_elem *used;
	int idx = vring->avail->idx;
	int i, len;

	desc = vring->desc;

	if (vq->local == idx)
		return 0;

	i = vq->vring.avail->ring[vq->local % vq->vring.num];

	wmb();
	if (output) {
		write(0, (desc[i].addr - uio_mem.address)
		      + uio_mem.iomem, desc[i].len);
		len = 0;
	}

	if (input) {
		unsigned char in_buf;
		len = read(1, &in_buf, 1);
		if (len > 0)
			*(unsigned char *)((desc[i].addr - uio_mem.address) +
					   uio_mem.iomem) = in_buf;

		if (len <= 0)
			return -1;
	}

	wmb();

	used = &vring->used->ring[vring->used->idx % vring->num];
	used->id = i;
	used->len = len;

	wmb();

	vring->used->idx++;

	vq->local++;

	return 1;
}

static int vq_first(volatile struct __vq *vq,
		    unsigned long *addrp,
		    int *lenp, int *headp)
{
	volatile struct vring *vring = &vq->vring;
	volatile struct vring_desc *desc;
	int idx = vring->avail->idx;
	int i;

	desc = vring->desc;

	if (vq->local == idx)
		return 0;

	i = vq->vring.avail->ring[vq->local % vq->vring.num];

	wmb();

	*addrp = desc[i].addr;
	*lenp = desc[i].len;
	*headp = i;

	return 1;
}

static int vq_next(volatile struct __vq *vq,
		    unsigned long *addrp,
		    int *lenp, int *currp)
{
	volatile struct vring *vring = &vq->vring;
	volatile struct vring_desc *desc = vring->desc;
	int i;

	/* handle looped descriptor */
	if (desc[*currp].flags & 1) {
		i = desc[*currp].next;

		*addrp = desc[i].addr;
		*lenp = desc[i].len;
		*currp = i;
		return 1;
	}

	return 0;
}

static void *uio_addr(unsigned long addr)
{
	return uio_mem.iomem + (addr - uio_mem.address);
}

static void vq_used(volatile struct __vq *vq, int len, int head)
{
	volatile struct vring *vring = &vq->vring;
	volatile struct vring_used_elem *used;

	used = &vring->used->ring[vring->used->idx % vring->num];
	used->id = head;
	used->len = len;

	wmb();

	vring->used->idx++;

	vq->local++;
}

int vq_do_net(volatile struct __vq *vq, int input, int output)
{
	struct iovec iov[vq->vring.num];
	int sum = 0;
	unsigned long addr;
	int len;
	int head;
	int curr;
	int k, i;

	int n = 0;
	int m;

	if (vq_first(vq, &addr, &len, &head)) {

		curr = head;
		do {
			iov[n].iov_base = uio_addr(addr);
			iov[n].iov_len = len;
			n++;
		} while (vq_next(vq, &addr, &len, &curr));

		if (output) {
			sum = writev(output, iov, n);
			len = 0;
		}

		if (input) {
			sum = readv(input, iov, n);
			if (sum <= 0)
				return -1;
			len = sum;
		}
#if 0
		printf("iov %d/%d: %d vectors of total %d bytes:\n",
		       input, output, n, sum);
		m = 0;
		for (i = 0; i < n; i++) {
		  printf("%d: ", i);
		  for (k = 0; k < iov[i].iov_len; k++) 
		    if (m < sum) {
		      printf("%02x ", *(unsigned char *)(iov[i].iov_base + k));
		      m++;
		    }

		  printf("\n");
		}
#endif
		vq_used(vq, len, head);
		return 1;
	}

	return 0;
}

unsigned long setup_virtio_device(void *buf, int type,
				  int irq_in, unsigned long pfn_in,
				  volatile struct __vq *vq_in,
				  int irq_out, unsigned long pfn_out,
				  volatile struct __vq *vq_out)
{
	struct lguest_device_desc *dd = buf;
	struct lguest_vqconfig vq0, vq1;

	dd->type = type; /* VIRTIO_ID_... */
	dd->num_vq = 2;

	memset(&vq0, 0, sizeof(vq0));
	vq0.num = RING_NR;
	vq0.irq = irq_in;
	vq0.pfn = pfn_in;
	memcpy(buf + sizeof(*dd), &vq0, sizeof(vq0));
	setup_vq(vq_in, vq0.pfn);

	memset(&vq1, 0, sizeof(vq1));
	vq1.num = RING_NR;
	vq1.irq = irq_out;
	vq1.pfn = pfn_out;
	memcpy(buf + sizeof(*dd) + sizeof(vq0), &vq1, sizeof(vq1));
	setup_vq(vq_out, vq1.pfn);

	return sizeof(*dd) + sizeof(vq0) + sizeof(vq1);
}

static int setup_tun(char *name)
{
	struct ifreq ifr;
	int fd, err;
	char *clonedev = "/dev/net/tun";

	if ((fd = open(clonedev, O_RDWR)) < 0)
		return 0;

	memset(&ifr, 0, sizeof(ifr));
	ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
	strncpy(ifr.ifr_name, name, IFNAMSIZ);

	if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
		close(fd);
		return 0;
	}
	return fd;
}

/* The original tty settings to restore on exit. */
static struct termios orig_term;

static void cleanup_devices(void)
{

	/* If we saved off the original terminal settings, restore them now. */
	if (orig_term.c_lflag & (ISIG|ICANON|ECHO))
		tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
}

static void dump_pending(char *str, unsigned long *pending)
{
#if 0
	int k;
	
	printf("%s ", str);
	for (k = 0; k < IRQ_NR; k++)
		printf("%d ", pending[k]);
	printf("\n");
#endif
}

static struct timeval select_time;
static struct timeval *select_timeval; /* NULL - always blocking by default */

static struct timeval last_time;
static struct timeval next_time;

static void update_next_time(void)
{
	next_time.tv_usec += 10000;

	if (next_time.tv_usec / 1000000) {
		next_time.tv_usec %= 1000000;
		next_time.tv_sec += 1;
	}
}

static void update_last_time(void)
{
	last_time = next_time;
	update_next_time();
}

static void init_timer(void)
{
	gettimeofday(&last_time, NULL);
	next_time = last_time;
	update_next_time();

	select_timeval = &select_time;
}

static int pending_timer(void)
{
	struct timeval now;

	gettimeofday(&now, NULL);

	if ((now.tv_sec >= next_time.tv_sec) ||
	    ((now.tv_sec == next_time.tv_sec) &&
	     (now.tv_usec >= next_time.tv_usec))) {
		update_last_time();
		return 1;
	}

	return 0;
}

static void update_timeout(void)
{
	struct timeval now;
	int usec;

	gettimeofday(&now, NULL);

	if (next_time.tv_sec > now.tv_sec) {
		usec = (next_time.tv_sec - now.tv_sec) * 1000000;
		usec += next_time.tv_usec - now.tv_usec;
	}
	else {
		if (next_time.tv_usec > now.tv_usec)
			usec = next_time.tv_usec - now.tv_usec;
		else
			usec = 0;
	}

	select_time.tv_sec = 0;
	select_time.tv_usec = usec % 1000000;
}

int main(int argc, char *argv[])
{
	int ret;
	unsigned long offset = 0;
	int tun_fd = 0;

	if (argc < 2) {
		fprintf(stderr, "usage: %s sh-binary [offset]\n", argv[0]);
		return 1;
	}

	ret = locate_uio_device("MFIS", &uio_dev);
	if (ret < 0)
		return ret;
       
	ret = setup_uio_map(&uio_dev, 0, &uio_mmio);
	if (ret < 0)
		return ret;

	ret = setup_uio_map(&uio_dev, 1, &uio_mem);
	if (ret < 0)
		return ret;

	if (argc >= 3) {
		offset = strtoul(argv[2], NULL, 0);

		/* fill the up the offset window with ADD opcodes */
		memset(uio_mem.iomem, 0x3c, offset);
	}

	/* If we can save the initial standard input settings... */
	if (tcgetattr(STDIN_FILENO, &orig_term) == 0) {
		struct termios term = orig_term;

		/* If we exit this restores the tty. */
		atexit(cleanup_devices);
		/*
		 * Then we turn off echo, line buffering and ^C etc: We want a
		 * raw input stream to the Guest.
		 */
		term.c_lflag &= ~(ISIG|ICANON|ECHO);
		tcsetattr(STDIN_FILENO, TCSANOW, &term);
	}

	{
		int fd = open(argv[1], O_RDONLY);
		int n;

		if (fd < 0) {
			perror("open");
			return -1;
		}

		n = read(fd, uio_mem.iomem + offset, uio_mem.size - offset);
		if (n <= 0) {
			perror("read");
			return -1;
		}
	}

	/* Clear pending interrupts in MFIS */
	{
		unsigned long *p = (uio_mmio.iomem + 0x14); /* mfiseicr */

		*p = 0;
	}

	/* Enable interrupt in UIO driver */
	{
		unsigned long enable = 1;

		write(uio_dev.fd, &enable, sizeof(u_long));
	}

	/* Setup lguest VIRTIO device descriptor at top 1M of memory.
	 * The kernel must have a matching end-of-memory setup,
	 * either using mem=63M kernel command line option or
	 * using CONFIG_MEMORY_SIZE=0x03f00000 (on a 64M system).
 	 */

	{
		unsigned long top_offset = 1 << 20; /* 1M for virtio */
		unsigned long max_pfn;
		unsigned long offset;

		max_pfn = (uio_mem.address + uio_mem.size - top_offset) >> 12;
		offset = (max_pfn << 12) - uio_mem.address;

		/* clear the page to begin with */
		memset(uio_mem.iomem + offset, 0x00, 4096);

		/* setup console device */
		offset += setup_virtio_device(uio_mem.iomem + offset,
					      3, /* VIRTIO_ID_CONSOLE */
					      IRQ_CONSOLE_RX,
					      max_pfn + 16, &console_in,
					      IRQ_CONSOLE_TX,
					      max_pfn + 32, &console_out);

		/* setup network device if possible */
		tun_fd = setup_tun("tap0");
		if (tun_fd) {
			setup_virtio_device(uio_mem.iomem + offset,
					    1, /* VIRTIO_ID_NET */
					    IRQ_NET_RX,
					    max_pfn + 48, &net_in,
					    IRQ_NET_TX,
					    max_pfn + 64, &net_out);
		}
	}

	{
		unsigned long *mfiseicr = uio_mmio.iomem + 0x14;
		unsigned long *mfisiicr = uio_mmio.iomem + 0x10;
		unsigned long *a2s_enabled = uio_mmio.iomem + 0x10100;
		unsigned long *a2s_pending = uio_mmio.iomem + 0x10200;
		unsigned long *s2a_pending = uio_mmio.iomem + 0x10300;
		int a2s_irq, s2a_irq;
		int timer_enabled = 0;
		fd_set rfds;
		int ret;
		int hi_fd;
		int tmp;

		/* clear soft interrupt data structures */
		memset(a2s_enabled, 0, 0x100);
		memset(a2s_pending, 0, 0x100);
		memset(s2a_pending, 0, 0x100);

		/* Boot RT-CPU by generating MFI IRQ to RT-CPU */
		*mfisiicr = 1;

		while (1) {
			FD_ZERO(&rfds);
			FD_SET(uio_dev.fd, &rfds);
			FD_SET(1, &rfds);
			hi_fd = uio_dev.fd;
			if (tun_fd)
				FD_SET(tun_fd, &rfds);

			if (tun_fd > hi_fd)
				hi_fd = tun_fd;

			ret = select(hi_fd + 1, &rfds, NULL, NULL,
				     select_timeval);

			a2s_irq = s2a_irq = 0;

			/* incoming data from UIO */
			if (ret && FD_ISSET(uio_dev.fd, &rfds)) {
				unsigned long enable = 1;
				unsigned long n_pending;
				read(uio_dev.fd, &n_pending, sizeof(u_long));
				dump_pending("s2a", s2a_pending);
				*mfiseicr = 0;
				write(uio_dev.fd, &enable, sizeof(u_long));
				s2a_irq = 1;
			}

			/* incoming data from stdin */
			if (ret && FD_ISSET(1, &rfds)) {
				vq_do(&console_in, 1, 0);
				a2s_pending[IRQ_CONSOLE_RX] = 1;
				a2s_irq |= a2s_enabled[IRQ_CONSOLE_RX];
			}

			/* incoming data from the network */
			if (ret && tun_fd && FD_ISSET(tun_fd, &rfds)) {
				if (vq_do_net(&net_in, tun_fd, 0)) {
					a2s_pending[IRQ_NET_RX] = 1;
					a2s_irq |= a2s_enabled[IRQ_NET_RX];
				}
			}

			/* only generate timer ticks after requested by slave */
			if (s2a_irq && s2a_pending[IRQ_TIMER]) {
				s2a_pending[IRQ_TIMER] = 0;
				if (!timer_enabled) {
					init_timer();
					timer_enabled = 1;
				}
			}

			if (timer_enabled && pending_timer()) {
				a2s_pending[IRQ_TIMER] = 1;
				a2s_irq |= a2s_enabled[IRQ_TIMER];
			}

			if (s2a_irq && s2a_pending[IRQ_EARLY_CONSOLE]) {
				printf("%c", s2a_pending[IRQ_EARLY_CONSOLE]);
				s2a_pending[IRQ_EARLY_CONSOLE] = 0;
			}

			if (s2a_irq && s2a_pending[IRQ_CONSOLE_TX]) {
				s2a_pending[IRQ_CONSOLE_TX] = 0;
				while (vq_do(&console_out, 0, 1))
				  ;

				a2s_pending[IRQ_CONSOLE_TX] = 1;
				a2s_irq |= a2s_enabled[IRQ_CONSOLE_TX];
			}

			if (s2a_irq && s2a_pending[IRQ_NET_TX] && tun_fd) {
				s2a_pending[IRQ_NET_TX] = 0;

				while (vq_do_net(&net_out, 0, tun_fd))
					;

				a2s_pending[IRQ_NET_TX] = 1;
				a2s_irq |= a2s_enabled[IRQ_NET_TX];
			}

			if (s2a_irq && s2a_pending[IRQ_POWER_OFF]) {
				s2a_pending[IRQ_POWER_OFF] = 0;
				exit(0);
			}

			/* trigger RT-CPU IRQ */
			if (a2s_irq) {
				dump_pending("a2s", a2s_pending);
				*mfisiicr = 1;
			}

			update_timeout();
		}
		
	}

	return 0;
}

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-03-17 11:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-17  6:40 SH Core Linux 20110317 Magnus Damm
2011-03-17  6:56 ` Mike Frysinger
2011-03-17  7:10 ` Magnus Damm
2011-03-17  7:55 ` Mike Frysinger
2011-03-17 11:21 ` Magnus Damm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).