Patchwork [1/5] powerpc: Add platform support for AmigaOne

login
register
mail settings
Submitter Gerhard Pircher
Date Jan. 7, 2009, 1:54 p.m.
Message ID <20090107135457.234150@gmx.net>
Download mbox | patch
Permalink /patch/17116/
State Changes Requested
Headers show

Comments

Gerhard Pircher - Jan. 7, 2009, 1:54 p.m.
This commit adds the setup code for booting Linux on AmigaOne G3SE (G3
only), AmigaOne XE and uA1 (G3/G4) desktop computers. These boards were
sold by Eyetech and are based on MAI Logic's Teron boards and its
Articia S northbridge.
The AmigaOne uses U-boot as firmware, which doesn't support a flattened
device tree yet. The northbridge has some design flaws, which makes it
necessary to use non cacheable memory for DMA operations
(CONFIG_NOT_COHERENT_CACHE) and to avoid setting the coherence (M) flag
for memory pages.

Signed-off-by: Gerhard Pircher <gerhard_pircher@gmx.net>
---
 arch/powerpc/platforms/Kconfig           |    1 +
 arch/powerpc/platforms/Makefile          |    1 +
 arch/powerpc/platforms/amigaone/Kconfig  |   18 +++
 arch/powerpc/platforms/amigaone/Makefile |    1 +
 arch/powerpc/platforms/amigaone/setup.c  |  197 ++++++++++++++++++++++++++++++
 5 files changed, 218 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/platforms/amigaone/Kconfig
 create mode 100644 arch/powerpc/platforms/amigaone/Makefile
 create mode 100644 arch/powerpc/platforms/amigaone/setup.c
Grant Likely - Jan. 7, 2009, 4:07 p.m.
On Wed, Jan 7, 2009 at 6:54 AM, Gerhard Pircher <gerhard_pircher@gmx.net> wrote:
> +
> +void amigaone_show_cpuinfo(struct seq_file *m)
> +{
> +       struct device_node *root;
> +       const char *model = "";
> +
> +       root = of_find_node_by_path("/");
> +       if (root)
> +               model = of_get_property(root, "model", NULL);
> +       seq_printf(m, "machine\t\t: %s\n", model);
> +
> +       of_node_put(root);
> +       return;
> +}

show_cpuinfo() common code already prints the model.  You can drop this hook.

> +void __init amigaone_setup_arch(void)
> +{
> +       struct device_node *np;
> +
> +       /* Initialization until calibrate_delay() runs. */
> +       loops_per_jiffy = 50000000/HZ;

If it doesn't noticeably slow down the boot then I would remove this line.

Otherwise looks okay to me.

g.
Scott Wood - Jan. 7, 2009, 7:07 p.m.
On Wed, Jan 07, 2009 at 02:54:57PM +0100, Gerhard Pircher wrote:
> +void amigaone_show_cpuinfo(struct seq_file *m)
> +{
> +	struct device_node *root;
> +	const char *model = "";
> +
> +	root = of_find_node_by_path("/");
> +	if (root)
> +		model = of_get_property(root, "model", NULL);
> +	seq_printf(m, "machine\t\t: %s\n", model);
> +
> +	of_node_put(root);
> +	return;

This is already printed by the generic cpuinfo.

> +void __init amigaone_setup_arch(void)
> +{
> +	struct device_node *np;
> +
> +	/* Initialization until calibrate_delay() runs. */
> +	loops_per_jiffy = 50000000/HZ;

Is this really necessary?

> +	/* Flush and disable I/D cache. */
> +	__asm__ __volatile__ ("mfspr	3, 1008"	::: "r3");
> +	__asm__ __volatile__ ("ori	5, 5, 0xcc00"	::: "r5");
> +	__asm__ __volatile__ ("ori	4, 3, 0xc00"	::: "r4");
> +	__asm__ __volatile__ ("andc	5, 3, 5"	::: "r5");

Don't do this; instead, have one multi-line asm statement (or better yet,
just use mfspr()/mtspr()/sync()/isync()).

GCC is perfectly free to trash your registers in between statements.

-Scott
Gerhard Pircher - Jan. 7, 2009, 10:50 p.m.
-------- Original-Nachricht --------
> Datum: Wed, 7 Jan 2009 09:41:14 -0700
> Von: "Grant Likely" <grant.likely@secretlab.ca>
> An: "Gerhard Pircher" <gerhard_pircher@gmx.net>
> CC: linuxppc-dev@ozlabs.org
> Betreff: Re: [PATCH 2/5] powerpc: Generic device tree for all AmigaOne boards

> Sounds to me like you need different device tree variants for each of
> the AmigaOne boards.  Do any of the boards have physical PCI slots?
> If so, then the lack of an interrupt map will break them.
Yes, all AmigaOne boards have physical PCI slots (at least 1). The different
interrupt routing wasn't a problem so far, as the firmware writes the IRQ number
to the interrupt line register of every PCI device.
Currently the kernel reads the IRQ number from this register, if there is no
interrupt mapping property. I know that it's not a good idea to rely on kernel
fallback behavior, but it makes a lot of things easier in this case.

> > +/ {
> > +       model = "AmigaOne";
> > +       compatible = "eyetech,amigaone","mai-logic,teron";
> 
> Experience has shown that trying to claim one board to be compatible
> with another is too ambiguous.  It is better to make the board level
> compatible property have a single value specifying the exact board
> model and have the platform support code include a list of supported
> board models.  Otherwise you end up with odd heuristic code to try and
> differentiate between the models (for bug fixes and such).
Okay, I'll remove the "mai-logic,teron" entry. I have never seen a MAI Teron
in the wild, so I can't even tell if it uses a U-boot firmware.

> > +               host@0 {
> > +                       compatible = "pciclass,0600";
> > +                       vendor-id = <0x000010cc>;
> > +                       device-id = <0x00000660>;
> > +                       revision-id = <0x00000001>;
> > +                       class-code = <0x00060000>;
> > +                       subsystem-id = <0>;
> > +                       subsystem-vendor-id = <0>;
> > +                       devsel-speed = <0x00000001>;
> > +                       66mhz-capable;
> > +                       min-grant = <0>;
> > +                       max-latency = <0>;
> > +                       // AGP aperture is unset.
> > +//                     reg = <0x42000010 0 0x00000000 0 0x00400000>;
> > +//                     assigned-addresses = <0x42000010 0 0x00000000 0
> 0x00400000>;
> > +               };
> 
> What does this node describe?  Is it something that isn't probeable?
Yes, the information in this node is probeable. Originally I planned to add
some child nodes to this node, as the PCI host bridge contains other PCI
functions (e.g. power management) which are not probeable. The host bridge
has an index register, which switches between the host bridge config space
and the config space of the other PCI functions. I don't know yet how this is
described correctly in a device tree, so I'll probably remove this node for now.

> > +                       8042@60 {
> > +                               device_type = "8042";
> > +                               reg = <1 0x00000060 0x00000001
> > +                                      1 0x00000064 0x00000001>;
> > +                               // IRQ1, IRQ12 (rising edge)
> > +                               interrupts = <1 3 12 3>;
> 
> For the flattened device tree, I think we've settled on the convention
> that every node with an IRQ connection should have both the
> interrupt-parent and interrupts properties.  (ie. don't rely on the
> parent node's interrupt-parent property.)
Even for ISA devices?

> > +                               #address-cells = <1>;
> > +                               #size-cells = <0>;
> > +
> > +                               keyboard@0 {
> > +                                       device_type = "keyboard";
> 
> Drop device type in the flattened tree.  There are a few places where
> you still need to have it for Linux to work; but it Linux doesn't look
> for it, then don't add it.
Okay.

> > +                       serial@3f8 {
> > +                               device_type = "serial";
> > +                               compatible = "pnpPNP,501","pnpPNP,500";
> > +                               reg = <1 0x000003f8 0x00000008>;
> > +                               // IRQ4 (rising edge)
> > +                               interrupts = <4 3>;
> > +                               clock-frequency = <1843200>;
> > +                               current-speed = <115200>;
> > +                       };
> > +
> > +                       serial@2f8 {
> > +                               device_type = "serial";
> > +                               compatible = "pnpPNP,501","pnpPNP,500";
> > +                               reg = <1 0x000002f8 0x00000008>;
> > +                               // IRQ3 (rising edge)
> > +                               interrupts = <3 3>;
> > +                               clock-frequency = <1843200>;
> > +                               current-speed = <115200>;
> > +                       };
> > +
> > +                       parallel@378 {
> > +                               device_type = "parallel";
> > +                               // No ECP support for now, otherwise add
> "pnpPNP,401".
> > +                               compatible = "pnpPNP,400";
> > +                               reg = <1 0x00000378 0x00000003
> > +                                      1 0x00000778 0x00000003>;
> > +/*                             interrupts = <7 0>; */
> > +                               // Parallel port DMA mode unknown.
> > +/*                             dma = <0x3 0x0 0x0 0x0>; */
> > +                       };
> > +
> > +                       fdc@3f0 {
> > +                               device_type = "fdc";
> > +                               compatible = "pnpPNP,700";
> > +                               reg = <1 0x000003f0 0x00000008>;
> > +                               // IRQ6 (rising edge)
> > +                               interrupts = <6 3>;
> > +                               // Floppy DMA mode unknown.
> > +/*                             dma = < >; */
> > +                               #address-cells = <1>;
> > +                               #size-cells = <0>;
> > +
> > +                               disk@0 {
> > +                                       device_type = "block";
> > +                                       reg = <0>;
> > +                               };
> > +                       };
> > +               };
> > +
> > +               ide@7,1 {
> > +                       compatible = "pciclass,01018a";
> > +                       vendor-id = <0x00001106>;
> > +                       device-id = <0x00000571>;
> > +                       revision-id = <0x00000006>;
> 
> Can this PCI device be probed?  Typically PCI devices don't get added
> to the flattened device tree because PCI is a probeable bus.
Yes, it can be probed. I thought it would be a good idea to include it,
because the IDE controller operates in legacy mode. I planned to specify the
two legacy interrupts in this node (as you can see), but the kernel didn't like
them.

Thanks!

Gerhard
Benjamin Herrenschmidt - Jan. 12, 2009, 5:08 a.m.
> > +	/* Flush and disable I/D cache. */
> > +	__asm__ __volatile__ ("mfspr	3, 1008"	::: "r3");
> > +	__asm__ __volatile__ ("ori	5, 5, 0xcc00"	::: "r5");
> > +	__asm__ __volatile__ ("ori	4, 3, 0xc00"	::: "r4");
> > +	__asm__ __volatile__ ("andc	5, 3, 5"	::: "r5");
> 
> Don't do this; instead, have one multi-line asm statement (or better yet,
> just use mfspr()/mtspr()/sync()/isync()).
> 
> GCC is perfectly free to trash your registers in between statements.

Also there's already some code around to properly flush & disable the
caches on those processors.

Ben.
Benjamin Herrenschmidt - Jan. 12, 2009, 5:12 a.m.
> Yes, all AmigaOne boards have physical PCI slots (at least 1). The different
> interrupt routing wasn't a problem so far, as the firmware writes the IRQ number
> to the interrupt line register of every PCI device.

The code in the kernel that retreives the interrupt that way is clearly
marked as a fishy workaround for bogus firmwares :-)

But I'm not going to reject things based on that, it will work for
simple board using really only legacy interrupts like yours...

> Currently the kernel reads the IRQ number from this register, if there is no
> interrupt mapping property. I know that it's not a good idea to rely on kernel
> fallback behavior, but it makes a lot of things easier in this case.

Sort-of. As long as it's really 8259 interrupts, I suppose it's
acceptable.

> > For the flattened device tree, I think we've settled on the convention
> > that every node with an IRQ connection should have both the
> > interrupt-parent and interrupts properties.  (ie. don't rely on the
> > parent node's interrupt-parent property.)
> Even for ISA devices?

I disagree with Grant here. Especially in simple ISA cases like that,
there's really no point in bloating the device-tree.

> > Can this PCI device be probed?  Typically PCI devices don't get added
> > to the flattened device tree because PCI is a probeable bus.
> Yes, it can be probed. I thought it would be a good idea to include it,
> because the IDE controller operates in legacy mode. I planned to specify the
> two legacy interrupts in this node (as you can see), but the kernel didn't like
> them.

Well, the kernel just didn't make use of them I'd say :-) But that can
probably be fixed with the appropriate hacks. 

Cheers,
Ben.
Gerhard Pircher - Jan. 12, 2009, 11:39 p.m.
-------- Original-Nachricht --------
> Datum: Mon, 12 Jan 2009 16:08:07 +1100
> Von: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> An: Scott Wood <scottwood@freescale.com>
> CC: Gerhard Pircher <gerhard_pircher@gmx.net>, linuxppc-dev@ozlabs.org
> Betreff: Re: [PATCH 1/5] powerpc: Add platform support for AmigaOne

> 
> > > +	/* Flush and disable I/D cache. */
> > > +	__asm__ __volatile__ ("mfspr	3, 1008"	::: "r3");
> > > +	__asm__ __volatile__ ("ori	5, 5, 0xcc00"	::: "r5");
> > > +	__asm__ __volatile__ ("ori	4, 3, 0xc00"	::: "r4");
> > > +	__asm__ __volatile__ ("andc	5, 3, 5"	::: "r5");
> > 
> > Don't do this; instead, have one multi-line asm statement (or better
> yet,
> > just use mfspr()/mtspr()/sync()/isync()).
> > 
> > GCC is perfectly free to trash your registers in between statements.
> 
> Also there's already some code around to properly flush & disable the
> caches on those processors.
Ouch! I searched through all assembler files in arch/powerpc/kernel/ for
such a function, but I have missed flush_disable_L1 in l2cr_6xx.S.
Thanks for the hint!

Gerhard

Patch

diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 47fe2be..3fce996 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -28,6 +28,7 @@  source "arch/powerpc/platforms/86xx/Kconfig"
 source "arch/powerpc/platforms/embedded6xx/Kconfig"
 source "arch/powerpc/platforms/44x/Kconfig"
 source "arch/powerpc/platforms/40x/Kconfig"
+source "arch/powerpc/platforms/amigaone/Kconfig"
 
 config PPC_NATIVE
 	bool
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index 8079e0b..f741919 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -19,3 +19,4 @@  obj-$(CONFIG_PPC_PASEMI)	+= pasemi/
 obj-$(CONFIG_PPC_CELL)		+= cell/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
 obj-$(CONFIG_EMBEDDED6xx)	+= embedded6xx/
+obj-$(CONFIG_AMIGAONE)		+= amigaone/
diff --git a/arch/powerpc/platforms/amigaone/Kconfig b/arch/powerpc/platforms/amigaone/Kconfig
new file mode 100644
index 0000000..9276a96
--- /dev/null
+++ b/arch/powerpc/platforms/amigaone/Kconfig
@@ -0,0 +1,18 @@ 
+config AMIGAONE
+	bool "Eyetech AmigaOne/MAI Teron"
+	depends on PPC32 && BROKEN_ON_SMP && PPC_MULTIPLATFORM
+	select PPC_I8259
+	select PPC_INDIRECT_PCI
+	select PPC_UDBG_16550
+	select PCI
+	select NOT_COHERENT_CACHE
+	select CHECK_CACHE_COHERENCY
+	select DEFAULT_UIMAGE
+	select PCSPKR_PLATFORM
+	help
+	Select AmigaOne for the following machines:
+	- AmigaOne SE/Teron CX (G3 only)
+	- AmigaOne XE/Teron PX
+	- uA1/Teron mini
+	  More information is available at:
+	  <http://amigaone-linux.sourceforge.net/>.
diff --git a/arch/powerpc/platforms/amigaone/Makefile b/arch/powerpc/platforms/amigaone/Makefile
new file mode 100644
index 0000000..e6885b3
--- /dev/null
+++ b/arch/powerpc/platforms/amigaone/Makefile
@@ -0,0 +1 @@ 
+obj-y	+= setup.o
diff --git a/arch/powerpc/platforms/amigaone/setup.c b/arch/powerpc/platforms/amigaone/setup.c
new file mode 100644
index 0000000..30c9b46
--- /dev/null
+++ b/arch/powerpc/platforms/amigaone/setup.c
@@ -0,0 +1,197 @@ 
+/*
+ * AmigaOne platform setup
+ *
+ * Copyright 2008 Gerhard Pircher (gerhard_pircher@gmx.net)
+ *
+ *   Based on original amigaone_setup.c source code
+ * Copyright 2003 by Hans-Joerg Frieden and Thomas Frieden
+ *
+ * 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/delay.h>
+#include <linux/seq_file.h>
+#include <linux/utsrelease.h>
+
+#include <asm/machdep.h>
+#include <asm/cputable.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/i8259.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+
+void amigaone_show_cpuinfo(struct seq_file *m)
+{
+	struct device_node *root;
+	const char *model = "";
+
+	root = of_find_node_by_path("/");
+	if (root)
+		model = of_get_property(root, "model", NULL);
+	seq_printf(m, "machine\t\t: %s\n", model);
+
+	of_node_put(root);
+	return;
+}
+
+static int __init amigaone_add_bridge(struct device_node *dev)
+{
+	int len;
+	struct pci_controller *hose;
+	const int *bus_range;
+
+	printk(KERN_INFO "Adding PCI host bridge %s\n", dev->full_name);
+
+	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 %s, assume"
+		       " bus 0\n", dev->full_name);
+
+	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, 0xfec00cf8, 0xfee00cfc, 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);
+
+	return 0;
+}
+
+void __init amigaone_setup_arch(void)
+{
+	struct device_node *np;
+
+	/* Initialization until calibrate_delay() runs. */
+	loops_per_jiffy = 50000000/HZ;
+
+	/* Lookup PCI host bridges. */
+	for_each_compatible_node(np, "pci", "mai-logic,articia-s")
+		amigaone_add_bridge(np);
+
+	if (ppc_md.progress)
+		ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0);
+}
+
+void __init amigaone_init_IRQ(void)
+{
+	struct device_node *pic, *np = NULL;
+	const unsigned long *prop = NULL;
+	unsigned long int_ack = 0;
+
+	/* Search for ISA interrupt controller. */
+	pic = of_find_compatible_node(NULL, "interrupt-controller",
+	                              "pnpPNP,000");
+	BUG_ON(pic == NULL);
+
+	/* Look for interrupt acknowledge address in the PCI root node. */
+	np = of_find_compatible_node(NULL, "pci", "mai-logic,articia-s");
+	if (np) {
+		prop = of_get_property(np, "8259-interrupt-acknowledge", NULL);
+		if (prop)
+			int_ack = prop[0];
+		of_node_put(np);
+	}
+
+	if (int_ack == 0)
+		printk(KERN_WARNING "Cannot find PCI interrupt acknowledge"
+		       " address, polling\n");
+
+	i8259_init(pic, int_ack);
+	ppc_md.get_irq = i8259_irq;
+	irq_set_default_host(i8259_get_host());
+}
+
+void __init amigaone_init(void)
+{
+	request_region(0x00, 0x20, "dma1");
+	request_region(0x40, 0x20, "timer");
+	request_region(0x80, 0x10, "dma page reg");
+	request_region(0xc0, 0x20, "dma2");
+}
+
+/* Copied from U-Boot. */
+static inline void amigaone_soft_restart(unsigned long addr)
+{
+	/* SRR0 has system reset vector, SRR1 has default MSR value.
+	 * rfi restores MSR from SRR1 and sets the PC to the SRR0 value.
+	 */
+	__asm__ __volatile__ ("mtspr	26, %0"		:: "r" (addr));
+	__asm__ __volatile__ ("li	4, (1 << 6)"	::: "r4");
+	__asm__ __volatile__ ("mtspr	27, 4");
+	__asm__ __volatile__ ("rfi");
+}
+
+void amigaone_restart(char *cmd)
+{
+	unsigned long addr;
+
+	local_irq_disable();
+
+	/* Flush and disable I/D cache. */
+	__asm__ __volatile__ ("mfspr	3, 1008"	::: "r3");
+	__asm__ __volatile__ ("ori	5, 5, 0xcc00"	::: "r5");
+	__asm__ __volatile__ ("ori	4, 3, 0xc00"	::: "r4");
+	__asm__ __volatile__ ("andc	5, 3, 5"	::: "r5");
+	__asm__ __volatile__ ("sync");
+	__asm__ __volatile__ ("mtspr	1008, 4");
+	__asm__ __volatile__ ("isync");
+	__asm__ __volatile__ ("sync");
+	__asm__ __volatile__ ("mtspr	1008, 5");
+	__asm__ __volatile__ ("isync");
+	__asm__ __volatile__ ("sync");
+
+	addr = 0xfff00100;
+	amigaone_soft_restart(addr);
+
+	/* Not reached. */
+	while (1);
+}
+
+static int __init amigaone_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+
+	if (of_flat_dt_is_compatible(root, "eyetech,amigaone")) {
+		/* Coherent memory access cause complete system lockup! Thus
+		 * disable this CPU feature, even if the CPU needs it. The L2
+		 * cache prefetch engines sould be disabled later on.
+		 */
+		cur_cpu_spec->cpu_features &= ~CPU_FTR_NEED_COHERENT;
+
+		/* Patch out unwanted feature. */
+		do_feature_fixups(cur_cpu_spec->cpu_features,
+		                  PTRRELOC(&__start___ftr_fixup),
+		                  PTRRELOC(&__stop___ftr_fixup));
+
+		ISA_DMA_THRESHOLD = 0x00ffffff;
+		DMA_MODE_READ = 0x44;
+		DMA_MODE_WRITE = 0x48;
+
+		return 1;
+	}
+
+	return 0;
+}
+
+define_machine(amigaone) {
+	.name			= "AmigaOne",
+	.probe			= amigaone_probe,
+	.setup_arch		= amigaone_setup_arch,
+	.init			= amigaone_init,
+	.show_cpuinfo		= amigaone_show_cpuinfo,
+	.init_IRQ		= amigaone_init_IRQ,
+	.restart		= amigaone_restart,
+	.calibrate_decr		= generic_calibrate_decr,
+	.progress		= udbg_progress,
+};