diff options
author | David Howells <dhowells@redhat.com> | 2008-02-08 04:19:31 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 09:22:30 -0800 |
commit | b920de1b77b72ca9432ac3f97edb26541e65e5dd (patch) | |
tree | 40fa9be1470e929c47927dea7eddf184c0204229 /arch/mn10300/unit-asb2305 | |
parent | ef3d534754f31fed9c3b976fee1ece1b3bc38282 (diff) | |
download | op-kernel-dev-b920de1b77b72ca9432ac3f97edb26541e65e5dd.zip op-kernel-dev-b920de1b77b72ca9432ac3f97edb26541e65e5dd.tar.gz |
mn10300: add the MN10300/AM33 architecture to the kernel
Add architecture support for the MN10300/AM33 CPUs produced by MEI to the
kernel.
This patch also adds board support for the ASB2303 with the ASB2308 daughter
board, and the ASB2305. The only processor supported is the MN103E010, which
is an AM33v2 core plus on-chip devices.
[akpm@linux-foundation.org: nuke cvs control strings]
Signed-off-by: Masakazu Urade <urade.masakazu@jp.panasonic.com>
Signed-off-by: Koichi Yasutake <yasutake.koichi@jp.panasonic.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/mn10300/unit-asb2305')
-rw-r--r-- | arch/mn10300/unit-asb2305/Makefile | 8 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/leds.c | 124 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/pci-asb2305.c | 303 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/pci-asb2305.h | 82 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/pci-iomap.c | 31 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/pci-irq.c | 51 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/pci.c | 545 | ||||
-rw-r--r-- | arch/mn10300/unit-asb2305/unit-init.c | 61 |
8 files changed, 1205 insertions, 0 deletions
diff --git a/arch/mn10300/unit-asb2305/Makefile b/arch/mn10300/unit-asb2305/Makefile new file mode 100644 index 0000000..0551022 --- /dev/null +++ b/arch/mn10300/unit-asb2305/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for the ASB2305 board +# +############################################################################### +obj-y := unit-init.o leds.o + +obj-$(CONFIG_PCI) += pci.o pci-asb2305.o pci-irq.o pci-iomap.o diff --git a/arch/mn10300/unit-asb2305/leds.c b/arch/mn10300/unit-asb2305/leds.c new file mode 100644 index 0000000..e99dcc9 --- /dev/null +++ b/arch/mn10300/unit-asb2305/leds.c @@ -0,0 +1,124 @@ +/* ASB2305 Peripheral 7-segment LEDs x4 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/cpu/intctl-regs.h> +#include <asm/cpu/rtc-regs.h> +#include <asm/unit/leds.h> + +static const u8 asb2305_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; + +static const u32 asb2305_led_chase_tbl[6] = { + ~0x02020202, /* top - segA */ + ~0x04040404, /* right top - segB */ + ~0x08080808, /* right bottom - segC */ + ~0x10101010, /* bottom - segD */ + ~0x20202020, /* left bottom - segE */ + ~0x40404040, /* left top - segF */ +}; + +static unsigned asb2305_led_chase; + +void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds_display_exception(enum exception_code code) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(code/0x100) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(code/0x10) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[code % 0x10]; + leds |= 0x6d010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_minssecs(unsigned int time, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(time/600) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/60) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/10) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[time % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_rtc(void) +{ + unsigned int clock; + u8 mins, secs; + + mins = RTMCR; + secs = RTSCR; + + clock = ((mins & 0xf0) >> 4); + clock *= 10; + clock += (mins & 0x0f); + clock *= 6; + + clock += ((secs & 0xf0) >> 4); + clock *= 10; + clock += (secs & 0x0f); + + peripheral_leds7x4_display_minssecs(clock, 0); +} + +void peripheral_leds_led_chase(void) +{ + ASB2305_7SEGLEDS = asb2305_led_chase_tbl[asb2305_led_chase]; + asb2305_led_chase++; + if (asb2305_led_chase >= 6) + asb2305_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c new file mode 100644 index 0000000..d100ca7 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -0,0 +1,303 @@ +/* ASB2305 PCI resource stuff + * + * Copyright (C) 2001 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/pci-i386.c + * - Copyright 1997--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include "pci-asb2305.h" + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) +{ +#if 0 + struct pci_dev *dev = data; + + printk(KERN_DEBUG + "### PCIBIOS_ALIGN_RESOURCE(%s,,{%08lx-%08lx,%08lx},%lx)\n", + pci_name(dev), + res->start, + res->end, + res->flags, + size + ); +#endif + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + list_for_each_entry(bus, bus_list, node) { + dev = bus->self; + if (dev) { + for (idx = PCI_BRIDGE_RESOURCES; + idx < PCI_NUM_RESOURCES; + idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + pr = pci_find_parent_resource(dev, r); + if (!r->start || + !pr || + request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of bridge %s\n", + idx, pci_name(dev)); + /* Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. */ + r->flags = 0; + } + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + for_each_pci_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI[%s]: Resource %08lx-%08lx" + " (f=%lx, d=%d, p=%d)\n", + pci_name(dev), r->start, r->end, r->flags, + disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of device %s\n", + idx, pci_name(dev)); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, + * but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", + pci_name(dev)); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword( + dev, dev->rom_base_reg, ®); + pci_write_config_dword( + dev, dev->rom_base_reg, + reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static int __init pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + struct resource *r, *pr; + + if (!(pci_probe & PCI_ASSIGN_ROMS)) { + /* Try to use BIOS settings for ROMs, otherwise let + pci_assign_unassigned_resources() allocate the new + addresses. */ + for_each_pci_dev(dev) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (!r->flags || !r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + r->end -= r->start; + r->start = 0; + } + } + } + + pci_assign_unassigned_resources(); + + return 0; +} + +fs_initcall(pcibios_assign_resources); + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); +} + +int pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + + for (idx = 0; idx < 6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1 << idx))) + continue; + + r = &dev->resource[idx]; + + if (!r->start && r->end) { + printk(KERN_ERR + "PCI: Device %s not available because of" + " resource collisions\n", + pci_name(dev)); + return -EINVAL; + } + + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + + if (cmd != old_cmd) + pci_write_config_word(dev, PCI_COMMAND, cmd); + + return 0; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= VM_LOCKED | VM_IO; + + prot = pgprot_val(vma->vm_page_prot); + prot &= ~_PAGE_CACHE; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored */ + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h new file mode 100644 index 0000000..84634fa --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.h @@ -0,0 +1,82 @@ +/* ASB2305 Arch-specific PCI declarations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-i386.h: (c) 1999 Martin Mares <mj@ucw.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _PCI_ASB2305_H +#define _PCI_ASB2305_H + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 +#define PCI_NO_SORT 0x100 +#define PCI_BIOS_SORT 0x200 +#define PCI_NO_CHECKS 0x400 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 + +extern unsigned int pci_probe; + +/* pci-asb2305.c */ + +extern unsigned int pcibios_max_latency; + +extern void pcibios_resource_survey(void); +extern int pcibios_enable_resources(struct pci_dev *dev, int mask); + +/* pci.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +extern struct irq_routing_table *pcibios_get_irq_routing_table(void); +extern int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, + * 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +extern void pcibios_irq_init(void); +extern void pcibios_fixup_irqs(void); +extern void pcibios_enable_irq(struct pci_dev *dev); + +#endif /* PCI_ASB2305_H */ diff --git a/arch/mn10300/unit-asb2305/pci-iomap.c b/arch/mn10300/unit-asb2305/pci-iomap.c new file mode 100644 index 0000000..dbceae4 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-iomap.c @@ -0,0 +1,31 @@ +/* ASB2305 PCI I/O mapping handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/pci.h> +#include <linux/module.h> + +/* + * Create a virtual mapping cookie for a PCI BAR (memory or IO) + */ +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len || !start) + return NULL; + + if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) + return (void __iomem *) start; + + return NULL; +} +EXPORT_SYMBOL(pci_iomap); diff --git a/arch/mn10300/unit-asb2305/pci-irq.c b/arch/mn10300/unit-asb2305/pci-irq.c new file mode 100644 index 0000000..58cfb44 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-irq.c @@ -0,0 +1,51 @@ +/* PCI IRQ routing on the MN103E010 based ASB2305 + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * This is simple: All PCI interrupts route through the CPU's XIRQ1 pin [IRQ 35] + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <asm/io.h> +#include <asm/smp.h> +#include "pci-asb2305.h" + +void __init pcibios_irq_init(void) +{ +} + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev = NULL; + u8 line, pin; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + dev->irq = XIRQ1; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, + dev->irq); + } + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line); + } +} + +void __init pcibios_penalize_isa_irq(int irq) +{ +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); +} diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c new file mode 100644 index 0000000..1a86425 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci.c @@ -0,0 +1,545 @@ +/* ASB2305 PCI support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from arch/i386/kernel/pci-pc.c + * (c) 1999--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <asm/io.h> +#include "pci-asb2305.h" + +unsigned int pci_probe = 1; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus; +struct pci_ops *pci_root_ops; + +/* + * Functions for accessing PCI configuration space + */ + +#define CONFIG_CMD(bus, devfn, where) \ + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) + +#define MEM_PAGING_REG (*(volatile __u32 *) 0xBFFFFFF4) +#define CONFIG_ADDRESS (*(volatile __u32 *) 0xBFFFFFF8) +#define CONFIG_DATAL(X) (*(volatile __u32 *) 0xBFFFFFFC) +#define CONFIG_DATAW(X) (*(volatile __u16 *) (0xBFFFFFFC + ((X) & 2))) +#define CONFIG_DATAB(X) (*(volatile __u8 *) (0xBFFFFFFC + ((X) & 3))) + +#define BRIDGEREGB(X) (*(volatile __u8 *) (0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *) (0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *) (0xBE040000 + (X))) + +static inline int __query(const struct pci_bus *bus, unsigned int devfn) +{ +#if 0 + return bus->number == 0 && (devfn == PCI_DEVFN(0, 0)); + return bus->number == 1; + return bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(3, 0)); +#endif + return 1; +} + +/* + * translate Linuxcentric addresses to PCI bus addresses + */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + if (res->flags & IORESOURCE_IO) { + region->start = (res->start & 0x00ffffff); + region->end = (res->end & 0x00ffffff); + } + + if (res->flags & IORESOURCE_MEM) { + region->start = (res->start & 0x03ffffff) | MEM_PAGING_REG; + region->end = (res->end & 0x03ffffff) | MEM_PAGING_REG; + } + +#if 0 + printk(KERN_DEBUG "RES->BUS: %lx-%lx => %lx-%lx\n", + res->start, res->end, region->start, region->end); +#endif +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +/* + * translate PCI bus addresses to Linuxcentric addresses + */ +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + if (res->flags & IORESOURCE_IO) { + res->start = (region->start & 0x00ffffff) | 0xbe000000; + res->end = (region->end & 0x00ffffff) | 0xbe000000; + } + + if (res->flags & IORESOURCE_MEM) { + res->start = (region->start & 0x03ffffff) | 0xb8000000; + res->end = (region->end & 0x03ffffff) | 0xb8000000; + } + +#if 0 + printk(KERN_INFO "BUS->RES: %lx-%lx => %lx-%lx\n", + region->start, region->end, res->start, res->end); +#endif +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +/* + * + */ +static int pci_ampci_read_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGB(where); + __pcbdebug("=> %02hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAB(where); + if (__query(bus, devfn)) + __pcidebug("=> %02hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGW(where); + __pcbdebug("=> %04hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAW(where); + if (__query(bus, devfn)) + __pcidebug("=> %04hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGL(where); + __pcbdebug("=> %08x", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAL(where); + if (__query(bus, devfn)) + __pcidebug("=> %08x", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u8 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %02x", &BRIDGEREGB(where), value); + BRIDGEREGB(where) = value; + } else { + if (bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) && devfn == PCI_DEVFN(3, 0)) + ) + __pcidebug("<= %02x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAB(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u16 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %04hx", &BRIDGEREGW(where), value); + BRIDGEREGW(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %04hx", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAW(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %08x", &BRIDGEREGL(where), value); + BRIDGEREGL(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %08x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAL(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + switch (size) { + case 1: + return pci_ampci_read_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_read_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_read_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static int pci_ampci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) { + case 1: + return pci_ampci_write_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_write_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_write_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static struct pci_ops pci_direct_ampci = { + pci_ampci_read_config, + pci_ampci_write_config, +}; + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __init pci_sanity_check(struct pci_ops *o) +{ + struct pci_bus bus; /* Fake bus and device */ + u32 x; + + bus.number = 0; + + if ((!o->read(&bus, 0, PCI_CLASS_DEVICE, 2, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read(&bus, 0, PCI_VENDOR_ID, 2, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + + printk(KERN_ERROR "PCI: Sanity check failed\n"); + return 0; +} + +static int __init pci_check_direct(void) +{ + unsigned long flags; + + local_irq_save(flags); + + /* + * Check if access works. + */ + if (pci_sanity_check(&pci_direct_ampci)) { + local_irq_restore(flags); + printk(KERN_INFO "PCI: Using configuration ampci\n"); + request_mem_region(0xBE040000, 256, "AMPCI bridge"); + request_mem_region(0xBFFFFFF4, 12, "PCI ampci"); + return 0; + } + + local_irq_restore(flags); + return -ENODEV; +} + +static int __devinit is_valid_resource(struct pci_dev *dev, int idx) +{ + unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; + struct resource *devr = &dev->resource[idx]; + + if (dev->bus) { + for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { + struct resource *busr = dev->bus->resource[i]; + + if (!busr || (busr->flags ^ devr->flags) & type_mask) + continue; + + if (devr->start && + devr->start >= busr->start && + devr->end <= busr->end) + return 1; + } + } + + return 0; +} + +static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev) +{ + struct pci_bus_region region; + int i; + int limit; + + if (dev->bus->number != 0) + return; + + limit = (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) ? + PCI_BRIDGE_RESOURCES : PCI_NUM_RESOURCES; + + for (i = 0; i < limit; i++) { + if (!dev->resource[i].flags) + continue; + + region.start = dev->resource[i].start; + region.end = dev->resource[i].end; + pcibios_bus_to_resource(dev, &dev->resource[i], ®ion); + if (is_valid_resource(dev, i)) + pci_claim_resource(dev, i); + } +} + +/* + * Called after each bus is probed, but before its children + * are examined. + */ +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev; + + if (bus->self) { + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(bus->self); + } + + list_for_each_entry(dev, &bus->devices, bus_list) + pcibios_fixup_device_resources(dev); +} + +/* + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away some day. + */ +static int __init pcibios_init(void) +{ + ioport_resource.start = 0xA0000000; + ioport_resource.end = 0xDFFFFFFF; + iomem_resource.start = 0xA0000000; + iomem_resource.end = 0xDFFFFFFF; + + if (!pci_probe) + return 0; + + if (pci_check_direct() < 0) { + printk(KERN_WARNING "PCI: No PCI bus detected\n"); + return 0; + } + + printk(KERN_INFO "PCI: Probing PCI hardware [mempage %08x]\n", + MEM_PAGING_REG); + + { +#if 0 + static struct pci_bus am33_root_bus = { + .children = LIST_HEAD_INIT(am33_root_bus.children), + .devices = LIST_HEAD_INIT(am33_root_bus.devices), + .number = 0, + .secondary = 0, + .resource = { &ioport_resource, &iomem_resource }, + }; + + am33_root_bus.ops = pci_root_ops; + list_add_tail(&am33_root_bus.node, &pci_root_buses); + + am33_root_bus.subordinate = pci_do_scan_bus(0); + + pci_root_bus = &am33_root_bus; +#else + pci_root_bus = pci_scan_bus(0, &pci_direct_ampci, NULL); +#endif + } + + pcibios_irq_init(); + pcibios_fixup_irqs(); +#if 0 + pcibios_resource_survey(); +#endif + return 0; +} + +arch_initcall(pcibios_init); + +char *__init pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + + return str; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + + err = pcibios_enable_resources(dev, mask); + if (err == 0) + pcibios_enable_irq(dev); + return err; +} + +/* + * disable the ethernet chipset + */ +static void __init unit_disable_pcnet(struct pci_bus *bus, struct pci_ops *o) +{ + u32 x; + + bus->number = 0; + + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, x); + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + o->write(bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, 0x00030001); + o->read (bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, &x); + +#define RDP (*(volatile u32 *) 0xBE030010) +#define RAP (*(volatile u32 *) 0xBE030014) +#define __set_RAP(X) do { RAP = (X); x = RAP; } while (0) +#define __set_RDP(X) do { RDP = (X); x = RDP; } while (0) +#define __get_RDP() ({ RDP & 0xffff; }) + + __set_RAP(0); + __set_RDP(0x0004); /* CSR0 = STOP */ + + __set_RAP(88); /* check CSR88 indicates an Am79C973 */ + BUG_ON(__get_RDP() != 0x5003); + + for (x = 0; x < 100; x++) + asm volatile("nop"); + + __set_RDP(0x0004); /* CSR0 = STOP */ +} + +/* + * initialise the unit hardware + */ +asmlinkage void __init unit_pci_init(void) +{ + struct pci_bus bus; /* Fake bus and device */ + struct pci_ops *o = &pci_direct_ampci; + u32 x; + + set_intr_level(XIRQ1, GxICR_LEVEL_3); + + memset(&bus, 0, sizeof(bus)); + + MEM_PAGING_REG = 0xE8000000; + + /* we need to set up the bridge _now_ or we won't be able to access the + * PCI config registers + */ + BRIDGEREGW(PCI_COMMAND) |= + PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_MASTER; + BRIDGEREGW(PCI_STATUS) = 0xF800; + BRIDGEREGB(PCI_LATENCY_TIMER) = 0x10; + BRIDGEREGL(PCI_BASE_ADDRESS_0) = 0x80000000; + BRIDGEREGB(PCI_INTERRUPT_LINE) = 1; + BRIDGEREGL(0x48) = 0x98000000; /* AMPCI base addr */ + BRIDGEREGB(0x41) = 0x00; /* secondary bus + * number */ + BRIDGEREGB(0x42) = 0x01; /* subordinate bus + * number */ + BRIDGEREGB(0x44) = 0x01; + BRIDGEREGL(0x50) = 0x00000001; + BRIDGEREGL(0x58) = 0x00001002; + BRIDGEREGL(0x5C) = 0x00000011; + + /* we also need to set up the PCI-PCI bridge */ + bus.number = 0; + + /* IO: 0x00000000-0x00020000 */ + o->read (&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, x); + + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, 0x01); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, 0x00020000); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, 0xEBB0EA00); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, 0xE9F0E800); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + unit_disable_pcnet(&bus, o); +} diff --git a/arch/mn10300/unit-asb2305/unit-init.c b/arch/mn10300/unit-asb2305/unit-init.c new file mode 100644 index 0000000..6a35241 --- /dev/null +++ b/arch/mn10300/unit-asb2305/unit-init.c @@ -0,0 +1,61 @@ +/* ASB2305 Initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/cpu/intctl-regs.h> +#include <asm/cpu/rtc-regs.h> +#include <asm/cpu/serial-regs.h> +#include <asm/unit/serial.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + /* set the 16550 interrupt line to level 3 if not being used for GDB */ + set_intr_level(XIRQ0, GxICR_LEVEL_3); +#endif +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +#ifdef CONFIG_PCI + unit_pci_init(); +#endif +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + set_irq_handler(XIRQ2IRQ(extnum), handle_level_irq); + break; + default: + break; + } + } +} |